diff --git a/client/analytics/settings/historical-data/index.js b/client/analytics/settings/historical-data/index.js
index f8f50fb62d5..920cd180efd 100644
--- a/client/analytics/settings/historical-data/index.js
+++ b/client/analytics/settings/historical-data/index.js
@@ -51,13 +51,13 @@ class HistoricalData extends Component {
}
makeQuery( path, errorMessage ) {
- const { createNotice } = this.props;
+ const { addNotice } = this.props;
apiFetch( { path, method: 'POST' } )
.then( response => {
if ( 'success' === response.status ) {
- createNotice( 'success', response.message );
+ addNotice( { status: 'success', message: response.message } );
} else {
- createNotice( 'error', errorMessage );
+ addNotice( { status: 'error', message: errorMessage } );
this.setState( {
activeImport: false,
lastImportStopTimestamp: Date.now(),
@@ -66,7 +66,7 @@ class HistoricalData extends Component {
} )
.catch( error => {
if ( error && error.message ) {
- createNotice( 'error', error.message );
+ addNotice( { status: 'error', message: error.message } );
this.setState( {
activeImport: false,
lastImportStopTimestamp: Date.now(),
diff --git a/client/analytics/settings/index.js b/client/analytics/settings/index.js
index 6e396e2cd73..44fe86a56a8 100644
--- a/client/analytics/settings/index.js
+++ b/client/analytics/settings/index.js
@@ -84,22 +84,25 @@ class Settings extends Component {
};
componentDidUpdate() {
- const { createNotice, isError, isRequesting } = this.props;
+ const { addNotice, isError, isRequesting } = this.props;
const { saving, isDirty } = this.state;
let newIsDirtyState = isDirty;
if ( saving && ! isRequesting ) {
if ( ! isError ) {
- createNotice(
- 'success',
- __( 'Your settings have been successfully saved.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'success',
+ message: __( 'Your settings have been successfully saved.', 'woocommerce-admin' ),
+ } );
newIsDirtyState = false;
} else {
- createNotice(
- 'error',
- __( 'There was an error saving your settings. Please try again.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __(
+ 'There was an error saving your settings. Please try again.',
+ 'woocommerce-admin'
+ ),
+ } );
}
/* eslint-disable react/no-did-update-set-state */
this.setState( { saving: false, isDirty: newIsDirtyState } );
@@ -148,7 +151,7 @@ class Settings extends Component {
}
render() {
- const { createNotice } = this.props;
+ const { addNotice } = this.props;
const { hasError } = this.state;
if ( hasError ) {
return null;
@@ -175,7 +178,7 @@ class Settings extends Component {
-
+
);
}
@@ -192,11 +195,11 @@ export default compose(
return { getSettings, isError, isRequesting, settings };
} ),
withDispatch( dispatch => {
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice } = dispatch( 'wc-admin' );
const { updateSettings } = dispatch( 'wc-api' );
return {
- createNotice,
+ addNotice,
updateSettings,
};
} )
diff --git a/client/analytics/settings/setting.js b/client/analytics/settings/setting.js
index bb782503cef..e22b4bdebcc 100644
--- a/client/analytics/settings/setting.js
+++ b/client/analytics/settings/setting.js
@@ -74,7 +74,7 @@ class Setting extends Component {
};
handleInputCallback = () => {
- const { createNotice, callback } = this.props;
+ const { addNotice, callback } = this.props;
if ( 'function' !== typeof callback ) {
return;
@@ -82,7 +82,7 @@ class Setting extends Component {
return new Promise( ( resolve, reject ) => {
this.setState( { disabled: true } );
- callback( resolve, reject, createNotice );
+ callback( resolve, reject, addNotice );
} )
.then( () => {
this.setState( { disabled: false } );
@@ -197,7 +197,7 @@ Setting.propTypes = {
export default compose(
withDispatch( dispatch => {
- const { createNotice } = dispatch( 'core/notices' );
- return { createNotice };
+ const { addNotice } = dispatch( 'wc-admin' );
+ return { addNotice };
} )
)( Setting );
diff --git a/client/dashboard/profile-wizard/index.js b/client/dashboard/profile-wizard/index.js
index a1ecc423bae..b102d040841 100644
--- a/client/dashboard/profile-wizard/index.js
+++ b/client/dashboard/profile-wizard/index.js
@@ -93,7 +93,7 @@ class ProfileWizard extends Component {
}
async goToNextStep() {
- const { createNotice, isError, updateProfileItems } = this.props;
+ const { addNotice, isError, updateProfileItems } = this.props;
const currentStep = this.getCurrentStep();
const currentStepIndex = getSteps().findIndex( s => s.key === currentStep.key );
const nextStep = getSteps()[ currentStepIndex + 1 ];
@@ -102,10 +102,10 @@ class ProfileWizard extends Component {
await updateProfileItems( { completed: true } );
if ( isError ) {
- createNotice(
- 'error',
- __( 'There was a problem completing the profiler.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __( 'There was a problem completing the profiler.', 'woocommerce-admin' ),
+ } );
}
return;
}
@@ -142,11 +142,10 @@ export default compose(
return { isError };
} ),
withDispatch( dispatch => {
- const { updateProfileItems } = dispatch( 'wc-api' );
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice, updateProfileItems } = dispatch( 'wc-api' );
return {
- createNotice,
+ addNotice,
updateProfileItems,
};
} )
diff --git a/client/dashboard/profile-wizard/steps/business-details.js b/client/dashboard/profile-wizard/steps/business-details.js
index 8f17db6adaf..83556652ea4 100644
--- a/client/dashboard/profile-wizard/steps/business-details.js
+++ b/client/dashboard/profile-wizard/steps/business-details.js
@@ -40,7 +40,7 @@ class BusinessDetails extends Component {
}
async onContinue() {
- const { createNotice, goToNextStep, isError, updateProfileItems } = this.props;
+ const { addNotice, goToNextStep, isError, updateProfileItems } = this.props;
const { other_platform, product_count, selling_venues } = this.state;
const extensions = keys( pickBy( this.state.extensions ) );
@@ -62,10 +62,10 @@ class BusinessDetails extends Component {
if ( ! isError ) {
goToNextStep();
} else {
- createNotice(
- 'error',
- __( 'There was a problem updating your business details.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __( 'There was a problem updating your business details.', 'woocommerce-admin' ),
+ } );
}
}
@@ -316,11 +316,10 @@ export default compose(
return { isError };
} ),
withDispatch( dispatch => {
- const { updateProfileItems } = dispatch( 'wc-api' );
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice, updateProfileItems } = dispatch( 'wc-api' );
return {
- createNotice,
+ addNotice,
updateProfileItems,
};
} )
diff --git a/client/dashboard/profile-wizard/steps/industry.js b/client/dashboard/profile-wizard/steps/industry.js
index f93a73ed1ee..011f4db2678 100644
--- a/client/dashboard/profile-wizard/steps/industry.js
+++ b/client/dashboard/profile-wizard/steps/industry.js
@@ -27,7 +27,7 @@ class Industry extends Component {
}
async onContinue() {
- const { createNotice, goToNextStep, isError, updateProfileItems } = this.props;
+ const { addNotice, goToNextStep, isError, updateProfileItems } = this.props;
recordEvent( 'storeprofiler_store_industry_continue', { store_industry: this.state.selected } );
await updateProfileItems( { industry: this.state.selected } );
@@ -35,10 +35,10 @@ class Industry extends Component {
if ( ! isError ) {
goToNextStep();
} else {
- createNotice(
- 'error',
- __( 'There was a problem updating your industries.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __( 'There was a problem updating your industries.', 'woocommerce-admin' ),
+ } );
}
}
@@ -104,11 +104,10 @@ export default compose(
return { isError };
} ),
withDispatch( dispatch => {
- const { updateProfileItems } = dispatch( 'wc-api' );
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice, updateProfileItems } = dispatch( 'wc-api' );
return {
- createNotice,
+ addNotice,
updateProfileItems,
};
} )
diff --git a/client/dashboard/profile-wizard/steps/plugins.js b/client/dashboard/profile-wizard/steps/plugins.js
index 60fae53fda6..af0affb7c99 100644
--- a/client/dashboard/profile-wizard/steps/plugins.js
+++ b/client/dashboard/profile-wizard/steps/plugins.js
@@ -119,7 +119,10 @@ class Plugins extends Component {
} );
return pluginResponse;
} catch ( err ) {
- this.props.createNotice( 'error', this.getErrorMessage( action, plugin ) );
+ this.props.addNotice( {
+ status: 'error',
+ message: this.getErrorMessage( action, plugin ),
+ } );
this.setState( {
isPending: false,
isError: true,
@@ -138,7 +141,10 @@ class Plugins extends Component {
}
throw new Error();
} catch ( err ) {
- this.props.createNotice( 'error', this.getErrorMessage( 'activate', 'jetpack' ) );
+ this.props.addNotice( {
+ status: 'error',
+ message: this.getErrorMessage( 'activate', 'jetpack' ),
+ } );
this.setState( {
isPending: false,
isError: true,
@@ -202,9 +208,9 @@ class Plugins extends Component {
export default compose(
withDispatch( dispatch => {
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice } = dispatch( 'wc-admin' );
return {
- createNotice,
+ addNotice,
};
} )
)( Plugins );
diff --git a/client/dashboard/profile-wizard/steps/product-types.js b/client/dashboard/profile-wizard/steps/product-types.js
index 862d1a1e595..122ed7aa398 100644
--- a/client/dashboard/profile-wizard/steps/product-types.js
+++ b/client/dashboard/profile-wizard/steps/product-types.js
@@ -30,7 +30,7 @@ class ProductTypes extends Component {
}
async onContinue() {
- const { createNotice, goToNextStep, isError, updateProfileItems } = this.props;
+ const { addNotice, goToNextStep, isError, updateProfileItems } = this.props;
recordEvent( 'storeprofiler_store_product_type_continue', {
product_type: this.state.selected,
@@ -40,10 +40,10 @@ class ProductTypes extends Component {
if ( ! isError ) {
goToNextStep();
} else {
- createNotice(
- 'error',
- __( 'There was a problem updating your product types.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __( 'There was a problem updating your product types.', 'woocommerce-admin' ),
+ } );
}
}
@@ -137,11 +137,10 @@ export default compose(
return { isError };
} ),
withDispatch( dispatch => {
- const { updateProfileItems } = dispatch( 'wc-api' );
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice, updateProfileItems } = dispatch( 'wc-api' );
return {
- createNotice,
+ addNotice,
updateProfileItems,
};
} )
diff --git a/client/dashboard/profile-wizard/steps/start/index.js b/client/dashboard/profile-wizard/steps/start/index.js
index 7a2957791d9..eb478964b71 100644
--- a/client/dashboard/profile-wizard/steps/start/index.js
+++ b/client/dashboard/profile-wizard/steps/start/index.js
@@ -76,7 +76,7 @@ class Start extends Component {
}
async skipWizard() {
- const { createNotice, isProfileItemsError, updateProfileItems, isSettingsError } = this.props;
+ const { addNotice, isProfileItemsError, updateProfileItems, isSettingsError } = this.props;
recordEvent( 'storeprofiler_welcome_clicked', { proceed_without_install: true } );
@@ -84,15 +84,15 @@ class Start extends Component {
await this.updateTracking();
if ( isProfileItemsError || isSettingsError ) {
- createNotice(
- 'error',
- __( 'There was a problem updating your preferences.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __( 'There was a problem updating your preferences.', 'woocommerce-admin' ),
+ } );
}
}
async startWizard() {
- const { createNotice, isSettingsError } = this.props;
+ const { addNotice, isSettingsError } = this.props;
recordEvent( 'storeprofiler_welcome_clicked', { get_started: true } );
@@ -101,10 +101,10 @@ class Start extends Component {
if ( ! isSettingsError ) {
this.props.goToNextStep();
} else {
- createNotice(
- 'error',
- __( 'There was a problem updating your preferences.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __( 'There was a problem updating your preferences.', 'woocommerce-admin' ),
+ } );
}
}
@@ -217,11 +217,10 @@ export default compose(
return { getSettings, isSettingsError, isProfileItemsError, isSettingsRequesting };
} ),
withDispatch( dispatch => {
- const { updateProfileItems, updateSettings } = dispatch( 'wc-api' );
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice, updateProfileItems, updateSettings } = dispatch( 'wc-api' );
return {
- createNotice,
+ addNotice,
updateProfileItems,
updateSettings,
};
diff --git a/client/dashboard/profile-wizard/steps/store-details.js b/client/dashboard/profile-wizard/steps/store-details.js
index 2dde6034827..51236a3d6d7 100644
--- a/client/dashboard/profile-wizard/steps/store-details.js
+++ b/client/dashboard/profile-wizard/steps/store-details.js
@@ -52,7 +52,7 @@ class StoreDetails extends Component {
return;
}
- const { createNotice, goToNextStep, isError, updateSettings } = this.props;
+ const { addNotice, goToNextStep, isError, updateSettings } = this.props;
const { addressLine1, addressLine2, city, countryState, postCode } = this.state;
recordEvent( 'storeprofiler_store_details_continue', {
@@ -72,10 +72,10 @@ class StoreDetails extends Component {
if ( ! isError ) {
goToNextStep();
} else {
- createNotice(
- 'error',
- __( 'There was a problem saving your store details.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __( 'There was a problem saving your store details.', 'woocommerce-admin' ),
+ } );
}
}
@@ -185,11 +185,11 @@ export default compose(
return { getSettings, isError, isRequesting, settings };
} ),
withDispatch( dispatch => {
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice } = dispatch( 'wc-api' );
const { updateSettings } = dispatch( 'wc-api' );
return {
- createNotice,
+ addNotice,
updateSettings,
};
} )
diff --git a/client/dashboard/profile-wizard/steps/theme/index.js b/client/dashboard/profile-wizard/steps/theme/index.js
index 79c8c9b6a53..043b71f6968 100644
--- a/client/dashboard/profile-wizard/steps/theme/index.js
+++ b/client/dashboard/profile-wizard/steps/theme/index.js
@@ -40,7 +40,7 @@ class Theme extends Component {
}
async onChoose( theme ) {
- const { createNotice, goToNextStep, isError, updateProfileItems } = this.props;
+ const { addNotice, goToNextStep, isError, updateProfileItems } = this.props;
recordEvent( 'storeprofiler_store_theme_choose', { theme } );
await updateProfileItems( { theme } );
@@ -49,10 +49,10 @@ class Theme extends Component {
// @todo This should send profile information to woocommerce.com.
goToNextStep();
} else {
- createNotice(
- 'error',
- __( 'There was a problem selecting your store theme.', 'woocommerce-admin' )
- );
+ addNotice( {
+ status: 'error',
+ message: __( 'There was a problem selecting your store theme.', 'woocommerce-admin' ),
+ } );
}
}
@@ -206,11 +206,10 @@ export default compose(
return { isError };
} ),
withDispatch( dispatch => {
- const { updateProfileItems } = dispatch( 'wc-api' );
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice, updateProfileItems } = dispatch( 'wc-api' );
return {
- createNotice,
+ addNotice,
updateProfileItems,
};
} )
diff --git a/client/embedded.js b/client/embedded.js
index af3a7fa9285..a5a75525bf5 100644
--- a/client/embedded.js
+++ b/client/embedded.js
@@ -2,7 +2,6 @@
/**
* External dependencies
*/
-import '@wordpress/notices';
import { render } from '@wordpress/element';
/**
@@ -10,6 +9,7 @@ import { render } from '@wordpress/element';
*/
import './stylesheets/_embedded.scss';
import { EmbedLayout, PrimaryLayout as NoticeArea } from './layout';
+import 'store';
import 'wc-api/wp-data-store';
const embeddedRoot = document.getElementById( 'woocommerce-embedded-root' );
diff --git a/client/index.js b/client/index.js
index 2437114734a..b5d548d7925 100644
--- a/client/index.js
+++ b/client/index.js
@@ -2,7 +2,6 @@
/**
* External dependencies
*/
-import '@wordpress/notices';
import { render } from '@wordpress/element';
/**
@@ -10,6 +9,7 @@ import { render } from '@wordpress/element';
*/
import './stylesheets/_index.scss';
import { PageLayout } from './layout';
+import 'store';
import 'wc-api/wp-data-store';
render( , document.getElementById( 'root' ) );
diff --git a/client/layout/transient-notices/index.js b/client/layout/transient-notices/index.js
index 25c86aeb103..454096cc4ad 100644
--- a/client/layout/transient-notices/index.js
+++ b/client/layout/transient-notices/index.js
@@ -6,25 +6,24 @@ import classnames from 'classnames';
import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import PropTypes from 'prop-types';
-import { SnackbarList } from '@wordpress/components';
-import { withDispatch } from '@wordpress/data';
/**
* Internal dependencies
*/
import './style.scss';
+import TransientNotice from './transient-notice';
import withSelect from 'wc-api/with-select';
class TransientNotices extends Component {
render() {
- const { className, notices, onRemove } = this.props;
- const classes = classnames(
- 'woocommerce-transient-notices',
- 'components-notices__snackbar',
- className
- );
+ const { className, notices } = this.props;
+ const classes = classnames( 'woocommerce-transient-notices', className );
- return ;
+ return (
+
+ { notices && notices.map( ( notice, i ) => ) }
+
+ );
}
}
@@ -41,11 +40,9 @@ TransientNotices.propTypes = {
export default compose(
withSelect( select => {
- const notices = select( 'core/notices' ).getNotices();
+ const { getNotices } = select( 'wc-admin' );
+ const notices = getNotices();
return { notices };
- } ),
- withDispatch( dispatch => ( {
- onRemove: dispatch( 'core/notices' ).removeNotice,
- } ) )
+ } )
)( TransientNotices );
diff --git a/client/layout/transient-notices/style.scss b/client/layout/transient-notices/style.scss
index ca006c09116..57a2826db30 100644
--- a/client/layout/transient-notices/style.scss
+++ b/client/layout/transient-notices/style.scss
@@ -5,13 +5,34 @@
bottom: $gap-small;
left: 0;
z-index: 99999;
+}
+
+.woocommerce-transient-notice {
+ transform: translateX(calc(-100% - #{$gap}));
+ transition: all 300ms cubic-bezier(0.42, 0, 0.58, 1);
+ max-height: 300px; // Used to animate sliding down when multiple notices exist on exit.
+
+ @media screen and (prefers-reduced-motion: reduce) {
+ transition: none;
+ }
+
+ &.slide-enter-active,
+ &.slide-enter-done {
+ transform: translateX(0%);
+ }
+
+ &.slide-exit-active {
+ transform: translateX(calc(-100% - #{$gap}));
+ }
- .components-snackbar-list__notice-container {
- margin-left: $gap-small;
- margin-right: $gap-small;
+ &.slide-exit-done {
+ max-height: 0;
+ margin: 0;
+ padding: 0;
+ visibility: hidden;
}
- .components-snackbar {
- margin: 0 auto;
+ .components-notice {
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
}
}
diff --git a/client/layout/transient-notices/transient-notice.js b/client/layout/transient-notices/transient-notice.js
new file mode 100644
index 00000000000..d74ceb6a598
--- /dev/null
+++ b/client/layout/transient-notices/transient-notice.js
@@ -0,0 +1,104 @@
+/** @format */
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+import { Component } from '@wordpress/element';
+import { CSSTransition } from 'react-transition-group';
+import { noop } from 'lodash';
+import { Notice } from '@wordpress/components';
+import PropTypes from 'prop-types';
+import { speak } from '@wordpress/a11y';
+
+class TransientNotice extends Component {
+ constructor( props ) {
+ super( props );
+
+ this.state = {
+ visible: false,
+ timeout: null,
+ };
+ }
+
+ componentDidMount() {
+ const exitTime = this.props.exitTime;
+ const timeout = setTimeout(
+ () => {
+ this.setState( { visible: false } );
+ },
+ exitTime,
+ name,
+ exitTime
+ );
+ /* eslint-disable react/no-did-mount-set-state */
+ this.setState( { visible: true, timeout } );
+ /* eslint-enable react/no-did-mount-set-state */
+ speak( this.props.message );
+ }
+
+ componentWillUnmount() {
+ clearTimeout( this.state.timeout );
+ }
+
+ render() {
+ const { actions, className, isDismissible, message, onRemove, status } = this.props;
+ const classes = classnames( 'woocommerce-transient-notice', className );
+
+ return (
+
+
+
+ { message }
+
+
+
+ );
+ }
+}
+
+TransientNotice.propTypes = {
+ /**
+ * Array of action objects.
+ * See https://wordpress.org/gutenberg/handbook/designers-developers/developers/components/notice/
+ */
+ actions: PropTypes.array,
+ /**
+ * Additional class name to style the component.
+ */
+ className: PropTypes.string,
+ /**
+ * Determines if the notice dimiss button should be shown.
+ * See https://wordpress.org/gutenberg/handbook/designers-developers/developers/components/notice/
+ */
+ isDismissible: PropTypes.bool,
+ /**
+ * Function called when dismissing the notice.
+ * See https://wordpress.org/gutenberg/handbook/designers-developers/developers/components/notice/
+ */
+ onRemove: PropTypes.func,
+ /**
+ * Type of notice to display.
+ * See https://wordpress.org/gutenberg/handbook/designers-developers/developers/components/notice/
+ */
+ status: PropTypes.oneOf( [ 'success', 'error', 'warning' ] ),
+ /**
+ * Time in milliseconds until exit.
+ */
+ exitTime: PropTypes.number,
+};
+
+TransientNotice.defaultProps = {
+ actions: [],
+ className: '',
+ exitTime: 7000,
+ isDismissible: false,
+ onRemove: noop,
+ status: 'warning',
+};
+
+export default TransientNotice;
diff --git a/client/store/index.js b/client/store/index.js
new file mode 100644
index 00000000000..1e9e141e1f8
--- /dev/null
+++ b/client/store/index.js
@@ -0,0 +1,22 @@
+/** @format */
+/**
+ * External dependencies
+ */
+import { combineReducers, registerStore } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import notices from './notices';
+
+registerStore( 'wc-admin', {
+ reducer: combineReducers( {
+ ...notices.reducers,
+ } ),
+ actions: {
+ ...notices.actions,
+ },
+ selectors: {
+ ...notices.selectors,
+ },
+} );
diff --git a/client/store/notices/actions.js b/client/store/notices/actions.js
new file mode 100644
index 00000000000..24065c7ecc2
--- /dev/null
+++ b/client/store/notices/actions.js
@@ -0,0 +1,9 @@
+/** @format */
+
+const addNotice = notice => {
+ return { type: 'ADD_NOTICE', notice };
+};
+
+export default {
+ addNotice,
+};
diff --git a/client/store/notices/index.js b/client/store/notices/index.js
new file mode 100644
index 00000000000..10e658b90ed
--- /dev/null
+++ b/client/store/notices/index.js
@@ -0,0 +1,13 @@
+/** @format */
+/**
+ * Internal dependencies
+ */
+import reducers from './reducers';
+import actions from './actions';
+import selectors from './selectors';
+
+export default {
+ reducers,
+ actions,
+ selectors,
+};
diff --git a/client/store/notices/reducers.js b/client/store/notices/reducers.js
new file mode 100644
index 00000000000..f0a2634160d
--- /dev/null
+++ b/client/store/notices/reducers.js
@@ -0,0 +1,15 @@
+/** @format */
+
+const DEFAULT_STATE = [];
+
+const notices = ( state = DEFAULT_STATE, action ) => {
+ if ( action.type === 'ADD_NOTICE' ) {
+ return [ ...state, action.notice ];
+ }
+
+ return state;
+};
+
+export default {
+ notices,
+};
diff --git a/client/store/notices/selectors.js b/client/store/notices/selectors.js
new file mode 100644
index 00000000000..5b805ba1109
--- /dev/null
+++ b/client/store/notices/selectors.js
@@ -0,0 +1,9 @@
+/** @format */
+
+const getNotices = state => {
+ return state.notices;
+};
+
+export default {
+ getNotices,
+};
diff --git a/client/store/notices/test/fixtures/index.js b/client/store/notices/test/fixtures/index.js
new file mode 100644
index 00000000000..4f5e29c4190
--- /dev/null
+++ b/client/store/notices/test/fixtures/index.js
@@ -0,0 +1,7 @@
+/** @format */
+
+export const DEFAULT_STATE = {
+ notices: [],
+};
+
+export const testNotice = { message: 'Test notice' };
diff --git a/client/store/notices/test/index.js b/client/store/notices/test/index.js
new file mode 100644
index 00000000000..e495b015751
--- /dev/null
+++ b/client/store/notices/test/index.js
@@ -0,0 +1,55 @@
+/** @format */
+/**
+ * Internal dependencies
+ */
+import actions from '../actions';
+import { DEFAULT_STATE, testNotice } from './fixtures';
+import reducers from '../reducers';
+import selectors from '../selectors';
+
+describe( 'actions', () => {
+ test( 'should create an add notice action', () => {
+ const expectedAction = {
+ type: 'ADD_NOTICE',
+ notice: testNotice,
+ };
+
+ expect( actions.addNotice( testNotice ) ).toEqual( expectedAction );
+ } );
+} );
+
+describe( 'selectors', () => {
+ const notices = [ testNotice ];
+ const updatedState = { ...DEFAULT_STATE, ...{ notices } };
+
+ test( 'should return an emtpy initial state', () => {
+ expect( selectors.getNotices( DEFAULT_STATE ) ).toEqual( [] );
+ } );
+
+ test( 'should have an array length matching number of notices', () => {
+ expect( selectors.getNotices( updatedState ).length ).toEqual( 1 );
+ } );
+
+ test( 'should return the message content', () => {
+ expect( selectors.getNotices( updatedState )[ 0 ].message ).toEqual( 'Test notice' );
+ } );
+} );
+
+describe( 'reducers', () => {
+ test( 'should return an emtpy initial state', () => {
+ expect( reducers.notices( DEFAULT_STATE.notices, {} ) ).toEqual( [] );
+ } );
+
+ test( 'should return the added notice', () => {
+ expect(
+ reducers.notices( DEFAULT_STATE.notices, { type: 'ADD_NOTICE', notice: testNotice } )
+ ).toEqual( [ testNotice ] );
+ } );
+
+ const initialNotices = [ { message: 'Initial notice' } ];
+ test( 'should return the initial notice and the added notice', () => {
+ expect(
+ reducers.notices( initialNotices, { type: 'ADD_NOTICE', notice: testNotice } )
+ ).toEqual( [ ...initialNotices, testNotice ] );
+ } );
+} );
diff --git a/client/stylesheets/abstracts/_breakpoints.scss b/client/stylesheets/abstracts/_breakpoints.scss
index a600db74add..763010dbd9c 100644
--- a/client/stylesheets/abstracts/_breakpoints.scss
+++ b/client/stylesheets/abstracts/_breakpoints.scss
@@ -22,15 +22,13 @@ $breakpoints: 320px, 400px, 600px, 782px, 960px, 1280px, 1440px;
@media (max-width: $breakpoint) {
@content;
}
- }
- @else {
+ } @else {
@if $size == $and-larger {
$approved-value: 2;
@media (min-width: $breakpoint + 1) {
@content;
}
- }
- @else {
+ } @else {
@each $breakpoint-end in $breakpoints {
$range: $breakpoint + '-' + $breakpoint-end;
@if $size == $range {
@@ -50,8 +48,7 @@ $breakpoints: 320px, 400px, 600px, 782px, 960px, 1280px, 1440px;
}
@warn 'ERROR in breakpoint( #{ $size } ) : You can only use these sizes[ #{$sizes} ] using the following syntax [ <#{ nth( $breakpoints, 1 ) } >#{ nth( $breakpoints, 1 ) } #{ nth( $breakpoints, 1 ) }-#{ nth( $breakpoints, 2 ) } ]';
}
- }
- @else {
+ } @else {
$sizes: '';
@each $breakpoint in $breakpoints {
$sizes: $sizes + ' ' + $breakpoint;
diff --git a/client/stylesheets/abstracts/_mixins.scss b/client/stylesheets/abstracts/_mixins.scss
index e7d7e7fa2a3..e33caae0882 100644
--- a/client/stylesheets/abstracts/_mixins.scss
+++ b/client/stylesheets/abstracts/_mixins.scss
@@ -37,7 +37,7 @@
}
}
-// Gutenberg mixins. These are temporary until Gutenberg's mixins are exposed.
+// Gutenberg Button variables. These are temporary until Gutenberg's variables are exposed.
@mixin button-style__focus-active() {
background-color: $white;
color: $dark-gray-900;
@@ -48,31 +48,6 @@
outline-offset: -2px;
}
-@mixin reduce-motion($property: '') {
- @if $property == 'transition' {
- @media (prefers-reduced-motion: reduce) {
- transition-duration: 0s;
- }
- }
- @else if $property == 'animation' {
- @media (prefers-reduced-motion: reduce) {
- animation-duration: 1ms;
- }
- }
- @else {
- @media (prefers-reduced-motion: reduce) {
- transition-duration: 0s;
- animation-duration: 1ms;
- }
- }
-}
-
-@mixin break-small() {
- @media (min-width: #{ ($break-small) }) {
- @content;
- }
-}
-
// Sets positions for children of grid elements
@mixin set-grid-item-position( $wrap-after, $number-of-items ) {
@for $i from 1 through $number-of-items {
diff --git a/client/stylesheets/abstracts/_variables.scss b/client/stylesheets/abstracts/_variables.scss
index 0688998ea2c..758fbc6a280 100644
--- a/client/stylesheets/abstracts/_variables.scss
+++ b/client/stylesheets/abstracts/_variables.scss
@@ -19,20 +19,14 @@ $header-height: 56px;
// @todo Remove this spacing variable
$spacing: 16px;
-// Gutenberg variables. These are temporary until Gutenberg's variables are exposed.
-$default-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
- 'Helvetica Neue', sans-serif;
-$default-font-size: 13px;
-$default-line-height: 1.4;
-$break-small: 600px;
+// Gutenberg Button variables. These are temporary until Gutenberg's variables are exposed.
$border-width: 1px;
+$default-font-size: 13px;
$blue-medium-focus: #007cba;
$light-gray-500: $core-grey-light-500;
$dark-gray-300: $core-grey-dark-300;
-$dark-gray-700: $core-grey-dark-700;
$dark-gray-900: $core-grey-dark-900;
$alert-red: $error-red;
-$radius-round-rectangle: 4px;
// WordPress defaults
$adminbar-height: 32px;
diff --git a/client/stylesheets/shared/_gutenberg-components.scss b/client/stylesheets/shared/_gutenberg-components.scss
index f4f4b705d06..a776e9ccaf5 100644
--- a/client/stylesheets/shared/_gutenberg-components.scss
+++ b/client/stylesheets/shared/_gutenberg-components.scss
@@ -5,4 +5,3 @@ Import Gutenberg component SCSS so webpack's postcss process can handle theme-in
allows Woo themed components based on the config found in postcss.config.js
*/
@import 'gutenberg-components/button/style.scss';
-@import 'gutenberg-components/snackbar/style.scss';
diff --git a/client/wc-api/items/mutations.js b/client/wc-api/items/mutations.js
index dcc7b04c48a..015ccee0d14 100644
--- a/client/wc-api/items/mutations.js
+++ b/client/wc-api/items/mutations.js
@@ -11,7 +11,7 @@ import { dispatch } from '@wordpress/data';
import { getResourceName } from '../utils';
const updateProductStock = operations => async ( product, newStock ) => {
- const { createNotice } = dispatch( 'core/notices' );
+ const { addNotice } = dispatch( 'wc-admin' );
const oldStockQuantity = product.stock_quantity;
const resourceName = getResourceName( 'items-query-products-item', product.id );
@@ -30,16 +30,16 @@ const updateProductStock = operations => async ( product, newStock ) => {
} );
const response = result[ 0 ][ resourceName ];
if ( response && response.data ) {
- createNotice(
- 'success',
- sprintf( __( '%s stock updated.', 'woocommerce-admin' ), product.name )
- );
+ addNotice( {
+ status: 'success',
+ message: sprintf( __( '%s stock updated.', 'woocommerce-admin' ), product.name ),
+ } );
}
if ( response && response.error ) {
- createNotice(
- 'error',
- sprintf( __( '%s stock could not be updated.', 'woocommerce-admin' ), product.name )
- );
+ addNotice( {
+ status: 'error',
+ message: sprintf( __( '%s stock could not be updated.', 'woocommerce-admin' ), product.name ),
+ } );
// Revert local changes if the operation failed in the server
operations.updateLocally( [ resourceName ], {
[ resourceName ]: { ...product, stock_quantity: oldStockQuantity },
diff --git a/package-lock.json b/package-lock.json
index ff57036cc48..e3b36a1dfdb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3106,20 +3106,21 @@
"dev": true
},
"@wordpress/components": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-8.0.0.tgz",
- "integrity": "sha512-8Lo9VHcNME+0s8RGQ0FaTYbSw7UdoIPZYK8+OB+jZgwM88kbVW6SkgCM45QdxSLtaxkCYdWX1IF/DRJzI22WyQ==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-7.4.0.tgz",
+ "integrity": "sha512-riVey0Z5835YdPZLWFSAs/4Hzo0nr7WA/393mRXwGuUtkqdk7ia++5emKfhyaCLYbinVBd6366xFFfiFxxcsCA==",
"requires": {
"@babel/runtime": "^7.4.4",
- "@wordpress/a11y": "^2.4.0",
- "@wordpress/compose": "^3.4.0",
+ "@wordpress/a11y": "^2.3.0",
+ "@wordpress/api-fetch": "^3.2.0",
+ "@wordpress/compose": "^3.3.0",
"@wordpress/dom": "^2.3.0",
- "@wordpress/element": "^2.5.0",
- "@wordpress/hooks": "^2.4.0",
- "@wordpress/i18n": "^3.5.0",
- "@wordpress/is-shallow-equal": "^1.4.0",
- "@wordpress/keycodes": "^2.4.0",
- "@wordpress/rich-text": "^3.4.0",
+ "@wordpress/element": "^2.4.0",
+ "@wordpress/hooks": "^2.3.0",
+ "@wordpress/i18n": "^3.4.0",
+ "@wordpress/is-shallow-equal": "^1.3.0",
+ "@wordpress/keycodes": "^2.3.0",
+ "@wordpress/rich-text": "^3.3.0",
"@wordpress/url": "^2.6.0",
"classnames": "^2.2.5",
"clipboard": "^2.0.1",
@@ -3132,53 +3133,34 @@
"re-resizable": "^4.7.1",
"react-click-outside": "^3.0.0",
"react-dates": "^17.1.1",
- "react-spring": "^8.0.20",
"rememo": "^3.0.0",
"tinycolor2": "^1.4.1",
"uuid": "^3.3.2"
},
"dependencies": {
- "@wordpress/element": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-2.5.0.tgz",
- "integrity": "sha512-gqtNcBkVtF//OHKJQAn4c7nwfDqsIy0UMq7ed9l6lpXOKS5ic8cw7BgNtdx4PiicV46ev9s/yHieFKWZNFpfIw==",
- "requires": {
- "@babel/runtime": "^7.4.4",
- "@wordpress/escape-html": "^1.4.0",
- "lodash": "^4.17.11",
- "react": "^16.8.4",
- "react-dom": "^16.8.4"
- }
- },
- "@wordpress/hooks": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-2.4.0.tgz",
- "integrity": "sha512-z3+8Yq4IQ3DOvUF4VsXOfntdyk1fJ8RZBYlZTW8WfmHV7VKTDbX3LfHXmC2VCjXhVkeWQVKozi2weoEYopfGKA==",
- "requires": {
- "@babel/runtime": "^7.4.4"
- }
- },
- "@wordpress/i18n": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-3.5.0.tgz",
- "integrity": "sha512-QAYFsHe/raG0MPUX32gCmuSM6UN/5gWCVSFWxOVSuXVuzPP5WnL78CgpuXtUresDZXTJm3FrFcB9IKvh93e1DQ==",
- "requires": {
- "@babel/runtime": "^7.4.4",
- "gettext-parser": "^1.3.1",
- "lodash": "^4.17.11",
- "memize": "^1.0.5",
- "sprintf-js": "^1.1.1",
- "tannin": "^1.0.1"
- }
- },
- "@wordpress/keycodes": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-2.4.0.tgz",
- "integrity": "sha512-qGRBKcDroYzUffAGMzotyos2k+FA89IjYYFjWlUrDp4TVaadCHK1ESPcmX6Y87ExgcsJKXDkNVZfsCPvh7V7Kw==",
+ "@wordpress/api-fetch": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-3.3.0.tgz",
+ "integrity": "sha512-EddkSzj2csdDWhIZueBthue8er5akGDBAnOtUQ5d8cdOQd1RrQbAgHmpKZtNHD8qXbfS40Ay1K42w7Si0uOksw==",
"requires": {
"@babel/runtime": "^7.4.4",
"@wordpress/i18n": "^3.5.0",
- "lodash": "^4.17.11"
+ "@wordpress/url": "^2.6.0"
+ },
+ "dependencies": {
+ "@wordpress/i18n": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-3.5.0.tgz",
+ "integrity": "sha512-QAYFsHe/raG0MPUX32gCmuSM6UN/5gWCVSFWxOVSuXVuzPP5WnL78CgpuXtUresDZXTJm3FrFcB9IKvh93e1DQ==",
+ "requires": {
+ "@babel/runtime": "^7.4.4",
+ "gettext-parser": "^1.3.1",
+ "lodash": "^4.17.11",
+ "memize": "^1.0.5",
+ "sprintf-js": "^1.1.1",
+ "tannin": "^1.0.1"
+ }
+ }
}
}
}
@@ -3394,50 +3376,6 @@
"lodash": "^4.17.11"
}
},
- "@wordpress/notices": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-1.5.0.tgz",
- "integrity": "sha512-aKCVDYPVVzYBm2cW4pQzAsshLOZx0f9AZiBZOV+AD9D+eSAscF0SBcvmbduFFQ4KhIgj76IXz5cxXIrafFXaDQ==",
- "requires": {
- "@babel/runtime": "^7.4.4",
- "@wordpress/a11y": "^2.4.0",
- "@wordpress/data": "^4.6.0",
- "lodash": "^4.17.11"
- },
- "dependencies": {
- "@wordpress/data": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-4.6.0.tgz",
- "integrity": "sha512-42BNtWtN0ppnzbnxz0tA72KNtuEKjrIRr4PbVCw3JVrHN2Nfhbo9SiCUutReKAXGQaT4LPnO7gAGmIsdC/mBUw==",
- "requires": {
- "@babel/runtime": "^7.4.4",
- "@wordpress/compose": "^3.4.0",
- "@wordpress/deprecated": "^2.4.0",
- "@wordpress/element": "^2.5.0",
- "@wordpress/is-shallow-equal": "^1.4.0",
- "@wordpress/priority-queue": "^1.2.0",
- "@wordpress/redux-routine": "^3.4.0",
- "equivalent-key-map": "^0.2.2",
- "is-promise": "^2.1.0",
- "lodash": "^4.17.11",
- "redux": "^4.0.0",
- "turbo-combine-reducers": "^1.0.2"
- }
- },
- "@wordpress/element": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-2.5.0.tgz",
- "integrity": "sha512-gqtNcBkVtF//OHKJQAn4c7nwfDqsIy0UMq7ed9l6lpXOKS5ic8cw7BgNtdx4PiicV46ev9s/yHieFKWZNFpfIw==",
- "requires": {
- "@babel/runtime": "^7.4.4",
- "@wordpress/escape-html": "^1.4.0",
- "lodash": "^4.17.11",
- "react": "^16.8.4",
- "react-dom": "^16.8.4"
- }
- }
- }
- },
"@wordpress/npm-package-json-lint-config": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-1.3.0.tgz",
@@ -13780,66 +13718,6 @@
"requires": {
"@wordpress/components": "^7.3.1",
"@wordpress/element": "^2.3.0"
- },
- "dependencies": {
- "@wordpress/api-fetch": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-3.3.0.tgz",
- "integrity": "sha512-EddkSzj2csdDWhIZueBthue8er5akGDBAnOtUQ5d8cdOQd1RrQbAgHmpKZtNHD8qXbfS40Ay1K42w7Si0uOksw==",
- "requires": {
- "@babel/runtime": "^7.4.4",
- "@wordpress/i18n": "^3.5.0",
- "@wordpress/url": "^2.6.0"
- },
- "dependencies": {
- "@wordpress/i18n": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-3.5.0.tgz",
- "integrity": "sha512-QAYFsHe/raG0MPUX32gCmuSM6UN/5gWCVSFWxOVSuXVuzPP5WnL78CgpuXtUresDZXTJm3FrFcB9IKvh93e1DQ==",
- "requires": {
- "@babel/runtime": "^7.4.4",
- "gettext-parser": "^1.3.1",
- "lodash": "^4.17.11",
- "memize": "^1.0.5",
- "sprintf-js": "^1.1.1",
- "tannin": "^1.0.1"
- }
- }
- }
- },
- "@wordpress/components": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-7.4.0.tgz",
- "integrity": "sha512-riVey0Z5835YdPZLWFSAs/4Hzo0nr7WA/393mRXwGuUtkqdk7ia++5emKfhyaCLYbinVBd6366xFFfiFxxcsCA==",
- "requires": {
- "@babel/runtime": "^7.4.4",
- "@wordpress/a11y": "^2.3.0",
- "@wordpress/api-fetch": "^3.2.0",
- "@wordpress/compose": "^3.3.0",
- "@wordpress/dom": "^2.3.0",
- "@wordpress/element": "^2.4.0",
- "@wordpress/hooks": "^2.3.0",
- "@wordpress/i18n": "^3.4.0",
- "@wordpress/is-shallow-equal": "^1.3.0",
- "@wordpress/keycodes": "^2.3.0",
- "@wordpress/rich-text": "^3.3.0",
- "@wordpress/url": "^2.6.0",
- "classnames": "^2.2.5",
- "clipboard": "^2.0.1",
- "diff": "^3.5.0",
- "dom-scroll-into-view": "^1.2.1",
- "lodash": "^4.17.11",
- "memize": "^1.0.5",
- "moment": "^2.22.1",
- "mousetrap": "^1.6.2",
- "re-resizable": "^4.7.1",
- "react-click-outside": "^3.0.0",
- "react-dates": "^17.1.1",
- "rememo": "^3.0.0",
- "tinycolor2": "^1.4.1",
- "uuid": "^3.3.2"
- }
- }
}
},
"nice-try": {
@@ -18675,15 +18553,6 @@
"tiny-warning": "^1.0.0"
}
},
- "react-spring": {
- "version": "8.0.27",
- "resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz",
- "integrity": "sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==",
- "requires": {
- "@babel/runtime": "^7.3.1",
- "prop-types": "^15.5.8"
- }
- },
"react-test-renderer": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.6.tgz",
diff --git a/package.json b/package.json
index 6e8cc721234..39eb99dcf3a 100644
--- a/package.json
+++ b/package.json
@@ -67,7 +67,7 @@
"dependencies": {
"@fresh-data/framework": "0.6.1",
"@wordpress/api-fetch": "2.2.8",
- "@wordpress/components": "8.0.0",
+ "@wordpress/components": "7.4.0",
"@wordpress/data": "4.5.0",
"@wordpress/date": "3.3.0",
"@wordpress/element": "2.4.0",
@@ -75,7 +75,6 @@
"@wordpress/html-entities": "2.3.0",
"@wordpress/i18n": "3.4.0",
"@wordpress/keycodes": "2.3.0",
- "@wordpress/notices": "1.5.0",
"@wordpress/scripts": "3.2.1",
"@wordpress/viewport": "2.4.0",
"browser-filesaver": "1.1.1",
diff --git a/packages/components/src/search-list-control/test/__snapshots__/index.js.snap b/packages/components/src/search-list-control/test/__snapshots__/index.js.snap
index e4d947cf2da..8d52c4efad2 100644
--- a/packages/components/src/search-list-control/test/__snapshots__/index.js.snap
+++ b/packages/components/src/search-list-control/test/__snapshots__/index.js.snap
@@ -43,7 +43,6 @@ exports[`SearchListControl should render a search box and list of hierarchical o
className="woocommerce-search-list__list components-menu-group"
>