From f7018d1d56125d516598e941d0c5526f8d34a6ef Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 2 Nov 2023 13:01:42 +0000 Subject: [PATCH] Add Notices Documentation to Storybook and upgrade to Storybook 7 (#11524) * Rename stories * MDX guidelines * dedupe and fix dependencies * Notice Banner Docs * Fix root elements and icon library docs * Fix ProductPrice stories * Organise storybook structure * Fix error placeholder story * Snackbar docs * Missing dotenv dependency * Update storybook/main.js Co-authored-by: Alex Florisca * Update assets/js/base/components/snackbar-list/docs/docs.mdx Co-authored-by: Alex Florisca * Update assets/js/base/components/notice-banner/docs/docs.mdx Co-authored-by: Alex Florisca * Update assets/js/base/components/snackbar-list/docs/docs.mdx Co-authored-by: Alex Florisca * Update assets/js/base/components/snackbar-list/docs/docs.mdx Co-authored-by: Alex Florisca * Update assets/js/base/components/snackbar-list/docs/docs.mdx Co-authored-by: Alex Florisca * Update assets/js/base/components/snackbar-list/docs/docs.mdx Co-authored-by: Alex Florisca * Update assets/js/base/components/snackbar-list/docs/docs.mdx Co-authored-by: Alex Florisca * Update assets/js/base/components/snackbar-list/docs/docs.mdx Co-authored-by: Alex Florisca * Update package lock * update snaps * fix json error check --------- Co-authored-by: Alex Florisca --- .eslintrc.js | 1 + .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../base/components/notice-banner/README.md | 138 - .../components/notice-banner/docs/docs.mdx | 112 + .../base/components/notice-banner/index.tsx | 25 +- .../notice-banner/screenshots/default.png | Bin 9342 -> 0 bytes .../notice-banner/screenshots/error.png | Bin 9593 -> 0 bytes .../notice-banner/screenshots/info.png | Bin 9418 -> 0 bytes .../notice-banner/screenshots/success.png | Bin 9356 -> 0 bytes .../notice-banner/screenshots/warning.png | Bin 9866 -> 0 bytes .../stories/{index.tsx => index.stories.tsx} | 18 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 11 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../base/components/snackbar-list/README.md | 62 - .../components/snackbar-list/docs/docs.mdx | 90 + .../components/snackbar-list/snackbar.tsx | 18 +- .../stories/{index.tsx => index.stories.tsx} | 12 +- .../stories/snackbar.stories.tsx | 97 + .../base/components/snackbar-list/style.scss | 39 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- assets/js/base/utils/test/errors.js | 10 +- .../error-placeholder/index.tsx | 4 +- ...-message.tsx => error-message.stories.tsx} | 2 +- ...lder.tsx => error-placeholder.stories.tsx} | 8 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../test/__snapshots__/index.js.snap | 520 +- assets/js/icons/docs/iconography.mdx | 25 + assets/js/icons/stories/index.tsx | 97 - package-lock.json | 91029 ++++++---------- package.json | 40 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- .../stories/{index.tsx => index.stories.tsx} | 2 +- packages/components/chip/chip.tsx | 1 - packages/components/chip/removable-chip.tsx | 10 - .../components/chip/stories/chip.stories.tsx | 2 +- .../chip/stories/removable-chip.stories.tsx | 2 +- .../stories/{index.tsx => index.stories.tsx} | 8 +- storybook/main.js | 33 +- storybook/preview.js | 8 + storybook/style.scss | 4 + 51 files changed, 33506 insertions(+), 58954 deletions(-) rename assets/js/base/components/button/stories/{index.tsx => index.stories.tsx} (94%) rename assets/js/base/components/cart-checkout/totals/coupon/stories/{index.tsx => index.stories.tsx} (95%) rename assets/js/base/components/cart-checkout/totals/discount/stories/{index.tsx => index.stories.tsx} (97%) rename assets/js/base/components/cart-checkout/totals/footer-item/stories/{index.tsx => index.stories.tsx} (95%) rename assets/js/base/components/country-input/stories/{index.tsx => index.stories.tsx} (96%) rename assets/js/base/components/form-token-field/stories/{index.tsx => index.stories.tsx} (92%) delete mode 100644 assets/js/base/components/notice-banner/README.md create mode 100644 assets/js/base/components/notice-banner/docs/docs.mdx delete mode 100644 assets/js/base/components/notice-banner/screenshots/default.png delete mode 100644 assets/js/base/components/notice-banner/screenshots/error.png delete mode 100644 assets/js/base/components/notice-banner/screenshots/info.png delete mode 100644 assets/js/base/components/notice-banner/screenshots/success.png delete mode 100644 assets/js/base/components/notice-banner/screenshots/warning.png rename assets/js/base/components/notice-banner/stories/{index.tsx => index.stories.tsx} (73%) rename assets/js/base/components/price-slider/stories/{index.tsx => index.stories.tsx} (95%) rename assets/js/base/components/product-name/stories/{index.tsx => index.stories.tsx} (88%) rename assets/js/base/components/product-price/stories/{index.tsx => index.stories.tsx} (82%) rename assets/js/base/components/quantity-selector/stories/{index.tsx => index.stories.tsx} (92%) rename assets/js/base/components/read-more/stories/{index.tsx => index.stories.tsx} (96%) delete mode 100644 assets/js/base/components/snackbar-list/README.md create mode 100644 assets/js/base/components/snackbar-list/docs/docs.mdx rename assets/js/base/components/snackbar-list/stories/{index.tsx => index.stories.tsx} (73%) create mode 100644 assets/js/base/components/snackbar-list/stories/snackbar.stories.tsx rename assets/js/base/components/tabs/stories/{index.tsx => index.stories.tsx} (95%) rename assets/js/editor-components/error-placeholder/stories/{error-message.tsx => error-message.stories.tsx} (88%) rename assets/js/editor-components/error-placeholder/stories/{error-placeholder.tsx => error-placeholder.stories.tsx} (84%) rename assets/js/editor-components/external-link-card/stories/{index.tsx => index.stories.tsx} (91%) create mode 100644 assets/js/icons/docs/iconography.mdx delete mode 100644 assets/js/icons/stories/index.tsx rename packages/checkout/components/checkbox-control/stories/{index.tsx => index.stories.tsx} (88%) rename packages/checkout/components/totals/fees/stories/{index.tsx => index.stories.tsx} (95%) rename packages/checkout/components/totals/item/stories/{index.tsx => index.stories.tsx} (92%) rename packages/checkout/components/totals/subtotal/stories/{index.tsx => index.stories.tsx} (93%) rename packages/checkout/components/totals/taxes/stories/{index.tsx => index.stories.tsx} (92%) rename packages/components/formatted-monetary-amount/stories/{index.tsx => index.stories.tsx} (94%) diff --git a/.eslintrc.js b/.eslintrc.js index 6dbbe97731a..5e1e4e1843c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -124,6 +124,7 @@ module.exports = { extends: [ 'plugin:@woocommerce/eslint-plugin/recommended', 'plugin:you-dont-need-lodash-underscore/compatible', + 'plugin:storybook/recommended', ], globals: { wcBlocksMiddlewareConfig: 'readonly', diff --git a/assets/js/base/components/button/stories/index.tsx b/assets/js/base/components/button/stories/index.stories.tsx similarity index 94% rename from assets/js/base/components/button/stories/index.tsx rename to assets/js/base/components/button/stories/index.stories.tsx index ecf3c5242c5..f445dbdca86 100644 --- a/assets/js/base/components/button/stories/index.tsx +++ b/assets/js/base/components/button/stories/index.stories.tsx @@ -10,7 +10,7 @@ import Button, { ButtonProps } from '..'; const availableTypes = [ 'button', 'input', 'submit' ]; export default { - title: 'WooCommerce Blocks/@base-components/Button', + title: 'Base Components/Button', argTypes: { children: { control: 'text', diff --git a/assets/js/base/components/cart-checkout/totals/coupon/stories/index.tsx b/assets/js/base/components/cart-checkout/totals/coupon/stories/index.stories.tsx similarity index 95% rename from assets/js/base/components/cart-checkout/totals/coupon/stories/index.tsx rename to assets/js/base/components/cart-checkout/totals/coupon/stories/index.stories.tsx index e5bae041855..4806602caf4 100644 --- a/assets/js/base/components/cart-checkout/totals/coupon/stories/index.tsx +++ b/assets/js/base/components/cart-checkout/totals/coupon/stories/index.stories.tsx @@ -13,7 +13,7 @@ import { VALIDATION_STORE_KEY } from '@woocommerce/block-data'; import { TotalsCoupon, TotalsCouponProps } from '..'; export default { - title: 'WooCommerce Blocks/@base-components/cart-checkout/totals/Coupon', + title: 'Base Components/Totals/Coupon', component: TotalsCoupon, args: { initialOpen: true, diff --git a/assets/js/base/components/cart-checkout/totals/discount/stories/index.tsx b/assets/js/base/components/cart-checkout/totals/discount/stories/index.stories.tsx similarity index 97% rename from assets/js/base/components/cart-checkout/totals/discount/stories/index.tsx rename to assets/js/base/components/cart-checkout/totals/discount/stories/index.stories.tsx index 15525b95f57..7b274b27e72 100644 --- a/assets/js/base/components/cart-checkout/totals/discount/stories/index.tsx +++ b/assets/js/base/components/cart-checkout/totals/discount/stories/index.stories.tsx @@ -67,7 +67,7 @@ function extractValuesFromCoupons( } export default { - title: 'WooCommerce Blocks/@base-components/cart-checkout/totals/Discount', + title: 'Base Components/Totals/Discount', component: Discount, argTypes: { currency: currencyControl, diff --git a/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.tsx b/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.stories.tsx similarity index 95% rename from assets/js/base/components/cart-checkout/totals/footer-item/stories/index.tsx rename to assets/js/base/components/cart-checkout/totals/footer-item/stories/index.stories.tsx index 80b05c092a4..6442babe6c7 100644 --- a/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.tsx +++ b/assets/js/base/components/cart-checkout/totals/footer-item/stories/index.stories.tsx @@ -21,7 +21,7 @@ const NZD: Currency = { }; export default { - title: 'WooCommerce Blocks/@base-components/cart-checkout/totals/FooterItem', + title: 'Base Components/Totals/FooterItem', component: FooterItem, args: { currency: NZD, diff --git a/assets/js/base/components/country-input/stories/index.tsx b/assets/js/base/components/country-input/stories/index.stories.tsx similarity index 96% rename from assets/js/base/components/country-input/stories/index.tsx rename to assets/js/base/components/country-input/stories/index.stories.tsx index 1818b52aa76..5afc34b79c2 100644 --- a/assets/js/base/components/country-input/stories/index.tsx +++ b/assets/js/base/components/country-input/stories/index.stories.tsx @@ -15,7 +15,7 @@ import { countries } from './countries-filler'; type CountryCode = keyof typeof countries; export default { - title: 'WooCommerce Blocks/@base-components/CountryInput', + title: 'Base Components/CountryInput', component: CountryInput, args: { countries, diff --git a/assets/js/base/components/form-token-field/stories/index.tsx b/assets/js/base/components/form-token-field/stories/index.stories.tsx similarity index 92% rename from assets/js/base/components/form-token-field/stories/index.tsx rename to assets/js/base/components/form-token-field/stories/index.stories.tsx index b3e20e21461..00d66f6b7ec 100644 --- a/assets/js/base/components/form-token-field/stories/index.tsx +++ b/assets/js/base/components/form-token-field/stories/index.stories.tsx @@ -10,7 +10,7 @@ import { useState } from '@wordpress/element'; import FormTokenField, { Props } from '..'; export default { - title: 'WooCommerce Blocks/@base-components/FormTokenField', + title: 'Base Components/FormTokenField', argTypes: {}, component: FormTokenField, } as Meta< Props >; diff --git a/assets/js/base/components/notice-banner/README.md b/assets/js/base/components/notice-banner/README.md deleted file mode 100644 index 9345d194672..00000000000 --- a/assets/js/base/components/notice-banner/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# NoticeBanner Component - -An informational UI displayed near the top of the store pages. - -## Table of contents - -- [Design Guidelines](#design-guidelines) -- [Development Guidelines](#development-guidelines) - - [Usage](#usage) - - [Props](#props) - - [`children`: `React.ReactNode`](#children-reactreactnode) - - [`className`: `string`](#classname-string) - - [`isDismissible`: `boolean`](#isdismissible-boolean) - - [`onRemove`: `() => void`](#onremove---void) - - [`politeness`: `'polite' | 'assertive'`](#politeness-polite--assertive) - - [`spokenMessage`: `string`](#spokenmessage-string) - - [`status`: `'success' | 'error' | 'info' | 'warning' | 'default'`](#status-success--error--info--warning--default) - - [`summary`: `string`](#summary-string) - - [Example](#example) - -## Design Guidelines - -Notices are informational UI displayed near the top of store pages. Notices are used to indicate the result of an action, or to draw the user’s attention to necessary information. - -Notices are color-coded to indicate the type of message being communicated, and also show an icon to reinforce the meaning of the message. The color and icon used for a notice are determined by the `status` prop. - -### Informational - -Blue notices used for general information for buyers that are not blocking and do not require action. - -![Informational notice](./screenshots/info.png) - -### Error - -Red notices to show that an error has occurred and that the user needs to take action. - -![Error notice](./screenshots/error.png) - -### Success - -Green notices that show an action was successful. - -![Success notice](./screenshots/success.png) - -### Warning - -Yellow notices that show that the user may need to take action, or needs to be aware of something important. - -![Warning notice](./screenshots/warning.png) - -### Default - -Gray notice, similar to info, but used for less important messaging. - -![Default notice](./screenshots/default.png) - -## Development Guidelines - -### Usage - -To display a plain notice, pass the notice message as a string: - -```jsx -import { NoticeBanner } from '@woocommerce/base-components'; - -Your message here; -``` - -For more complex markup, you can pass any JSX element: - -```jsx -import { NoticeBanner } from '@woocommerce/base-components'; - - -

- An error occurred: { errorDetails }. -

-
; -``` - -### Props - -#### `children`: `React.ReactNode` - -The displayed message of a notice. Also used as the spoken message for assistive technology, unless `spokenMessage` is provided as an alternative message. - -#### `className`: `string` - -Additional class name to give to the notice. - -#### `isDismissible`: `boolean` - -Determines whether the notice can be dismissed by the user. When set to true, a close icon will be displayed on the banner. - -#### `onRemove`: `() => void` - -Function called when dismissing the notice. When the close icon is clicked or the Escape key is pressed, this function will be called. - -#### `politeness`: `'polite' | 'assertive'` - -Determines the level of politeness for the notice for assistive technology. Acceptable values are 'polite' and 'assertive'. Default is 'polite'. - -#### `spokenMessage`: `string` - -Optionally provided to change the spoken message for assistive technology. If not provided, the `children` prop will be used as the spoken message. - -#### `status`: `'success' | 'error' | 'info' | 'warning' | 'default'` - -Status determines the color of the notice and the icon. Acceptable values are `success`, `error`, `info`, `warning`, and `default`. - -#### `summary`: `string` - -Optional summary text shown above notice content, used when several notices are listed together. - -##### Example - -```tsx -import { NoticeBanner } from '@woocommerce/base-components'; - -const errorMessages = [ - 'First error message', - 'Second error message', - 'Third error message', -]; - - -
    - { errorMessages.map( ( message ) => ( -
  • { message }
  • - ) ) } -
-
; -``` - -In this example, the summary prop is used to indicate to the user that there are errors in the form submission. The list of error messages is rendered within the NoticeBanner component using an unordered list (`
    `) and list items (`
  • `). The `status` prop is set to `error` to indicate that the notice represents an error message. diff --git a/assets/js/base/components/notice-banner/docs/docs.mdx b/assets/js/base/components/notice-banner/docs/docs.mdx new file mode 100644 index 00000000000..3808037083a --- /dev/null +++ b/assets/js/base/components/notice-banner/docs/docs.mdx @@ -0,0 +1,112 @@ +import { Canvas, Meta, ArgTypes, Primary, Source } from '@storybook/blocks'; + +import * as NoticeBannerStories from '../stories/index.stories.tsx'; + + + +# NoticeBanner + +An informational UI displayed near the top of the store pages. + + + +## Design Guidelines + +`NoticeBanner` is an informational UI element displayed near the top of store pages used to indicate the result of an action, or to draw the user's attention to necessary information. + +Notices are color-coded to indicate the type of message being communicated, and also show an icon to reinforce the meaning of the message. The color and icon used for a notice are determined by the `status` prop. + +### Default Notices + +By default, noices are grey and used for less important messaging. + + + +### Informational Notices + +Blue notices with an info icon are used for general information for buyers, but do not require them to take an action. + + + +### Error Notices + +Red notices with an alert icon are used to show that an error has occurred and that the user needs to take action. + + + +### Success Notices + +Green notices with a success icon are used to show an action was successful. + + + +### Warning Notices + +Yellow notices with an alert icon are used to show that the user may need to take action, or needs to be aware of something important. + + + +### Error Summary + +If you provide a `summary` it will be displayed above the notice content. This can be useful for displaying a summary of errors in a list format. + + + +## Development Guidelines + +### Props + + + +### Usage examples + +#### Example: string based notices + +To display a basic notice, pass the notice message as a string: + +```jsx +import { NoticeBanner } from '@woocommerce/base-components'; + +Your message here; +``` + +#### Example: components within notices + +For more complex markup, you can wrap any JSX element: + +```jsx +import { NoticeBanner } from '@woocommerce/base-components'; + + +

    + An error occurred: { errorDetails }. +

    +
    ; +``` + +#### Example: list of notices + +In this example, the summary prop is used to indicate to the user that there are errors in the form submission. + +```typescript +import { NoticeBanner } from '@woocommerce/base-components'; + +const errorMessages = [ + 'First error message', + 'Second error message', + 'Third error message', +]; + + +
      + { errorMessages.map( ( message ) => ( +
    • { message }
    • + ) ) } +
    +
    ; +``` + +The list of error messages is rendered within the NoticeBanner component using an unordered list (`
      `) and list items (`
    • `). The `status` prop is set to `error` to indicate that the notice represents an error message. diff --git a/assets/js/base/components/notice-banner/index.tsx b/assets/js/base/components/notice-banner/index.tsx index 50dcbac641b..91464d8dc78 100644 --- a/assets/js/base/components/notice-banner/index.tsx +++ b/assets/js/base/components/notice-banner/index.tsx @@ -14,29 +14,20 @@ import Button from '../button'; import { useSpokenMessage } from '../../hooks'; export interface NoticeBannerProps { - // The displayed message of a notice. Also used as the spoken message for assistive technology, unless `spokenMessage` is provided as an alternative message. children: React.ReactNode; - // Additional class name to give to the notice. - className?: string | undefined; - // Determines whether the notice can be dismissed by the user. - isDismissible?: boolean | undefined; - // Function called when dismissing the notice. - onRemove?: ( () => void ) | undefined; - // Determines the level of politeness for the notice for assistive technology. - politeness?: 'polite' | 'assertive' | undefined; - // Optionally provided to change the spoken message for assistive technology. - spokenMessage?: string | React.ReactNode | undefined; - // Status determines the color of the notice and the icon. + className?: string; + isDismissible?: boolean; + onRemove?: () => void; + politeness?: 'polite' | 'assertive'; + spokenMessage?: string | React.ReactNode; status: 'success' | 'error' | 'info' | 'warning' | 'default'; - // Optional summary text shown above notice content, used when several notices are listed together. - summary?: string | undefined; + summary?: string; } /** - * NoticeBanner: An informational UI displayed near the top of the store pages. + * NoticeBanner component. * - * Notices are informational UI displayed near the top of store pages. WooCommerce blocks, themes, and plugins all use - * notices to indicate the result of an action, or to draw the user’s attention to necessary information. + * An informational UI displayed near the top of the store pages. */ const NoticeBanner = ( { className, diff --git a/assets/js/base/components/notice-banner/screenshots/default.png b/assets/js/base/components/notice-banner/screenshots/default.png deleted file mode 100644 index b332186648e95164e6ba225874934eca051147ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9342 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 G;Qs-b4Gs-MNTVo%fYgAL#Lyi> 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 diff --git a/assets/js/base/components/notice-banner/screenshots/warning.png b/assets/js/base/components/notice-banner/screenshots/warning.png deleted file mode 100644 index 8fcf176155b350239b146e86788fed7fe0cac045..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9866 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 eB3IBm#NyDDSGAaL; -const Template: Story< NoticeBannerProps > = ( args ) => { +const Template: StoryFn< NoticeBannerProps > = ( args ) => { return ; }; @@ -84,7 +86,7 @@ Warning.args = { export const Info = Template.bind( {} ); Info.args = { - children: 'This is an info notice', + children: 'This is an informational notice', status: 'info', }; diff --git a/assets/js/base/components/price-slider/stories/index.tsx b/assets/js/base/components/price-slider/stories/index.stories.tsx similarity index 95% rename from assets/js/base/components/price-slider/stories/index.tsx rename to assets/js/base/components/price-slider/stories/index.stories.tsx index 7aef92ad2d3..053dd1ac0d2 100644 --- a/assets/js/base/components/price-slider/stories/index.tsx +++ b/assets/js/base/components/price-slider/stories/index.stories.tsx @@ -11,7 +11,7 @@ import { currencies, currencyControl } from '@woocommerce/storybook-controls'; import PriceSlider, { PriceSliderProps } from '..'; export default { - title: 'WooCommerce Blocks/@base-components/PriceSlider', + title: 'Base Components/PriceSlider', component: PriceSlider, args: { currency: currencies.USD, diff --git a/assets/js/base/components/product-name/stories/index.tsx b/assets/js/base/components/product-name/stories/index.stories.tsx similarity index 88% rename from assets/js/base/components/product-name/stories/index.tsx rename to assets/js/base/components/product-name/stories/index.stories.tsx index ee5546d23f4..ff9551fcf34 100644 --- a/assets/js/base/components/product-name/stories/index.tsx +++ b/assets/js/base/components/product-name/stories/index.stories.tsx @@ -9,7 +9,7 @@ import type { Story, Meta } from '@storybook/react'; import ProductName, { ProductNameProps } from '..'; export default { - title: 'WooCommerce Blocks/@base-components/cart-checkout/ProductName', + title: 'Base Components/ProductName', component: ProductName, args: { name: 'Test product', diff --git a/assets/js/base/components/product-price/stories/index.tsx b/assets/js/base/components/product-price/stories/index.stories.tsx similarity index 82% rename from assets/js/base/components/product-price/stories/index.tsx rename to assets/js/base/components/product-price/stories/index.stories.tsx index 736e2d8a850..a47a74570be 100644 --- a/assets/js/base/components/product-price/stories/index.tsx +++ b/assets/js/base/components/product-price/stories/index.stories.tsx @@ -12,7 +12,7 @@ import ProductPrice, { ProductPriceProps } from '..'; const ALLOWED_ALIGN_VALUES = [ 'left', 'center', 'right' ]; export default { - title: 'WooCommerce Blocks/@base-components/ProductPrice', + title: 'Base Components/ProductPrice', component: ProductPrice, argTypes: { align: { @@ -25,6 +25,15 @@ export default { align: 'left', format: '', price: 3000, + currency: { + code: 'USD', + symbol: '$', + thousandSeparator: ' ', + decimalSeparator: '.', + minorUnit: 2, + prefix: '$', + suffix: '', + }, }, } as Meta< ProductPriceProps >; diff --git a/assets/js/base/components/quantity-selector/stories/index.tsx b/assets/js/base/components/quantity-selector/stories/index.stories.tsx similarity index 92% rename from assets/js/base/components/quantity-selector/stories/index.tsx rename to assets/js/base/components/quantity-selector/stories/index.stories.tsx index 13c1e48e3da..321c8404ae9 100644 --- a/assets/js/base/components/quantity-selector/stories/index.tsx +++ b/assets/js/base/components/quantity-selector/stories/index.stories.tsx @@ -10,7 +10,7 @@ import type { Story, Meta } from '@storybook/react'; import QuantitySelector, { QuantitySelectorProps } from '..'; export default { - title: 'WooCommerce Blocks/@base-components/QuantitySelector', + title: 'Base Components/QuantitySelector', component: QuantitySelector, args: { itemName: 'widgets', diff --git a/assets/js/base/components/read-more/stories/index.tsx b/assets/js/base/components/read-more/stories/index.stories.tsx similarity index 96% rename from assets/js/base/components/read-more/stories/index.tsx rename to assets/js/base/components/read-more/stories/index.stories.tsx index c6d9470af83..c283dc56dfe 100644 --- a/assets/js/base/components/read-more/stories/index.tsx +++ b/assets/js/base/components/read-more/stories/index.stories.tsx @@ -9,7 +9,7 @@ import type { Story, Meta } from '@storybook/react'; import ReadMore, { defaultProps, ReadMoreProps } from '..'; export default { - title: 'WooCommerce Blocks/@base-components/ReadMore', + title: 'Base Components/ReadMore', component: ReadMore, args: defaultProps, argTypes: { diff --git a/assets/js/base/components/snackbar-list/README.md b/assets/js/base/components/snackbar-list/README.md deleted file mode 100644 index b3bc398b3ea..00000000000 --- a/assets/js/base/components/snackbar-list/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# SnackbarList Component - -A temporary informational UI displayed at the bottom of store pages. - -## Table of contents - -- [Design Guidelines](#design-guidelines) -- [Development Guidelines](#development-guidelines) - - [Usage](#usage) - - [Props](#props) - - [`className`: `string`](#classname-string) - - [`onRemove`: `( noticeId ) => void`](#onremove--noticeid---void) - - [`notices`: `NoticeType[]`](#notices-noticetype) - -## Design Guidelines - -The buyer notice snackbar is temporary informational UI displayed at the bottom of store pages. WooCommerce blocks, themes, and plugins all use snackbar notices to indicate the result of a successful action. - -Snackbar notices work in the same way as the NoticeBanner component, and support the same statuses and styles. - -## Development Guidelines - -### Usage - -To display snackbar notices, pass an array of `notices` to the `SnackbarList` component: - -```jsx -import { SnackbarList } from '@woocommerce/base-components'; - -const notices = [ - { - id: '1', - content: 'This is a snackbar notice.', - status: 'default', - isDismissible: true, - } -]; - -; -``` - -The component consuming `SnackbarList` is responsible for managing the notices state. The `SnackbarList` component will automatically remove notices from the list when they are dismissed by the user using the provided `onRemove` callback, and also when the notice times out after 10000ms. - -### Props - -#### `className`: `string` - -Additional class name to give to the notice. - -#### `onRemove`: `( noticeId ) => void` - -Function called when dismissing the notice. When the close icon is clicked or the Escape key is pressed, this function will be called. This is also called when the notice times out after 10000ms. - -#### `notices`: `NoticeType[]` - -A list of notices to display as snackbars. Each notice must have an `id` and `content` prop. - -- The `id` prop is used to identify the notice and should be unique. -- The `content` prop is the content to display in the notice. -- The `status` prop is used to determine the color of the notice and the icon. Acceptable values are 'success', 'error', 'info', 'warning', and 'default'. -- The `isDismissible` prop determines whether the notice can be dismissed by the user. -- The `spokenMessage` prop is used to change the spoken message for assistive technology. If not provided, the `content` prop will be used as the spoken message. diff --git a/assets/js/base/components/snackbar-list/docs/docs.mdx b/assets/js/base/components/snackbar-list/docs/docs.mdx new file mode 100644 index 00000000000..b52bb45da9d --- /dev/null +++ b/assets/js/base/components/snackbar-list/docs/docs.mdx @@ -0,0 +1,90 @@ +import { Canvas, Meta, ArgTypes } from '@storybook/blocks'; + +import * as SnackbarListStories from '../stories/index.stories.tsx'; +import * as SnackbarStories from '../stories/snackbar.stories.tsx'; + + + +# SnackbarList + +A temporary informational UI element displayed at the bottom of store pages. + + + +## Design Guidelines + +The Snackbar is a temporary informational UI element displayed at the bottom of store pages. WooCommerce blocks, themes, and plugins all use snackbar notices to indicate the result of a successful action. For example, adding something to the cart. + +Snackbar notices work in the same way as the NoticeBanner component, and support the same statuses and styles. + +### Default Snackbars + +By default, notices are grey and used for less important messaging. + + + +### Informational Snackbars + +Blue notices with an info icon are used for general information for buyers, but do not require action. + + + +### Error Snackbars + +Red notices with an alert icon are used to show that an error has occurred and that the user needs to take action. + + + +### Success Snackbars + +Green notices with a success icon are used to show an action was successful. + + + +### Warning Snackbars + +Yellow notices with an alert icon are used to show that the user may need to take action, or needs to be aware of something important. + + + +## Development Guidelines + +The component consuming `SnackbarList` is responsible for managing the notices state. The `SnackbarList` component will automatically remove notices from the list when they are dismissed by the user using the provided `onRemove` callback, and also when the notice times out after 10000ms. + +### Props + + + +### NoticeType + +Each notice can be provided with the following args. + +- The `id` (required) prop is used to identify the notice and should be unique. +- The `content` (required) prop is the content to display in the notice. +- The `status` prop is used to determine the color of the notice and the icon. Acceptable values are 'success', 'error', 'info', 'warning', and 'default'. +- The `isDismissible` prop determines whether the notice can be dismissed by the user. +- The `spokenMessage` prop is used to change the spoken message for assistive technology. If not provided, the `content` prop will be used as the spoken message. + +### Usage example + +To display snackbar notices, pass an array of `notices` to the `SnackbarList` component: + +```jsx +import { SnackbarList } from '@woocommerce/base-components'; + +const notices = [ + { + id: '1', + content: 'This is a snackbar notice.', + status: 'default', + isDismissible: true, + spokenMessage: "Hello snackbar!" + } +]; + +; +``` diff --git a/assets/js/base/components/snackbar-list/snackbar.tsx b/assets/js/base/components/snackbar-list/snackbar.tsx index 41718d819cf..a4758e1ea2e 100644 --- a/assets/js/base/components/snackbar-list/snackbar.tsx +++ b/assets/js/base/components/snackbar-list/snackbar.tsx @@ -2,6 +2,7 @@ * External dependencies */ import { useEffect } from '@wordpress/element'; +import classNames from 'classnames'; /** * Internal dependencies @@ -9,15 +10,18 @@ import { useEffect } from '@wordpress/element'; import NoticeBanner, { NoticeBannerProps } from '../notice-banner'; import { SNACKBAR_TIMEOUT } from './constants'; -const Snackbar = ( { +export interface SnackbarProps extends NoticeBannerProps { + // A ref to the list that contains the snackbar. + listRef?: React.MutableRefObject< HTMLDivElement | null >; +} + +export const Snackbar = ( { onRemove = () => void 0, children, listRef, + className, ...notice -}: { - // A ref to the list that contains the snackbar. - listRef?: React.MutableRefObject< HTMLDivElement | null >; -} & NoticeBannerProps ) => { +}: SnackbarProps ) => { // Only set up the timeout dismiss if we're not explicitly dismissing. useEffect( () => { const timeoutHandle = setTimeout( () => { @@ -29,6 +33,10 @@ const Snackbar = ( { return ( { // Prevent focus loss by moving it to the list element. diff --git a/assets/js/base/components/snackbar-list/stories/index.tsx b/assets/js/base/components/snackbar-list/stories/index.stories.tsx similarity index 73% rename from assets/js/base/components/snackbar-list/stories/index.tsx rename to assets/js/base/components/snackbar-list/stories/index.stories.tsx index 9c8df331e5d..73c4874efaa 100644 --- a/assets/js/base/components/snackbar-list/stories/index.tsx +++ b/assets/js/base/components/snackbar-list/stories/index.stories.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import type { Story, Meta } from '@storybook/react'; +import type { StoryFn, Meta } from '@storybook/react'; /** * Internal dependencies @@ -9,7 +9,7 @@ import type { Story, Meta } from '@storybook/react'; import SnackbarList, { SnackbarListProps } from '../'; export default { - title: 'WooCommerce Blocks/@base-components/SnackbarList', + title: 'Base Components/SnackbarList', args: { notices: [ { @@ -28,18 +28,20 @@ export default { control: 'text', }, notices: { - description: 'List of notice objects to show as snackbar notices.', + description: + 'A list of notices to display as snackbars. Each notice must have an `id` and `content` prop.', disable: true, }, onRemove: { - description: 'Function called when dismissing the notice(s).', + description: + 'Function called when dismissing the notice. When the close icon is clicked or the Escape key is pressed, this function will be called. This is also called when the notice times out after 10000ms.', disable: true, }, }, component: SnackbarList, } as Meta< SnackbarListProps >; -const Template: Story< SnackbarListProps > = ( args ) => { +const Template: StoryFn< SnackbarListProps > = ( args ) => { return ; }; diff --git a/assets/js/base/components/snackbar-list/stories/snackbar.stories.tsx b/assets/js/base/components/snackbar-list/stories/snackbar.stories.tsx new file mode 100644 index 00000000000..5b9f7dd332f --- /dev/null +++ b/assets/js/base/components/snackbar-list/stories/snackbar.stories.tsx @@ -0,0 +1,97 @@ +/** + * External dependencies + */ +import type { StoryFn, Meta } from '@storybook/react'; + +/** + * Internal dependencies + */ +import Snackbar, { SnackbarProps } from '../snackbar'; +const availableStatus = [ 'default', 'success', 'error', 'warning', 'info' ]; + +export default { + title: 'Base Components/SnackbarList/Snackbar', + argTypes: { + status: { + control: 'radio', + options: availableStatus, + description: + 'Status determines the color of the notice and the icon.', + }, + isDismissible: { + control: 'boolean', + description: + 'Determines whether the notice can be dismissed by the user. When set to true, a close icon will be displayed on the banner.', + }, + summary: { + description: + 'Optional summary text shown above notice content, used when several notices are listed together.', + control: 'text', + }, + className: { + description: 'Additional class name to give to the notice.', + control: 'text', + }, + spokenMessage: { + description: + 'Optionally provided to change the spoken message for assistive technology. If not provided, the `children` prop will be used as the spoken message.', + control: 'text', + }, + politeness: { + control: 'radio', + options: [ 'polite', 'assertive' ], + description: + 'Determines the level of politeness for the notice for assistive technology.', + }, + children: { + description: + 'The displayed message of a notice. Also used as the spoken message for assistive technology, unless `spokenMessage` is provided as an alternative message.', + disable: true, + }, + onRemove: { + description: + 'Function called when dismissing the notice. When the close icon is clicked or the Escape key is pressed, this function will be called.', + disable: true, + }, + }, + component: Snackbar, +} as Meta< SnackbarProps >; + +const Template: StoryFn< SnackbarProps > = ( args ) => { + return ; +}; + +export const Default = Template.bind( {} ); +Default.args = { + children: 'This is a default snackbar notice', + status: 'default', + isDismissible: true, + summary: undefined, + className: undefined, + spokenMessage: undefined, + politeness: undefined, +}; + +export const Error = Template.bind( {} ); +Error.args = { + children: 'This is an error snackbar notice', + status: 'error', +}; + +export const Warning = Template.bind( {} ); +Warning.args = { + children: 'This is a warning snackbar notice', + status: 'warning', +}; + +export const Info = Template.bind( {} ); +Info.args = { + children: 'This is an informational snackbar notice', + status: 'info', +}; + +export const Success = Template.bind( {} ); +Success.args = { + children: 'This is a success snackbar notice', + status: 'success', +}; diff --git a/assets/js/base/components/snackbar-list/style.scss b/assets/js/base/components/snackbar-list/style.scss index 75b68b8aec8..8cc5b70eba4 100644 --- a/assets/js/base/components/snackbar-list/style.scss +++ b/assets/js/base/components/snackbar-list/style.scss @@ -9,25 +9,32 @@ bottom: $gap-large; left: $gap-large; right: $gap-large; +} + +.wc-block-components-notice-snackbar-list .wc-block-components-notice-banner, +.wc-block-components-notice-banner.wc-block-components-notice-snackbar { + display: inline-flex; + width: auto; + max-width: 600px; + pointer-events: all; + border: 1px solid transparent; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + position: relative; + margin: $gap-large $gap 0 0; - .wc-block-components-notice-banner { - display: inline-flex; - width: auto; - max-width: 600px; - pointer-events: all; - border: 1px solid transparent; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); - position: relative; - margin: $gap-large 0 0; + &.is-error, + &.is-info, + &.is-success { + border-color: transparent; + } - &.is-default { - border-color: $gray-800; - } + &.is-default { + border-color: $gray-800; + } - @include breakpoint("<782px") { - width: 100%; - max-width: none; - } + @include breakpoint("<782px") { + width: 100%; + max-width: none; } } diff --git a/assets/js/base/components/tabs/stories/index.tsx b/assets/js/base/components/tabs/stories/index.stories.tsx similarity index 95% rename from assets/js/base/components/tabs/stories/index.tsx rename to assets/js/base/components/tabs/stories/index.stories.tsx index 880c2c54230..1be8237ade7 100644 --- a/assets/js/base/components/tabs/stories/index.tsx +++ b/assets/js/base/components/tabs/stories/index.stories.tsx @@ -10,7 +10,7 @@ import { useState } from '@wordpress/element'; import { __TabsWithoutInstanceId as Tabs, TabsProps } from '..'; export default { - title: 'WooCommerce Blocks/@base-components/Tabs', + title: 'Base Components/Tabs', component: Tabs, args: { tabs: [ diff --git a/assets/js/base/utils/test/errors.js b/assets/js/base/utils/test/errors.js index 50b5bd6a346..fca815580a7 100644 --- a/assets/js/base/utils/test/errors.js +++ b/assets/js/base/utils/test/errors.js @@ -35,12 +35,10 @@ describe( 'formatError', () => { const mockResponse = new Response( mockMalformedJson, { status: 400 } ); const error = await formatError( mockResponse ); - const expectedError = { - message: - 'invalid json response body at reason: Unexpected end of JSON input', - type: 'general', - }; - expect( error ).toEqual( expectedError ); + expect( error.message ).toContain( + 'invalid json response body at reason:' + ); + expect( error.type ).toEqual( 'general' ); } ); } ); diff --git a/assets/js/editor-components/error-placeholder/index.tsx b/assets/js/editor-components/error-placeholder/index.tsx index ff363b93e86..e284fa39f83 100644 --- a/assets/js/editor-components/error-placeholder/index.tsx +++ b/assets/js/editor-components/error-placeholder/index.tsx @@ -40,11 +40,11 @@ export interface ErrorPlaceholderProps { /** * Callback to retry an action. */ - onRetry?: () => void; + onRetry?: ( () => void ) | undefined; } const ErrorPlaceholder = ( { - className, + className = '', error, isLoading = false, onRetry, diff --git a/assets/js/editor-components/error-placeholder/stories/error-message.tsx b/assets/js/editor-components/error-placeholder/stories/error-message.stories.tsx similarity index 88% rename from assets/js/editor-components/error-placeholder/stories/error-message.tsx rename to assets/js/editor-components/error-placeholder/stories/error-message.stories.tsx index 03c43700202..94cc9368373 100644 --- a/assets/js/editor-components/error-placeholder/stories/error-message.tsx +++ b/assets/js/editor-components/error-placeholder/stories/error-message.stories.tsx @@ -9,7 +9,7 @@ import type { Story, Meta } from '@storybook/react'; import ErrorMessage, { ErrorMessageProps } from '../error-message'; export default { - title: 'WooCommerce Blocks/editor-components/Errors/Base Error Atom', + title: 'Editor Components/Errors/Base Error Atom', component: ErrorMessage, } as Meta< ErrorMessageProps >; diff --git a/assets/js/editor-components/error-placeholder/stories/error-placeholder.tsx b/assets/js/editor-components/error-placeholder/stories/error-placeholder.stories.tsx similarity index 84% rename from assets/js/editor-components/error-placeholder/stories/error-placeholder.tsx rename to assets/js/editor-components/error-placeholder/stories/error-placeholder.stories.tsx index c56a1d7d28e..bd133168a3e 100644 --- a/assets/js/editor-components/error-placeholder/stories/error-placeholder.tsx +++ b/assets/js/editor-components/error-placeholder/stories/error-placeholder.stories.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import type { Story, Meta } from '@storybook/react'; +import type { StoryFn, Meta } from '@storybook/react'; import { useArgs } from '@storybook/client-api'; import { INTERACTION_TIMEOUT } from '@woocommerce/storybook-controls'; @@ -11,11 +11,11 @@ import { INTERACTION_TIMEOUT } from '@woocommerce/storybook-controls'; import ErrorPlaceholder, { ErrorPlaceholderProps } from '..'; export default { - title: 'WooCommerce Blocks/editor-components/Errors', + title: 'Editor Components/Errors/Error Placeholder', component: ErrorPlaceholder, } as Meta< ErrorPlaceholderProps >; -const Template: Story< ErrorPlaceholderProps > = ( args ) => { +const Template: StoryFn< ErrorPlaceholderProps > = ( args ) => { const [ { isLoading }, setArgs ] = useArgs(); const onRetry = args.onRetry @@ -63,7 +63,7 @@ UnknownError.args = { }, }; -export const NoRetry: Story< ErrorPlaceholderProps > = ( args ) => { +export const NoRetry: StoryFn< ErrorPlaceholderProps > = ( args ) => { return ; }; NoRetry.args = { diff --git a/assets/js/editor-components/external-link-card/stories/index.tsx b/assets/js/editor-components/external-link-card/stories/index.stories.tsx similarity index 91% rename from assets/js/editor-components/external-link-card/stories/index.tsx rename to assets/js/editor-components/external-link-card/stories/index.stories.tsx index 578b179eece..9bc59a27d06 100644 --- a/assets/js/editor-components/external-link-card/stories/index.tsx +++ b/assets/js/editor-components/external-link-card/stories/index.stories.tsx @@ -9,7 +9,7 @@ import type { Story, Meta } from '@storybook/react'; import ExternalLinkCard, { ExternalLinkCardProps } from '..'; export default { - title: 'WooCommerce Blocks/editor-components/ExternalLinkCard', + title: 'Editor Components/ExternalLinkCard', component: ExternalLinkCard, } as Meta< ExternalLinkCardProps >; diff --git a/assets/js/editor-components/search-list-control/test/__snapshots__/index.js.snap b/assets/js/editor-components/search-list-control/test/__snapshots__/index.js.snap index b853b6a6eb9..bbe4da44b26 100644 --- a/assets/js/editor-components/search-list-control/test/__snapshots__/index.js.snap +++ b/assets/js/editor-components/search-list-control/test/__snapshots__/index.js.snap @@ -47,13 +47,13 @@ Object { class="woocommerce-search-list__search" >