Skip to content

Commit

Permalink
Merge branch 'main' into docs/status-account-product-switcher
Browse files Browse the repository at this point in the history
  • Loading branch information
SiTaggart committed Jun 27, 2023
2 parents 3c8eaab + a2c1c91 commit 55de2bc
Show file tree
Hide file tree
Showing 29 changed files with 1,158 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changeset/mean-pumpkins-enjoy.md
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.
5 changes: 5 additions & 0 deletions .changeset/silver-candles-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@twilio-paste/codemods': patch
---

[Codemods] add new ProgressSteps package
1 change: 1 addition & 0 deletions .codesandbox/ci.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"/packages/paste-core/components/paragraph",
"/packages/paste-core/components/popover",
"/packages/paste-core/components/product-switcher",
"/packages/paste-core/components/progress-steps",
"/packages/paste-core/components/radio-button-group",
"/packages/paste-core/components/radio-group",
"/packages/paste-libraries/react-textarea-autosize",
Expand Down
6 changes: 6 additions & 0 deletions packages/paste-codemods/tools/.cache/mappings.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@
"ProductSwitcherButton": "@twilio-paste/core/product-switcher",
"ProductSwitcherItem": "@twilio-paste/core/product-switcher",
"useProductSwitcherState": "@twilio-paste/core/product-switcher",
"ProgressStepComplete": "@twilio-paste/core/progress-steps",
"ProgressStepCurrent": "@twilio-paste/core/progress-steps",
"ProgressStepError": "@twilio-paste/core/progress-steps",
"ProgressStepIncomplete": "@twilio-paste/core/progress-steps",
"ProgressStepSeparator": "@twilio-paste/core/progress-steps",
"ProgressSteps": "@twilio-paste/core/progress-steps",
"RadioButton": "@twilio-paste/core/radio-button-group",
"RadioButtonGroup": "@twilio-paste/core/radio-button-group",
"Radio": "@twilio-paste/core/radio-group",
Expand Down
152 changes: 152 additions & 0 deletions packages/paste-core/components/progress-steps/__tests__/index.spec.tsx
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');
});
});
});
3 changes: 3 additions & 0 deletions packages/paste-core/components/progress-steps/build.js
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 packages/paste-core/components/progress-steps/package.json
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"
}
}
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};
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};
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};

0 comments on commit 55de2bc

Please sign in to comment.