Skip to content

Commit

Permalink
Introduce <Notice> component for WooCommerce Admin (#44620)
Browse files Browse the repository at this point in the history
* Introduce Notice component

* minor alignment fix

* Fix MD title increment

* Fix readme lint

* add changelog entry

* fix CSS lints

* Small tweaks.

Regularising the spelling of isDismissible.
Using sanitizeHTML to sanitize the notice description.
Making the variant classes like &-success to make them a bit shorter.
Adjusting wording of README to convey that this component is designed for the marketplace, though it can be used elsewhere.
Tweaking whitespace in one place in the TSX to please the linter.

* Update plugins/woocommerce-admin/client/marketplace/components/notice/notice.tsx

Co-authored-by: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>

* address feedback

* Changed notice classnames to use `__{$variant}` pattern as well. Restored 40px bottom margin to notices on viewports 600px and above. Added height 24px on icons to ensure they're nicely vertically centred.

---------

Co-authored-by: Remi Corson <1649788+corsonr@users.noreply.github.com>
Co-authored-by: And Finally <andfinally@users.noreply.github.com>
Co-authored-by: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
  • Loading branch information
4 people committed Feb 20, 2024
1 parent 267d530 commit 1cbc044
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Notice Component

The `Notice` component is a versatile notification UI element designed for use within the WooCommerce in-app marketplace.
It leverages the `@wordpress/icons` for displaying various icons and provides customizable options for content, appearance, and behavior.

## Usage

To use the Notice component, import it into your React component file and include it in your component's render method. Here is a basic example:

```jsx
import Notice from '../notice/notice';

function MyComponent() {
return (
<Notice
id="unique-notice-id"
description="This is a notice description."
icon="info"
isDismissible={true}
variant="info"
>
<p>Additional content can go here.</p>
</Notice>
);
}

export default MyComponent;
```

## Props

The Notice component accepts the following props for customization:

- `id` (string): A unique identifier for the notice. This is used for dismissing and storing the dismissal state.
- `children` (JSX.Element): Optional. Additional React components or HTML elements to be displayed within the notice.
- `description` (string): The content of the notice. Can include HTML.
- `icon` (string): Optional. The name of the icon to display. Available options are info, check, percent.
- `isDismissible` (boolean): Optional. If true, displays a close button that hides the notice. Defaults to true.
- `variant` (string): Determines the style of the notice. Options are info, warning, error, success.

## Styling

The notice component can be styled using the following CSS classes, based on the variant prop:

- `.woocommerce-marketplace__notice-info`: Styles the notice with an info appearance.
- `.woocommerce-marketplace__notice-warning`: Styles the notice with a warning appearance.
- `.woocommerce-marketplace__notice-error`: Styles the notice with an error appearance.
- `.woocommerce-marketplace__notice-success`: Styles the notice with a success appearance.

Icons within the notice adopt the variant prop to determine their color, aligning with the overall style of the notice.

## Dismissal Behavior

Notices can be dismissed if `isDismissible` is set to `true`. The dismissal state is persisted in the browser's localStorage, preventing the notice from reappearing on future visits.

## Examples

Here are more detailed examples, some require that you import the Button component and/or internationalization functions:

```jsx
<Notice
id="marketplace-sale-march-2024"
variant="info"
description={ __(
'<strong>Limited time sale</strong> Tup to 40% off on extensions and themes. Sale ends March 29 at 2pm UTC.',
'woocommerce'
) }
icon="percent"
isDismissible
>
<Button
variant="secondary"
onClick={ () => {
console.log( 'Primary button clicked' );
} }
text="Label"
/>
<Button
variant="tertiary"
onClick={ () => {
console.log( 'Secondary button clicked' );
} }
text="Label"
/>
</Notice>
```

```jsx
<Notice
id="success-notice"
variant="success"
description={ __(
'<strong>Congratulations</strong> You successfully installed the plugin.',
'woocommerce'
) }
icon="check"
isDismissible
>
<Button
variant="secondary"
onClick={ () => {
console.log( 'Primary button clicked' );
} }
text="Label"
/>
<Button
variant="tertiary"
onClick={ () => {
console.log( 'Secondary button clicked' );
} }
text="Label"
/>
</Notice>
```

```jsx
<Notice
id="warning-notice"
variant="warning"
description={ __(
'This is a warning and I cannot be dismissed. Nope.',
'woocommerce'
) }
icon="info"
isDismissible={ false }
/>

<Notice
id="error-notice"
variant="error"
description={ __(
'I am red and I cannot be dismissed. Nope. But I support <i>HTML</i> <strong>tags</strong>. So <a href="#">I can have links</a>.',
'woocommerce'
) }
icon="info"
isDismissible={ false }
/>
```

```jsx
<Notice
id="success-notice-no-icon"
variant="success"
description={ __(
'I am a success! But I am sad because I do not have an icon.',
'woocommerce'
) }
isDismissible={ false }
/>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
@import '../../stylesheets/_variables.scss';

.woocommerce-marketplace {
&__notice {
align-items: center;
border: 1px solid #f0f0f0;
box-shadow: 0 2px 6px 0 #0000000d;
display: flex;
gap: 12px;
margin-bottom: 32px;
opacity: 1;
padding: 12px;
transition: opacity 1s ease-out;

&--info {
border-left: 4px solid var(--wp-admin-theme-color, #007cba);
}

&--error {
border-left: 4px solid $alert-red;
}

&--warning {
border-left: 4px solid $alert-yellow;
}

&--success {
border-left: 4px solid $valid-green;
}

&-icon {
align-self: flex-start;
height: 24px;

&--info {
fill: var(--wp-admin-theme-color, #007cba);
}

&--error {
fill: $alert-red;
}

&--warning {
fill: $alert-yellow;
}

&--success {
fill: $valid-green;
}
}

&-content {
align-items: flex-start;
display: flex;
flex-direction: column;
flex: 1;
gap: 12px;
}

&-description {
color: $gray-900;
font-size: 13px;
font-weight: 400;
line-height: 20px;
margin: 0;
}

&-children {
color: $gray-900;
font-size: 13px;
font-weight: 400;
gap: 8px;
line-height: 20px;
margin: 0;

.components-button {
margin-right: 12px;
}
}

&-close {
align-self: flex-start;
background: none;
border: none;
cursor: pointer;
height: 24px;
}
}
}

@media only screen and (min-width: 600px) {
.woocommerce-marketplace__notice {
margin-bottom: 40px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* External dependencies
*/
import classNames from 'classnames';
import { useState } from '@wordpress/element';
import { Icon, check, closeSmall, info, percent } from '@wordpress/icons'; // See: https://wordpress.github.io/gutenberg/?path=/docs/icons-icon--docs

/**
* Internal dependencies
*/
import './notice.scss';
import sanitizeHTML from '../../../lib/sanitize-html';

export interface NoticeProps {
id: string;
children?: React.ReactNode;
description: string;
icon?: string;
isDismissible: boolean;
variant: string;
}

type IconKey = keyof typeof iconMap;

// Map the icon name (string) to the actual icon component
const iconMap = {
info,
check,
percent,
};

export default function Notice( props: NoticeProps ): JSX.Element | null {
const {
id,
description,
children,
icon,
isDismissible = true,
variant = 'info',
} = props;
const [ isVisible, setIsVisible ] = useState(
localStorage.getItem( `wc-marketplaceNoticeClosed-${ id }` ) !== 'true'
);

const handleClose = () => {
setIsVisible( false );
localStorage.setItem( `wc-marketplaceNoticeClosed-${ id }`, 'true' );
};

if ( ! isVisible ) return null;

const classes = classNames(
'woocommerce-marketplace__notice',
`woocommerce-marketplace__notice--${ variant }`,
{
'is-dismissible': isDismissible,
}
);

const iconElement = iconMap[ ( icon || 'info' ) as IconKey ];

const iconClass = classNames(
'woocommerce-marketplace__notice-icon',
`woocommerce-marketplace__notice-icon--${ variant }`
);

return (
<div className={ classes }>
{ icon && (
<span className={ iconClass }>
<Icon icon={ iconElement } />
</span>
) }
<div className="woocommerce-marketplace__notice-content">
<p
className="woocommerce-marketplace__notice-description"
dangerouslySetInnerHTML={ sanitizeHTML( description ) }
/>
{ children && (
<div className="woocommerce-marketplace__notice-children">
{ children }
</div>
) }
</div>
{ isDismissible && (
<button
className="woocommerce-marketplace__notice-close"
aria-label="Close"
onClick={ handleClose }
>
<Icon icon={ closeSmall } />
</button>
) }
</div>
);
}
4 changes: 4 additions & 0 deletions plugins/woocommerce/changelog/add-19573-notice-component
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Introduce WooCommerce Admin Notice component

0 comments on commit 1cbc044

Please sign in to comment.