diff --git a/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/ConfigTaskMapInput.js b/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/ConfigTaskMapInput.js index 0f978bab7..a1adc6949 100644 --- a/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/ConfigTaskMapInput.js +++ b/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/ConfigTaskMapInput.js @@ -30,6 +30,8 @@ const useStyles = makeStyles(theme => ({ export type Props = {| configField: string, + // In the event the metadata is a different path than the configField. + metadataField?: string, label?: string, customKeyLabel?: string, buttonText?: string, @@ -37,7 +39,14 @@ export type Props = {| |}; export default function ConfigTaskMapInput(props: Props) { - const {configField, label, buttonText, onChange, customKeyLabel} = props; + const { + configField, + metadataField, + label, + buttonText, + onChange, + customKeyLabel, + } = props; const classes = useStyles(); const {configData, configMetadata, onUpdate} = useConfigTaskContext(); const [configValue, setConfigValue] = React.useState<{ @@ -49,7 +58,7 @@ export default function ConfigTaskMapInput(props: Props) { configField, ]); - const metadata = configFieldArray.reduce( + const metadata = (metadataField?.split('.') ?? configFieldArray).reduce( (result, key) => (result ? result[key] : result), configMetadata, ); diff --git a/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/configTasks/QoSInterfaceConfig.js b/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/configTasks/QoSInterfaceConfig.js index 7db7229dc..c8733b55a 100644 --- a/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/configTasks/QoSInterfaceConfig.js +++ b/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/configTasks/QoSInterfaceConfig.js @@ -6,14 +6,12 @@ */ import * as React from 'react'; -import ConfigOptionSelector from '../ConfigOptionSelector'; -import ConfigTaskInput from '../ConfigTaskInput'; +import ConfigTaskGroup from '../ConfigTaskGroup'; import ConfigTaskMapInput from '../ConfigTaskMapInput'; -import useForm from '@fbcnms/tg-nms/app/hooks/useForm'; import {useConfigTaskContext} from '@fbcnms/tg-nms/app/contexts/ConfigTaskContext'; -const SIMPLE_TC = ['0', '1', '2', '3']; const POLICING_CONFIG_FIELD = 'cpeParams.policers'; +const METADATA_FIELD = 'cpeConfig.mapVal.objVal.properties.policers'; export default function QoSInterfaceConfig({ cpeInterface, @@ -22,7 +20,6 @@ export default function QoSInterfaceConfig({ }) { const {onUpdate} = useConfigTaskContext(); const onUpdateRef = React.useRef(onUpdate); - const handlePolicingConfigChange = React.useCallback( change => { const configField = `${POLICING_CONFIG_FIELD}.${cpeInterface}`; @@ -30,56 +27,17 @@ export default function QoSInterfaceConfig({ }, [onUpdateRef, cpeInterface], ); - - const {updateFormState} = useForm({ - initialState: {cir: 0, eir: 0}, - onFormUpdated: state => { - const formattedChange = SIMPLE_TC.reduce((res, tc) => { - res[tc] = state; - return res; - }, {}); - handlePolicingConfigChange(formattedChange); - }, - }); - - const handleSimpleChange = (key: string, value) => { - updateFormState({[key]: value}); - }; - return ( - - handleSimpleChange('cir', Number(val))} - /> - handleSimpleChange('eir', Number(val))} - /> - - ), - }, - custom: { - name: 'Custom', - description: 'Configure custom CIR and EIR for each Traffic Class', - configGroup: ( - - ), - }, - }} - /> + description={'Configure custom CIR and EIR for each Traffic Class'}> + + ); } diff --git a/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/configTasks/__tests__/QoSInterfaceConfig-test.js b/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/configTasks/__tests__/QoSInterfaceConfig-test.js index 6de135c0e..66593a057 100644 --- a/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/configTasks/__tests__/QoSInterfaceConfig-test.js +++ b/tgnms/fbcnms-projects/tgnms/app/components/taskBasedConfig/configTasks/__tests__/QoSInterfaceConfig-test.js @@ -11,45 +11,82 @@ import {TestApp} from '@fbcnms/tg-nms/app/tests/testHelpers'; import {fireEvent, render} from '@testing-library/react'; import {mockConfigTaskContextValue} from '@fbcnms/tg-nms/app/tests/data/NetworkConfig'; +const onUpdateMock = jest.fn(); jest .spyOn( require('@fbcnms/tg-nms/app/contexts/ConfigTaskContext'), 'useConfigTaskContext', ) - .mockReturnValue(mockConfigTaskContextValue()); + .mockReturnValue( + mockConfigTaskContextValue({ + onUpdate: onUpdateMock, + configData: [ + { + field: ['cpeParams', 'policers', 'test_cpe', '0', 'cir'], + hasOverride: false, + hasTopLevelOverride: false, + layers: [{id: 'Base value', value: 100}], + metadata: {action: 'RESTART_SQUIRE', desc: 'test', type: 'STRING'}, + }, + ], + configMetadata: { + cpeConfig: { + mapVal: { + objVal: { + properties: { + policers: { + mapVal: {objVal: {properties: {cir: {type: 'INT'}}}}, + }, + }, + }, + }, + }, + }, + }), + ); const defaultProps = { - cpeInterface: 'test', + cpeInterface: 'test_cpe', }; -test('renders', async () => { - const {getByText} = render( - - - , - ); - expect(getByText('Policing Classification for test')).toBeInTheDocument(); +beforeEach(() => { + onUpdateMock.mockReset(); }); -test('default renders on simple format', async () => { - const {getByText} = render( +test('modifications trigger an update', async () => { + const {getByText, getByDisplayValue} = render( , ); + expect(getByText('Policing Classification for test_cpe')).toBeInTheDocument(); expect( - getByText(/All Traffic Classes will have the same CIR/i), + getByText(/Configure custom CIR and EIR for each Traffic Class/i), ).toBeInTheDocument(); + + const input = getByDisplayValue(100); + fireEvent.change(input, {target: {value: 200}}); + expect(onUpdateMock).toHaveBeenCalledWith({ + configField: 'cpeParams.policers.test_cpe', + draftValue: {'0': {cir: 200}}, + }); }); -test('clicking Custom switches to custom format', async () => { +test('deletion triggers an update', async () => { const {getByText} = render( , ); - fireEvent.click(getByText('Custom')); + expect(getByText('Policing Classification for test_cpe')).toBeInTheDocument(); expect( getByText(/Configure custom CIR and EIR for each Traffic Class/i), ).toBeInTheDocument(); + + const btn = getByText('Delete'); + fireEvent.click(btn); + expect(onUpdateMock).toHaveBeenCalledWith({ + configField: 'cpeParams.policers.test_cpe', + draftValue: {}, + }); }); diff --git a/tgnms/fbcnms-projects/tgnms/app/helpers/ConfigHelpers.js b/tgnms/fbcnms-projects/tgnms/app/helpers/ConfigHelpers.js index f006be445..d21b7b2c9 100644 --- a/tgnms/fbcnms-projects/tgnms/app/helpers/ConfigHelpers.js +++ b/tgnms/fbcnms-projects/tgnms/app/helpers/ConfigHelpers.js @@ -600,14 +600,12 @@ export function getDraftConfig({ if (draftConfigValue === '' || draftConfigValue === null) { unset(currentDraftConfig, configField.split('.')); } else if (typeof draftConfigValue === 'object') { - Object.keys(draftConfigValue).forEach(key => { - setWith( - currentDraftConfig, - [...configField.split('.'), key], - draftConfigValue[key], - Object, - ); - }); + setWith( + currentDraftConfig, + configField.split('.'), + draftConfigValue, + Object, + ); } else { set(currentDraftConfig, configField.split('.'), draftConfigValue); }