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();
-});