diff --git a/app/javascript/packages/form-steps/form-steps.spec.tsx b/app/javascript/packages/form-steps/form-steps.spec.tsx index 6877d2c6d2e..cf883d4eac1 100644 --- a/app/javascript/packages/form-steps/form-steps.spec.tsx +++ b/app/javascript/packages/form-steps/form-steps.spec.tsx @@ -355,12 +355,6 @@ describe('FormSteps', () => { expect(window.location.hash).to.equal('#second'); }); - it('resets hash in URL if there is no matching step', () => { - window.location.hash = '#example'; - render(); - expect(window.location.hash).to.equal(''); - }); - it('syncs step by history events', async () => { const { getByText, findByText, getByLabelText } = render(); diff --git a/app/javascript/packages/form-steps/form-steps.tsx b/app/javascript/packages/form-steps/form-steps.tsx index d0c96c7e0e8..f454b303388 100644 --- a/app/javascript/packages/form-steps/form-steps.tsx +++ b/app/javascript/packages/form-steps/form-steps.tsx @@ -231,10 +231,11 @@ function FormSteps({ promptOnNavigate = true, titleFormat, }: FormStepsProps) { + const stepNames = steps.map((step) => step.name); const [values, setValues] = useState(initialValues); const [activeErrors, setActiveErrors] = useState(initialActiveErrors); const formRef = useRef(null as HTMLFormElement | null); - const [stepName, setStepName] = useHistoryParam(initialStep); + const [stepName, setStepName] = useHistoryParam(initialStep, stepNames); const [stepErrors, setStepErrors] = useState([] as Error[]); const [isSubmitting, setIsSubmitting] = useState(false); const [stepCanComplete, setStepCanComplete] = useState(undefined); diff --git a/app/javascript/packages/form-steps/use-history-param.spec.tsx b/app/javascript/packages/form-steps/use-history-param.spec.tsx index 94c83bd7061..cf9be56e18d 100644 --- a/app/javascript/packages/form-steps/use-history-param.spec.tsx +++ b/app/javascript/packages/form-steps/use-history-param.spec.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import { render, act } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import userEvent from '@testing-library/user-event'; import useHistoryParam, { getStepParam } from './use-history-param'; @@ -13,8 +13,14 @@ describe('getStepParam', () => { }); describe('useHistoryParam', () => { - function TestComponent({ initialValue }: { initialValue?: string }) { - const [count = 0, setCount] = useHistoryParam(initialValue); + function TestComponent({ + initialValue, + validValues, + }: { + initialValue?: string; + validValues?: string[]; + }) { + const [count = 0, setCount] = useHistoryParam(initialValue, validValues); return ( <> @@ -119,4 +125,41 @@ describe('useHistoryParam', () => { const [path2] = inst2.result.current; expect(path2).to.equal('root'); }); + + context('when specifying valid values', () => { + it('syncs by history events for a valid value', async () => { + const { getByText, getByDisplayValue, findByDisplayValue } = render( + , + ); + expect(getByDisplayValue('0')).to.be.ok(); + + await userEvent.click(getByText('Increment')); + + expect(getByDisplayValue('1')).to.be.ok(); + expect(window.location.hash).to.equal('#1'); + + act(() => { + window.history.back(); + }); + + expect(await findByDisplayValue('0')).to.be.ok(); + expect(window.location.hash).to.equal(''); + }); + + it('maintains value (does not sync) by history events for an invalid value', async () => { + const { getByDisplayValue } = render(); + expect(getByDisplayValue('0')).to.be.ok(); + const popstateHandled = new Promise((resolve) => + window.addEventListener('popstate', resolve, { once: true }), + ); + + act(() => { + window.location.hash = '#wrong'; + }); + + await popstateHandled; + expect(getByDisplayValue('0')).to.be.ok(); + expect(window.location.hash).to.equal('#wrong'); + }); + }); }); diff --git a/app/javascript/packages/form-steps/use-history-param.ts b/app/javascript/packages/form-steps/use-history-param.ts index 1839840924d..7f91b3609ff 100644 --- a/app/javascript/packages/form-steps/use-history-param.ts +++ b/app/javascript/packages/form-steps/use-history-param.ts @@ -29,13 +29,17 @@ const subscribers: Array<() => void> = []; */ function useHistoryParam( initialValue?: string, + validValues?: string[], ): [string | undefined, (nextParamValue: ParamValue) => void] { - function getCurrentValue(): ParamValue { + function getCurrentValue(currentValue?: string): ParamValue { const path = window.location.hash.slice(1); if (path) { - return getStepParam(path); + const value = getStepParam(path); + return !validValues || validValues.includes(value) ? value : currentValue; } + + return initialValue; } const [value, setValue] = useState(initialValue ?? getCurrentValue); diff --git a/app/javascript/packs/application.ts b/app/javascript/packs/application.ts index 6cd8cb725d5..ad2ce264d1c 100644 --- a/app/javascript/packs/application.ts +++ b/app/javascript/packs/application.ts @@ -2,8 +2,3 @@ import { accordion, banner, skipnav } from '@18f/identity-design-system'; const components = [accordion, banner, skipnav]; components.forEach((component) => component.on()); -const mainContent = document.getElementById('main-content'); -document.querySelector('.usa-skipnav')?.addEventListener('click', (event) => { - event.preventDefault(); - mainContent?.scrollIntoView(); -});