From 7d2c3cb5561c184c96144a0744a0fdc22de7d1d6 Mon Sep 17 00:00:00 2001 From: Brandon Lenz Date: Tue, 16 Feb 2021 13:00:00 -0500 Subject: [PATCH 1/3] Update uswds version to 2.9.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 33e6a38df9..d120759272 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "peerDependencies": { "react": "^16.x || ^17.x", "react-dom": "^16.x || ^17.x", - "uswds": "2.8.0" + "uswds": "2.9.0" }, "devDependencies": { "@babel/core": "^7.10.5", From 4cec2657d53cce8eb6fbac37e0151b0c3f01473e Mon Sep 17 00:00:00 2001 From: Brandon Lenz Date: Thu, 18 Feb 2021 14:05:37 -0500 Subject: [PATCH 2/3] Update uswds version to 2.9.0 --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index d120759272..4b7c913684 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "ts-jest": "^26.1.2", "typescript": "^3.8.0", "url-loader": "^4.0.0", - "uswds": "2.8.0", + "uswds": "2.9.0", "webpack": "^4.41.0", "webpack-cli": "^4.0.0" }, diff --git a/yarn.lock b/yarn.lock index 7af96f2d62..953083c377 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6146,10 +6146,10 @@ electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.634: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.645.tgz#c0b269ae2ecece5aedc02dd4586397d8096affb1" integrity sha512-T7mYop3aDpRHIQaUYcmzmh6j9MAe560n6ukqjJMbVC6bVTau7dSpvB18bcsBPPtOSe10cKxhJFtlbEzLa0LL1g== -elem-dataset@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/elem-dataset/-/elem-dataset-1.1.1.tgz#18f07fa7fc71ebd49b0f9f63819cb03c8276577a" - integrity sha1-GPB/p/xx69SbD59jgZywPIJ2V3o= +elem-dataset@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/elem-dataset/-/elem-dataset-2.0.0.tgz#4ed8b2b0217898bdf78c1a01b4eb722a1c89e799" + integrity sha512-e7gieGopWw5dMdEgythH3lUS7nMizutPDTtkzfQW/q2gCvFnACyNnK3ytCncAXKxdBXQWcXeKaYTTODiMnp8mw== element-closest@^2.0.1: version "2.0.2" @@ -14435,15 +14435,15 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -uswds@2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/uswds/-/uswds-2.8.0.tgz#635e829555a359562f28ab51e1b6c983f149f739" - integrity sha512-LI7ZNbh823ehtThvebwQ5eHNKGY791vcT5d3T9gJ+RY511HgstWLRSEEso7YO/ifjoV/FwuTAtDwQqt7zsXRvA== +uswds@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/uswds/-/uswds-2.9.0.tgz#002089d74582960099b0ee836f89f043cdf6d0bc" + integrity sha512-5IMVgMCUUlgWVFrB7Wf1qTvv5L3oDDlSsAgvJ02+/sV2uh4JzO9YPm3493RTaMuHTc+feRCuYyEIloXsEQY0Pg== dependencies: classlist-polyfill "^1.0.3" del "^5.1.0" domready "^1.0.8" - elem-dataset "^1.1.1" + elem-dataset "^2.0.0" lodash.debounce "^4.0.7" object-assign "^4.1.1" receptor "^1.0.0" From 5968c8de0a7097fa61c66b8b74aa71375db7e760 Mon Sep 17 00:00:00 2001 From: Brandon Lenz Date: Fri, 5 Mar 2021 18:07:16 -0500 Subject: [PATCH 3/3] feat: New Component: Step indicator (#952) --- .../StepIndicator/StepIndicator.stories.tsx | 66 ++++++++++ .../StepIndicator/StepIndicator.test.tsx | 113 ++++++++++++++++++ .../StepIndicator/StepIndicator.tsx | 74 ++++++++++++ .../StepIndicatorStep.test.tsx | 43 +++++++ .../StepIndicatorStep/StepIndicatorStep.tsx | 40 +++++++ src/index.ts | 6 +- 6 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 src/components/stepindicator/StepIndicator/StepIndicator.stories.tsx create mode 100644 src/components/stepindicator/StepIndicator/StepIndicator.test.tsx create mode 100644 src/components/stepindicator/StepIndicator/StepIndicator.tsx create mode 100644 src/components/stepindicator/StepIndicatorStep/StepIndicatorStep.test.tsx create mode 100644 src/components/stepindicator/StepIndicatorStep/StepIndicatorStep.tsx diff --git a/src/components/stepindicator/StepIndicator/StepIndicator.stories.tsx b/src/components/stepindicator/StepIndicator/StepIndicator.stories.tsx new file mode 100644 index 0000000000..49495f6412 --- /dev/null +++ b/src/components/stepindicator/StepIndicator/StepIndicator.stories.tsx @@ -0,0 +1,66 @@ +import React from 'react' +import { StepIndicatorStep } from '../StepIndicatorStep/StepIndicatorStep' +import { StepIndicator } from '../StepIndicator/StepIndicator' + +export default { + title: 'Components/Step Indicator', + component: StepIndicator, + parameters: { + info: ` + ### USWDS 2.0 Step Indicator component + Source: https://designsystem.digital.gov/components/step-indicator/ + + Updates users on their progress through a multi-step process. + `, + }, +} + +export const defaultStepIndicator = (): React.ReactElement => ( + + + + + + + +) + +export const noLabels = (): React.ReactElement => ( + + + + + + + +) + +export const centered = (): React.ReactElement => ( + + + + + + + +) + +export const counters = (): React.ReactElement => ( + + + + + + + +) + +export const smallCounters = (): React.ReactElement => ( + + + + + + + +) diff --git a/src/components/stepindicator/StepIndicator/StepIndicator.test.tsx b/src/components/stepindicator/StepIndicator/StepIndicator.test.tsx new file mode 100644 index 0000000000..38e7394c58 --- /dev/null +++ b/src/components/stepindicator/StepIndicator/StepIndicator.test.tsx @@ -0,0 +1,113 @@ +import React from 'react' +import { render } from '@testing-library/react' +import { StepIndicatorStep } from '../StepIndicatorStep/StepIndicatorStep' +import { StepIndicator } from '../StepIndicator/StepIndicator' + +const step1 = 'Step 1' +const step2 = 'Step 2' +const step3 = 'Step 3' + +describe('StepIndicator component', () => { + it('renders without errors', () => { + const { getByRole, queryByText, queryAllByText, queryByTestId } = render( + + + + + + ) + + const stepIndicator = queryByTestId('step-indicator') + + expect(stepIndicator).toBeInTheDocument() + expect(stepIndicator).toHaveClass('usa-step-indicator') + expect(queryByText(step1)).toBeInTheDocument() + expect(queryAllByText(step2)).toHaveLength(2) + expect(queryByText(step3)).toBeInTheDocument() + expect(getByRole('list')).toHaveClass('usa-step-indicator__segments') + }) + + it('renders properly with no labels', () => { + const { getByRole, queryByText, queryAllByText, queryByTestId } = render( + + + + + + ) + + const stepIndicator = queryByTestId('step-indicator') + + expect(stepIndicator).toBeInTheDocument() + expect(stepIndicator).toHaveClass( + 'usa-step-indicator usa-step-indicator--no-labels' + ) + expect(queryByText(step1)).toBeInTheDocument() + expect(queryAllByText(step2)).toHaveLength(2) + expect(queryByText(step3)).toBeInTheDocument() + expect(getByRole('list')).toHaveClass('usa-step-indicator__segments') + }) + + it('renders properly with counters', () => { + const { getByRole, queryByText, queryAllByText, queryByTestId } = render( + + + + + + ) + + const stepIndicator = queryByTestId('step-indicator') + + expect(stepIndicator).toBeInTheDocument() + expect(stepIndicator).toHaveClass( + 'usa-step-indicator usa-step-indicator--counters' + ) + expect(queryByText(step1)).toBeInTheDocument() + expect(queryAllByText(step2)).toHaveLength(2) + expect(queryByText(step3)).toBeInTheDocument() + expect(getByRole('list')).toHaveClass('usa-step-indicator__segments') + }) + + it('renders properly with small counters', () => { + const { getByRole, queryByText, queryAllByText, queryByTestId } = render( + + + + + + ) + + const stepIndicator = queryByTestId('step-indicator') + + expect(stepIndicator).toBeInTheDocument() + expect(stepIndicator).toHaveClass( + 'usa-step-indicator usa-step-indicator--counters-sm' + ) + expect(queryByText(step1)).toBeInTheDocument() + expect(queryAllByText(step2)).toHaveLength(2) + expect(queryByText(step3)).toBeInTheDocument() + expect(getByRole('list')).toHaveClass('usa-step-indicator__segments') + }) + + it('renders properly with centered labels', () => { + const { getByRole, queryByText, queryAllByText, queryByTestId } = render( + + + + + + ) + + const stepIndicator = queryByTestId('step-indicator') + + expect(stepIndicator).toBeInTheDocument() + expect(stepIndicator).toHaveClass( + 'usa-step-indicator usa-step-indicator--center' + ) + expect(queryByText(step1)).toBeInTheDocument() + expect(queryAllByText(step2)).toHaveLength(2) + expect(queryByText(step3)).toBeInTheDocument() + expect(getByRole('list')).toHaveClass('usa-step-indicator__segments') + }) +}) diff --git a/src/components/stepindicator/StepIndicator/StepIndicator.tsx b/src/components/stepindicator/StepIndicator/StepIndicator.tsx new file mode 100644 index 0000000000..d783cfcf38 --- /dev/null +++ b/src/components/stepindicator/StepIndicator/StepIndicator.tsx @@ -0,0 +1,74 @@ +import React from 'react' +import classnames from 'classnames' +import { StepIndicatorStepProps } from '../StepIndicatorStep/StepIndicatorStep' + +interface StepIndicatorProps { + showLabels?: boolean + counters?: 'none' | 'default' | 'small' + centered?: boolean + children: React.ReactElement[] + className?: string + divProps?: JSX.IntrinsicElements['div'] + listProps?: JSX.IntrinsicElements['ol'] +} +export const StepIndicator = ( + props: StepIndicatorProps +): React.ReactElement => { + const { + showLabels = true, + counters = 'none', + centered = false, + children, + className, + divProps, + listProps, + } = props + + const classes = classnames( + 'usa-step-indicator', + { + 'usa-step-indicator--no-labels': !showLabels, + 'usa-step-indicator--counters': counters === 'default', + 'usa-step-indicator--counters-sm': counters === 'small', + 'usa-step-indicator--center': centered, + }, + className + ) + + const findCurrentStepIndex = (): number => { + const i = children.findIndex((step) => step.props.status === 'current') + return i === -1 ? 0 : i + } + const currentStepIndex = findCurrentStepIndex() + const currentStepNumber = currentStepIndex + 1 + const currentStepLabel = children[parseInt(`${currentStepIndex}`)].props.label + const totalNumberOfSteps = children.length + + return ( +
+
    + {children} +
+
+

+ + Step + + {currentStepNumber} + +   + {`of ${totalNumberOfSteps}`} +   + + + {currentStepLabel} + +

+
+
+ ) +} diff --git a/src/components/stepindicator/StepIndicatorStep/StepIndicatorStep.test.tsx b/src/components/stepindicator/StepIndicatorStep/StepIndicatorStep.test.tsx new file mode 100644 index 0000000000..66a28f81a2 --- /dev/null +++ b/src/components/stepindicator/StepIndicatorStep/StepIndicatorStep.test.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import { render } from '@testing-library/react' +import { StepIndicatorStep } from './StepIndicatorStep' + +describe('Step component', () => { + it('renders without errors', () => { + const { getByRole, queryByText } = render( + + ) + expect(queryByText('Test Step')).toBeInTheDocument() + expect(getByRole('listitem')).toHaveClass('usa-step-indicator__segment') + }) + + it('renders with incomplete status', () => { + const { getByRole, queryByText } = render( + + ) + expect(queryByText('Test Step')).toBeInTheDocument() + expect(queryByText('not completed')).toHaveClass('usa-sr-only') + expect(getByRole('listitem')).toHaveClass('usa-step-indicator__segment') + }) + + it('renders with current status', () => { + const { getByRole, queryByText } = render( + + ) + expect(queryByText('Test Step')).toBeInTheDocument() + expect(getByRole('listitem')).toHaveClass( + 'usa-step-indicator__segment usa-step-indicator__segment--current' + ) + }) + + it('renders with complete status', () => { + const { getByRole, queryByText } = render( + + ) + expect(queryByText('Test Step')).toBeInTheDocument() + expect(queryByText('completed')).toHaveClass('usa-sr-only') + expect(getByRole('listitem')).toHaveClass( + 'usa-step-indicator__segment usa-step-indicator__segment--complete' + ) + }) +}) diff --git a/src/components/stepindicator/StepIndicatorStep/StepIndicatorStep.tsx b/src/components/stepindicator/StepIndicatorStep/StepIndicatorStep.tsx new file mode 100644 index 0000000000..8f29df0c46 --- /dev/null +++ b/src/components/stepindicator/StepIndicatorStep/StepIndicatorStep.tsx @@ -0,0 +1,40 @@ +import classnames from 'classnames' +import React from 'react' + +export interface StepIndicatorStepProps { + label: string + status?: 'complete' | 'current' | 'incomplete' + className?: string +} + +export const StepIndicatorStep = ( + props: StepIndicatorStepProps & JSX.IntrinsicElements['li'] +): React.ReactElement => { + const { label, status = 'incomplete', className, ...liProps } = props + + const classes = classnames( + 'usa-step-indicator__segment', + { + 'usa-step-indicator__segment--complete': status === 'complete', + 'usa-step-indicator__segment--current': status === 'current', + }, + className + ) + + return ( +
  • + + {label} +   + {status !== 'current' && ( + + {status === 'complete' ? 'completed' : 'not completed'} + + )} + +
  • + ) +} diff --git a/src/index.ts b/src/index.ts index e124f36f77..2623df173a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -56,7 +56,7 @@ export { FooterNav } from './components/Footer/FooterNav/FooterNav' export { Logo } from './components/Footer/Logo/Logo' export { SocialLinks } from './components/Footer/SocialLinks/SocialLinks' -/** Card Components */ +/** Card components */ export { CardGroup } from './components/card/CardGroup/CardGroup' export { Card } from './components/card/Card/Card' export { CardHeader } from './components/card/CardHeader/CardHeader' @@ -69,6 +69,10 @@ export { BreadcrumbBar } from './components/breadcrumb/BreadcrumbBar/BreadcrumbB export { Breadcrumb } from './components/breadcrumb/Breadcrumb/Breadcrumb' export { BreadcrumbLink } from './components/breadcrumb/BreadcrumbLink/BreadcrumbLink' +/** StepIndicator components */ +export { StepIndicator } from './components/stepindicator/StepIndicator/StepIndicator' +export { StepIndicatorStep } from './components/stepindicator/StepIndicatorStep/StepIndicatorStep' + export { Search } from './components/Search/Search' /** Truss-designed components */