diff --git a/packages/core/src/application/config/applicationConfig.controller.js b/packages/core/src/application/config/applicationConfig.controller.js index 14fb1cc0bde..4c9d3c2edb2 100644 --- a/packages/core/src/application/config/applicationConfig.controller.js +++ b/packages/core/src/application/config/applicationConfig.controller.js @@ -8,6 +8,7 @@ import { CORE_APPLICATION_CONFIG_APPLICATIONSNAPSHOTSECTION_COMPONENT } from './ import { CHAOS_MONKEY_CONFIG_COMPONENT } from '../../chaosMonkey/chaosMonkeyConfig.component'; import { SETTINGS } from '../../config/settings'; import { APPLICATION_DATA_SOURCE_EDITOR } from './dataSources/applicationDataSourceEditor.component'; +import { DEFAULT_TAG_FILTER_CONFIG } from './defaultTagFilter/defaultTagFilterConfig.component'; import { DELETE_APPLICATION_SECTION } from './deleteApplicationSection.module'; import { CORE_APPLICATION_CONFIG_LINKS_APPLICATIONLINKS_COMPONENT } from './links/applicationLinks.component'; import { ApplicationWriter } from '../service/ApplicationWriter'; @@ -25,6 +26,7 @@ module(CORE_APPLICATION_CONFIG_APPLICATIONCONFIG_CONTROLLER, [ CHAOS_MONKEY_CONFIG_COMPONENT, TRAFFIC_GUARD_CONFIG_COMPONENT, CORE_APPLICATION_CONFIG_LINKS_APPLICATIONLINKS_COMPONENT, + DEFAULT_TAG_FILTER_CONFIG, ]).controller('ApplicationConfigController', [ '$state', 'app', @@ -63,6 +65,30 @@ module(CORE_APPLICATION_CONFIG_APPLICATIONCONFIG_CONTROLLER, [ }); }; + this.defaultTagFilterProps = { + isSaving: false, + saveError: false, + }; + this.updateDefaultTagFilterConfigs = (tagConfigs /* IDefaultTagFilterConfig[] */) => { + const applicationAttributes = cloneDeep(this.application.attributes); + applicationAttributes.defaultFilteredTags = tagConfigs; + $scope.$applyAsync(() => { + this.defaultTagFilterProps.isSaving = true; + this.defaultTagFilterProps.saveError = false; + }); + ApplicationWriter.updateApplication(applicationAttributes) + .then(() => { + $scope.$applyAsync(() => { + this.defaultTagFilterProps.isSaving = false; + this.application.attributes = applicationAttributes; + }); + }) + .catch(() => { + this.defaultTagFilterProps.isSaving = false; + this.defaultTagFilterProps.saveError = true; + }); + }; + this.notifications = []; this.updateNotifications = (notifications) => { $scope.$applyAsync(() => { diff --git a/packages/core/src/application/config/applicationConfig.view.html b/packages/core/src/application/config/applicationConfig.view.html index 8e268432508..ca2912ce2e7 100644 --- a/packages/core/src/application/config/applicationConfig.view.html +++ b/packages/core/src/application/config/applicationConfig.view.html @@ -54,6 +54,15 @@ > + + + + diff --git a/packages/core/src/application/config/defaultTagFilter/DefaultTagFilterConfig.spec.tsx b/packages/core/src/application/config/defaultTagFilter/DefaultTagFilterConfig.spec.tsx new file mode 100644 index 00000000000..981d2affea6 --- /dev/null +++ b/packages/core/src/application/config/defaultTagFilter/DefaultTagFilterConfig.spec.tsx @@ -0,0 +1,75 @@ +import { shallow } from 'enzyme'; +import React from 'react'; + +import type { IDefaultTagFilterConfig } from './DefaultTagFilterConfig'; +import { DefaultTagFilterConfig } from './DefaultTagFilterConfig'; +import { noop } from '../../../utils'; + +describe('', () => { + let tagConfigs: IDefaultTagFilterConfig[]; + let wrapper: any; + + beforeEach(() => { + tagConfigs = getTestDefaultFilterTagConfigs(); + wrapper = shallow( + , + ); + }); + + describe('view', () => { + it('renders a row for each banner config', () => { + expect(wrapper.find('.default-filter-config-row').length).toEqual(tagConfigs.length); + }); + it('renders an "add" button', () => { + expect(wrapper.find('.add-new').length).toEqual(1); + }); + }); + + describe('functionality', () => { + it('update default tag filter config', () => { + expect(wrapper.state('defaultTagFilterConfigsEditing')).toEqual(tagConfigs); + wrapper + .find('textarea') + .at(1) + .simulate('change', { target: { value: 'hello' } }); + const updatedConfigs = [ + { + ...tagConfigs[0], + tagValue: 'hello', + }, + { + ...tagConfigs[1], + }, + ]; + expect(wrapper.state('defaultTagFilterConfigsEditing')).toEqual(updatedConfigs); + }); + it('add default filter tag config', () => { + expect(wrapper.state('defaultTagFilterConfigsEditing').length).toEqual(2); + wrapper.find('.add-new').simulate('click'); + expect(wrapper.state('defaultTagFilterConfigsEditing').length).toEqual(3); + }); + it('remove default filter tag config', () => { + expect(wrapper.state('defaultTagFilterConfigsEditing').length).toEqual(2); + wrapper.find('.default-filter-config-remove').at(1).simulate('click'); + expect(wrapper.state('defaultTagFilterConfigsEditing').length).toEqual(1); + }); + }); +}); + +export function getTestDefaultFilterTagConfigs(): IDefaultTagFilterConfig[] { + return [ + { + tagName: 'Pipeline Type', + tagValue: 'Deployment Pipelines', + }, + { + tagName: 'Pipeline Type', + tagValue: 'Repair Pipelines', + }, + ]; +} diff --git a/packages/core/src/application/config/defaultTagFilter/DefaultTagFilterConfig.tsx b/packages/core/src/application/config/defaultTagFilter/DefaultTagFilterConfig.tsx new file mode 100644 index 00000000000..8984ab50427 --- /dev/null +++ b/packages/core/src/application/config/defaultTagFilter/DefaultTagFilterConfig.tsx @@ -0,0 +1,161 @@ +import { isEqual } from 'lodash'; +import React from 'react'; + +import { ConfigSectionFooter } from '../footer/ConfigSectionFooter'; +import { noop } from '../../../utils'; + +import './defaultTagFilterConfig.less'; + +export interface IDefaultTagFilterConfig { + tagName: string; + tagValue: string; +} + +export interface IDefaultTagFilterProps { + defaultTagFilterConfigs: IDefaultTagFilterConfig[]; + isSaving: boolean; + saveError: boolean; + updateDefaultTagFilterConfigs: (defaultTagFilterConfigs: IDefaultTagFilterConfig[]) => void; +} + +export interface IDefaultTagFilterState { + defaultTagFilterConfigsEditing: IDefaultTagFilterConfig[]; +} + +export class DefaultTagFilterConfig extends React.Component { + public static defaultProps: Partial = { + defaultTagFilterConfigs: [], + isSaving: false, + saveError: false, + updateDefaultTagFilterConfigs: noop, + }; + + constructor(props: IDefaultTagFilterProps) { + super(props); + this.state = { + defaultTagFilterConfigsEditing: props.defaultTagFilterConfigs, + }; + } + + private onTagNameChange = (idx: number, text: string) => { + this.setState({ + defaultTagFilterConfigsEditing: this.state.defaultTagFilterConfigsEditing.map((config, i) => { + if (i === idx) { + return { + ...config, + tagName: text, + }; + } + return config; + }), + }); + }; + + private onTagValueChange = (idx: number, text: string) => { + this.setState({ + defaultTagFilterConfigsEditing: this.state.defaultTagFilterConfigsEditing.map((config, i) => { + if (i === idx) { + return { + ...config, + tagValue: text, + }; + } + return config; + }), + }); + }; + + private addFilterTag = (): void => { + this.setState({ + defaultTagFilterConfigsEditing: this.state.defaultTagFilterConfigsEditing.concat([ + { + tagName: 'Name of the tag (E.g. Pipeline Type)', + tagValue: 'Value of the tag (E.g. Default Pipelines)', + } as IDefaultTagFilterConfig, + ]), + }); + }; + + private removeFilterTag = (idx: number): void => { + this.setState({ + defaultTagFilterConfigsEditing: this.state.defaultTagFilterConfigsEditing.filter((_config, i) => i !== idx), + }); + }; + + private isDirty = (): boolean => { + return !isEqual(this.props.defaultTagFilterConfigs, this.state.defaultTagFilterConfigsEditing); + }; + + private onRevertClicked = (): void => { + this.setState({ + defaultTagFilterConfigsEditing: this.props.defaultTagFilterConfigs, + }); + }; + + private onSaveClicked = (): void => { + this.props.updateDefaultTagFilterConfigs(this.state.defaultTagFilterConfigsEditing); + }; + + public render() { + return ( +
+
+ Default Tag filters allow you to specify which tags are immediately filtered to when the pipeline execution + page is loaded in. +
+
+ + + + + + + + + {this.state.defaultTagFilterConfigsEditing.map((defaultTagFilter, idx) => ( + +
Tag NameTag Value
+