diff --git a/app/controllers/verify_controller.rb b/app/controllers/verify_controller.rb
index b60c4f9968a..24f5dc44cc3 100644
--- a/app/controllers/verify_controller.rb
+++ b/app/controllers/verify_controller.rb
@@ -11,6 +11,7 @@ def show
def app_data
{
+ base_path: idv_app_root_path,
initial_values: { 'personalKey' => '0000-0000-0000-0000' },
}
end
diff --git a/app/javascript/packages/form-steps/form-steps.spec.tsx b/app/javascript/packages/form-steps/form-steps.spec.tsx
index bc3466eed0d..e683351077a 100644
--- a/app/javascript/packages/form-steps/form-steps.spec.tsx
+++ b/app/javascript/packages/form-steps/form-steps.spec.tsx
@@ -18,7 +18,11 @@ interface StepValues {
}
describe('FormSteps', () => {
- const { spy } = sinon.createSandbox();
+ const sandbox = sinon.createSandbox();
+
+ afterEach(() => {
+ sandbox.restore();
+ });
const STEPS = [
{
@@ -237,7 +241,7 @@ describe('FormSteps', () => {
userEvent.click(getByText('forms.buttons.continue'));
- expect(window.location.hash).to.equal('#step=second');
+ expect(window.location.hash).to.equal('#second');
});
it('syncs step by history events', async () => {
@@ -257,22 +261,7 @@ describe('FormSteps', () => {
expect(await findByText('Second Title')).to.be.ok();
expect((getByLabelText('Second Input One') as HTMLInputElement).value).to.equal('one');
expect((getByLabelText('Second Input Two') as HTMLInputElement).value).to.equal('two');
- expect(window.location.hash).to.equal('#step=second');
- });
-
- it('clear URL parameter after submission', async () => {
- const onComplete = sinon.spy();
- const { getByText, getByLabelText } = render(
- ,
- );
-
- userEvent.click(getByText('forms.buttons.continue'));
- await userEvent.type(getByLabelText('Second Input One'), 'one');
- await userEvent.type(getByLabelText('Second Input Two'), 'two');
- userEvent.click(getByText('forms.buttons.continue'));
- userEvent.click(getByText('forms.buttons.submit.default'));
- await waitFor(() => expect(onComplete.calledOnce).to.be.true());
- expect(window.location.hash).to.equal('');
+ expect(window.location.hash).to.equal('#second');
});
it('shifts focus to next heading on step change', () => {
@@ -289,14 +278,6 @@ describe('FormSteps', () => {
expect(document.activeElement).to.equal(originalActiveElement);
});
- it('resets to first step at mount', () => {
- window.location.hash = '#step=last';
-
- render();
-
- expect(window.location.hash).to.equal('');
- });
-
it('optionally auto-focuses', () => {
const { getByText } = render();
@@ -320,7 +301,7 @@ describe('FormSteps', () => {
userEvent.click(getByText('forms.buttons.continue'));
userEvent.click(getByText('forms.buttons.continue'));
- expect(window.location.hash).to.equal('#step=second');
+ expect(window.location.hash).to.equal('#second');
expect(document.activeElement).to.equal(getByLabelText('Second Input One'));
expect(container.querySelectorAll('[data-is-error]')).to.have.lengthOf(2);
@@ -432,11 +413,11 @@ describe('FormSteps', () => {
});
userEvent.click(getByRole('button', { name: 'forms.buttons.continue' }));
- expect(window.location.hash).to.equal('#step=second');
+ expect(window.location.hash).to.equal('#second');
// Trigger validation errors on second step.
userEvent.click(getByRole('button', { name: 'forms.buttons.continue' }));
- expect(window.location.hash).to.equal('#step=second');
+ expect(window.location.hash).to.equal('#second');
expect(JSON.parse(getByTestId('context-value').textContent!)).to.deep.equal({
isLastStep: false,
});
@@ -445,7 +426,7 @@ describe('FormSteps', () => {
userEvent.type(getByLabelText('Second Input Two'), 'two');
userEvent.click(getByRole('button', { name: 'forms.buttons.continue' }));
- expect(window.location.hash).to.equal('#step=last');
+ expect(window.location.hash).to.equal('#last');
expect(JSON.parse(getByTestId('context-value').textContent!)).to.deep.equal({
isLastStep: true,
});
@@ -472,7 +453,7 @@ describe('FormSteps', () => {
window.scrollY = 100;
userEvent.click(getByRole('button', { name: 'Replace' }));
- spy(window.history, 'pushState');
+ sandbox.spy(window.history, 'pushState');
expect(window.scrollY).to.equal(0);
expect(document.activeElement).to.equal(getByRole('heading', { name: 'Content Title' }));
diff --git a/app/javascript/packages/form-steps/form-steps.tsx b/app/javascript/packages/form-steps/form-steps.tsx
index de5d15b1420..a456cc12aa9 100644
--- a/app/javascript/packages/form-steps/form-steps.tsx
+++ b/app/javascript/packages/form-steps/form-steps.tsx
@@ -131,6 +131,12 @@ interface FormStepsProps {
* Defaults to true.
*/
promptOnNavigate?: boolean;
+
+ /**
+ * When using path fragments for maintaining history, the base path to which the current step name
+ * is appended.
+ */
+ basePath?: string;
}
/**
@@ -142,8 +148,8 @@ interface FormStepsProps {
*
* @return Step index.
*/
-export function getStepIndexByName(steps: FormStep[], name: string) {
- return steps.findIndex((step) => step.name === name);
+export function getStepIndexByName(steps: FormStep[], name?: string) {
+ return name ? steps.findIndex((step) => step.name === name) : -1;
}
/**
@@ -171,11 +177,12 @@ function FormSteps({
initialActiveErrors = [],
autoFocus,
promptOnNavigate = true,
+ basePath,
}: FormStepsProps) {
const [values, setValues] = useState(initialValues);
const [activeErrors, setActiveErrors] = useState(initialActiveErrors);
const formRef = useRef(null as HTMLFormElement | null);
- const [stepName, setStepName] = useHistoryParam('step', null);
+ const [stepName, setStepName] = useHistoryParam({ basePath });
const [stepErrors, setStepErrors] = useState([] as Error[]);
const fields = useRef({} as Record);
const didSubmitWithErrors = useRef(false);
@@ -271,8 +278,6 @@ function FormSteps({
const nextStepIndex = stepIndex + 1;
const isComplete = nextStepIndex === steps.length;
if (isComplete) {
- // Clear step parameter from URL.
- setStepName(null);
onComplete(values);
} else {
const { name: nextStepName } = steps[nextStepIndex];
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 b31c40a68ea..99130db0e3e 100644
--- a/app/javascript/packages/form-steps/use-history-param.spec.tsx
+++ b/app/javascript/packages/form-steps/use-history-param.spec.tsx
@@ -1,32 +1,15 @@
+import sinon from 'sinon';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import useHistoryParam, { getQueryParam } from './use-history-param';
-
-describe('getQueryParam', () => {
- const queryString = 'a&b=Hello%20world&c';
-
- it('returns null does not exist', () => {
- const value = getQueryParam(queryString, 'd');
-
- expect(value).to.be.null();
- });
-
- it('returns decoded value of parameter', () => {
- const value = getQueryParam(queryString, 'b');
-
- expect(value).to.equal('Hello world');
- });
-
- it('defaults to empty string for empty value', () => {
- const value = getQueryParam(queryString, 'c');
-
- expect(value).to.equal('');
- });
-});
+import { useDefineProperty } from '@18f/identity-test-helpers';
+import useHistoryParam from './use-history-param';
describe('useHistoryParam', () => {
- function TestComponent({ initialValue }: { initialValue?: string | null }) {
- const [count = 0, setCount] = useHistoryParam('the count', initialValue);
+ const sandbox = sinon.createSandbox();
+ const defineProperty = useDefineProperty();
+
+ function TestComponent({ basePath }: { basePath?: string }) {
+ const [count = 0, setCount] = useHistoryParam({ basePath });
return (
<>
@@ -35,7 +18,7 @@ describe('useHistoryParam', () => {
-