diff --git a/app/javascript/packages/verify-flow/verify-flow-step-indicator.spec.tsx b/app/javascript/packages/verify-flow/verify-flow-step-indicator.spec.tsx
new file mode 100644
index 00000000000..7c2086a2257
--- /dev/null
+++ b/app/javascript/packages/verify-flow/verify-flow-step-indicator.spec.tsx
@@ -0,0 +1,35 @@
+import { render } from '@testing-library/react';
+import { StepStatus } from '@18f/identity-step-indicator';
+import VerifyFlowStepIndicator, { getStepStatus } from './verify-flow-step-indicator';
+
+describe('getStepStatus', () => {
+ it('returns incomplete if step is after current step', () => {
+ const result = getStepStatus(1, 0);
+
+ expect(result).to.equal(StepStatus.INCOMPLETE);
+ });
+
+ it('returns current if step is current step', () => {
+ const result = getStepStatus(1, 1);
+
+ expect(result).to.equal(StepStatus.CURRENT);
+ });
+
+ it('returns complete if step is before current step', () => {
+ const result = getStepStatus(0, 1);
+
+ expect(result).to.equal(StepStatus.COMPLETE);
+ });
+});
+
+describe('VerifyFlowStepIndicator', () => {
+ it('renders step indicator for the current step', () => {
+ const { getByText } = render();
+
+ const current = getByText('step_indicator.flows.idv.secure_account');
+ expect(current.closest('.step-indicator__step--current')).to.exist();
+
+ const previous = getByText('step_indicator.flows.idv.verify_phone_or_address');
+ expect(previous.closest('.step-indicator__step--complete')).to.exist();
+ });
+});
diff --git a/app/javascript/packages/verify-flow/verify-flow-step-indicator.tsx b/app/javascript/packages/verify-flow/verify-flow-step-indicator.tsx
new file mode 100644
index 00000000000..8b134f51db5
--- /dev/null
+++ b/app/javascript/packages/verify-flow/verify-flow-step-indicator.tsx
@@ -0,0 +1,80 @@
+import { StepIndicator, StepIndicatorStep, StepStatus } from '@18f/identity-step-indicator';
+import { t } from '@18f/identity-i18n';
+
+// i18n-tasks-use t('step_indicator.flows.idv.getting_started')
+// i18n-tasks-use t('step_indicator.flows.idv.verify_id')
+// i18n-tasks-use t('step_indicator.flows.idv.verify_info')
+// i18n-tasks-use t('step_indicator.flows.idv.verify_phone_or_address')
+// i18n-tasks-use t('step_indicator.flows.idv.secure_account')
+
+type VerifyFlowStepIndicatorStep =
+ | 'getting_started'
+ | 'verify_id'
+ | 'verify_info'
+ | 'verify_phone_or_address'
+ | 'secure_account';
+
+/**
+ * Mapping of flow form steps to corresponding step indicator step.
+ */
+const FLOW_STEP_STEP_MAPPING: Record = {
+ personal_key: 'secure_account',
+ personal_key_confirm: 'secure_account',
+};
+
+/**
+ * Sequence of step indicator steps.
+ */
+const STEP_INDICATOR_STEPS: VerifyFlowStepIndicatorStep[] = [
+ 'getting_started',
+ 'verify_id',
+ 'verify_info',
+ 'verify_phone_or_address',
+ 'secure_account',
+];
+
+interface VerifyFlowStepIndicatorProps {
+ /**
+ * Current step name.
+ */
+ currentStep: string;
+}
+
+/**
+ * Given an index of a step and the current step index, returns the status of the step relative to
+ * the current step.
+ *
+ * @param index Index of step against which to compare current step.
+ * @param currentStepIndex Index of current step.
+ *
+ * @return Step status.
+ */
+export function getStepStatus(index, currentStepIndex): StepStatus {
+ if (index === currentStepIndex) {
+ return StepStatus.CURRENT;
+ }
+
+ if (index < currentStepIndex) {
+ return StepStatus.COMPLETE;
+ }
+
+ return StepStatus.INCOMPLETE;
+}
+
+function VerifyFlowStepIndicator({ currentStep }: VerifyFlowStepIndicatorProps) {
+ const currentStepIndex = STEP_INDICATOR_STEPS.indexOf(FLOW_STEP_STEP_MAPPING[currentStep]);
+
+ return (
+
+ {STEP_INDICATOR_STEPS.map((step, index) => (
+
+ ))}
+
+ );
+}
+
+export default VerifyFlowStepIndicator;
diff --git a/app/javascript/packages/verify-flow/verify-flow.tsx b/app/javascript/packages/verify-flow/verify-flow.tsx
index 6fce4686195..54623ebdde7 100644
--- a/app/javascript/packages/verify-flow/verify-flow.tsx
+++ b/app/javascript/packages/verify-flow/verify-flow.tsx
@@ -1,9 +1,9 @@
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { FormSteps } from '@18f/identity-form-steps';
-import { StepIndicator, StepIndicatorStep, StepStatus } from '@18f/identity-step-indicator';
import { t } from '@18f/identity-i18n';
import { Alert } from '@18f/identity-components';
import { trackEvent } from '@18f/identity-analytics';
+import VerifyFlowStepIndicator from './verify-flow-step-indicator';
import { STEPS } from './steps';
export interface VerifyFlowValues {
@@ -67,9 +67,11 @@ export function VerifyFlow({
appName,
onComplete,
}: VerifyFlowProps) {
+ const [currentStep, setCurrentStep] = useState(STEPS[0].name);
+
useEffect(() => {
- logStepVisited(STEPS[0].name);
- }, []);
+ logStepVisited(currentStep);
+ }, [currentStep]);
let steps = STEPS;
if (enabledStepNames) {
@@ -78,13 +80,7 @@ export function VerifyFlow({
return (
<>
-
-
-
-
-
-
-
+
{t('idv.messages.confirm')}
@@ -95,7 +91,7 @@ export function VerifyFlow({
basePath={basePath}
titleFormat={`%{step} - ${appName}`}
onStepSubmit={logStepSubmitted}
- onStepChange={logStepVisited}
+ onStepChange={setCurrentStep}
onComplete={onComplete}
/>
>