Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Feature Branch: Updated Shopper Notices #8659

Merged
merged 34 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5615532
Notice banner component
mikejolley Mar 3, 2023
b6d75c3
Snackbar support
mikejolley Mar 6, 2023
2232685
Switch to new components
mikejolley Mar 6, 2023
a582681
Finish snackbar implementation
mikejolley Mar 7, 2023
3abf7dd
Summary notice
mikejolley Mar 7, 2023
a46319e
Styling issues
mikejolley Mar 7, 2023
bbd6969
Fix text wrap in shipping calculator
mikejolley Mar 7, 2023
2fac70b
Storybook entries
mikejolley Mar 7, 2023
7f733f8
Docs and tests for NoticeBanner
mikejolley Mar 8, 2023
0c20dd6
Framer motion to avoid components dependency
mikejolley Mar 8, 2023
0cf3634
Snackbar list stories
mikejolley Mar 8, 2023
6dee634
Docs for snackbar list
mikejolley Mar 8, 2023
eab547f
Update assets/js/base/components/notice-banner/README.md
mikejolley Mar 10, 2023
94223e7
Update assets/js/base/components/notice-banner/README.md
mikejolley Mar 10, 2023
a803982
Update assets/js/base/components/notice-banner/README.md
mikejolley Mar 10, 2023
e0664d5
Update assets/js/base/components/notice-banner/README.md
mikejolley Mar 10, 2023
b12e81e
Update assets/js/base/components/notice-banner/README.md
mikejolley Mar 10, 2023
162f6c0
Types/docblocks
mikejolley Mar 10, 2023
858cbb6
Docs
mikejolley Mar 10, 2023
57aee76
Update notice type
mikejolley Mar 10, 2023
d2febad
Use NoticeBannerProps for type of noticeProps
mikejolley Mar 10, 2023
588ff75
Raw html to fix notice encoding
mikejolley Mar 10, 2023
ee26899
getClassNameFromStatus is unused
mikejolley Mar 14, 2023
3c6ca19
Update position text
mikejolley Mar 27, 2023
7be7e07
Clarify notice text
mikejolley Mar 27, 2023
7136d35
Fix hover style in whisper TT3 theme
mikejolley Mar 27, 2023
b4b058f
remove div styles
mikejolley Mar 27, 2023
bfd6e24
Add new templates for legacy buyer notices in WooCommerce core (#8732)
mikejolley Mar 28, 2023
19fdfeb
Fix view box tag
mikejolley Mar 30, 2023
dd0c8ef
Hover and focus styles
mikejolley Mar 30, 2023
34abab0
Styling when notices added via ajax
mikejolley Mar 30, 2023
5010ba1
Remove margin change
mikejolley Mar 30, 2023
b03fa4a
Implement react-transition-group instead of framer (#8920)
mikejolley Apr 4, 2023
04b6f7e
Add screenshots to docs
mikejolley Apr 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ module.exports = {
'prop-types',
'react',
'requireindex',
'react-transition-group',
],
'import/resolver': {
node: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
import { __ } from '@wordpress/i18n';
import { useEffect } from '@wordpress/element';
import LoadingMask from '@woocommerce/base-components/loading-mask';
import {
ExperimentalOrderShippingPackages,
StoreNotice,
} from '@woocommerce/blocks-checkout';
import { ExperimentalOrderShippingPackages } from '@woocommerce/blocks-checkout';
import {
getShippingRatesPackageCount,
getShippingRatesRateCount,
Expand All @@ -17,6 +14,7 @@ import {
useEditorContext,
useShippingData,
} from '@woocommerce/base-context';
import NoticeBanner from '@woocommerce/base-components/notice-banner';

/**
* Internal dependencies
Expand Down Expand Up @@ -111,7 +109,7 @@ const ShippingRatesControl = ( {
{ hasSelectedLocalPickup &&
shippingRates.length > 1 &&
! isEditor && (
<StoreNotice
<NoticeBanner
className="wc-block-components-notice"
isDismissible={ false }
status="warning"
Expand All @@ -120,7 +118,7 @@ const ShippingRatesControl = ( {
'Multiple shipments must have the same pickup location',
'woo-gutenberg-products-block'
) }
</StoreNotice>
</NoticeBanner>
) }
<ExperimentalOrderShippingPackages>
<Packages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import type { CartResponseShippingRate } from '@woocommerce/types';
import NoticeBanner from '@woocommerce/base-components/notice-banner';

/**
* Internal dependencies
Expand Down Expand Up @@ -32,11 +33,18 @@ export const ShippingRateSelector = ( {
className="wc-block-components-totals-shipping__options"
noResultsMessage={
<>
{ isAddressComplete &&
__(
'There are no shipping options available. Please check your shipping address.',
'woo-gutenberg-products-block'
) }
{ isAddressComplete && (
<NoticeBanner
isDismissible={ false }
className="wc-block-components-shipping-rates-control__no-results-notice"
status="error"
>
{ __(
'There are no shipping options available. Please check your shipping address.',
'woo-gutenberg-products-block'
) }
</NoticeBanner>
) }
</>
}
shippingRates={ shippingRates }
Expand Down
40 changes: 40 additions & 0 deletions assets/js/base/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export * from './block-error-boundary';
export * from './button';
export * from './cart-checkout';
export * from './checkbox-list';
export * from './chip';
export * from './combobox';
export * from './country-input';
export * from './drawer';
export * from './filter-element-label';
export * from './filter-placeholder';
export * from './filter-reset-button';
export * from './filter-submit-button';
export * from './form';
export * from './form-token-field';
export * from './formatted-monetary-amount';
export * from './label';
export * from './load-more-button';
export * from './loading-mask';
export * from './noninteractive';
export * from './notice-banner';
export * from './pagination';
export * from './price-slider';
export * from './product-list';
export * from './product-name';
export * from './product-price';
export * from './product-rating';
export * from './quantity-selector';
export * from './radio-control';
export * from './radio-control-accordion';
export * from './read-more';
export * from './reviews';
export * from './sidebar-layout';
export * from './snackbar-list';
export * from './sort-select';
export * from './spinner';
export * from './state-input';
export * from './summary';
export * from './tabs';
export * from './textarea';
export * from './title';
138 changes: 138 additions & 0 deletions assets/js/base/components/notice-banner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# NoticeBanner Component <!-- omit in toc -->

An informational UI displayed near the top of the store pages.

## Table of contents <!-- omit in toc -->

- [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';

<NoticeBanner status="info">Your message here</NoticeBanner>;
```

For more complex markup, you can pass any JSX element:

```jsx
import { NoticeBanner } from '@woocommerce/base-components';

<NoticeBanner status="error">
<p>
An error occurred: <code>{ errorDetails }</code>.
</p>
</NoticeBanner>;
```

### 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',
];

<NoticeBanner
status="error"
summary="There are errors in your form submission:"
>
<ul>
{ errorMessages.map( ( message ) => (
<li key={ message }>{ message }</li>
) ) }
</ul>
</NoticeBanner>;
```

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 (`<ul>`) and list items (`<li>`). The `status` prop is set to `error` to indicate that the notice represents an error message.
99 changes: 99 additions & 0 deletions assets/js/base/components/notice-banner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { __ } from '@wordpress/i18n';
import { Icon, close } from '@wordpress/icons';

/**
* Internal dependencies
*/
import './style.scss';
import { getDefaultPoliteness, getStatusIcon } from './utils';
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.
status: 'success' | 'error' | 'info' | 'warning' | 'default';
mikejolley marked this conversation as resolved.
Show resolved Hide resolved
// Optional summary text shown above notice content, used when several notices are listed together.
summary?: string | undefined;
}

/**
* NoticeBanner: An informational UI displayed near the top of the store pages.
*
* 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.
mikejolley marked this conversation as resolved.
Show resolved Hide resolved
*/
const NoticeBanner = ( {
className,
status = 'default',
children,
spokenMessage = children,
onRemove = () => void 0,
isDismissible = true,
politeness = getDefaultPoliteness( status ),
summary,
}: NoticeBannerProps ) => {
useSpokenMessage( spokenMessage, politeness );

const dismiss = ( event: React.SyntheticEvent ) => {
if (
typeof event?.preventDefault === 'function' &&
event.preventDefault
) {
event.preventDefault();
}
onRemove();
};

return (
<div
className={ classnames(
className,
'wc-block-components-notice-banner',
'is-' + status,
{
'is-dismissible': isDismissible,
}
) }
>
<Icon icon={ getStatusIcon( status ) } />
<div className="wc-block-components-notice-banner__content">
{ summary && (
<p className="wc-block-components-notice-banner__summary">
{ summary }
</p>
) }
{ children }
</div>
{ !! isDismissible && (
<Button
className="wc-block-components-notice-banner__dismiss"
icon={ close }
label={ __(
'Dismiss this notice',
'woo-gutenberg-products-block'
) }
onClick={ dismiss }
showTooltip={ false }
/>
) }
</div>
);
};

export default NoticeBanner;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading