diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 000000000..e07d83fbd --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,17 @@ +name: 'Chromatic' + +on: push + +jobs: + chromatic-deployment: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Install dependencies + run: npm install + - name: Publish to Chromatic + uses: chromaui/action@v1 + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + skip: dependabot/** + onlyChanged: true diff --git a/.storybook/main.js b/.storybook/main.js index f452020ce..116175f20 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -4,7 +4,9 @@ module.exports = { stories: ['../src/**/*.stories.@(tsx|mdx)', '../stories/**/*.stories.@(js|jsx|ts|tsx|mdx)'], siteUrl: 'https://github.com/Doist/reactist', features: { + // Needed for Chromatic/Storybooks interactive tests // See https://storybook.js.org/docs/react/writing-tests/interaction-testing + // See https://www.chromatic.com/docs/interactions#how-to-write-interaction-tests interactionsDebugger: true, }, addons: [ diff --git a/.storybook/preview.js b/.storybook/preview.js index 278c4ea39..92be3d3a3 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -13,6 +13,11 @@ export const parameters = { }, }, decorators, + chromatic: { + // This allows us to opt-in to Chromatic snapshots per story + // See https://www.chromatic.com/docs/ignoring-elements#ignore-stories + disableSnapshot: true, + }, } const badgeFontStyles = { diff --git a/package-lock.json b/package-lock.json index 6b24b214c..1248a49a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,6 +64,7 @@ "autoprefixer": "^9.8.0", "babel-core": "^7.0.0-bridge.0", "babel-loader": "^8.1.0", + "chromatic": "^6.11.4", "classnames": "^2.2.5", "css-loader": "^4.2.2", "cssnano": "^4.1.10", @@ -12938,6 +12939,21 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "node_modules/chromatic": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-6.11.4.tgz", + "integrity": "sha512-f1TcuIXKjGUuOjPuwFF44kzbuEcESFcDxHzrzWPLmHuC90dV8HLxbufqYaTOBYMO/rJ32Zftb7S9pXuF/Rhfog==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.7", + "@types/webpack-env": "^1.17.0" + }, + "bin": { + "chroma": "bin/main.cjs", + "chromatic": "bin/main.cjs", + "chromatic-cli": "bin/main.cjs" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.2", "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", @@ -47823,6 +47839,16 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "chromatic": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-6.11.4.tgz", + "integrity": "sha512-f1TcuIXKjGUuOjPuwFF44kzbuEcESFcDxHzrzWPLmHuC90dV8HLxbufqYaTOBYMO/rJ32Zftb7S9pXuF/Rhfog==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.7", + "@types/webpack-env": "^1.17.0" + } + }, "chrome-trace-event": { "version": "1.0.2", "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", diff --git a/package.json b/package.json index 9622fccdf..bbab6a37a 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "autoprefixer": "^9.8.0", "babel-core": "^7.0.0-bridge.0", "babel-loader": "^8.1.0", + "chromatic": "^6.11.4", "classnames": "^2.2.5", "css-loader": "^4.2.2", "cssnano": "^4.1.10", diff --git a/src/new-components/alert/alert.stories.mdx b/src/new-components/alert/alert.stories.mdx index afe69e863..76f020e0e 100644 --- a/src/new-components/alert/alert.stories.mdx +++ b/src/new-components/alert/alert.stories.mdx @@ -49,6 +49,7 @@ A simple Alert component. name="Main demo" parameters={{ docs: { source: { type: 'code' } }, + chromatic: { disableSnapshot: false }, }} > diff --git a/src/new-components/badge/badge.stories.mdx b/src/new-components/badge/badge.stories.mdx index bbc7a242b..77149d4a4 100644 --- a/src/new-components/badge/badge.stories.mdx +++ b/src/new-components/badge/badge.stories.mdx @@ -4,6 +4,7 @@ import { Box } from '../box' import { Button } from '../button' import { Columns, Column } from '../columns' import { Stack } from '../stack' + import { TextLink } from '../text-link' import { Text } from '../text' @@ -69,6 +70,7 @@ export function PlaygroundTemplate() { name="Main demo" parameters={{ docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false }, }} > {PlaygroundTemplate.bind({})} @@ -82,6 +84,7 @@ export function PlaygroundTemplate() { name="Inside other elements" parameters={{ docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false }, }} > diff --git a/src/new-components/button/button.stories.mdx b/src/new-components/button/button.stories.mdx index 00680d7b5..508b1031f 100644 --- a/src/new-components/button/button.stories.mdx +++ b/src/new-components/button/button.stories.mdx @@ -36,6 +36,7 @@ A semantic button that provides all the necessary visual variants. It follows th name="Main demo" parameters={{ docs: { source: { type: 'code' } }, + chromatic: { disableSnapshot: false }, }} > @@ -153,6 +154,7 @@ this is discouraged, and not guaranteed to be supported in the future. name="With label and icon" parameters={{ docs: { source: { type: 'code' } }, + chromatic: { disableSnapshot: false }, }} > @@ -223,6 +225,7 @@ as a tooltip if no tooltip is provided. name="Icon-only buttons" parameters={{ docs: { source: { type: 'code' } }, + chromatic: { disableSnapshot: false }, }} > @@ -268,6 +271,7 @@ for this purpose. name="With different size" parameters={{ docs: { source: { type: 'code' } }, + chromatic: { disableSnapshot: false }, }} > @@ -531,6 +535,7 @@ export function FullWidthTemplate({ label, ...otherProps }) { name="Full-width" parameters={{ docs: { source: { type: 'code' } }, + chromatic: { disableSnapshot: false }, }} argTypes={{ label: { diff --git a/src/new-components/checkbox-field/checkbox-field.stories.mdx b/src/new-components/checkbox-field/checkbox-field.stories.mdx index ba5be477c..dac54d6fa 100644 --- a/src/new-components/checkbox-field/checkbox-field.stories.mdx +++ b/src/new-components/checkbox-field/checkbox-field.stories.mdx @@ -131,6 +131,7 @@ A checkbox field with a `ReactNode` as label. name="Indeterminate Example" parameters={{ docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false }, }} > {() => { diff --git a/src/new-components/heading/heading.stories.tsx b/src/new-components/heading/heading.stories.tsx index 1928f231a..526158b28 100644 --- a/src/new-components/heading/heading.stories.tsx +++ b/src/new-components/heading/heading.stories.tsx @@ -69,6 +69,10 @@ export function HeadingStory() { ) } +HeadingStory.parameters = { + chromatic: { disableSnapshot: false }, +} + export function TruncatedHeadingStory() { return (
@@ -87,6 +91,10 @@ export function TruncatedHeadingStory() { ) } +TruncatedHeadingStory.parameters = { + chromatic: { disableSnapshot: false }, +} + export function ResponsiveHeadingStory(props: React.ComponentProps) { return ( <> diff --git a/src/new-components/modal/modal-examples.stories.tsx b/src/new-components/modal/modal-examples.stories.tsx index 27b59c762..7db1a6971 100644 --- a/src/new-components/modal/modal-examples.stories.tsx +++ b/src/new-components/modal/modal-examples.stories.tsx @@ -102,6 +102,7 @@ ModalWithStandardActionsFooter.storyName = 'Modal with standard actions footer' ModalWithStandardActionsFooter.play = openModal ModalWithStandardActionsFooter.parameters = { docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false, pauseAnimationAtEnd: true }, } // @@ -144,6 +145,7 @@ ModalWithHeaderBodyAndCustomFooter.storyName = 'Modal with header, body and cust ModalWithHeaderBodyAndCustomFooter.play = openModal ModalWithHeaderBodyAndCustomFooter.parameters = { docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false, pauseAnimationAtEnd: true }, } // @@ -201,6 +203,7 @@ ModalWithSidebar.storyName = 'Modal with a sidebar' ModalWithSidebar.play = openModal ModalWithSidebar.parameters = { docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false, pauseAnimationAtEnd: true }, } // @@ -277,6 +280,7 @@ ModalWithScrollableTabPanels.storyName = 'Modal with scrollable tab panels' ModalWithScrollableTabPanels.play = openModal ModalWithScrollableTabPanels.parameters = { docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false, pauseAnimationAtEnd: true }, } // @@ -312,6 +316,7 @@ MinimalisticConfirmationModal.storyName = 'Minimalistic confirmation modal' MinimalisticConfirmationModal.play = openModal MinimalisticConfirmationModal.parameters = { docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false, pauseAnimationAtEnd: true }, } // @@ -353,6 +358,7 @@ EnrichedConfirmationModal.storyName = 'Enriched confirmation modal' EnrichedConfirmationModal.play = openModal EnrichedConfirmationModal.parameters = { docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false, pauseAnimationAtEnd: true }, } // @@ -362,7 +368,7 @@ EnrichedConfirmationModal.parameters = { export function ModalAutofocus() { return ( - + @@ -408,6 +414,7 @@ ModalAutofocus.storyName = 'Autofocus' ModalAutofocus.play = openModal ModalAutofocus.parameters = { docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false, pauseAnimationAtEnd: true }, } // @@ -417,7 +424,7 @@ ModalAutofocus.parameters = { export function StackingModals() { return ( - + @@ -439,9 +446,11 @@ export function StackingModals() { - + + + Nested modal @@ -462,4 +471,5 @@ StackingModals.storyName = 'Stacking modals' StackingModals.play = openModal StackingModals.parameters = { docs: { source: { type: 'dynamic' } }, + chromatic: { disableSnapshot: false, pauseAnimationAtEnd: true }, } diff --git a/src/new-components/modal/modal-stories-components.tsx b/src/new-components/modal/modal-stories-components.tsx index fbcef83a4..662123092 100644 --- a/src/new-components/modal/modal-stories-components.tsx +++ b/src/new-components/modal/modal-stories-components.tsx @@ -197,6 +197,7 @@ function Modal(props: WithOptionals { const div = document.createElement('div') diff --git a/src/new-components/password-field/password-field.stories.tsx b/src/new-components/password-field/password-field.stories.tsx index a692834fb..3687bd8a8 100644 --- a/src/new-components/password-field/password-field.stories.tsx +++ b/src/new-components/password-field/password-field.stories.tsx @@ -40,6 +40,10 @@ export function InteractivePropsStory({ ) } +InteractivePropsStory.parameters = { + chromatic: { disableSnapshot: false }, +} + InteractivePropsStory.argTypes = { label: { control: { type: 'text' }, @@ -115,6 +119,10 @@ export function MessageToneStory() { ) } +MessageToneStory.parameters = { + chromatic: { disableSnapshot: false }, +} + export function WithoutLabelStory() { return ( diff --git a/src/new-components/select-field/select-field.stories.tsx b/src/new-components/select-field/select-field.stories.tsx index 2f739ff20..027cbfa38 100644 --- a/src/new-components/select-field/select-field.stories.tsx +++ b/src/new-components/select-field/select-field.stories.tsx @@ -53,6 +53,10 @@ export function InteractivePropsStory({ ) } +InteractivePropsStory.parameters = { + chromatic: { disableSnapshot: false }, +} + InteractivePropsStory.argTypes = { label: { control: { type: 'text' }, @@ -143,6 +147,10 @@ export function MessageToneStory() { ) } +MessageToneStory.parameters = { + chromatic: { disableSnapshot: false }, +} + export function WithoutLabelStory() { return ( diff --git a/src/new-components/spinner/spinner.tsx b/src/new-components/spinner/spinner.tsx index cdd2d459d..d76cf7856 100644 --- a/src/new-components/spinner/spinner.tsx +++ b/src/new-components/spinner/spinner.tsx @@ -3,7 +3,14 @@ import styles from './spinner.module.css' function Spinner({ size = 24 }: { size?: number }) { return ( - + diff --git a/src/new-components/text-field/text-field.stories.tsx b/src/new-components/text-field/text-field.stories.tsx index 4ebe06d00..b28c578ea 100644 --- a/src/new-components/text-field/text-field.stories.tsx +++ b/src/new-components/text-field/text-field.stories.tsx @@ -41,6 +41,10 @@ export function InteractivePropsStory({ ) } +InteractivePropsStory.parameters = { + chromatic: { disableSnapshot: false }, +} + InteractivePropsStory.argTypes = { label: { control: { type: 'text' }, @@ -116,6 +120,10 @@ export function MessageToneStory() { ) } +MessageToneStory.parameters = { + chromatic: { disableSnapshot: false }, +} + export function WithoutLabelStory() { return ( diff --git a/src/new-components/text/text.stories.tsx b/src/new-components/text/text.stories.tsx index 89ee32255..a20f3ae5a 100644 --- a/src/new-components/text/text.stories.tsx +++ b/src/new-components/text/text.stories.tsx @@ -96,6 +96,10 @@ export function TextStory() { ) } +TextStory.parameters = { + chromatic: { disableSnapshot: false }, +} + export function TruncatedTextStory() { return (
@@ -130,6 +134,10 @@ export function TruncatedTextStory() { ) } +TruncatedTextStory.parameters = { + chromatic: { disableSnapshot: false }, +} + export function ResponsiveTextStory(props: React.ComponentProps) { return ( <> diff --git a/src/new-components/toast/toast.stories.tsx b/src/new-components/toast/toast.stories.tsx index f42b8e838..33e37e19e 100644 --- a/src/new-components/toast/toast.stories.tsx +++ b/src/new-components/toast/toast.stories.tsx @@ -219,3 +219,7 @@ export function StaticToastStory() {
) } + +StaticToastStory.parameters = { + chromatic: { disableSnapshot: false }, +}