Add support for an explicit dismiss button to snackbar, as well as an onDismiss callback (https://github.com/woocommerce/woocommerce-admin/pull/5623)

* Add support for an explicit dismiss button to snackbar, as well as an onDismiss callback

* Fix effect dependencies

* Fix disabling auto-dismiss when explicit dismissing is on, fix cursor styles

* fix noops and dismiss on action

* refactor action click handler

* rename dismiss button class

* increase CES modal placeholder z index

* white. space.

Co-authored-by: Rebecca Scott <me@becdetat.com>
This commit is contained in:
Bec Scott 2020-11-18 12:15:42 +10:00 committed by GitHub
parent 970cf81892
commit ff923bcb82
4 changed files with 80 additions and 17 deletions

View File

@ -40,19 +40,54 @@ function Snackbar(
actions = [], actions = [],
onRemove = noop, onRemove = noop,
icon = null, icon = null,
explicitDismiss = false,
// onDismiss is a callback executed when the snackbar is dismissed.
// It is distinct from onRemove, which _looks_ like a callback but is
// actually the function to call to remove the snackbar from the UI.
onDismiss = null,
}, },
ref ref
) { ) {
onDismiss = onDismiss || noop;
function dismissMe( event ) {
if ( event && event.preventDefault ) {
event.preventDefault();
}
onDismiss();
onRemove();
}
function onActionClick( event, onClick ) {
event.stopPropagation();
if ( explicitDismiss ) {
onRemove();
}
if ( onClick ) {
onClick( event );
}
}
useSpokenMessage( spokenMessage, politeness ); useSpokenMessage( spokenMessage, politeness );
// Only set up the timeout dismiss if we're not explicitly dismissing.
useEffect( () => { useEffect( () => {
const timeoutHandle = setTimeout( () => { const timeoutHandle = setTimeout( () => {
onRemove(); if ( ! explicitDismiss ) {
onDismiss();
onRemove();
}
}, NOTICE_TIMEOUT ); }, NOTICE_TIMEOUT );
return () => clearTimeout( timeoutHandle ); return () => clearTimeout( timeoutHandle );
}, [] ); }, [ onDismiss, onRemove ] );
const classes = classnames( className, 'components-snackbar' ); const classes = classnames( className, 'components-snackbar', {
'components-snackbar-explicit-dismiss': !! explicitDismiss,
} );
if ( actions && actions.length > 1 ) { if ( actions && actions.length > 1 ) {
// we need to inform developers that snackbar only accepts 1 action // we need to inform developers that snackbar only accepts 1 action
warning( warning(
@ -73,11 +108,11 @@ function Snackbar(
<div <div
ref={ ref } ref={ ref }
className={ classes } className={ classes }
onClick={ onRemove } onClick={ ! explicitDismiss ? dismissMe : noop }
tabIndex="0" tabIndex="0"
role="button" role={ ! explicitDismiss ? 'button' : '' }
onKeyPress={ onRemove } onKeyPress={ ! explicitDismiss ? dismissMe : noop }
aria-label={ __( 'Dismiss this notice' ) } aria-label={ ! explicitDismiss ? __( 'Dismiss this notice' ) : '' }
> >
<div className={ snackbarContentClassnames }> <div className={ snackbarContentClassnames }>
{ icon && ( { icon && (
@ -90,18 +125,27 @@ function Snackbar(
key={ index } key={ index }
href={ url } href={ url }
isTertiary isTertiary
onClick={ ( event ) => { onClick={ ( event ) =>
event.stopPropagation(); onActionClick( event, onClick )
if ( onClick ) { }
onClick( event );
}
} }
className="components-snackbar__action" className="components-snackbar__action"
> >
{ label } { label }
</Button> </Button>
); );
} ) } } ) }
{ explicitDismiss && (
<span
role="button"
aria-label="Dismiss this notice"
tabIndex="0"
className="components-snackbar__dismiss-button"
onClick={ dismissMe }
onKeyPress={ dismissMe }
>
&#x2715;
</span>
) }
</div> </div>
</div> </div>
); );

View File

@ -25,6 +25,10 @@
} }
.components-snackbar { .components-snackbar {
&.components-snackbar-explicit-dismiss {
cursor: default;
}
.components-snackbar__content-with-icon { .components-snackbar__content-with-icon {
margin-left: 32px; margin-left: 32px;
} }
@ -34,4 +38,9 @@
top: 24px; top: 24px;
left: 26px; left: 26px;
} }
.components-snackbar__dismiss_button {
margin-left: 32px;
cursor: pointer;
}
} }

View File

@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { withDispatch } from '@wordpress/data'; import { withDispatch } from '@wordpress/data';
import { noop } from 'lodash';
/** /**
* Use `CustomerEffortScore` to gather a customer effort score. * Use `CustomerEffortScore` to gather a customer effort score.
@ -24,7 +25,7 @@ function CustomerEffortScore( {
trackCallback, trackCallback,
label, label,
createNotice, createNotice,
openedCallback, openedCallback = noop,
icon, icon,
} ) { } ) {
const [ score, setScore ] = useState( 0 ); const [ score, setScore ] = useState( 0 );
@ -39,13 +40,13 @@ function CustomerEffortScore( {
onClick: () => { onClick: () => {
setVisible( true ); setVisible( true );
if ( openedCallback ) { openedCallback();
openedCallback();
}
}, },
}, },
], ],
icon, icon,
explicitDismiss: true,
onDismiss: openedCallback,
} ); } );
setShouldCreateNotice( false ); setShouldCreateNotice( false );

View File

@ -41,6 +41,11 @@ import { DEFAULT_CONTEXT, DEFAULT_STATUS } from './constants';
* @param {Array<WPNoticeAction>} [options.actions] User actions to be * @param {Array<WPNoticeAction>} [options.actions] User actions to be
* presented with notice. * presented with notice.
* @param {Object} [options.icon] An icon displayed with the notice. * @param {Object} [options.icon] An icon displayed with the notice.
* @param {boolean} [options.explicitDismiss] Whether the notice includes
* an explict dismiss button and
* can't be dismissed by clicking
* the body of the notice.
* @param {Function} [options.onDismiss] Called when the notice is dismissed.
* *
* @return {Object} Action object. * @return {Object} Action object.
*/ */
@ -54,6 +59,8 @@ export function createNotice( status = DEFAULT_STATUS, content, options = {} ) {
type = 'default', type = 'default',
__unstableHTML, __unstableHTML,
icon = null, icon = null,
explicitDismiss = false,
onDismiss = null,
} = options; } = options;
// The supported value shape of content is currently limited to plain text // The supported value shape of content is currently limited to plain text
@ -74,6 +81,8 @@ export function createNotice( status = DEFAULT_STATUS, content, options = {} ) {
actions, actions,
type, type,
icon, icon,
explicitDismiss,
onDismiss,
}, },
}; };
} }