-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(progress-steps): adding new ProgressSteps component package (#3272)
* feat(progress-steps): add new package * chore: pr fixes * chore: pr fixes and improvements * chore: a11y fixes * chore: pr fixes * chore: add aria-current=step and i18n story * chore: update title to completed * test(progress-steps): fix test issues and add test for aria-current * chore: lint fix * chore: design pass fixes
- Loading branch information
Showing
29 changed files
with
1,158 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@twilio-paste/core': minor | ||
'@twilio-paste/progress-steps': major | ||
--- | ||
|
||
[ProgressSteps] Add new ProgressSteps package. Progress steps show a user a clear path to complete a complex multi step task. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@twilio-paste/codemods': patch | ||
--- | ||
|
||
[Codemods] add new ProgressSteps package |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 152 additions & 0 deletions
152
packages/paste-core/components/progress-steps/__tests__/index.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import * as React from 'react'; | ||
import {render} from '@testing-library/react'; | ||
import {CustomizationProvider} from '@twilio-paste/customization'; | ||
|
||
import { | ||
ProgressSteps, | ||
ProgressStepSeparator, | ||
ProgressStepComplete, | ||
ProgressStepCurrent, | ||
ProgressStepError, | ||
ProgressStepIncomplete, | ||
} from '../src'; | ||
import {Divs, Buttons, Anchors} from '../stories/horizontal.stories'; | ||
|
||
describe('ProgressSteps', () => { | ||
it('should render divs correctly', () => { | ||
const {queryAllByText, getAllByRole} = render(<Divs />); | ||
const [completeIcon, completeLabel] = queryAllByText('Completed'); | ||
const [incompleteIcon, incompleteLabel] = queryAllByText('Incomplete'); | ||
const [errorIcon, errorLabel] = queryAllByText('Error'); | ||
const [currentIcon, currentLabel] = queryAllByText('Current'); | ||
|
||
expect(completeIcon).toBeDefined(); | ||
expect(completeLabel).toBeDefined(); | ||
expect(incompleteIcon).toBeDefined(); | ||
expect(incompleteLabel).toBeDefined(); | ||
expect(errorIcon).toBeDefined(); | ||
expect(errorLabel).toBeDefined(); | ||
expect(currentIcon).toBeDefined(); | ||
expect(currentLabel).toBeDefined(); | ||
|
||
expect(completeLabel.parentElement?.tagName).toEqual('DIV'); | ||
|
||
// Make sure each step is a listitem | ||
const items = getAllByRole('listitem'); | ||
expect(items.length).toBe(4); | ||
|
||
// Make sure current step has correct aria attributes | ||
expect(currentLabel.parentElement?.getAttribute('aria-current')).toEqual('step'); | ||
}); | ||
|
||
it('should render buttons correctly', () => { | ||
const {getByRole} = render(<Buttons />); | ||
const signupLabel = getByRole('button', {name: 'Completed Sign up'}); | ||
|
||
expect(signupLabel?.tagName).toEqual('BUTTON'); | ||
expect(signupLabel?.getAttribute('type')).toEqual('button'); | ||
}); | ||
|
||
it('should render anchors correctly', () => { | ||
const {getByRole} = render(<Anchors />); | ||
const signupLabel = getByRole('link', {name: 'Completed Sign up'}); | ||
|
||
expect(signupLabel?.tagName).toEqual('A'); | ||
expect(signupLabel?.getAttribute('href')).toEqual('#'); | ||
}); | ||
|
||
describe('element naming', () => { | ||
it('should set all default element names', async () => { | ||
const {getByRole} = render(<Buttons />); | ||
const signupLabel = getByRole('button', {name: 'Completed Sign up'}); | ||
const validateEmail = getByRole('button', {name: 'Error Validate email'}); | ||
const completeProfile = getByRole('button', {name: 'Current Complete profile'}); | ||
const addFriends = getByRole('button', {name: 'Incomplete Add friends'}); | ||
const wrapper = getByRole('list'); | ||
|
||
expect(signupLabel?.dataset.pasteElement).toEqual('PROGRESS_STEP_COMPLETE'); | ||
expect(validateEmail?.dataset.pasteElement).toEqual('PROGRESS_STEP_ERROR'); | ||
expect(completeProfile?.dataset.pasteElement).toEqual('PROGRESS_STEP_CURRENT'); | ||
expect(addFriends?.dataset.pasteElement).toEqual('PROGRESS_STEP_INCOMPLETE'); | ||
expect(wrapper?.dataset.pasteElement).toEqual('PROGRESS_STEPS'); | ||
}); | ||
}); | ||
describe('custom element naming', () => { | ||
it('should set all custom element names', async () => { | ||
const {getByRole} = render( | ||
<CustomizationProvider | ||
disableAnimations | ||
theme={TestTheme} | ||
elements={{ | ||
STEPS: { | ||
padding: 'space50', | ||
}, | ||
STEP_SEPARATOR: { | ||
borderBottomWidth: 'borderWidth40', | ||
borderRadius: 'borderRadiusCircle', | ||
}, | ||
STEP_COMPLETE: { | ||
borderWidth: 'borderWidth10', | ||
borderStyle: 'solid', | ||
borderColor: 'colorBorderPrimary', | ||
}, | ||
STEP_CURRENT: { | ||
borderWidth: 'borderWidth10', | ||
borderStyle: 'solid', | ||
borderColor: 'colorBorderPrimary', | ||
}, | ||
STEP_ERROR: { | ||
borderWidth: 'borderWidth10', | ||
borderStyle: 'solid', | ||
borderColor: 'colorBorderPrimary', | ||
}, | ||
STEP_INCOMPLETE: { | ||
borderWidth: 'borderWidth10', | ||
borderStyle: 'solid', | ||
borderColor: 'colorBorderPrimary', | ||
}, | ||
}} | ||
> | ||
<ProgressSteps element="STEPS"> | ||
<ProgressStepComplete element="STEP_COMPLETE" as="a" href="#"> | ||
Sign up | ||
</ProgressStepComplete> | ||
<ProgressStepSeparator element="STEP_SEPARATOR" /> | ||
<ProgressStepError element="STEP_ERROR" as="button" onClick={() => {}}> | ||
Validate email | ||
</ProgressStepError> | ||
<ProgressStepSeparator element="STEP_SEPARATOR" /> | ||
<ProgressStepCurrent element="STEP_CURRENT" as="button" onClick={() => {}}> | ||
Complete profile | ||
</ProgressStepCurrent> | ||
<ProgressStepSeparator element="STEP_SEPARATOR" /> | ||
<ProgressStepIncomplete element="STEP_INCOMPLETE" as="button" onClick={() => {}}> | ||
Add friends | ||
</ProgressStepIncomplete> | ||
<ProgressStepSeparator element="STEP_SEPARATOR" /> | ||
<ProgressStepIncomplete element="STEP_INCOMPLETE" as="button" onClick={() => {}} disabled> | ||
Start event | ||
</ProgressStepIncomplete> | ||
</ProgressSteps> | ||
</CustomizationProvider> | ||
); | ||
const signupLabel = getByRole('link', {name: 'Completed Sign up'}); | ||
const validateEmail = getByRole('button', {name: 'Error Validate email'}); | ||
const completeProfile = getByRole('button', {name: 'Current Complete profile'}); | ||
const addFriends = getByRole('button', {name: 'Incomplete Add friends'}); | ||
const wrapper = getByRole('list'); | ||
|
||
expect(signupLabel?.dataset.pasteElement).toEqual('STEP_COMPLETE'); | ||
expect(validateEmail?.dataset.pasteElement).toEqual('STEP_ERROR'); | ||
expect(completeProfile?.dataset.pasteElement).toEqual('STEP_CURRENT'); | ||
expect(addFriends?.dataset.pasteElement).toEqual('STEP_INCOMPLETE'); | ||
expect(wrapper?.dataset.pasteElement).toEqual('STEPS'); | ||
|
||
expect(signupLabel).toHaveStyleRule('border-color', 'rgb(2, 99, 224)'); | ||
expect(validateEmail).toHaveStyleRule('border-color', 'rgb(2, 99, 224)'); | ||
expect(completeProfile).toHaveStyleRule('border-color', 'rgb(2, 99, 224)'); | ||
expect(addFriends).toHaveStyleRule('border-color', 'rgb(2, 99, 224)'); | ||
expect(wrapper).toHaveStyleRule('padding', '1rem'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const {build} = require('../../../../tools/build/esbuild'); | ||
|
||
build(require('./package.json')); |
63 changes: 63 additions & 0 deletions
63
packages/paste-core/components/progress-steps/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
{ | ||
"name": "@twilio-paste/progress-steps", | ||
"version": "0.0.0", | ||
"category": "data display", | ||
"status": "production", | ||
"description": "Progress Steps can be a presentational or interactive component and shows users an outline of a complex multi-step task.", | ||
"author": "Twilio Inc.", | ||
"license": "MIT", | ||
"main:dev": "src/index.tsx", | ||
"main": "dist/index.js", | ||
"module": "dist/index.es.js", | ||
"types": "dist/index.d.ts", | ||
"sideEffects": false, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"build": "yarn clean && NODE_ENV=production node build.js && tsc", | ||
"build:js": "NODE_ENV=development node build.js", | ||
"clean": "rm -rf ./dist", | ||
"tsc": "tsc" | ||
}, | ||
"peerDependencies": { | ||
"@twilio-paste/animation-library": "^1.0.0", | ||
"@twilio-paste/box": "^9.1.0", | ||
"@twilio-paste/color-contrast-utils": "^4.0.0", | ||
"@twilio-paste/customization": "^7.0.0", | ||
"@twilio-paste/design-tokens": "^9.0.0", | ||
"@twilio-paste/icons": "^11.1.0", | ||
"@twilio-paste/style-props": "^8.0.0", | ||
"@twilio-paste/styling-library": "^2.0.0", | ||
"@twilio-paste/theme": "^10.0.0", | ||
"@twilio-paste/types": "^5.0.0", | ||
"@twilio-paste/uid-library": "^1.0.0", | ||
"@types/react": "^16.8.6 || ^17.0.2 || ^18.0.27", | ||
"@types/react-dom": "^16.8.6 || ^17.0.2 || ^18.0.10", | ||
"prop-types": "^15.7.2", | ||
"react": "^18.0.0", | ||
"react-dom": "^18.0.0" | ||
}, | ||
"devDependencies": { | ||
"@twilio-paste/animation-library": "^1.0.0", | ||
"@twilio-paste/box": "^9.1.0", | ||
"@twilio-paste/color-contrast-utils": "^4.0.0", | ||
"@twilio-paste/customization": "^7.0.0", | ||
"@twilio-paste/design-tokens": "^9.0.0", | ||
"@twilio-paste/icons": "^11.1.0", | ||
"@twilio-paste/style-props": "^8.0.0", | ||
"@twilio-paste/styling-library": "^2.0.0", | ||
"@twilio-paste/theme": "^10.0.0", | ||
"@twilio-paste/types": "^5.0.0", | ||
"@twilio-paste/uid-library": "^1.0.0", | ||
"@types/react": "^18.0.27", | ||
"@types/react-dom": "^18.0.10", | ||
"prop-types": "^15.7.2", | ||
"react": "^18.0.0", | ||
"react-dom": "^18.0.0", | ||
"typescript": "^4.9.4" | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
packages/paste-core/components/progress-steps/src/ProgressStepComplete.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import {Box, safelySpreadBoxProps} from '@twilio-paste/box'; | ||
import {SuccessIcon as ProgressSuccessIcon} from '@twilio-paste/icons/esm/SuccessIcon'; | ||
|
||
import type {ProgressStepCompleteProps} from './types'; | ||
import {ProgressStepPropTypes} from './propTypes'; | ||
|
||
export const ProgressStepComplete = React.forwardRef<HTMLDivElement, ProgressStepCompleteProps>( | ||
({element = 'PROGRESS_STEP_COMPLETE', as = 'div', children, i18nCompleteLabel = 'Completed', ...props}, ref) => { | ||
return ( | ||
<div role="listitem"> | ||
<Box | ||
{...safelySpreadBoxProps(props)} | ||
element={element} | ||
as={as} | ||
type={as === 'button' ? 'button' : undefined} | ||
ref={ref} | ||
display="flex" | ||
columnGap="space30" | ||
border="none" | ||
background="none" | ||
padding="space0" | ||
margin="space0" | ||
fontFamily="fontFamilyText" | ||
fontSize="fontSize20" | ||
lineHeight="lineHeight20" | ||
fontWeight={as === 'button' ? 'fontWeightSemibold' : 'fontWeightMedium'} | ||
outline="none" | ||
borderRadius="borderRadius20" | ||
cursor={as !== 'div' ? 'pointer' : 'default'} | ||
color="colorTextWeak" | ||
_hover={as !== 'div' ? {textDecoration: 'none', color: 'colorTextPrimary'} : undefined} | ||
_focus={as !== 'div' ? {boxShadow: 'shadowFocus'} : undefined} | ||
> | ||
<ProgressSuccessIcon decorative={false} title={i18nCompleteLabel} /> | ||
<Box as="span">{children}</Box> | ||
</Box> | ||
</div> | ||
); | ||
} | ||
); | ||
ProgressStepComplete.displayName = 'ProgressStepComplete'; | ||
ProgressStepComplete.propTypes = {...ProgressStepPropTypes, i18nCompleteLabel: PropTypes.string}; |
45 changes: 45 additions & 0 deletions
45
packages/paste-core/components/progress-steps/src/ProgressStepCurrent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import {Box, safelySpreadBoxProps} from '@twilio-paste/box'; | ||
|
||
import {ProgressCurrentIcon} from './icons/ProgressCurrentIcon'; | ||
import type {ProgressStepCurrentProps} from './types'; | ||
import {ProgressStepPropTypes} from './propTypes'; | ||
|
||
export const ProgressStepCurrent = React.forwardRef<HTMLDivElement, ProgressStepCurrentProps>( | ||
({element = 'PROGRESS_STEP_CURRENT', as = 'div', children, i18nCurrentLabel = 'Current', ...props}, ref) => { | ||
return ( | ||
<div role="listitem"> | ||
<Box | ||
{...safelySpreadBoxProps(props)} | ||
element={element} | ||
as={as} | ||
type={as === 'button' ? 'button' : undefined} | ||
ref={ref} | ||
aria-current="step" | ||
display="flex" | ||
columnGap="space30" | ||
border="none" | ||
background="none" | ||
padding="space0" | ||
margin="space0" | ||
fontFamily="fontFamilyText" | ||
fontSize="fontSize20" | ||
lineHeight="lineHeight20" | ||
fontWeight={as === 'button' ? 'fontWeightSemibold' : 'fontWeightMedium'} | ||
outline="none" | ||
borderRadius="borderRadius20" | ||
cursor={as !== 'div' ? 'pointer' : 'default'} | ||
color="colorTextPrimary" | ||
_hover={as !== 'div' ? {textDecoration: 'none', color: 'colorTextPrimaryStrongest'} : undefined} | ||
_focus={as !== 'div' ? {boxShadow: 'shadowFocus'} : undefined} | ||
> | ||
<ProgressCurrentIcon decorative={false} title={i18nCurrentLabel} /> | ||
<Box as="span">{children}</Box> | ||
</Box> | ||
</div> | ||
); | ||
} | ||
); | ||
ProgressStepCurrent.displayName = 'ProgressStepCurrent'; | ||
ProgressStepCurrent.propTypes = {...ProgressStepPropTypes, i18nCurrentLabel: PropTypes.string}; |
44 changes: 44 additions & 0 deletions
44
packages/paste-core/components/progress-steps/src/ProgressStepError.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import {Box, safelySpreadBoxProps} from '@twilio-paste/box'; | ||
|
||
import {ProgressErrorIcon} from './icons/ProgressErrorIcon'; | ||
import type {ProgressStepErrorProps} from './types'; | ||
import {ProgressStepPropTypes} from './propTypes'; | ||
|
||
export const ProgressStepError = React.forwardRef<HTMLDivElement, ProgressStepErrorProps>( | ||
({element = 'PROGRESS_STEP_ERROR', as = 'div', children, i18nErrorLabel = 'Error', ...props}, ref) => { | ||
return ( | ||
<div role="listitem"> | ||
<Box | ||
{...safelySpreadBoxProps(props)} | ||
element={element} | ||
as={as} | ||
type={as === 'button' ? 'button' : undefined} | ||
ref={ref} | ||
display="flex" | ||
columnGap="space30" | ||
border="none" | ||
background="none" | ||
padding="space0" | ||
margin="space0" | ||
fontFamily="fontFamilyText" | ||
fontSize="fontSize20" | ||
lineHeight="lineHeight20" | ||
fontWeight={as === 'button' ? 'fontWeightSemibold' : 'fontWeightMedium'} | ||
outline="none" | ||
borderRadius="borderRadius20" | ||
cursor={as !== 'div' ? 'pointer' : 'default'} | ||
color="colorTextError" | ||
_hover={as !== 'div' ? {textDecoration: 'none', color: 'colorTextErrorStrongest'} : undefined} | ||
_focus={as !== 'div' ? {boxShadow: 'shadowFocus'} : undefined} | ||
> | ||
<ProgressErrorIcon decorative={false} title={i18nErrorLabel} /> | ||
<Box as="span">{children}</Box> | ||
</Box> | ||
</div> | ||
); | ||
} | ||
); | ||
ProgressStepError.displayName = 'ProgressStepError'; | ||
ProgressStepError.propTypes = {...ProgressStepPropTypes, i18nErrorLabel: PropTypes.string}; |
Oops, something went wrong.