From 4fa584da6b0699e1f658fe82d133cca3299c9bb2 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 31 May 2017 15:31:02 +0100 Subject: [PATCH] Utils: Adding the "accept" utility as a replacement for window.confirm --- editor/assets/stylesheets/_z-index.scss | 1 + editor/sidebar/post-visibility/index.js | 12 ++++-- index.php | 10 ++++- utils/accept/dialog.js | 43 +++++++++++++++++++ utils/accept/index.js | 38 +++++++++++++++++ utils/accept/style.scss | 36 ++++++++++++++++ utils/accept/test/index.js | 55 +++++++++++++++++++++++++ utils/index.js | 1 + 8 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 utils/accept/dialog.js create mode 100644 utils/accept/index.js create mode 100644 utils/accept/style.scss create mode 100644 utils/accept/test/index.js diff --git a/editor/assets/stylesheets/_z-index.scss b/editor/assets/stylesheets/_z-index.scss index 951d8c582082dc..99053b7bb7a915 100644 --- a/editor/assets/stylesheets/_z-index.scss +++ b/editor/assets/stylesheets/_z-index.scss @@ -10,6 +10,7 @@ $z-layers: ( '.editor-header': 20, '.editor-post-visibility__dialog': 30, '.editor-post-schedule__dialog': 30, + '.utils-accept__backdrop': 100000 ); @function z-index( $key ) { diff --git a/editor/sidebar/post-visibility/index.js b/editor/sidebar/post-visibility/index.js index a96550075d4959..8d8c50e1427b5e 100644 --- a/editor/sidebar/post-visibility/index.js +++ b/editor/sidebar/post-visibility/index.js @@ -10,6 +10,7 @@ import { find } from 'lodash'; */ import { __ } from 'i18n'; import { Component } from 'element'; +import { accept } from 'utils'; /** * Internal Dependencies @@ -52,10 +53,13 @@ class PostVisibility extends Component { this.setState( { hasPassword: false } ); }; const setPrivate = () => { - if ( window.confirm( __( 'Would you like to privately publish this post now?' ) ) ) { // eslint-disable-line no-alert - onSave( post, { ...edits, status: 'private' }, blocks ); - this.setState( { opened: false } ); - } + const message = __( 'Would you like to privately publish this post now?' ); + accept( message, ( accepted ) => { + if ( accepted ) { + onSave( post, { ...edits, status: 'private' }, blocks ); + this.setState( { opened: false } ); + } + }, __( 'Yes' ), __( 'No' ) ); }; const setPasswordProtected = () => { onUpdateVisibility( visibility === 'private' ? 'publish' : status, password || '' ); diff --git a/index.php b/index.php index fcca4adf570a3b..cb8fae39faad09 100644 --- a/index.php +++ b/index.php @@ -192,7 +192,7 @@ function gutenberg_register_scripts() { wp_register_script( 'wp-utils', plugins_url( 'utils/build/index.js', __FILE__ ), - array(), + array( 'wp-components', 'wp-i18n' ), filemtime( plugin_dir_path( __FILE__ ) . 'utils/build/index.js' ) ); wp_register_script( @@ -253,6 +253,12 @@ function gutenberg_register_scripts() { ); // Editor Styles. + wp_register_style( + 'wp-utils', + plugins_url( 'utils/build/style.css', __FILE__ ), + array(), + filemtime( plugin_dir_path( __FILE__ ) . 'components/build/style.css' ) + ); wp_register_style( 'wp-components', plugins_url( 'components/build/style.css', __FILE__ ), @@ -548,7 +554,7 @@ function gutenberg_scripts_and_styles( $hook ) { wp_enqueue_style( 'wp-editor', plugins_url( 'editor/build/style.css', __FILE__ ), - array( 'wp-components', 'wp-blocks' ), + array( 'wp-components', 'wp-blocks', 'wp-utils' ), filemtime( plugin_dir_path( __FILE__ ) . 'editor/build/style.css' ) ); } diff --git a/utils/accept/dialog.js b/utils/accept/dialog.js new file mode 100644 index 00000000000000..80dae58f5a7036 --- /dev/null +++ b/utils/accept/dialog.js @@ -0,0 +1,43 @@ +/** + * External dependencies + */ +import clickOutside from 'react-click-outside'; + +/** + * WordPress Dependencies + */ +import { Component } from 'element'; +import { Button } from 'components'; +import { __ } from 'i18n'; + +class AcceptDialog extends Component { + handleClickOutside() { + this.props.onClose( 'cancel' ); + } + + render() { + const { message, onClose, confirmButtonText, cancelButtonText } = this.props; + const accept = () => onClose( 'accept' ); + const cancel = () => onClose( 'cancel' ); + + return ( +
+
+
+ { message } +
+
+ + +
+
+
+ ); + } +} + +export default clickOutside( AcceptDialog ); diff --git a/utils/accept/index.js b/utils/accept/index.js new file mode 100644 index 00000000000000..b25a6126643136 --- /dev/null +++ b/utils/accept/index.js @@ -0,0 +1,38 @@ +/** + * External dependencies + */ +import { render, unmountComponentAtNode } from 'react-dom'; + +/** + * Internal dependencies + */ +import './style.scss'; +import AcceptDialog from './dialog'; + +export default function accept( message, callback, confirmButtonText, cancelButtonText ) { + let wrapper = document.createElement( 'div' ); + wrapper.className = 'utils-accept__backdrop'; + document.body.appendChild( wrapper ); + + function onClose( result ) { + if ( wrapper ) { + unmountComponentAtNode( wrapper ); + document.body.removeChild( wrapper ); + wrapper = null; + } + + if ( callback ) { + callback( result === 'accept' ); + } + } + + render( + , + wrapper + ); +} diff --git a/utils/accept/style.scss b/utils/accept/style.scss new file mode 100644 index 00000000000000..344d199158eb99 --- /dev/null +++ b/utils/accept/style.scss @@ -0,0 +1,36 @@ +.utils-accept__dialog { + top: 10px; + bottom: 10px; + width: 400px; + margin: auto; + background: $white; + box-shadow: $shadow-popover; + border: 1px solid $light-gray-500; +} + +.utils-accept__dialog-content { + padding: 20px; +} + +.utils-accept__dialog-buttons { + text-align: right; + padding: 10px 20px; + border-top: 1px solid $light-gray-500; + + .components-button { + margin-left: 10px; + } +} + +.utils-accept__backdrop { + align-items: center; + bottom: 0; + left: 0; + right: 0; + top: 0; + display: flex; + justify-content: center; + position: fixed; + z-index: z-index( '.utils-accept__backdrop' ); + background-color: rgba( $light-gray-900, 0.8 ); +} diff --git a/utils/accept/test/index.js b/utils/accept/test/index.js new file mode 100644 index 00000000000000..2093b5c243665d --- /dev/null +++ b/utils/accept/test/index.js @@ -0,0 +1,55 @@ +/** + * External dependencies + */ +import { expect } from 'chai'; + +/** + * Internal dependencies + */ +import accept from '../'; + +describe( '#accept()', () => { + beforeEach( () => { + document.body.innerHTML = ''; + } ); + + it( 'should render a dialog to the document body', () => { + const message = 'Are you sure?'; + + accept( message, () => {} ); + + const dialog = document.querySelector( '.utils-accept__dialog-content' ); + expect( dialog ).to.be.an.instanceof( window.Element ); + expect( dialog.textContent ).to.equal( message ); + } ); + + it( 'should trigger the callback with an accepted prompt', ( done ) => { + accept( 'Are you sure?', ( accepted ) => { + expect( accepted ).to.be.be.true(); + done(); + } ); + + document.querySelector( '.components-button.button-primary' ).click(); + } ); + + it( 'should trigger the callback with a denied prompt', ( done ) => { + accept( 'Are you sure?', ( accepted ) => { + expect( accepted ).to.be.be.false(); + done(); + } ); + + document.querySelector( '.components-button:not( .button-primary )' ).click(); + } ); + + it( 'should clean up after itself once the prompt is closed', ( done ) => { + accept( 'Are you sure?', () => { + process.nextTick( () => { + expect( document.querySelector( '.utils-accept__dialog' ) ).to.be.null(); + + done(); + } ); + } ); + + document.querySelector( '.components-button.button-primary' ).click(); + } ); +} ); diff --git a/utils/index.js b/utils/index.js index 6400ec8e4489f0..4d8201a93fa14e 100644 --- a/utils/index.js +++ b/utils/index.js @@ -1,3 +1,4 @@ import * as keycodes from './keycodes'; +export { default as accept } from './accept'; export { keycodes };