Skip to content

Commit

Permalink
Try using snackbar notices instead of prominent ones for saving/failu…
Browse files Browse the repository at this point in the history
…re notices (#15594)
  • Loading branch information
youknowriad authored May 29, 2019
1 parent 45cd52d commit 4956082
Show file tree
Hide file tree
Showing 29 changed files with 379 additions and 88 deletions.
4 changes: 4 additions & 0 deletions assets/stylesheets/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ $z-layers: (
// .edit-post-header { z-index: 30 }
".components-notice-list": 29,


// Show snackbars above everything (similar to popovers)
".components-snackbar-list": 100000,

// Show modal under the wp-admin menus and the popover
".components-modal__screen-overlay": 100000,

Expand Down
6 changes: 6 additions & 0 deletions docs/manifest-devhub.json
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,12 @@
"markdown_source": "../packages/components/src/slot-fill/README.md",
"parent": "components"
},
{
"title": "Snackbar",
"slug": "snackbar",
"markdown_source": "../packages/components/src/snackbar/README.md",
"parent": "components"
},
{
"title": "Spinner",
"slug": "spinner",
Expand Down
6 changes: 6 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,12 @@
"markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/components/src/slot-fill/README.md",
"parent": "components"
},
{
"title": "Snackbar",
"slug": "snackbar",
"markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/components/src/snackbar/README.md",
"parent": "components"
},
{
"title": "Spinner",
"slug": "spinner",
Expand Down
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### New Feature

- Added a new `HorizontalRule` component.
- Added a new `Snackbar` component.

### Bug Fix

Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export { default as ResizableBox } from './resizable-box';
export { default as ResponsiveWrapper } from './responsive-wrapper';
export { default as SandBox } from './sandbox';
export { default as SelectControl } from './select-control';
export { default as Snackbar } from './snackbar';
export { default as SnackbarList } from './snackbar/list';
export { default as Spinner } from './spinner';
export { default as ServerSideRender } from './server-side-render';
export { default as TabPanel } from './tab-panel';
Expand Down
48 changes: 48 additions & 0 deletions packages/components/src/snackbar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Snackbar

Use Snackbars to communicate low priority, non-interruptive messages to the user.

## Table of contents

1. [Design guidelines](#design-guidelines)
2. [Development guidelines](#development-guidelines)
3. [Related components](#related-components)

## Design guidelines

A Snackbar displays a succinct message that is cleared out after a small delay. It can also offer the user options, like viewing a published post but these options should also be available elsewhere in the UI.

## Development guidelines

### Usage

To display a plain snackbar, pass the message as a `children` prop:

```jsx
const MySnackbarNotice = () => (
<Snackbar>
Post published successfully.
</Snackbar>
);
```

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

```jsx
const MySnackbarNotice = () => (
<Snackbar>
<p>An error occurred: <code>{ errorDetails }</code>.</p>
</Snackbar>
);
```

#### Props

The following props are used to control the display of the component.

* `onRemove`: function called when dismissing the notice.
* `actions`: (array) an array of action objects. Each member object should contain a `label` and either a `url` link string or `onClick` callback function. A `className` property can be used to add custom classes to the button styles.

## Related components

- To create a prominent message that requires a higher-level of attention, use a Notice.
86 changes: 86 additions & 0 deletions packages/components/src/snackbar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* External dependencies
*/
import { noop } from 'lodash';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { Button } from '../';

const NOTICE_TIMEOUT = 10000;

function Snackbar( {
className,
children,
actions = [],
onRemove = noop,
} ) {
useEffect( () => {
// This rule doesn't account yet for React Hooks
// eslint-disable-next-line @wordpress/react-no-unsafe-timeout
const timeoutHandle = setTimeout( () => {
onRemove();
}, NOTICE_TIMEOUT );

return () => clearTimeout( timeoutHandle );
}, [] );

const classes = classnames( className, 'components-snackbar' );

return (
<div
className={ classes }
onClick={ onRemove }
tabIndex="0"
role="button"
onKeyPress={ onRemove }
label={ __( 'Dismiss this notice' ) }
>
<div className="components-snackbar__content">
{ children }
{ actions.map(
(
{
className: buttonCustomClasses,
label,
onClick,
url,
},
index
) => {
return (
<Button
key={ index }
href={ url }
isTertiary
onClick={ ( event ) => {
event.stopPropagation();
if ( onClick ) {
onClick( event );
}
} }
className={ classnames(
'components-snackbar__action',
buttonCustomClasses
) }
>
{ label }
</Button>
);
}

) }
</div>
</div>
);
}

export default Snackbar;
42 changes: 42 additions & 0 deletions packages/components/src/snackbar/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { omit, noop } from 'lodash';

/**
* Internal dependencies
*/
import Snackbar from './';

/**
* Renders a list of notices.
*
* @param {Object} $0 Props passed to the component.
* @param {Array} $0.notices Array of notices to render.
* @param {Function} $0.onRemove Function called when a notice should be removed / dismissed.
* @param {Object} $0.className Name of the class used by the component.
* @param {Object} $0.children Array of children to be rendered inside the notice list.
* @return {Object} The rendered notices list.
*/
function SnackbarList( { notices, className, children, onRemove = noop } ) {
className = classnames( 'components-snackbar-list', className );
const removeNotice = ( id ) => () => onRemove( id );

return (
<div className={ className }>
{ children }
{ notices.map( ( notice ) => (
<Snackbar
{ ...omit( notice, [ 'content' ] ) }
key={ notice.id }
onRemove={ removeNotice( notice.id ) }
>
{ notice.content }
</Snackbar>
) ) }
</div>
);
}

export default SnackbarList;
57 changes: 57 additions & 0 deletions packages/components/src/snackbar/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.components-snackbar {
font-family: $default-font;
font-size: $default-font-size;
background-color: $dark-gray-700;
border-radius: $radius-round-rectangle;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
color: $white;
padding: 16px 24px;
width: 100%;
max-width: 600px;
margin: 8px 0 0;

@include break-small() {
width: fit-content;
}

&:hover {
background-color: $dark-gray-900;
}

&:focus {
background-color: $dark-gray-900;
box-shadow:
0 0 0 1px $white,
0 0 0 3px $blue-medium-focus;
}
}

.components-snackbar__action.components-button {
margin-left: 32px;
color: $white;
height: auto;
flex-shrink: 0;
line-height: $default-line-height;

&:not(:disabled):not([aria-disabled="true"]):not(.is-default) {
text-decoration: underline;

&:hover {
color: $white;
text-decoration: none;
}
}
}

.components-snackbar__content {
display: flex;
align-items: baseline;
justify-content: space-between;
line-height: $default-line-height;
}

.components-snackbar-list {
position: absolute;
z-index: z-index(".components-snackbar-list");
width: 100%;
}
1 change: 1 addition & 0 deletions packages/components/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
@import "./sandbox/style.scss";
@import "./scroll-lock/style.scss";
@import "./select-control/style.scss";
@import "./snackbar/style.scss";
@import "./spinner/style.scss";
@import "./text-control/style.scss";
@import "./textarea-control/style.scss";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
*/
export async function publishPostWithPrePublishChecksDisabled() {
await page.click( '.editor-post-publish-button' );
return page.waitForSelector( '.components-notice.is-success' );
return page.waitForSelector( '.components-snackbar' );
}
2 changes: 1 addition & 1 deletion packages/e2e-test-utils/src/publish-post.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ export async function publishPost() {
await page.click( '.editor-post-publish-button' );

// A success notice should show up
return page.waitForSelector( '.components-notice.is-success' );
return page.waitForSelector( '.components-snackbar' );
}
8 changes: 4 additions & 4 deletions packages/e2e-tests/specs/reusable-blocks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe( 'Reusable Blocks', () => {

// Wait for creation to finish
await page.waitForXPath(
'//*[contains(@class, "components-notice") and contains(@class, "is-success")]/*[text()="Block created."]'
'//*[contains(@class, "components-snackbar")]/*[text()="Block created."]'
);

// Select all of the text in the title field by triple-clicking on it. We
Expand Down Expand Up @@ -84,7 +84,7 @@ describe( 'Reusable Blocks', () => {

// Wait for creation to finish
await page.waitForXPath(
'//*[contains(@class, "components-notice") and contains(@class, "is-success")]/*[text()="Block created."]'
'//*[contains(@class, "components-snackbar")]/*[text()="Block created."]'
);

// Save the reusable block
Expand Down Expand Up @@ -184,7 +184,7 @@ describe( 'Reusable Blocks', () => {

// Wait for deletion to finish
await page.waitForXPath(
'//*[contains(@class, "components-notice") and contains(@class, "is-success")]/*[text()="Block deleted."]'
'//*[contains(@class, "components-snackbar")]/*[text()="Block deleted."]'
);

// Check that we have an empty post again
Expand Down Expand Up @@ -221,7 +221,7 @@ describe( 'Reusable Blocks', () => {

// Wait for creation to finish
await page.waitForXPath(
'//*[contains(@class, "components-notice") and contains(@class, "is-success")]/*[text()="Block created."]'
'//*[contains(@class, "components-snackbar")]/*[text()="Block created."]'
);

// Select all of the text in the title field by triple-clicking on it. We
Expand Down
3 changes: 1 addition & 2 deletions packages/edit-post/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ function Layout( {
aria-label={ __( 'Editor content' ) }
tabIndex="-1"
>
<EditorNotices dismissible={ false } className="is-pinned" />
<EditorNotices dismissible={ true } />
<EditorNotices />
<PreserveScrollInReorder />
<EditorModeKeyboardShortcuts />
<KeyboardShortcutHelpModal />
Expand Down
Loading

0 comments on commit 4956082

Please sign in to comment.