From a406f9553af4d88e6051fbaad66511c3ecf86154 Mon Sep 17 00:00:00 2001 From: Haz Date: Wed, 9 Oct 2019 21:21:05 -0600 Subject: [PATCH 01/75] Add initial accessible toolbar implementation --- package-lock.json | 32 +++++++++++++++++++ packages/components/package.json | 1 + .../accessible-toolbar-button.js | 25 +++++++++++++++ .../accessible-toolbar-button.native.js | 10 ++++++ .../components/src/toolbar-button/index.js | 6 ++-- .../components/src/toolbar-context/index.js | 8 +++++ .../src/toolbar/accessible-toolbar.js | 26 +++++++++++++++ .../src/toolbar/accessible-toolbar.native.js | 17 ++++++++++ packages/components/src/toolbar/index.js | 11 +++++-- 9 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 packages/components/src/toolbar-button/accessible-toolbar-button.js create mode 100644 packages/components/src/toolbar-button/accessible-toolbar-button.native.js create mode 100644 packages/components/src/toolbar-context/index.js create mode 100644 packages/components/src/toolbar/accessible-toolbar.js create mode 100644 packages/components/src/toolbar/accessible-toolbar.native.js diff --git a/package-lock.json b/package-lock.json index d7586bbb615730..f1b5949fcc2a81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4864,6 +4864,7 @@ "re-resizable": "^6.0.0", "react-dates": "^17.1.1", "react-spring": "^8.0.20", + "reakit": "^1.0.0-beta.8", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "uuid": "^3.3.2" @@ -7023,6 +7024,11 @@ } } }, + "body-scroll-lock": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-2.6.4.tgz", + "integrity": "sha512-NP08WsovlmxEoZP9pdlqrE+AhNaivlTrz9a0FF37BQsnOrpN48eNqivKkE7SYpM9N+YIPjsdVzfLAUQDBm6OQw==" + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -21271,6 +21277,11 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "popper.js": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", + "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==" + }, "portfinder": { "version": "1.0.20", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", @@ -23400,6 +23411,27 @@ "readable-stream": "^2.0.2" } }, + "reakit": { + "version": "1.0.0-beta.7", + "resolved": "https://registry.npmjs.org/reakit/-/reakit-1.0.0-beta.7.tgz", + "integrity": "sha512-sL4dKEwbWxN2ID+g74SlMNykSG0+zdjePwIIj8il+9XRM4pR2B9V1Ooapc0pPiPV5Bl3bU5wU0G5iLdrWLI5lw==", + "requires": { + "body-scroll-lock": "^2.6.4", + "popper.js": "^1.15.0", + "reakit-system": "^0.6.4", + "reakit-utils": "^0.6.4" + } + }, + "reakit-system": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/reakit-system/-/reakit-system-0.6.4.tgz", + "integrity": "sha512-sQtu2LmyMjG9a6l0oJMmQ+KCFo4abLQ8/YpWyUxppzTTY2NJWUCuBrEvlnwGiGMRW44Y91B2PtJK/IgVQutZng==" + }, + "reakit-utils": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/reakit-utils/-/reakit-utils-0.6.4.tgz", + "integrity": "sha512-sOwxhRHvYI17gyYh8aE+mCEpHXhHmLPlXT6xFUR61+qvWIC9YwXHM3Iw2xEY6CLIJd7sVxV5L/34QO1qbWW3UA==" + }, "realpath-native": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", diff --git a/packages/components/package.json b/packages/components/package.json index 08a0481d9eae24..ed468b9cef5707 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -42,6 +42,7 @@ "re-resizable": "^6.0.0", "react-dates": "^17.1.1", "react-spring": "^8.0.20", + "reakit": "^1.0.0-beta.8", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "uuid": "^3.3.2" diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button.js b/packages/components/src/toolbar-button/accessible-toolbar-button.js new file mode 100644 index 00000000000000..c574caca46c6f2 --- /dev/null +++ b/packages/components/src/toolbar-button/accessible-toolbar-button.js @@ -0,0 +1,25 @@ +/** + * External dependencies + */ +import { ToolbarItem } from 'reakit/Toolbar'; + +/** + * WordPress dependencies + */ +import { useContext, Children, cloneElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import ToolbarContext from '../toolbar-context'; + +function AccessibleToolbarButton( props ) { + const toolbar = useContext( ToolbarContext ); + return ( + + { ( htmlProps ) => cloneElement( Children.only( props.children ), htmlProps ) } + + ); +} + +export default AccessibleToolbarButton; diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button.native.js b/packages/components/src/toolbar-button/accessible-toolbar-button.native.js new file mode 100644 index 00000000000000..08037c8ab01ab7 --- /dev/null +++ b/packages/components/src/toolbar-button/accessible-toolbar-button.native.js @@ -0,0 +1,10 @@ +/** + * External dependencies + */ +import { View } from 'react-native'; + +export default ( props ) => ( + + { props.children } + +); diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index 8c2374b0284d1d..71b9e1070b1238 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; * Internal dependencies */ import IconButton from '../icon-button'; -import ToolbarButtonContainer from './toolbar-button-container'; +import AccessibleToolbarButton from './accessible-toolbar-button'; function ToolbarButton( { containerClassName, @@ -23,7 +23,7 @@ function ToolbarButton( { children, } ) { return ( - + { children } - + ); } diff --git a/packages/components/src/toolbar-context/index.js b/packages/components/src/toolbar-context/index.js new file mode 100644 index 00000000000000..dddd75af33cf15 --- /dev/null +++ b/packages/components/src/toolbar-context/index.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { createContext } from '@wordpress/element'; + +const ToolbarContext = createContext( {} ); + +export default ToolbarContext; diff --git a/packages/components/src/toolbar/accessible-toolbar.js b/packages/components/src/toolbar/accessible-toolbar.js new file mode 100644 index 00000000000000..3376b0e8f09754 --- /dev/null +++ b/packages/components/src/toolbar/accessible-toolbar.js @@ -0,0 +1,26 @@ +/** + * External dependencies + */ +import { useToolbarState, Toolbar } from 'reakit/Toolbar'; + +/** + * WordPress dependencies + */ +import { useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import ToolbarContext from '../toolbar-context'; + +function AccessibleToolbar( props ) { + const toolbar = useToolbarState( { loop: true } ); + const value = useMemo( () => toolbar, Object.values( toolbar ) ); + return ( + + + + ); +} + +export default AccessibleToolbar; diff --git a/packages/components/src/toolbar/accessible-toolbar.native.js b/packages/components/src/toolbar/accessible-toolbar.native.js new file mode 100644 index 00000000000000..c2a85598f5490c --- /dev/null +++ b/packages/components/src/toolbar/accessible-toolbar.native.js @@ -0,0 +1,17 @@ +/** + * External dependencies + */ +import { View } from 'react-native'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; + +const AccessibleToolbar = ( props ) => ( + + { props.children } + +); + +export default AccessibleToolbar; diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index ca41d32bb6e1b9..2ac6a8e04fc100 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -10,6 +10,7 @@ import { flatMap } from 'lodash'; import ToolbarButton from '../toolbar-button'; import DropdownMenu from '../dropdown-menu'; import ToolbarContainer from './toolbar-container'; +import AccessibleToolbar from './accessible-toolbar'; /** * Renders a toolbar with controls. @@ -50,6 +51,12 @@ function Toolbar( { controls = [], children, className, isCollapsed, icon, label return null; } + const cx = classnames( 'components-toolbar', className ); + + if ( ! controls || ! controls.length ) { + return { children }; + } + // Normalize controls to nested array of objects (sets of controls) let controlSets = controls; if ( ! Array.isArray( controlSets[ 0 ] ) ) { @@ -63,13 +70,13 @@ function Toolbar( { controls = [], children, className, isCollapsed, icon, label icon={ icon } label={ label } controls={ controlSets } - className={ classnames( 'components-toolbar', className ) } + className={ cx } /> ); } return ( - + { flatMap( controlSets, ( controlSet, indexOfSet ) => ( controlSet.map( ( control, indexOfControl ) => ( Date: Thu, 10 Oct 2019 13:08:06 -0600 Subject: [PATCH 02/75] Fix ToolbarButton style by putting box-sizing on the component styles --- packages/components/src/toolbar-button/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/toolbar-button/style.scss b/packages/components/src/toolbar-button/style.scss index cd434448784a20..c7e64b5a04bf10 100644 --- a/packages/components/src/toolbar-button/style.scss +++ b/packages/components/src/toolbar-button/style.scss @@ -29,6 +29,7 @@ border-radius: $radius-round-rectangle; height: 30px; width: 30px; + box-sizing: border-box; } // Subscript for numbered icon buttons, like headings From b39d864da38f84de560f5c9944738ba41b0f289f Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 11 Oct 2019 19:52:44 -0600 Subject: [PATCH 03/75] Support current ToolbarButton usage --- .../components/src/toolbar-button/index.js | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index 71b9e1070b1238..d294c41f7a8545 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -3,11 +3,18 @@ */ import classnames from 'classnames'; +/** + * WordPress dependencies + */ +import { useContext } from '@wordpress/element'; + /** * Internal dependencies */ import IconButton from '../icon-button'; +import ToolbarContext from '../toolbar-context'; import AccessibleToolbarButton from './accessible-toolbar-button'; +import ToolbarButtonContainer from './toolbar-button-container'; function ToolbarButton( { containerClassName, @@ -22,28 +29,41 @@ function ToolbarButton( { extraProps, children, } ) { + const context = useContext( ToolbarContext ); + const button = ( + { + event.stopPropagation(); + onClick(); + } } + className={ classnames( + 'components-toolbar__control', + className, + { 'is-active': isActive } + ) } + aria-pressed={ isActive } + disabled={ isDisabled } + { ...extraProps } + /> + ); + + if ( context ) { + return ( + + { button } + + ); + } + return ( - - { - event.stopPropagation(); - onClick(); - } } - className={ classnames( - 'components-toolbar__control', - className, - { 'is-active': isActive } - ) } - aria-pressed={ isActive } - disabled={ isDisabled } - { ...extraProps } - /> + + { button } { children } - + ); } From f47de25f63d0c72867438d140ddfa1b66f9f011b Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 11 Oct 2019 19:57:44 -0600 Subject: [PATCH 04/75] Remove useMemo This should be done upstream --- packages/components/src/toolbar/accessible-toolbar.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/components/src/toolbar/accessible-toolbar.js b/packages/components/src/toolbar/accessible-toolbar.js index 3376b0e8f09754..f92285e9c4cbf1 100644 --- a/packages/components/src/toolbar/accessible-toolbar.js +++ b/packages/components/src/toolbar/accessible-toolbar.js @@ -3,11 +3,6 @@ */ import { useToolbarState, Toolbar } from 'reakit/Toolbar'; -/** - * WordPress dependencies - */ -import { useMemo } from '@wordpress/element'; - /** * Internal dependencies */ @@ -15,9 +10,8 @@ import ToolbarContext from '../toolbar-context'; function AccessibleToolbar( props ) { const toolbar = useToolbarState( { loop: true } ); - const value = useMemo( () => toolbar, Object.values( toolbar ) ); return ( - + ); From 3fe4c5a028c15e5be21d7aaad29a1f556ab30e15 Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 11 Oct 2019 20:29:30 -0600 Subject: [PATCH 05/75] Re-export current native versions --- .../accessible-toolbar-button.native.js | 10 +++------- .../src/toolbar/accessible-toolbar.native.js | 15 ++------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button.native.js b/packages/components/src/toolbar-button/accessible-toolbar-button.native.js index 08037c8ab01ab7..7fe5e6657bcd10 100644 --- a/packages/components/src/toolbar-button/accessible-toolbar-button.native.js +++ b/packages/components/src/toolbar-button/accessible-toolbar-button.native.js @@ -1,10 +1,6 @@ /** - * External dependencies + * Internal dependencies */ -import { View } from 'react-native'; +import ToolbarButtonContainer from './toolbar-button-container'; -export default ( props ) => ( - - { props.children } - -); +export default ToolbarButtonContainer; diff --git a/packages/components/src/toolbar/accessible-toolbar.native.js b/packages/components/src/toolbar/accessible-toolbar.native.js index c2a85598f5490c..534f84295c4ea2 100644 --- a/packages/components/src/toolbar/accessible-toolbar.native.js +++ b/packages/components/src/toolbar/accessible-toolbar.native.js @@ -1,17 +1,6 @@ -/** - * External dependencies - */ -import { View } from 'react-native'; - /** * Internal dependencies */ -import styles from './style.scss'; - -const AccessibleToolbar = ( props ) => ( - - { props.children } - -); +import ToolbarContainer from './toolbar-container'; -export default AccessibleToolbar; +export default ToolbarContainer; From 73226d152e931bf89ebe7958d480b239f69462e5 Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 11 Oct 2019 20:31:21 -0600 Subject: [PATCH 06/75] Fix AccessibleToolbarButton overwriting existing IconButton props --- .../src/toolbar-button/accessible-toolbar-button.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button.js b/packages/components/src/toolbar-button/accessible-toolbar-button.js index c574caca46c6f2..191263f3823b96 100644 --- a/packages/components/src/toolbar-button/accessible-toolbar-button.js +++ b/packages/components/src/toolbar-button/accessible-toolbar-button.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { ToolbarItem } from 'reakit/Toolbar'; +import { useToolbarItem } from 'reakit/Toolbar'; /** * WordPress dependencies @@ -15,11 +15,9 @@ import ToolbarContext from '../toolbar-context'; function AccessibleToolbarButton( props ) { const toolbar = useContext( ToolbarContext ); - return ( - - { ( htmlProps ) => cloneElement( Children.only( props.children ), htmlProps ) } - - ); + const children = Children.only( props.children ); + const itemProps = useToolbarItem( toolbar, children.props ); + return cloneElement( children, itemProps ); } export default AccessibleToolbarButton; From adef315fdcb5636fd1c833c88dd115bebdfea52d Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 11 Oct 2019 20:31:56 -0600 Subject: [PATCH 07/75] Add label to Toolbar --- .../components/src/toolbar/accessible-toolbar.js | 4 ++-- packages/components/src/toolbar/index.js | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/components/src/toolbar/accessible-toolbar.js b/packages/components/src/toolbar/accessible-toolbar.js index f92285e9c4cbf1..2a49765233992d 100644 --- a/packages/components/src/toolbar/accessible-toolbar.js +++ b/packages/components/src/toolbar/accessible-toolbar.js @@ -8,11 +8,11 @@ import { useToolbarState, Toolbar } from 'reakit/Toolbar'; */ import ToolbarContext from '../toolbar-context'; -function AccessibleToolbar( props ) { +function AccessibleToolbar( { label, ...props } ) { const toolbar = useToolbarState( { loop: true } ); return ( - + ); } diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 2ac6a8e04fc100..9d5c305bcbcea2 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -51,10 +51,15 @@ function Toolbar( { controls = [], children, className, isCollapsed, icon, label return null; } - const cx = classnames( 'components-toolbar', className ); + const cls = classnames( 'components-toolbar', className ); + const isLegacy = Boolean( controls.length ) || typeof isCollapsed !== 'undefined' || icon; - if ( ! controls || ! controls.length ) { - return { children }; + if ( ! isLegacy ) { + return ( + + { children } + + ); } // Normalize controls to nested array of objects (sets of controls) @@ -70,13 +75,13 @@ function Toolbar( { controls = [], children, className, isCollapsed, icon, label icon={ icon } label={ label } controls={ controlSets } - className={ cx } + className={ cls } /> ); } return ( - + { flatMap( controlSets, ( controlSet, indexOfSet ) => ( controlSet.map( ( control, indexOfControl ) => ( Date: Fri, 11 Oct 2019 20:32:09 -0600 Subject: [PATCH 08/75] Create stories --- .../components/src/toolbar/stories/index.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 packages/components/src/toolbar/stories/index.js diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js new file mode 100644 index 00000000000000..2528cbaf886610 --- /dev/null +++ b/packages/components/src/toolbar/stories/index.js @@ -0,0 +1,36 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Toolbar from '../'; +import ToolbarButton from '../../toolbar-button'; + +export default { title: 'Toolbar', component: Toolbar }; + +export function _default() { + const MyToolbar = () => { + const [ active, setActive ] = useState(); + + const createToolbarButton = ( thumbs ) => ( + setActive( thumbs ) } + /> + ); + + return ( + + { [ 'up', 'down' ].map( createToolbarButton ) } + + ); + }; + + return ; +} From f7aaa2e974a7782db8908fe6787163baefe188f5 Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 11 Oct 2019 23:06:54 -0600 Subject: [PATCH 09/75] Upgrade reakit dependency --- package-lock.json | 2 +- packages/components/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ce616497f68629..fbb44ed6619258 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7070,7 +7070,7 @@ "re-resizable": "^6.0.0", "react-dates": "^17.1.1", "react-spring": "^8.0.20", - "reakit": "^1.0.0-beta.8", + "reakit": "^1.0.0-beta.9", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "uuid": "^3.3.2" diff --git a/packages/components/package.json b/packages/components/package.json index 557bb02016b260..5be2b68aad3616 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -42,7 +42,7 @@ "re-resizable": "^6.0.0", "react-dates": "^17.1.1", "react-spring": "^8.0.20", - "reakit": "^1.0.0-beta.8", + "reakit": "^1.0.0-beta.9", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "uuid": "^3.3.2" From 1ab417911527d7484f4b0c0f4b17242d8696daa9 Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 11 Oct 2019 23:45:58 -0600 Subject: [PATCH 10/75] Fix ToolbarContext value --- packages/components/src/toolbar-context/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/toolbar-context/index.js b/packages/components/src/toolbar-context/index.js index dddd75af33cf15..a5095c503f6ece 100644 --- a/packages/components/src/toolbar-context/index.js +++ b/packages/components/src/toolbar-context/index.js @@ -3,6 +3,6 @@ */ import { createContext } from '@wordpress/element'; -const ToolbarContext = createContext( {} ); +const ToolbarContext = createContext(); export default ToolbarContext; From 98d1f258955206816891d8835f20a92d38f0fd65 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 12 Oct 2019 00:02:00 -0600 Subject: [PATCH 11/75] Update package-lock.json --- package-lock.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index fbb44ed6619258..bf889827201ada 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28761,25 +28761,25 @@ } }, "reakit": { - "version": "1.0.0-beta.8", - "resolved": "https://registry.npmjs.org/reakit/-/reakit-1.0.0-beta.8.tgz", - "integrity": "sha512-ePfumMrM75+OgCT96dHuC05SknEIF0PwdvzDFK7V9gvHpLCR5nn4WqyO8A+kLVAe9SHOQMVH0MPv7kMV90kyyg==", + "version": "1.0.0-beta.9", + "resolved": "https://registry.npmjs.org/reakit/-/reakit-1.0.0-beta.9.tgz", + "integrity": "sha512-rWHW3QnuKDrxfgL3hv/ah/nqbhEozpApLWtrwNpWPMZOoRAuzydgKM2l6rsmxS1aCVR7SmOt6Bv4iwl651SuIg==", "requires": { "body-scroll-lock": "^2.6.4", "popper.js": "^1.15.0", - "reakit-system": "^0.6.5", - "reakit-utils": "^0.6.5" + "reakit-system": "^0.6.6", + "reakit-utils": "^0.6.6" } }, "reakit-system": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/reakit-system/-/reakit-system-0.6.5.tgz", - "integrity": "sha512-KcCI8NA/HZTUkO3nmOv8bfR8XKzahydeK1gjlcYpXxf/AAvjb2wLoiXDXN05tMJsZq9euj+BB6vrpuvpS82qtA==" + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/reakit-system/-/reakit-system-0.6.6.tgz", + "integrity": "sha512-EzhnYAuYTqRci7xkv68PiquHRJpRGDWGEJKTO+0s5cr1f4soooJ3ms0tE7/dQRSSseGon+79jJ4bvfVDvCNing==" }, "reakit-utils": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/reakit-utils/-/reakit-utils-0.6.5.tgz", - "integrity": "sha512-EJz8A3reLagPuVXRXBd6wZ32J4EtoKnWLcu0bnJuJ0TruIV00GPRLNJCCCYJBnkXiE9rPr+OijX4U57wZIzULQ==" + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/reakit-utils/-/reakit-utils-0.6.6.tgz", + "integrity": "sha512-YbyHQ310biVmDkhl4wRhXMvn2cl4ooL2nlpfI753dwPJxEsSqvyayhqLth/j+a4+44J0YeqBCCH3LDoYBrGg2Q==" }, "realpath-native": { "version": "1.1.0", From d1807670d3fda70304ef1c6098f3ab19adbb8f4b Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 12 Oct 2019 00:02:51 -0600 Subject: [PATCH 12/75] Use accessibilityLabel instead of label --- packages/components/src/toolbar/accessible-toolbar.js | 4 ++-- packages/components/src/toolbar/index.js | 7 +++---- packages/components/src/toolbar/stories/index.js | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/components/src/toolbar/accessible-toolbar.js b/packages/components/src/toolbar/accessible-toolbar.js index 2a49765233992d..66422968d3e9d7 100644 --- a/packages/components/src/toolbar/accessible-toolbar.js +++ b/packages/components/src/toolbar/accessible-toolbar.js @@ -8,11 +8,11 @@ import { useToolbarState, Toolbar } from 'reakit/Toolbar'; */ import ToolbarContext from '../toolbar-context'; -function AccessibleToolbar( { label, ...props } ) { +function AccessibleToolbar( { accessibilityLabel, ...props } ) { const toolbar = useToolbarState( { loop: true } ); return ( - + ); } diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 9d5c305bcbcea2..d21978badffe20 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -43,7 +43,7 @@ import AccessibleToolbar from './accessible-toolbar'; * * @return {ReactElement} The rendered toolbar. */ -function Toolbar( { controls = [], children, className, isCollapsed, icon, label, ...otherProps } ) { +function Toolbar( { controls = [], children, className, isCollapsed, icon, accessibilityLabel, label, ...otherProps } ) { if ( ( ! controls || ! controls.length ) && ! children @@ -52,11 +52,10 @@ function Toolbar( { controls = [], children, className, isCollapsed, icon, label } const cls = classnames( 'components-toolbar', className ); - const isLegacy = Boolean( controls.length ) || typeof isCollapsed !== 'undefined' || icon; - if ( ! isLegacy ) { + if ( accessibilityLabel ) { return ( - + { children } ); diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 2528cbaf886610..c78791237cd3a3 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -26,7 +26,7 @@ export function _default() { ); return ( - + { [ 'up', 'down' ].map( createToolbarButton ) } ); From f3152cd371a14318e1f008a57e08a8f5261dbc4d Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 13 Oct 2019 22:15:34 -0600 Subject: [PATCH 13/75] Merge toggleProps event handlers in DropdownMenu --- packages/components/src/dropdown-menu/index.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/components/src/dropdown-menu/index.js b/packages/components/src/dropdown-menu/index.js index e94b246231ad1b..09de07141a6f25 100644 --- a/packages/components/src/dropdown-menu/index.js +++ b/packages/components/src/dropdown-menu/index.js @@ -30,6 +30,16 @@ function mergeProps( defaultProps = {}, props = {} ) { return mergedProps; } +function callAll( ...fns ) { + return ( ...args ) => { + for ( const fn of fns ) { + if ( typeof fn === 'function' ) { + fn( ...args ); + } + } + }; +} + function DropdownMenu( { children, className, @@ -98,8 +108,8 @@ function DropdownMenu( { Date: Sun, 13 Oct 2019 22:17:07 -0600 Subject: [PATCH 14/75] Add ToolbarGroup component --- .../components/src/toolbar-group/index.js | 95 +++++++++++++++ .../src/toolbar-group/style.native.scss | 11 ++ .../components/src/toolbar-group/style.scss | 41 +++++++ .../src/toolbar-group/test/index.js | 110 ++++++++++++++++++ .../toolbar-group/toolbar-group-collapsed.js | 32 +++++ .../toolbar-group-collapsed.native.js | 18 +++ .../toolbar-group/toolbar-group-container.js | 6 + .../toolbar-group-container.native.js | 22 ++++ 8 files changed, 335 insertions(+) create mode 100644 packages/components/src/toolbar-group/index.js create mode 100644 packages/components/src/toolbar-group/style.native.scss create mode 100644 packages/components/src/toolbar-group/style.scss create mode 100644 packages/components/src/toolbar-group/test/index.js create mode 100644 packages/components/src/toolbar-group/toolbar-group-collapsed.js create mode 100644 packages/components/src/toolbar-group/toolbar-group-collapsed.native.js create mode 100644 packages/components/src/toolbar-group/toolbar-group-container.js create mode 100644 packages/components/src/toolbar-group/toolbar-group-container.native.js diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js new file mode 100644 index 00000000000000..0b61aa317bd19b --- /dev/null +++ b/packages/components/src/toolbar-group/index.js @@ -0,0 +1,95 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { flatMap } from 'lodash'; + +/** + * Internal dependencies + */ +import ToolbarButton from '../toolbar-button'; +import ToolbarGroupContainer from './toolbar-group-container'; +import ToolbarGroupCollapsed from './toolbar-group-collapsed'; + +/** + * Renders a toolbar with controls. + * + * The `controls` prop accepts an array of sets. A set is an array of controls. + * Controls have the following shape: + * + * ``` + * { + * icon: string, + * title: string, + * subscript: string, + * onClick: Function, + * isActive: boolean, + * isDisabled: boolean + * } + * ``` + * + * For convenience it is also possible to pass only an array of controls. It is + * then assumed this is the only set. + * + * Either `controls` or `children` is required, otherwise this components + * renders nothing. + * + * @param {Object} props + * @param {Array} [props.controls] The controls to render in this toolbar. + * @param {ReactElement} [props.children] Any other things to render inside the + * toolbar besides the controls. + * @param {string} [props.className] Class to set on the container div. + * + * @return {ReactElement} The rendered toolbar. + */ +function ToolbarGroup( { + controls = [], + children, + className, + isCollapsed, + icon, + label, + ...otherProps +} ) { + if ( ( ! controls || ! controls.length ) && ! children ) { + return null; + } + + const cls = classnames( 'components-toolbar', className ); + + // Normalize controls to nested array of objects (sets of controls) + let controlSets = controls; + if ( ! Array.isArray( controlSets[ 0 ] ) ) { + controlSets = [ controlSets ]; + } + + if ( isCollapsed ) { + return ( + + ); + } + + return ( + + { flatMap( controlSets, ( controlSet, indexOfSet ) => + controlSet.map( ( control, indexOfControl ) => ( + 0 && indexOfControl === 0 ? 'has-left-divider' : null + } + { ...control } + /> + ) ) + ) } + { children } + + ); +} + +export default ToolbarGroup; diff --git a/packages/components/src/toolbar-group/style.native.scss b/packages/components/src/toolbar-group/style.native.scss new file mode 100644 index 00000000000000..e218aa37363e37 --- /dev/null +++ b/packages/components/src/toolbar-group/style.native.scss @@ -0,0 +1,11 @@ +.container { + flex-direction: row; + border-left-width: 1px; + border-color: #e9eff3; + padding-left: 5px; + padding-right: 5px; +} + +.containerDark { + border-color: #525354; +} diff --git a/packages/components/src/toolbar-group/style.scss b/packages/components/src/toolbar-group/style.scss new file mode 100644 index 00000000000000..ae09279ca8b9c5 --- /dev/null +++ b/packages/components/src/toolbar-group/style.scss @@ -0,0 +1,41 @@ +.components-toolbar-group { + margin: 0; + border: $border-width solid $light-gray-500; + background-color: $white; + display: flex; + flex-shrink: 0; +} + +div.components-toolbar-group { + & > div { + // IE11 does not support `position: sticky`, or Flex very well, so use block. + display: block; + @supports (position: sticky) { + display: flex; + } + + margin: 0; + } + + & > div + div { + margin-left: -3px; + + &.has-left-divider { + margin-left: 6px; + position: relative; + overflow: visible; + } + + &.has-left-divider::before { + display: inline-block; + content: ""; + box-sizing: content-box; + background-color: $light-gray-500; + position: absolute; + top: 8px; + left: -3px; + width: 1px; + height: $icon-button-size - 16px; + } + } +} diff --git a/packages/components/src/toolbar-group/test/index.js b/packages/components/src/toolbar-group/test/index.js new file mode 100644 index 00000000000000..6abcad9c843440 --- /dev/null +++ b/packages/components/src/toolbar-group/test/index.js @@ -0,0 +1,110 @@ +/** + * External dependencies + */ +import { shallow } from 'enzyme'; + +/** + * Internal dependencies + */ +import Toolbar from '../'; + +describe( 'Toolbar', () => { + describe( 'basic rendering', () => { + it( 'should render an empty node, when controls are not passed', () => { + const toolbar = shallow( ); + expect( toolbar.type() ).toBeNull(); + } ); + + it( 'should render an empty node, when controls are empty', () => { + const toolbar = shallow( ); + expect( toolbar.type() ).toBeNull(); + } ); + + it( 'should render a list of controls with ToolbarButtons', () => { + const clickHandler = ( event ) => event; + const controls = [ + { + icon: 'wordpress', + title: 'WordPress', + subscript: 'wp', + onClick: clickHandler, + isActive: false, + }, + ]; + const toolbar = shallow( ); + const listItem = toolbar.find( 'ToolbarButton' ); + expect( listItem.props() ).toMatchObject( { + containerClassName: null, + icon: 'wordpress', + title: 'WordPress', + subscript: 'wp', + onClick: clickHandler, + isActive: false, + } ); + } ); + + it( 'should render a list of controls with ToolbarButtons and active control', () => { + const clickHandler = ( event ) => event; + const controls = [ + { + icon: 'wordpress', + title: 'WordPress', + subscript: 'wp', + onClick: clickHandler, + isActive: true, + }, + ]; + const toolbar = shallow( ); + const listItem = toolbar.find( 'ToolbarButton' ); + expect( listItem.props() ).toMatchObject( { + containerClassName: null, + icon: 'wordpress', + title: 'WordPress', + subscript: 'wp', + onClick: clickHandler, + isActive: true, + } ); + } ); + + it( 'should render a nested list of controls with separator between', () => { + const controls = [ + [ // First set + { + icon: 'wordpress', + title: 'WordPress', + }, + ], + [ // Second set + { + icon: 'wordpress', + title: 'WordPress', + }, + ], + ]; + + const toolbar = shallow( ); + expect( toolbar.children() ).toHaveLength( 2 ); + expect( toolbar.childAt( 0 ).prop( 'containerClassName' ) ).toBeNull(); + expect( toolbar.childAt( 1 ).prop( 'containerClassName' ) ).toBe( 'has-left-divider' ); + } ); + + it( 'should call the clickHandler on click.', () => { + const clickHandler = jest.fn(); + const event = { stopPropagation: () => undefined }; + const controls = [ + { + icon: 'wordpress', + title: 'WordPress', + subscript: 'wp', + onClick: clickHandler, + isActive: true, + }, + ]; + const toolbar = shallow( ); + const listItem = toolbar.find( 'ToolbarButton' ); + listItem.simulate( 'click', event ); + expect( clickHandler ).toHaveBeenCalledTimes( 1 ); + expect( clickHandler ).toHaveBeenCalledWith( event ); + } ); + } ); +} ); diff --git a/packages/components/src/toolbar-group/toolbar-group-collapsed.js b/packages/components/src/toolbar-group/toolbar-group-collapsed.js new file mode 100644 index 00000000000000..8f709dbf8ce652 --- /dev/null +++ b/packages/components/src/toolbar-group/toolbar-group-collapsed.js @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import { useToolbarItem } from 'reakit/Toolbar'; + +/** + * WordPress dependencies + */ +import { useContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import DropdownMenu from '../dropdown-menu'; +import ToolbarContext from '../toolbar-context'; + +function ToolbarGroupCollapsed( { controls = [], className, icon, label, toggleProps } ) { + const context = useContext( ToolbarContext ); + const itemProps = useToolbarItem( context, toggleProps ); + return ( + + ); +} + +export default ToolbarGroupCollapsed; diff --git a/packages/components/src/toolbar-group/toolbar-group-collapsed.native.js b/packages/components/src/toolbar-group/toolbar-group-collapsed.native.js new file mode 100644 index 00000000000000..718e1238792c43 --- /dev/null +++ b/packages/components/src/toolbar-group/toolbar-group-collapsed.native.js @@ -0,0 +1,18 @@ +/** + * Internal dependencies + */ +import DropdownMenu from '../dropdown-menu'; + +function ToolbarGroupCollapsed( { controls = [], className, icon, label } ) { + return ( + + ); +} + +export default ToolbarGroupCollapsed; diff --git a/packages/components/src/toolbar-group/toolbar-group-container.js b/packages/components/src/toolbar-group/toolbar-group-container.js new file mode 100644 index 00000000000000..d86e066ce45a11 --- /dev/null +++ b/packages/components/src/toolbar-group/toolbar-group-container.js @@ -0,0 +1,6 @@ +const ToolbarGroupContainer = ( props ) => ( +
+ { props.children } +
+); +export default ToolbarGroupContainer; diff --git a/packages/components/src/toolbar-group/toolbar-group-container.native.js b/packages/components/src/toolbar-group/toolbar-group-container.native.js new file mode 100644 index 00000000000000..705c6b00806c38 --- /dev/null +++ b/packages/components/src/toolbar-group/toolbar-group-container.native.js @@ -0,0 +1,22 @@ +/** + * External dependencies + */ +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { withPreferredColorScheme } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; + +const ToolbarGroupContainer = ( { getStylesFromColorScheme, passedStyle, children } ) => ( + + { children } + +); + +export default withPreferredColorScheme( ToolbarGroupContainer ); From 71618897ac50184db7f65b3c9591e1dedbf03fb8 Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 13 Oct 2019 22:18:12 -0600 Subject: [PATCH 15/75] Use ToolbarGroup on Toolbar legacy --- .../src/toolbar/accessible-toolbar.js | 20 -------- .../src/toolbar/accessible-toolbar.native.js | 6 --- packages/components/src/toolbar/index.js | 51 +++---------------- .../src/toolbar/toolbar-container.js | 24 +++++++-- .../src/toolbar/toolbar-container.native.js | 18 ++----- 5 files changed, 28 insertions(+), 91 deletions(-) delete mode 100644 packages/components/src/toolbar/accessible-toolbar.js delete mode 100644 packages/components/src/toolbar/accessible-toolbar.native.js diff --git a/packages/components/src/toolbar/accessible-toolbar.js b/packages/components/src/toolbar/accessible-toolbar.js deleted file mode 100644 index 66422968d3e9d7..00000000000000 --- a/packages/components/src/toolbar/accessible-toolbar.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * External dependencies - */ -import { useToolbarState, Toolbar } from 'reakit/Toolbar'; - -/** - * Internal dependencies - */ -import ToolbarContext from '../toolbar-context'; - -function AccessibleToolbar( { accessibilityLabel, ...props } ) { - const toolbar = useToolbarState( { loop: true } ); - return ( - - - - ); -} - -export default AccessibleToolbar; diff --git a/packages/components/src/toolbar/accessible-toolbar.native.js b/packages/components/src/toolbar/accessible-toolbar.native.js deleted file mode 100644 index 534f84295c4ea2..00000000000000 --- a/packages/components/src/toolbar/accessible-toolbar.native.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Internal dependencies - */ -import ToolbarContainer from './toolbar-container'; - -export default ToolbarContainer; diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index d21978badffe20..2f913fd98bc873 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -2,15 +2,12 @@ * External dependencies */ import classnames from 'classnames'; -import { flatMap } from 'lodash'; /** * Internal dependencies */ -import ToolbarButton from '../toolbar-button'; -import DropdownMenu from '../dropdown-menu'; import ToolbarContainer from './toolbar-container'; -import AccessibleToolbar from './accessible-toolbar'; +import ToolbarGroup from '../toolbar-group'; /** * Renders a toolbar with controls. @@ -43,56 +40,20 @@ import AccessibleToolbar from './accessible-toolbar'; * * @return {ReactElement} The rendered toolbar. */ -function Toolbar( { controls = [], children, className, isCollapsed, icon, accessibilityLabel, label, ...otherProps } ) { - if ( - ( ! controls || ! controls.length ) && - ! children - ) { - return null; - } - +function Toolbar( { className, accessibilityLabel, ...otherProps } ) { const cls = classnames( 'components-toolbar', className ); if ( accessibilityLabel ) { return ( - - { children } - - ); - } - - // Normalize controls to nested array of objects (sets of controls) - let controlSets = controls; - if ( ! Array.isArray( controlSets[ 0 ] ) ) { - controlSets = [ controlSets ]; - } - - if ( isCollapsed ) { - return ( - ); } - return ( - - { flatMap( controlSets, ( controlSet, indexOfSet ) => ( - controlSet.map( ( control, indexOfControl ) => ( - 0 && indexOfControl === 0 ? 'has-left-divider' : null } - { ...control } - /> - ) ) - ) ) } - { children } - - ); + return ; } export default Toolbar; diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index 9361c1fcf0bbbb..e6d3cdc0053741 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -1,6 +1,20 @@ -const ToolbarContainer = ( props ) => ( -
- { props.children } -
-); +/** + * External dependencies + */ +import { useToolbarState, Toolbar } from 'reakit/Toolbar'; + +/** + * Internal dependencies + */ +import ToolbarContext from '../toolbar-context'; + +function ToolbarContainer( { accessibilityLabel, ...props } ) { + const toolbar = useToolbarState( { loop: true } ); + return ( + + + + ); +} + export default ToolbarContainer; diff --git a/packages/components/src/toolbar/toolbar-container.native.js b/packages/components/src/toolbar/toolbar-container.native.js index 33fe77d11db4c0..75a8a45ebb92ca 100644 --- a/packages/components/src/toolbar/toolbar-container.native.js +++ b/packages/components/src/toolbar/toolbar-container.native.js @@ -3,20 +3,8 @@ */ import { View } from 'react-native'; -/** - * WordPress dependencies - */ -import { withPreferredColorScheme } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import styles from './style.scss'; - -const ToolbarContainer = ( { getStylesFromColorScheme, passedStyle, children } ) => ( - - { children } - +const ToolbarContainer = ( { children } ) => ( + { children } ); -export default withPreferredColorScheme( ToolbarContainer ); +export default ToolbarContainer; From 60d67a852ad2ec4f4a8db93a62a3d2113d9d1af5 Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 13 Oct 2019 22:18:28 -0600 Subject: [PATCH 16/75] Update Toolbar stories --- .../components/src/toolbar/stories/index.js | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index c78791237cd3a3..7bf624c166b56e 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -8,26 +8,29 @@ import { useState } from '@wordpress/element'; */ import Toolbar from '../'; import ToolbarButton from '../../toolbar-button'; +import ToolbarGroup from '../../toolbar-group'; export default { title: 'Toolbar', component: Toolbar }; export function _default() { const MyToolbar = () => { - const [ active, setActive ] = useState(); + const [ thumb, setThumb ] = useState(); - const createToolbarButton = ( thumbs ) => ( - setActive( thumbs ) } - /> - ); + const getThumbProps = ( icon ) => ( { + icon: `thumbs-${ icon }`, + title: `Thumbs ${ icon }`, + isActive: thumb === icon, + onClick: () => setThumb( thumb === icon ? null : icon ), + } ); return ( - - { [ 'up', 'down' ].map( createToolbarButton ) } + + + + ); }; From ef73cedf2779a3b7993b245e87fb84022b11a5e6 Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 13 Oct 2019 22:18:43 -0600 Subject: [PATCH 17/75] Update Toolbar README.md --- packages/components/src/toolbar/README.md | 35 ++++++++++++----------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/components/src/toolbar/README.md b/packages/components/src/toolbar/README.md index 7458cb2e00936e..cdbadce52735c3 100644 --- a/packages/components/src/toolbar/README.md +++ b/packages/components/src/toolbar/README.md @@ -42,23 +42,24 @@ Toolbars that cannot be selected can either be given a disabled state, or be hid ### Usage ```jsx -import { Toolbar } from '@wordpress/components'; -import { withState } from '@wordpress/compose'; - -const MyToolbar = withState( { - activeControl: 'up', -} )( ( { activeControl, setState } ) => { - function createThumbsControl( thumbs ) { - return { - icon: `thumbs-${ thumbs }`, - title: `Thumbs ${ thumbs }`, - isActive: activeControl === thumbs, - onClick: () => setState( { activeControl: thumbs } ), - }; - } - +import { Toolbar, ToolbarButton } from '@wordpress/components'; +import { useState } from '@wordpress/element'; + +function MyToolbar() { + const [ thumb, setThumb ] = useState(); + + const getThumbProps = ( icon ) => ( { + icon: `thumbs-${ icon }`, + title: `Thumbs ${ icon }`, + isActive: thumb === icon, + onClick: () => setThumb( thumb === icon ? null : icon ), + } ); + return ( - + + + + ); -} ); +} ``` From 8dca51c62321664c379f23dc417a1b25fb72fdb7 Mon Sep 17 00:00:00 2001 From: Haz Date: Mon, 14 Oct 2019 11:49:14 -0600 Subject: [PATCH 18/75] Fix ToolbarGroup not being included in the tab order on the legacy usage --- .../components/src/toolbar-group/toolbar-group-collapsed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/toolbar-group/toolbar-group-collapsed.js b/packages/components/src/toolbar-group/toolbar-group-collapsed.js index 8f709dbf8ce652..921fac7818b894 100644 --- a/packages/components/src/toolbar-group/toolbar-group-collapsed.js +++ b/packages/components/src/toolbar-group/toolbar-group-collapsed.js @@ -24,7 +24,7 @@ function ToolbarGroupCollapsed( { controls = [], className, icon, label, toggleP label={ label } controls={ controls } className={ className } - toggleProps={ itemProps } + toggleProps={ context ? itemProps : toggleProps } /> ); } From 72ce182b8b7a579338a7a63f0ab56dcdd5545b78 Mon Sep 17 00:00:00 2001 From: Haz Date: Wed, 16 Oct 2019 23:31:50 -0600 Subject: [PATCH 19/75] Update AccessibleToolbarButton --- ...n.js => accessible-toolbar-button-container.js} | 14 +++++++++----- ... accessible-toolbar-button-container.native.js} | 0 packages/components/src/toolbar-button/index.js | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) rename packages/components/src/toolbar-button/{accessible-toolbar-button.js => accessible-toolbar-button-container.js} (51%) rename packages/components/src/toolbar-button/{accessible-toolbar-button.native.js => accessible-toolbar-button-container.native.js} (100%) diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button.js b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js similarity index 51% rename from packages/components/src/toolbar-button/accessible-toolbar-button.js rename to packages/components/src/toolbar-button/accessible-toolbar-button-container.js index 191263f3823b96..f1ea74f3c4d71d 100644 --- a/packages/components/src/toolbar-button/accessible-toolbar-button.js +++ b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js @@ -13,11 +13,15 @@ import { useContext, Children, cloneElement } from '@wordpress/element'; */ import ToolbarContext from '../toolbar-context'; -function AccessibleToolbarButton( props ) { +function AccessibleToolbarButtonContainer( props ) { const toolbar = useContext( ToolbarContext ); - const children = Children.only( props.children ); - const itemProps = useToolbarItem( toolbar, children.props ); - return cloneElement( children, itemProps ); + const button = Children.only( props.children ); + const itemHTMLProps = useToolbarItem( toolbar, button.props ); + return ( +
+ { cloneElement( button, itemHTMLProps ) } +
+ ); } -export default AccessibleToolbarButton; +export default AccessibleToolbarButtonContainer; diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button.native.js b/packages/components/src/toolbar-button/accessible-toolbar-button-container.native.js similarity index 100% rename from packages/components/src/toolbar-button/accessible-toolbar-button.native.js rename to packages/components/src/toolbar-button/accessible-toolbar-button-container.native.js diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index d294c41f7a8545..75564944629bfa 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -13,7 +13,7 @@ import { useContext } from '@wordpress/element'; */ import IconButton from '../icon-button'; import ToolbarContext from '../toolbar-context'; -import AccessibleToolbarButton from './accessible-toolbar-button'; +import AccessibleToolbarButtonContainer from './accessible-toolbar-button-container'; import ToolbarButtonContainer from './toolbar-button-container'; function ToolbarButton( { @@ -53,9 +53,9 @@ function ToolbarButton( { if ( context ) { return ( - + { button } - + ); } From 8a9e38128826818c532de65c5c3e747f50770744 Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 17 Oct 2019 02:43:13 -0600 Subject: [PATCH 20/75] Refactor ToolbarContext and deprecate Toolbar being used as collapsible group --- .../accessible-toolbar-button-container.js | 12 +++++----- .../components/src/toolbar-button/index.js | 7 +++--- .../toolbar-group/toolbar-group-collapsed.js | 24 +++++++++++++------ packages/components/src/toolbar/index.js | 14 ++++++++++- .../src/toolbar/toolbar-container.js | 2 +- .../index.js => toolbar/toolbar-context.js} | 0 6 files changed, 41 insertions(+), 18 deletions(-) rename packages/components/src/{toolbar-context/index.js => toolbar/toolbar-context.js} (100%) diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js index f1ea74f3c4d71d..4b409f29a79a40 100644 --- a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js +++ b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js @@ -6,20 +6,20 @@ import { useToolbarItem } from 'reakit/Toolbar'; /** * WordPress dependencies */ -import { useContext, Children, cloneElement } from '@wordpress/element'; +import { Children, cloneElement, useContext } from '@wordpress/element'; /** * Internal dependencies */ -import ToolbarContext from '../toolbar-context'; +import { __unstableToolbarContext } from '../toolbar'; function AccessibleToolbarButtonContainer( props ) { - const toolbar = useContext( ToolbarContext ); - const button = Children.only( props.children ); - const itemHTMLProps = useToolbarItem( toolbar, button.props ); + const accessibleToolbarState = useContext( __unstableToolbarContext ); + const childButton = Children.only( props.children ); + const itemHTMLProps = useToolbarItem( accessibleToolbarState, childButton.props ); return (
- { cloneElement( button, itemHTMLProps ) } + { cloneElement( childButton, itemHTMLProps ) }
); } diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index 75564944629bfa..896515e591c8da 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -12,7 +12,7 @@ import { useContext } from '@wordpress/element'; * Internal dependencies */ import IconButton from '../icon-button'; -import ToolbarContext from '../toolbar-context'; +import { __unstableToolbarContext } from '../toolbar'; import AccessibleToolbarButtonContainer from './accessible-toolbar-button-container'; import ToolbarButtonContainer from './toolbar-button-container'; @@ -29,7 +29,8 @@ function ToolbarButton( { extraProps, children, } ) { - const context = useContext( ToolbarContext ); + const accessibleToolbarState = useContext( __unstableToolbarContext ); + const button = ( ); - if ( context ) { + if ( accessibleToolbarState ) { return ( { button } diff --git a/packages/components/src/toolbar-group/toolbar-group-collapsed.js b/packages/components/src/toolbar-group/toolbar-group-collapsed.js index 921fac7818b894..8ba7b228e4ffac 100644 --- a/packages/components/src/toolbar-group/toolbar-group-collapsed.js +++ b/packages/components/src/toolbar-group/toolbar-group-collapsed.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { useToolbarItem } from 'reakit/Toolbar'; +import { ToolbarItem } from 'reakit/Toolbar'; /** * WordPress dependencies @@ -12,21 +12,31 @@ import { useContext } from '@wordpress/element'; * Internal dependencies */ import DropdownMenu from '../dropdown-menu'; -import ToolbarContext from '../toolbar-context'; +import { __unstableToolbarContext } from '../toolbar'; -function ToolbarGroupCollapsed( { controls = [], className, icon, label, toggleProps } ) { - const context = useContext( ToolbarContext ); - const itemProps = useToolbarItem( context, toggleProps ); - return ( +function ToolbarGroupCollapsed( { controls = [], className, icon, label } ) { + const accessibleToolbarState = useContext( __unstableToolbarContext ); + + const renderDropdownMenu = ( toggleProps ) => ( ); + + if ( accessibleToolbarState ) { + return ( + + { ( toolbarItemHTMLProps ) => renderDropdownMenu( toolbarItemHTMLProps ) } + + ); + } + + return renderDropdownMenu(); } export default ToolbarGroupCollapsed; diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 2f913fd98bc873..f58be071c76123 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -3,11 +3,19 @@ */ import classnames from 'classnames'; +/** + * WordPress dependencies + */ +import deprecated from '@wordpress/deprecated'; + /** * Internal dependencies */ -import ToolbarContainer from './toolbar-container'; import ToolbarGroup from '../toolbar-group'; +import ToolbarContainer from './toolbar-container'; +import ToolbarContext from './toolbar-context'; + +export const __unstableToolbarContext = ToolbarContext; /** * Renders a toolbar with controls. @@ -53,6 +61,10 @@ function Toolbar( { className, accessibilityLabel, ...otherProps } ) { ); } + deprecated( 'Using `Toolbar` as a collapsible group of controls', { + alternative: '`ToolbarGroup`', + } ); + return ; } diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index e6d3cdc0053741..47a62e7c8dba7d 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -6,7 +6,7 @@ import { useToolbarState, Toolbar } from 'reakit/Toolbar'; /** * Internal dependencies */ -import ToolbarContext from '../toolbar-context'; +import ToolbarContext from './toolbar-context'; function ToolbarContainer( { accessibilityLabel, ...props } ) { const toolbar = useToolbarState( { loop: true } ); diff --git a/packages/components/src/toolbar-context/index.js b/packages/components/src/toolbar/toolbar-context.js similarity index 100% rename from packages/components/src/toolbar-context/index.js rename to packages/components/src/toolbar/toolbar-context.js From 064a47c01cfeb1e7d7912939f78c41f326e6ba6c Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 17 Oct 2019 02:43:27 -0600 Subject: [PATCH 21/75] Update stories --- packages/components/src/toolbar/stories/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 7bf624c166b56e..6507436b68041a 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -28,6 +28,7 @@ export function _default() { From f9492bbd9c128ffe41c175177c91688a768d1211 Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 17 Oct 2019 02:43:41 -0600 Subject: [PATCH 22/75] Refactor ToolbarGroup --- packages/components/src/toolbar-group/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index 0b61aa317bd19b..a720a97f1fbe3c 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -55,7 +55,7 @@ function ToolbarGroup( { return null; } - const cls = classnames( 'components-toolbar', className ); + const finalClassName = classnames( 'components-toolbar', className ); // Normalize controls to nested array of objects (sets of controls) let controlSets = controls; @@ -69,13 +69,13 @@ function ToolbarGroup( { icon={ icon } label={ label } controls={ controlSets } - className={ cls } + className={ finalClassName } /> ); } return ( - + { flatMap( controlSets, ( controlSet, indexOfSet ) => controlSet.map( ( control, indexOfControl ) => ( Date: Fri, 18 Oct 2019 00:59:23 -0600 Subject: [PATCH 23/75] Export ToolbarGroup --- packages/components/src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 8542a86ba9b659..26a0ca31adbd19 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -60,6 +60,7 @@ export { default as Tip } from './tip'; export { default as ToggleControl } from './toggle-control'; export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; +export { default as ToolbarGroup } from './toolbar-group'; export { default as Tooltip } from './tooltip'; export { default as TreeSelect } from './tree-select'; export { default as IsolatedEventContainer } from './isolated-event-container'; From 5e9a5482600e176d6d5172163252fe702ae21ef9 Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 18 Oct 2019 01:01:22 -0600 Subject: [PATCH 24/75] Fix tests --- .../src/components/block-switcher/index.js | 10 +- .../test/__snapshots__/index.js.snap | 4 +- .../components/src/toolbar-button/index.js | 2 +- .../src/toolbar-group/test/index.js | 68 +++++------ packages/components/src/toolbar/test/index.js | 110 ------------------ 5 files changed, 40 insertions(+), 154 deletions(-) delete mode 100644 packages/components/src/toolbar/test/index.js diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js index 4f3510e4ebbb2e..4f7b343c03c914 100644 --- a/packages/block-editor/src/components/block-switcher/index.js +++ b/packages/block-editor/src/components/block-switcher/index.js @@ -7,7 +7,7 @@ import { castArray, filter, first, mapKeys, orderBy, uniq, map } from 'lodash'; * WordPress dependencies */ import { __, _n, sprintf } from '@wordpress/i18n'; -import { Dropdown, IconButton, Toolbar, PanelBody, Path, SVG } from '@wordpress/components'; +import { Dropdown, IconButton, ToolbarGroup, PanelBody, Path, SVG } from '@wordpress/components'; import { getBlockType, getPossibleBlockTransformations, switchToBlockType, cloneBlock, getBlockFromExample } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; import { DOWN } from '@wordpress/keycodes'; @@ -71,14 +71,14 @@ export class BlockSwitcher extends Component { if ( ! hasBlockStyles && ! possibleBlockTransformations.length ) { return ( - + } /> - + ); } @@ -109,7 +109,7 @@ export class BlockSwitcher extends Component { ); return ( - + ) } /> - + ); } } renderContent={ ( { onClose } ) => ( diff --git a/packages/block-editor/src/components/block-switcher/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/block-switcher/test/__snapshots__/index.js.snap index 09a84bb9fbef01..9e93052e7e0aed 100644 --- a/packages/block-editor/src/components/block-switcher/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/block-switcher/test/__snapshots__/index.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`BlockSwitcher should render disabled block switcher with multi block of different types when no transforms 1`] = ` - + - +
`; exports[`BlockSwitcher should render enabled block switcher with multi block when transforms exist 1`] = ` diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index 896515e591c8da..8c4987043adcf0 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -39,7 +39,7 @@ function ToolbarButton( { data-subscript={ subscript } onClick={ ( event ) => { event.stopPropagation(); - onClick(); + onClick( event ); } } className={ classnames( 'components-toolbar__control', diff --git a/packages/components/src/toolbar-group/test/index.js b/packages/components/src/toolbar-group/test/index.js index 6abcad9c843440..f77a0158593d1a 100644 --- a/packages/components/src/toolbar-group/test/index.js +++ b/packages/components/src/toolbar-group/test/index.js @@ -1,26 +1,26 @@ /** * External dependencies */ -import { shallow } from 'enzyme'; +import { mount } from 'enzyme'; /** * Internal dependencies */ -import Toolbar from '../'; +import ToolbarGroup from '../'; -describe( 'Toolbar', () => { +describe( 'ToolbarGroup', () => { describe( 'basic rendering', () => { it( 'should render an empty node, when controls are not passed', () => { - const toolbar = shallow( ); - expect( toolbar.type() ).toBeNull(); + const wrapper = mount( ); + expect( wrapper.html() ).toBeNull(); } ); it( 'should render an empty node, when controls are empty', () => { - const toolbar = shallow( ); - expect( toolbar.type() ).toBeNull(); + const wrapper = mount( ); + expect( wrapper.html() ).toBeNull(); } ); - it( 'should render a list of controls with ToolbarButtons', () => { + it( 'should render a list of controls with buttons', () => { const clickHandler = ( event ) => event; const controls = [ { @@ -31,19 +31,17 @@ describe( 'Toolbar', () => { isActive: false, }, ]; - const toolbar = shallow( ); - const listItem = toolbar.find( 'ToolbarButton' ); - expect( listItem.props() ).toMatchObject( { - containerClassName: null, - icon: 'wordpress', - title: 'WordPress', - subscript: 'wp', - onClick: clickHandler, - isActive: false, + const wrapper = mount( ); + const button = wrapper.find( '[aria-label="WordPress"]' ).hostNodes(); + expect( button.props() ).toMatchObject( { + 'aria-label': 'WordPress', + 'aria-pressed': false, + 'data-subscript': 'wp', + type: 'button', } ); } ); - it( 'should render a list of controls with ToolbarButtons and active control', () => { + it( 'should render a list of controls with buttons and active control', () => { const clickHandler = ( event ) => event; const controls = [ { @@ -54,15 +52,13 @@ describe( 'Toolbar', () => { isActive: true, }, ]; - const toolbar = shallow( ); - const listItem = toolbar.find( 'ToolbarButton' ); - expect( listItem.props() ).toMatchObject( { - containerClassName: null, - icon: 'wordpress', - title: 'WordPress', - subscript: 'wp', - onClick: clickHandler, - isActive: true, + const wrapper = mount( ); + const button = wrapper.find( '[aria-label="WordPress"]' ).hostNodes(); + expect( button.props() ).toMatchObject( { + 'aria-label': 'WordPress', + 'aria-pressed': true, + 'data-subscript': 'wp', + type: 'button', } ); } ); @@ -82,15 +78,16 @@ describe( 'Toolbar', () => { ], ]; - const toolbar = shallow( ); - expect( toolbar.children() ).toHaveLength( 2 ); - expect( toolbar.childAt( 0 ).prop( 'containerClassName' ) ).toBeNull(); - expect( toolbar.childAt( 1 ).prop( 'containerClassName' ) ).toBe( 'has-left-divider' ); + const wrapper = mount( ); + const buttons = wrapper.find( 'button' ).hostNodes(); + const hasLeftDivider = wrapper.find( '.has-left-divider' ).hostNodes(); + expect( buttons ).toHaveLength( 2 ); + expect( hasLeftDivider ).toHaveLength( 1 ); + expect( hasLeftDivider.html() ).toContain( buttons.at( 1 ).html() ); } ); it( 'should call the clickHandler on click.', () => { const clickHandler = jest.fn(); - const event = { stopPropagation: () => undefined }; const controls = [ { icon: 'wordpress', @@ -100,11 +97,10 @@ describe( 'Toolbar', () => { isActive: true, }, ]; - const toolbar = shallow( ); - const listItem = toolbar.find( 'ToolbarButton' ); - listItem.simulate( 'click', event ); + const wrapper = mount( ); + const button = wrapper.find( '[aria-label="WordPress"]' ).hostNodes(); + button.simulate( 'click' ); expect( clickHandler ).toHaveBeenCalledTimes( 1 ); - expect( clickHandler ).toHaveBeenCalledWith( event ); } ); } ); } ); diff --git a/packages/components/src/toolbar/test/index.js b/packages/components/src/toolbar/test/index.js deleted file mode 100644 index 6abcad9c843440..00000000000000 --- a/packages/components/src/toolbar/test/index.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * External dependencies - */ -import { shallow } from 'enzyme'; - -/** - * Internal dependencies - */ -import Toolbar from '../'; - -describe( 'Toolbar', () => { - describe( 'basic rendering', () => { - it( 'should render an empty node, when controls are not passed', () => { - const toolbar = shallow( ); - expect( toolbar.type() ).toBeNull(); - } ); - - it( 'should render an empty node, when controls are empty', () => { - const toolbar = shallow( ); - expect( toolbar.type() ).toBeNull(); - } ); - - it( 'should render a list of controls with ToolbarButtons', () => { - const clickHandler = ( event ) => event; - const controls = [ - { - icon: 'wordpress', - title: 'WordPress', - subscript: 'wp', - onClick: clickHandler, - isActive: false, - }, - ]; - const toolbar = shallow( ); - const listItem = toolbar.find( 'ToolbarButton' ); - expect( listItem.props() ).toMatchObject( { - containerClassName: null, - icon: 'wordpress', - title: 'WordPress', - subscript: 'wp', - onClick: clickHandler, - isActive: false, - } ); - } ); - - it( 'should render a list of controls with ToolbarButtons and active control', () => { - const clickHandler = ( event ) => event; - const controls = [ - { - icon: 'wordpress', - title: 'WordPress', - subscript: 'wp', - onClick: clickHandler, - isActive: true, - }, - ]; - const toolbar = shallow( ); - const listItem = toolbar.find( 'ToolbarButton' ); - expect( listItem.props() ).toMatchObject( { - containerClassName: null, - icon: 'wordpress', - title: 'WordPress', - subscript: 'wp', - onClick: clickHandler, - isActive: true, - } ); - } ); - - it( 'should render a nested list of controls with separator between', () => { - const controls = [ - [ // First set - { - icon: 'wordpress', - title: 'WordPress', - }, - ], - [ // Second set - { - icon: 'wordpress', - title: 'WordPress', - }, - ], - ]; - - const toolbar = shallow( ); - expect( toolbar.children() ).toHaveLength( 2 ); - expect( toolbar.childAt( 0 ).prop( 'containerClassName' ) ).toBeNull(); - expect( toolbar.childAt( 1 ).prop( 'containerClassName' ) ).toBe( 'has-left-divider' ); - } ); - - it( 'should call the clickHandler on click.', () => { - const clickHandler = jest.fn(); - const event = { stopPropagation: () => undefined }; - const controls = [ - { - icon: 'wordpress', - title: 'WordPress', - subscript: 'wp', - onClick: clickHandler, - isActive: true, - }, - ]; - const toolbar = shallow( ); - const listItem = toolbar.find( 'ToolbarButton' ); - listItem.simulate( 'click', event ); - expect( clickHandler ).toHaveBeenCalledTimes( 1 ); - expect( clickHandler ).toHaveBeenCalledWith( event ); - } ); - } ); -} ); From d7e41f4c7b17a795b62105e88077d88d1b8183b5 Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 18 Oct 2019 01:01:42 -0600 Subject: [PATCH 25/75] Fix Styling --- .../components/src/toolbar-group/index.js | 2 +- packages/components/src/toolbar/style.scss | 34 ------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index a720a97f1fbe3c..c52de10ae8523d 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -55,7 +55,7 @@ function ToolbarGroup( { return null; } - const finalClassName = classnames( 'components-toolbar', className ); + const finalClassName = classnames( 'components-toolbar-group', className ); // Normalize controls to nested array of objects (sets of controls) let controlSets = controls; diff --git a/packages/components/src/toolbar/style.scss b/packages/components/src/toolbar/style.scss index 1356b9d83d4afd..5f619acb4d74b0 100644 --- a/packages/components/src/toolbar/style.scss +++ b/packages/components/src/toolbar/style.scss @@ -5,37 +5,3 @@ display: flex; flex-shrink: 0; } - -div.components-toolbar { - & > div { - // IE11 does not support `position: sticky`, or Flex very well, so use block. - display: block; - @supports (position: sticky) { - display: flex; - } - - margin: 0; - } - - & > div + div { - margin-left: -3px; - - &.has-left-divider { - margin-left: 6px; - position: relative; - overflow: visible; - } - - &.has-left-divider::before { - display: inline-block; - content: ""; - box-sizing: content-box; - background-color: $light-gray-500; - position: absolute; - top: 8px; - left: -3px; - width: 1px; - height: $icon-button-size - 16px; - } - } -} From fadd820907f59ba89a0bb043fd6618a1e1aec359 Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 18 Oct 2019 02:32:20 -0600 Subject: [PATCH 26/75] Update comments --- .../accessible-toolbar-button-container.js | 3 ++ .../components/src/toolbar-button/index.js | 1 + .../components/src/toolbar-group/index.js | 10 +---- .../toolbar-group/toolbar-group-collapsed.js | 1 + packages/components/src/toolbar/index.js | 37 ++----------------- .../src/toolbar/toolbar-container.js | 8 ++-- 6 files changed, 14 insertions(+), 46 deletions(-) diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js index 4b409f29a79a40..08ff7897aa27dd 100644 --- a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js +++ b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js @@ -16,7 +16,10 @@ import { __unstableToolbarContext } from '../toolbar'; function AccessibleToolbarButtonContainer( props ) { const accessibleToolbarState = useContext( __unstableToolbarContext ); const childButton = Children.only( props.children ); + + // https://reakit.io/docs/composition/#props-hooks const itemHTMLProps = useToolbarItem( accessibleToolbarState, childButton.props ); + return (
{ cloneElement( childButton, itemHTMLProps ) } diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index 8c4987043adcf0..85aec7657f8aa1 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -60,6 +60,7 @@ function ToolbarButton( { ); } + // ToolbarButton is being used outside of the accessible Toolbar return ( { button } diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index c52de10ae8523d..9f1c1aaa985f2d 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -12,7 +12,7 @@ import ToolbarGroupContainer from './toolbar-group-container'; import ToolbarGroupCollapsed from './toolbar-group-collapsed'; /** - * Renders a toolbar with controls. + * Renders a collapsible group of controls * * The `controls` prop accepts an array of sets. A set is an array of controls. * Controls have the following shape: @@ -33,14 +33,6 @@ import ToolbarGroupCollapsed from './toolbar-group-collapsed'; * * Either `controls` or `children` is required, otherwise this components * renders nothing. - * - * @param {Object} props - * @param {Array} [props.controls] The controls to render in this toolbar. - * @param {ReactElement} [props.children] Any other things to render inside the - * toolbar besides the controls. - * @param {string} [props.className] Class to set on the container div. - * - * @return {ReactElement} The rendered toolbar. */ function ToolbarGroup( { controls = [], diff --git a/packages/components/src/toolbar-group/toolbar-group-collapsed.js b/packages/components/src/toolbar-group/toolbar-group-collapsed.js index 8ba7b228e4ffac..7567bd959e1193 100644 --- a/packages/components/src/toolbar-group/toolbar-group-collapsed.js +++ b/packages/components/src/toolbar-group/toolbar-group-collapsed.js @@ -30,6 +30,7 @@ function ToolbarGroupCollapsed( { controls = [], className, icon, label } ) { if ( accessibleToolbarState ) { return ( + // https://reakit.io/docs/composition/#render-props { ( toolbarItemHTMLProps ) => renderDropdownMenu( toolbarItemHTMLProps ) } diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index f58be071c76123..3c4adeb0d8b3b3 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -17,44 +17,13 @@ import ToolbarContext from './toolbar-context'; export const __unstableToolbarContext = ToolbarContext; -/** - * Renders a toolbar with controls. - * - * The `controls` prop accepts an array of sets. A set is an array of controls. - * Controls have the following shape: - * - * ``` - * { - * icon: string, - * title: string, - * subscript: string, - * onClick: Function, - * isActive: boolean, - * isDisabled: boolean - * } - * ``` - * - * For convenience it is also possible to pass only an array of controls. It is - * then assumed this is the only set. - * - * Either `controls` or `children` is required, otherwise this components - * renders nothing. - * - * @param {Object} props - * @param {Array} [props.controls] The controls to render in this toolbar. - * @param {ReactElement} [props.children] Any other things to render inside the - * toolbar besides the controls. - * @param {string} [props.className] Class to set on the container div. - * - * @return {ReactElement} The rendered toolbar. - */ function Toolbar( { className, accessibilityLabel, ...otherProps } ) { - const cls = classnames( 'components-toolbar', className ); + const finalClassName = classnames( 'components-toolbar', className ); if ( accessibilityLabel ) { return ( @@ -65,7 +34,7 @@ function Toolbar( { className, accessibilityLabel, ...otherProps } ) { alternative: '`ToolbarGroup`', } ); - return ; + return ; } export default Toolbar; diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index 47a62e7c8dba7d..a13b7c0f60190d 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -9,10 +9,12 @@ import { useToolbarState, Toolbar } from 'reakit/Toolbar'; import ToolbarContext from './toolbar-context'; function ToolbarContainer( { accessibilityLabel, ...props } ) { - const toolbar = useToolbarState( { loop: true } ); + // https://reakit.io/docs/basic-concepts/#state-hooks + const toolbarState = useToolbarState( { loop: true } ); + return ( - - + + ); } From 797a6bec34b348637ded7663b4ade5bc3bc30436 Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 24 Oct 2019 00:14:10 -0600 Subject: [PATCH 27/75] Test Toolbar --- packages/components/src/toolbar/test/index.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 packages/components/src/toolbar/test/index.js diff --git a/packages/components/src/toolbar/test/index.js b/packages/components/src/toolbar/test/index.js new file mode 100644 index 00000000000000..0d4259cf08fc13 --- /dev/null +++ b/packages/components/src/toolbar/test/index.js @@ -0,0 +1,63 @@ +/** + * External dependencies + */ +import { mount } from 'enzyme'; + +/** + * Internal dependencies + */ +import Toolbar from '../'; +import ToolbarButton from '../../toolbar-button'; + +describe( 'Toolbar', () => { + describe( 'basic rendering', () => { + it( 'should render a toolbar with toolbar buttons', () => { + const wrapper = mount( + + + + + ); + const control1 = wrapper.find( 'button[aria-label="control1"]' ); + const control2 = wrapper.find( 'button[aria-label="control1"]' ); + expect( control1 ).toHaveLength( 1 ); + expect( control2 ).toHaveLength( 1 ); + } ); + } ); + + describe( 'deprecated', () => { + it( 'should render an empty node, when controls are not passed', () => { + const wrapper = mount( ); + expect( wrapper.html() ).toBeNull(); + expect( console ).toHaveWarnedWith( + 'Using `Toolbar` as a collapsible group of controls is deprecated. Please use `ToolbarGroup` instead.' + ); + } ); + + it( 'should render an empty node, when controls are empty', () => { + const wrapper = mount( ); + expect( wrapper.html() ).toBeNull(); + } ); + + it( 'should render a list of controls with buttons', () => { + const clickHandler = ( event ) => event; + const controls = [ + { + icon: 'wordpress', + title: 'WordPress', + subscript: 'wp', + onClick: clickHandler, + isActive: false, + }, + ]; + const wrapper = mount( ); + const button = wrapper.find( '[aria-label="WordPress"]' ).hostNodes(); + expect( button.props() ).toMatchObject( { + 'aria-label': 'WordPress', + 'aria-pressed': false, + 'data-subscript': 'wp', + type: 'button', + } ); + } ); + } ); +} ); From f5c314a873f98445a16e052eb9bebc4896dd876c Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 24 Oct 2019 00:40:26 -0600 Subject: [PATCH 28/75] Replace `callAll` by inline merge on DropdownMenu --- .../components/src/dropdown-menu/index.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/components/src/dropdown-menu/index.js b/packages/components/src/dropdown-menu/index.js index 09de07141a6f25..768e2edff5209d 100644 --- a/packages/components/src/dropdown-menu/index.js +++ b/packages/components/src/dropdown-menu/index.js @@ -30,16 +30,6 @@ function mergeProps( defaultProps = {}, props = {} ) { return mergedProps; } -function callAll( ...fns ) { - return ( ...args ) => { - for ( const fn of fns ) { - if ( typeof fn === 'function' ) { - fn( ...args ); - } - } - }; -} - function DropdownMenu( { children, className, @@ -108,8 +98,18 @@ function DropdownMenu( { { + onToggle( event ); + if ( mergedToggleProps.onClick ) { + mergedToggleProps.onClick( event ); + } + } } + onKeyDown={ ( event ) => { + openOnArrowDown( event ); + if ( mergedToggleProps.onKeyDown ) { + mergedToggleProps.onKeyDown( event ); + } + } } aria-haspopup="true" aria-expanded={ isOpen } label={ label } From de78c1baccd195d0cf4f4543dc8aceaedc71cf21 Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 24 Oct 2019 00:45:06 -0600 Subject: [PATCH 29/75] Add hint to deprecation warning --- packages/components/src/toolbar/index.js | 1 + packages/components/src/toolbar/test/index.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 3c4adeb0d8b3b3..c7281726e0090c 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -32,6 +32,7 @@ function Toolbar( { className, accessibilityLabel, ...otherProps } ) { deprecated( 'Using `Toolbar` as a collapsible group of controls', { alternative: '`ToolbarGroup`', + hint: 'If you want to render an accessible toolbar, pass in an `accessibilityLabel` prop.', } ); return ; diff --git a/packages/components/src/toolbar/test/index.js b/packages/components/src/toolbar/test/index.js index 0d4259cf08fc13..c71be6f3ab8808 100644 --- a/packages/components/src/toolbar/test/index.js +++ b/packages/components/src/toolbar/test/index.js @@ -30,7 +30,7 @@ describe( 'Toolbar', () => { const wrapper = mount( ); expect( wrapper.html() ).toBeNull(); expect( console ).toHaveWarnedWith( - 'Using `Toolbar` as a collapsible group of controls is deprecated. Please use `ToolbarGroup` instead.' + 'Using `Toolbar` as a collapsible group of controls is deprecated. Please use `ToolbarGroup` instead. Note: If you want to render an accessible toolbar, pass in an `accessibilityLabel` prop.' ); } ); From 3d68c0273dccd3a34a7f3907a2f2eb663d9b0209 Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 24 Oct 2019 01:00:08 -0600 Subject: [PATCH 30/75] Add jsdocs to Toolbar --- packages/components/src/toolbar/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index c7281726e0090c..6a7e32d1d87eb4 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -17,6 +17,18 @@ import ToolbarContext from './toolbar-context'; export const __unstableToolbarContext = ToolbarContext; +/** + * Renders an accessible toolbar that follows the + * [WAI-ARIA Toolbar Pattern](https://www.w3.org/TR/wai-aria-practices/#toolbar). + * + * The `accessibilityLabel` is required. Otherwise it will produce a warning. + * + * To add controls, simply pass `ToolbarButton` components as children. + * + * @param {Object} props + * @param {string} props.accessibilityLabel Required label for assistive technology users + * @param {string} [props.className] + */ function Toolbar( { className, accessibilityLabel, ...otherProps } ) { const finalClassName = classnames( 'components-toolbar', className ); From 77311b5053c8343d408a6e4724cbe9409883689a Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 24 Oct 2019 02:03:09 -0600 Subject: [PATCH 31/75] Import toolbar-group style into style.css --- packages/components/src/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index 6365bff68cf568..90abec15fec0a7 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -42,5 +42,6 @@ @import "./tip/style.scss"; @import "./toggle-control/style.scss"; @import "./toolbar/style.scss"; +@import "./toolbar-group/style.scss"; @import "./toolbar-button/style.scss"; @import "./tooltip/style.scss"; From fe8a04f646c5b62ea8e8ec7a0549fc65d98bfc82 Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 24 Oct 2019 02:04:45 -0600 Subject: [PATCH 32/75] Rename Toolbar/ToolbarGroup class names Kept `.components-toolbar` for ToolbarGroup, so references to this selector don't break. --- packages/components/src/toolbar-group/index.js | 2 +- packages/components/src/toolbar-group/style.scss | 4 ++-- packages/components/src/toolbar/index.js | 6 ++---- packages/components/src/toolbar/style.scss | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index 9f1c1aaa985f2d..38bee17515175d 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -47,7 +47,7 @@ function ToolbarGroup( { return null; } - const finalClassName = classnames( 'components-toolbar-group', className ); + const finalClassName = classnames( 'components-toolbar', className ); // Normalize controls to nested array of objects (sets of controls) let controlSets = controls; diff --git a/packages/components/src/toolbar-group/style.scss b/packages/components/src/toolbar-group/style.scss index ae09279ca8b9c5..1356b9d83d4afd 100644 --- a/packages/components/src/toolbar-group/style.scss +++ b/packages/components/src/toolbar-group/style.scss @@ -1,4 +1,4 @@ -.components-toolbar-group { +.components-toolbar { margin: 0; border: $border-width solid $light-gray-500; background-color: $white; @@ -6,7 +6,7 @@ flex-shrink: 0; } -div.components-toolbar-group { +div.components-toolbar { & > div { // IE11 does not support `position: sticky`, or Flex very well, so use block. display: block; diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 6a7e32d1d87eb4..98045819f3ca5a 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -30,12 +30,10 @@ export const __unstableToolbarContext = ToolbarContext; * @param {string} [props.className] */ function Toolbar( { className, accessibilityLabel, ...otherProps } ) { - const finalClassName = classnames( 'components-toolbar', className ); - if ( accessibilityLabel ) { return ( @@ -47,7 +45,7 @@ function Toolbar( { className, accessibilityLabel, ...otherProps } ) { hint: 'If you want to render an accessible toolbar, pass in an `accessibilityLabel` prop.', } ); - return ; + return ; } export default Toolbar; diff --git a/packages/components/src/toolbar/style.scss b/packages/components/src/toolbar/style.scss index 5f619acb4d74b0..92eaffff3b0920 100644 --- a/packages/components/src/toolbar/style.scss +++ b/packages/components/src/toolbar/style.scss @@ -1,4 +1,4 @@ -.components-toolbar { +.components-accessible-toolbar { margin: 0; border: $border-width solid $light-gray-500; background-color: $white; From 3ecfb347f69108c0cb9382b0dda0b9e3399393be Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 24 Oct 2019 03:10:47 -0600 Subject: [PATCH 33/75] Update Toolbar usage in components --- .../src/components/alignment-toolbar/index.js | 4 ++-- .../src/components/block-alignment-toolbar/index.js | 4 ++-- .../src/components/block-controls/index.js | 4 ++-- .../src/components/block-list/breadcrumb.js | 6 +++--- .../src/components/block-settings-menu/index.js | 6 +++--- .../block-vertical-alignment-toolbar/index.js | 4 ++-- .../src/components/rich-text/format-toolbar/index.js | 6 +++--- packages/block-library/src/audio/edit.js | 6 +++--- packages/block-library/src/cover/edit.js | 6 +++--- packages/block-library/src/embed/embed-controls.js | 6 +++--- packages/block-library/src/file/edit.js | 6 +++--- packages/block-library/src/heading/heading-toolbar.js | 4 ++-- packages/block-library/src/image/edit.js | 10 +++++----- packages/block-library/src/latest-posts/edit.js | 4 ++-- packages/block-library/src/legacy-widget/edit/index.js | 6 +++--- packages/block-library/src/list/edit.js | 4 ++-- packages/block-library/src/media-text/edit.js | 4 ++-- .../block-library/src/media-text/media-container.js | 6 +++--- packages/block-library/src/paragraph/edit.js | 4 ++-- packages/block-library/src/rss/edit.js | 4 ++-- packages/block-library/src/table/edit.js | 6 +++--- packages/block-library/src/video/edit.js | 6 +++--- 22 files changed, 58 insertions(+), 58 deletions(-) diff --git a/packages/block-editor/src/components/alignment-toolbar/index.js b/packages/block-editor/src/components/alignment-toolbar/index.js index aa0233ae0f7518..788aa6be761492 100644 --- a/packages/block-editor/src/components/alignment-toolbar/index.js +++ b/packages/block-editor/src/components/alignment-toolbar/index.js @@ -7,7 +7,7 @@ import { find } from 'lodash'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { Toolbar } from '@wordpress/components'; +import { ToolbarGroup } from '@wordpress/components'; const DEFAULT_ALIGNMENT_CONTROLS = [ { @@ -43,7 +43,7 @@ export function AlignmentToolbar( props ) { const activeAlignment = find( alignmentControls, ( control ) => control.align === value ); return ( - ( - + { children } ); diff --git a/packages/block-editor/src/components/block-list/breadcrumb.js b/packages/block-editor/src/components/block-list/breadcrumb.js index 81f05b40bf7080..93054baef097ff 100644 --- a/packages/block-editor/src/components/block-list/breadcrumb.js +++ b/packages/block-editor/src/components/block-list/breadcrumb.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { Toolbar, Button } from '@wordpress/components'; +import { ToolbarGroup, Button } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { forwardRef } from '@wordpress/element'; @@ -28,7 +28,7 @@ const BlockBreadcrumb = forwardRef( ( { clientId }, ref ) => { return (
- + { rootClientId && ( <> @@ -38,7 +38,7 @@ const BlockBreadcrumb = forwardRef( ( { clientId }, ref ) => { - +
); } ); diff --git a/packages/block-editor/src/components/block-settings-menu/index.js b/packages/block-editor/src/components/block-settings-menu/index.js index e4f7219d1a3803..7939b6ce48cd33 100644 --- a/packages/block-editor/src/components/block-settings-menu/index.js +++ b/packages/block-editor/src/components/block-settings-menu/index.js @@ -8,7 +8,7 @@ import { castArray, flow } from 'lodash'; */ import { __, _n } from '@wordpress/i18n'; import { - Toolbar, + ToolbarGroup, DropdownMenu, MenuGroup, MenuItem, @@ -46,7 +46,7 @@ export function BlockSettingsMenu( { clientIds } ) { onInsertBefore, onRemove, } ) => ( - + ) } - +
) } ); diff --git a/packages/block-editor/src/components/block-vertical-alignment-toolbar/index.js b/packages/block-editor/src/components/block-vertical-alignment-toolbar/index.js index 22206984dc2706..f0d8535806b68c 100644 --- a/packages/block-editor/src/components/block-vertical-alignment-toolbar/index.js +++ b/packages/block-editor/src/components/block-vertical-alignment-toolbar/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { _x } from '@wordpress/i18n'; -import { Toolbar } from '@wordpress/components'; +import { ToolbarGroup } from '@wordpress/components'; /** * Internal dependencies @@ -36,7 +36,7 @@ export function BlockVerticalAlignmentToolbar( { value, onChange, controls = DEF const defaultAlignmentControl = BLOCK_ALIGNMENTS_CONTROLS[ DEFAULT_CONTROL ]; return ( - { return (
- + { [ 'bold', 'italic', 'link' ].map( ( format ) => ) } @@ -32,7 +32,7 @@ const FormatToolbar = () => { /> } - +
); }; diff --git a/packages/block-library/src/audio/edit.js b/packages/block-library/src/audio/edit.js index 15387acbd90c7a..319040b718cc00 100644 --- a/packages/block-library/src/audio/edit.js +++ b/packages/block-library/src/audio/edit.js @@ -9,7 +9,7 @@ import { PanelBody, SelectControl, ToggleControl, - Toolbar, + ToolbarGroup, withNotices, } from '@wordpress/components'; import { @@ -154,14 +154,14 @@ class AudioEdit extends Component { return ( <> - + - +
diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index 1994f4a57e0713..be7a4d5bfc080a 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -21,7 +21,7 @@ import { PanelRow, RangeControl, ToggleControl, - Toolbar, + ToolbarGroup, withNotices, ResizableBox, BaseControl, @@ -292,7 +292,7 @@ class CoverEdit extends Component { { !! ( url || overlayColor.color ) && ( <> - + ) } /> - +
) } diff --git a/packages/block-library/src/embed/embed-controls.js b/packages/block-library/src/embed/embed-controls.js index fe115b403be224..2292d9edf49ea7 100644 --- a/packages/block-library/src/embed/embed-controls.js +++ b/packages/block-library/src/embed/embed-controls.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { IconButton, Toolbar, PanelBody, ToggleControl } from '@wordpress/components'; +import { IconButton, ToolbarGroup, PanelBody, ToggleControl } from '@wordpress/components'; import { BlockControls, InspectorControls } from '@wordpress/block-editor'; const EmbedControls = ( props ) => { @@ -18,7 +18,7 @@ const EmbedControls = ( props ) => { return ( <> - + { showEditButton && ( { onClick={ switchBackToURLInput } /> ) } - +
{ themeSupportsResponsive && blockSupportsResponsive && ( diff --git a/packages/block-library/src/file/edit.js b/packages/block-library/src/file/edit.js index c6f6382a1336d4..cd78a497b7a972 100644 --- a/packages/block-library/src/file/edit.js +++ b/packages/block-library/src/file/edit.js @@ -15,7 +15,7 @@ import { Animate, ClipboardButton, IconButton, - Toolbar, + ToolbarGroup, withNotices, } from '@wordpress/components'; import { compose } from '@wordpress/compose'; @@ -190,7 +190,7 @@ class FileEdit extends Component { /> - + ) } /> - +
diff --git a/packages/block-library/src/heading/heading-toolbar.js b/packages/block-library/src/heading/heading-toolbar.js index 6d19cc0822f894..330712435e6409 100644 --- a/packages/block-library/src/heading/heading-toolbar.js +++ b/packages/block-library/src/heading/heading-toolbar.js @@ -8,7 +8,7 @@ import { range } from 'lodash'; */ import { __, sprintf } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; -import { Toolbar } from '@wordpress/components'; +import { ToolbarGroup } from '@wordpress/components'; /** * Internal dependencies @@ -31,7 +31,7 @@ class HeadingToolbar extends Component { const { isCollapsed = true, minLevel, maxLevel, selectedLevel, onChange } = this.props; return ( - } controls={ range( minLevel, maxLevel ).map( diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index b5779e71180e7d..dcefdcee424acf 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -33,7 +33,7 @@ import { TextareaControl, TextControl, ToggleControl, - Toolbar, + ToolbarGroup, withNotices, } from '@wordpress/components'; import { compose } from '@wordpress/compose'; @@ -595,7 +595,7 @@ export class ImageEdit extends Component { /> { url && ( <> - + - - + + } /> - + ) } diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index 6d0e4198e8242e..a0018d07f4e5bc 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -15,7 +15,7 @@ import { RangeControl, Spinner, ToggleControl, - Toolbar, + ToolbarGroup, RadioControl, } from '@wordpress/components'; import apiFetch from '@wordpress/api-fetch'; @@ -179,7 +179,7 @@ class LatestPostsEdit extends Component { <> { inspectorControls } - +
    - + { ! widgetObject.isHidden && ( ) } - + { inspectorControls } { ! isCallbackWidget && ( diff --git a/packages/block-library/src/list/edit.js b/packages/block-library/src/list/edit.js index e9496cf359d9b2..5373589c0b1281 100644 --- a/packages/block-library/src/list/edit.js +++ b/packages/block-library/src/list/edit.js @@ -9,7 +9,7 @@ import { RichTextShortcut, } from '@wordpress/block-editor'; import { - Toolbar, + ToolbarGroup, } from '@wordpress/components'; import { __unstableIndentListItems as indentListItems, @@ -70,7 +70,7 @@ export default function ListEdit( { } } /> - - - + ) } /> - + ); } diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js index 0d41efd5c26836..d5550ca818903f 100644 --- a/packages/block-library/src/paragraph/edit.js +++ b/packages/block-library/src/paragraph/edit.js @@ -11,7 +11,7 @@ import { Component } from '@wordpress/element'; import { PanelBody, ToggleControl, - Toolbar, + ToolbarGroup, withFallbackStyles, } from '@wordpress/components'; import { @@ -98,7 +98,7 @@ class ParagraphBlock extends Component { } } /> { isRTL && ( - - + diff --git a/packages/block-library/src/table/edit.js b/packages/block-library/src/table/edit.js index b86c0e282d7f9b..1398a073c6d234 100644 --- a/packages/block-library/src/table/edit.js +++ b/packages/block-library/src/table/edit.js @@ -22,7 +22,7 @@ import { ToggleControl, TextControl, Button, - Toolbar, + ToolbarGroup, DropdownMenu, Placeholder, } from '@wordpress/components'; @@ -544,14 +544,14 @@ export class TableEdit extends Component { return ( <> - + - + - + - + From 7d98ff3d7460df7c0b5d6999e5234931bb0694bb Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 24 Oct 2019 03:13:59 -0600 Subject: [PATCH 34/75] Update snapshots --- .../alignment-toolbar/test/__snapshots__/index.js.snap | 4 ++-- .../block-alignment-toolbar/test/__snapshots__/index.js.snap | 2 +- .../test/__snapshots__/index.js.snap | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap index 5ea348074add60..a93e6c3fc2a4a7 100644 --- a/packages/block-editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`AlignmentToolbar should allow custom alignment controls to be specified 1`] = ` - Date: Thu, 24 Oct 2019 03:52:15 -0600 Subject: [PATCH 35/75] Update experimental Toolbar usage --- .../src/navigation-menu/block-colors-selector.js | 6 +++--- packages/block-library/src/navigation-menu/edit.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/block-library/src/navigation-menu/block-colors-selector.js b/packages/block-library/src/navigation-menu/block-colors-selector.js index 0c1c98b691ef4f..1465ffc182e7f9 100644 --- a/packages/block-library/src/navigation-menu/block-colors-selector.js +++ b/packages/block-library/src/navigation-menu/block-colors-selector.js @@ -7,7 +7,7 @@ import { noop } from 'lodash'; /** * WordPress dependencies */ -import { IconButton, Dropdown, Toolbar } from '@wordpress/components'; +import { IconButton, Dropdown, ToolbarGroup } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { DOWN } from '@wordpress/keycodes'; import { ColorPaletteControl, ContrastChecker } from '@wordpress/block-editor'; @@ -43,7 +43,7 @@ const renderToggleComponent = ( style ) => ( { onToggle, isOpen } ) => { }; return ( - + ( { onToggle, isOpen } ) => { onKeyDown={ openOnArrowDown } icon={ } /> - + ); }; diff --git a/packages/block-library/src/navigation-menu/edit.js b/packages/block-library/src/navigation-menu/edit.js index 3fc18a43b2259a..3783eb490949c1 100644 --- a/packages/block-library/src/navigation-menu/edit.js +++ b/packages/block-library/src/navigation-menu/edit.js @@ -21,7 +21,7 @@ import { CheckboxControl, PanelBody, Spinner, - Toolbar, + ToolbarGroup, } from '@wordpress/components'; import { compose } from '@wordpress/compose'; @@ -106,9 +106,9 @@ function NavigationMenu( { return ( - + { navigatorToolbarButton } - + Date: Thu, 24 Oct 2019 03:55:02 -0600 Subject: [PATCH 36/75] Update header toolbar --- .../src/components/header/fullscreen-mode-close/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/index.js b/packages/edit-post/src/components/header/fullscreen-mode-close/index.js index 3b1c42472db6d2..4793eb63fe0e5a 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/index.js +++ b/packages/edit-post/src/components/header/fullscreen-mode-close/index.js @@ -7,7 +7,7 @@ import { get } from 'lodash'; * WordPress dependencies */ import { withSelect } from '@wordpress/data'; -import { IconButton, Toolbar } from '@wordpress/components'; +import { IconButton, ToolbarGroup } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { addQueryArgs } from '@wordpress/url'; @@ -17,7 +17,7 @@ function FullscreenModeClose( { isActive, postType } ) { } return ( - + - + ); } From d7339b7d91e225e2ab0c31ad7bf8a0427dfc1226 Mon Sep 17 00:00:00 2001 From: Haz Date: Fri, 25 Oct 2019 22:03:12 -0600 Subject: [PATCH 37/75] Fix Fill not updating when Slot fillProps change --- packages/components/src/slot-fill/context.js | 7 +++ packages/components/src/slot-fill/fill.js | 4 +- packages/components/src/slot-fill/slot.js | 5 +- .../components/src/slot-fill/test/slot.js | 55 ++++++++++++++++++- 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/packages/components/src/slot-fill/context.js b/packages/components/src/slot-fill/context.js index 587c1ccab5ccb8..5e407988a0abd0 100644 --- a/packages/components/src/slot-fill/context.js +++ b/packages/components/src/slot-fill/context.js @@ -60,6 +60,9 @@ class SlotFillProvider extends Component { // will be empty (the new Slot will subsume all fills for this name). if ( previousSlot ) { previousSlot.forceUpdate(); + + // See 'should re-render Fill when fillProps are updated' test. + this.forceUpdateFills( name ); } } @@ -119,6 +122,10 @@ class SlotFillProvider extends Component { } } + forceUpdateFills( name ) { + forEach( this.fills[ name ], ( instance ) => instance.forceUpdate() ); + } + triggerListeners() { this.listeners.forEach( ( listener ) => listener() ); } diff --git a/packages/components/src/slot-fill/fill.js b/packages/components/src/slot-fill/fill.js index 3600e177e5434f..ac1be6b15853a7 100644 --- a/packages/components/src/slot-fill/fill.js +++ b/packages/components/src/slot-fill/fill.js @@ -6,7 +6,7 @@ import { isFunction } from 'lodash'; /** * WordPress dependencies */ -import { createPortal, useLayoutEffect, useRef } from '@wordpress/element'; +import { createPortal, useLayoutEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies @@ -17,10 +17,12 @@ let occurrences = 0; function FillComponent( { name, children, registerFill, unregisterFill } ) { const slot = useSlot( name ); + const [ , setState ] = useState( {} ); const ref = useRef( { name, children, + forceUpdate: () => setState( {} ), } ); if ( ! ref.current.occurrence ) { diff --git a/packages/components/src/slot-fill/slot.js b/packages/components/src/slot-fill/slot.js index 4578656b5efaa9..e1195b7d4317d5 100644 --- a/packages/components/src/slot-fill/slot.js +++ b/packages/components/src/slot-fill/slot.js @@ -11,6 +11,7 @@ import { /** * WordPress dependencies */ +import isShallowEqual from '@wordpress/is-shallow-equal'; import { Children, Component, @@ -43,9 +44,9 @@ class SlotComponent extends Component { } componentDidUpdate( prevProps ) { - const { name, unregisterSlot, registerSlot } = this.props; + const { name, fillProps, unregisterSlot, registerSlot } = this.props; - if ( prevProps.name !== name ) { + if ( prevProps.name !== name || ! isShallowEqual( prevProps.fillProps, fillProps ) ) { unregisterSlot( prevProps.name ); registerSlot( name, this ); } diff --git a/packages/components/src/slot-fill/test/slot.js b/packages/components/src/slot-fill/test/slot.js index c4a61e8e2a9a55..5d9c7b438b1edb 100644 --- a/packages/components/src/slot-fill/test/slot.js +++ b/packages/components/src/slot-fill/test/slot.js @@ -2,6 +2,8 @@ * External dependencies */ import { isEmpty } from 'lodash'; +import { unmountComponentAtNode, render } from 'react-dom'; +import { act } from 'react-dom/test-utils'; import ReactTestRenderer from 'react-test-renderer'; /** @@ -14,7 +16,7 @@ import Provider from '../context'; /** * WordPress dependencies */ -import { Component } from '@wordpress/element'; +import { Component, useState } from '@wordpress/element'; class Filler extends Component { constructor() { @@ -252,6 +254,57 @@ describe( 'Slot', () => { expect( testRenderer.getInstance().slots ).toHaveProperty( 'egg' ); } ); + + it( 'should re-render Fill when fillProps are updated', () => { + // react-test-renderer doesn't support portal + const container = document.createElement( 'div' ); + document.body.appendChild( container ); + + // Creating a separate component so only this one will be re-rendered + // when its internal state (children) changes. App will not. + // This is necessary so we can ensure that Fill components will re-render + // when fillProps change even when they're in a separate tree. + const StatefulSlot = () => { + const [ children, setChildren ] = useState( 'a' ); + return ( + <> + + + + ); + }; + + const App = () => ( + + + + { ( props ) =>
    { props.children }
    } +
    +
    + ); + + act( () => { + render( , container ); + } ); + + const fill = container.querySelector( '[data-testid="fill"]' ); + const button = container.querySelector( '[data-testid="change"]' ); + + expect( fill.innerHTML ).toBe( 'a' ); + + button.click(); + + expect( fill.innerHTML ).toBe( 'b' ); + + unmountComponentAtNode( container ); + container.remove(); + } ); } ); } ); From 1b4b0bc7ca62b7b75bc7a9544953d26892fa66b2 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 26 Oct 2019 19:01:16 -0600 Subject: [PATCH 38/75] Update Toolbar --- packages/components/src/index.js | 1 + packages/components/src/index.native.js | 2 + packages/components/src/style.scss | 3 +- .../accessible-toolbar-button-container.js | 10 +- .../components/src/toolbar-button/index.js | 23 +++- .../components/src/toolbar-group/index.js | 23 +++- .../components/src/toolbar-group/style.scss | 17 +++ .../toolbar-group/toolbar-group-collapsed.js | 15 ++- .../components/src/toolbar-slot-fill/index.js | 59 +++++++++ .../src/toolbar-slot-fill/style.scss | 4 + packages/components/src/toolbar/index.js | 16 ++- .../components/src/toolbar/stories/index.js | 117 ++++++++++++++---- packages/components/src/toolbar/style.scss | 4 - .../src/toolbar/toolbar-container.js | 44 ++++++- 14 files changed, 279 insertions(+), 59 deletions(-) create mode 100644 packages/components/src/toolbar-slot-fill/index.js create mode 100644 packages/components/src/toolbar-slot-fill/style.scss diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 26a0ca31adbd19..672dd885236bf8 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -61,6 +61,7 @@ export { default as ToggleControl } from './toggle-control'; export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; export { default as ToolbarGroup } from './toolbar-group'; +export { createToolbarSlotFill, ToolbarSlot, ToolbarFill } from './toolbar-slot-fill'; export { default as Tooltip } from './tooltip'; export { default as TreeSelect } from './tree-select'; export { default as IsolatedEventContainer } from './isolated-event-container'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 3fada7034deeb2..085bf4ee543d04 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -4,6 +4,8 @@ export { default as Dashicon } from './dashicon'; export { default as Dropdown } from './dropdown'; export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; +export { default as ToolbarGroup } from './toolbar-group'; +export { createToolbarSlotFill, ToolbarSlot, ToolbarFill } from './toolbar-slot-fill'; export { default as Icon } from './icon'; export { default as IconButton } from './icon-button'; export { default as Spinner } from './spinner'; diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index 90abec15fec0a7..00900b57c4e5ad 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -42,6 +42,7 @@ @import "./tip/style.scss"; @import "./toggle-control/style.scss"; @import "./toolbar/style.scss"; -@import "./toolbar-group/style.scss"; @import "./toolbar-button/style.scss"; +@import "./toolbar-group/style.scss"; +@import "./toolbar-slot-fill/style.scss"; @import "./tooltip/style.scss"; diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js index 08ff7897aa27dd..5451c782f36a20 100644 --- a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js +++ b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js @@ -11,20 +11,16 @@ import { Children, cloneElement, useContext } from '@wordpress/element'; /** * Internal dependencies */ -import { __unstableToolbarContext } from '../toolbar'; +import { ToolbarContext } from '../toolbar'; function AccessibleToolbarButtonContainer( props ) { - const accessibleToolbarState = useContext( __unstableToolbarContext ); + const accessibleToolbarState = useContext( ToolbarContext ); const childButton = Children.only( props.children ); // https://reakit.io/docs/composition/#props-hooks const itemHTMLProps = useToolbarItem( accessibleToolbarState, childButton.props ); - return ( -
    - { cloneElement( childButton, itemHTMLProps ) } -
    - ); + return
    { cloneElement( childButton, itemHTMLProps ) }
    ; } export default AccessibleToolbarButtonContainer; diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index 85aec7657f8aa1..e4777c7e1895e9 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -12,7 +12,7 @@ import { useContext } from '@wordpress/element'; * Internal dependencies */ import IconButton from '../icon-button'; -import { __unstableToolbarContext } from '../toolbar'; +import { ToolbarContext } from '../toolbar'; import AccessibleToolbarButtonContainer from './accessible-toolbar-button-container'; import ToolbarButtonContainer from './toolbar-button-container'; @@ -28,10 +28,13 @@ function ToolbarButton( { isDisabled, extraProps, children, + ...rest } ) { - const accessibleToolbarState = useContext( __unstableToolbarContext ); + // It'll contain state if `ToolbarButton` is being used within + // `` + const accessibleToolbarState = useContext( ToolbarContext ); - const button = ( + const renderButton = ( otherProps ) => ( { event.stopPropagation(); - onClick( event ); + if ( onClick ) { + onClick( event ); + } } } className={ classnames( 'components-toolbar__control', @@ -48,14 +53,20 @@ function ToolbarButton( { ) } aria-pressed={ isActive } disabled={ isDisabled } + // With this attribute, can check if `ToolbarButton` is used within the + // tree and then decide whether to use the accessible Toolbar (which only + // accepts `ToolbarButton` as toolbar items) or fallback to the legacy + // `NavigableToolbar` + data-toolbar-button={ true } { ...extraProps } + { ...otherProps } /> ); if ( accessibleToolbarState ) { return ( - { button } + { renderButton( rest ) } ); } @@ -63,7 +74,7 @@ function ToolbarButton( { // ToolbarButton is being used outside of the accessible Toolbar return ( - { button } + { renderButton() } { children } ); diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index 38bee17515175d..28854d05248d77 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -4,12 +4,18 @@ import classnames from 'classnames'; import { flatMap } from 'lodash'; +/** + * WordPress dependencies + */ +import { useContext } from '@wordpress/element'; + /** * Internal dependencies */ import ToolbarButton from '../toolbar-button'; import ToolbarGroupContainer from './toolbar-group-container'; import ToolbarGroupCollapsed from './toolbar-group-collapsed'; +import { ToolbarContext } from '../toolbar'; /** * Renders a collapsible group of controls @@ -40,14 +46,23 @@ function ToolbarGroup( { className, isCollapsed, icon, - label, + title, ...otherProps } ) { + // It'll contain state if `ToolbarGroup` is being used within + // `` + const accessibleToolbarState = useContext( ToolbarContext ); + if ( ( ! controls || ! controls.length ) && ! children ) { return null; } - const finalClassName = classnames( 'components-toolbar', className ); + const finalClassName = classnames( + // Unfortunately, there's legacy code referencing to `.components-toolbar` + // So we can't get rid of it + accessibleToolbarState ? 'components-toolbar-group' : 'components-toolbar', + className + ); // Normalize controls to nested array of objects (sets of controls) let controlSets = controls; @@ -59,9 +74,11 @@ function ToolbarGroup( { return ( ); } diff --git a/packages/components/src/toolbar-group/style.scss b/packages/components/src/toolbar-group/style.scss index 1356b9d83d4afd..c6680a689bcfe5 100644 --- a/packages/components/src/toolbar-group/style.scss +++ b/packages/components/src/toolbar-group/style.scss @@ -1,3 +1,20 @@ +.components-toolbar-group { + border: $border-width solid $light-gray-500; + background-color: $white; + display: flex; + flex-shrink: 0; + + & + & { + border-left-width: 0; + } + + & & { + border-width: 0; + } +} + +// Legacy toolbar group +// External code references to it, so we can't change it? .components-toolbar { margin: 0; border: $border-width solid $light-gray-500; diff --git a/packages/components/src/toolbar-group/toolbar-group-collapsed.js b/packages/components/src/toolbar-group/toolbar-group-collapsed.js index 7567bd959e1193..700f64fb138b65 100644 --- a/packages/components/src/toolbar-group/toolbar-group-collapsed.js +++ b/packages/components/src/toolbar-group/toolbar-group-collapsed.js @@ -12,10 +12,18 @@ import { useContext } from '@wordpress/element'; * Internal dependencies */ import DropdownMenu from '../dropdown-menu'; -import { __unstableToolbarContext } from '../toolbar'; +import { ToolbarContext } from '../toolbar'; -function ToolbarGroupCollapsed( { controls = [], className, icon, label } ) { - const accessibleToolbarState = useContext( __unstableToolbarContext ); +function ToolbarGroupCollapsed( { + controls = [], + className, + icon, + label, + ...props +} ) { + // It'll contain state if `ToolbarGroup` is being used within + // `` + const accessibleToolbarState = useContext( ToolbarContext ); const renderDropdownMenu = ( toggleProps ) => ( ); diff --git a/packages/components/src/toolbar-slot-fill/index.js b/packages/components/src/toolbar-slot-fill/index.js new file mode 100644 index 00000000000000..18e5e784995dab --- /dev/null +++ b/packages/components/src/toolbar-slot-fill/index.js @@ -0,0 +1,59 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { useContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { Slot, Fill } from '../slot-fill'; +import { ToolbarContext } from '../toolbar'; + +export function ToolbarSlot( { className, ...props } ) { + const accessibleToolbarState = useContext( ToolbarContext ); + const finalClassName = classnames( 'components-toolbar-slot-fill', className ); + return ( + + ); +} + +export function ToolbarFill( props ) { + return ( + + { ( fillProps ) => ( + + { props.children } + + ) } + + ); +} + +/** + * Toolbar requires context to be passed to its children (ToolbarButton's), + * which doesn't happen when using Slot/Fill with bubblesVirtually prop set to + * true. Toolbar Slot/Fill will pass the context automatically. + * + * @param {string} name + */ +export function createToolbarSlotFill( name ) { + const FillComponent = ( props ) => ; + FillComponent.displayName = name + 'Fill'; + + const SlotComponent = ( props ) => ; + SlotComponent.displayName = name + 'Slot'; + + return { + Fill: FillComponent, + Slot: SlotComponent, + }; +} diff --git a/packages/components/src/toolbar-slot-fill/style.scss b/packages/components/src/toolbar-slot-fill/style.scss new file mode 100644 index 00000000000000..297a3cbe51acf2 --- /dev/null +++ b/packages/components/src/toolbar-slot-fill/style.scss @@ -0,0 +1,4 @@ +.components-toolbar-slot-fill { + display: flex; + flex-shrink: 0; +} diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 98045819f3ca5a..ea6b1e870ac5d2 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -6,6 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ +import { forwardRef } from '@wordpress/element'; import deprecated from '@wordpress/deprecated'; /** @@ -15,7 +16,7 @@ import ToolbarGroup from '../toolbar-group'; import ToolbarContainer from './toolbar-container'; import ToolbarContext from './toolbar-context'; -export const __unstableToolbarContext = ToolbarContext; +export { ToolbarContext }; /** * Renders an accessible toolbar that follows the @@ -26,16 +27,19 @@ export const __unstableToolbarContext = ToolbarContext; * To add controls, simply pass `ToolbarButton` components as children. * * @param {Object} props - * @param {string} props.accessibilityLabel Required label for assistive technology users + * @param {string} props.accessibilityLabel Required label for assistive technology users. * @param {string} [props.className] + * @param {Object} ref */ -function Toolbar( { className, accessibilityLabel, ...otherProps } ) { +const Toolbar = forwardRef( ( { className, accessibilityLabel, ...props }, ref ) => { if ( accessibilityLabel ) { return ( ); } @@ -45,7 +49,7 @@ function Toolbar( { className, accessibilityLabel, ...otherProps } ) { hint: 'If you want to render an accessible toolbar, pass in an `accessibilityLabel` prop.', } ); - return ; -} + return ; +} ); export default Toolbar; diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 6507436b68041a..0b19ffc94850d0 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -1,40 +1,107 @@ /** - * WordPress dependencies + * External dependencies */ -import { useState } from '@wordpress/element'; +import { text } from '@storybook/addon-knobs'; /** * Internal dependencies */ import Toolbar from '../'; -import ToolbarButton from '../../toolbar-button'; -import ToolbarGroup from '../../toolbar-group'; +import { + SVG, + Path, + ToolbarButton, + ToolbarGroup, + createToolbarSlotFill, + SlotFillProvider, +} from '../../'; export default { title: 'Toolbar', component: Toolbar }; -export function _default() { - const MyToolbar = () => { - const [ thumb, setThumb ] = useState(); - - const getThumbProps = ( icon ) => ( { - icon: `thumbs-${ icon }`, - title: `Thumbs ${ icon }`, - isActive: thumb === icon, - onClick: () => setThumb( thumb === icon ? null : icon ), - } ); +function InlineImageIcon() { + return ( + + + + ); +} - return ( - - - +export const _default = () => { + return ( + + + + + + + + + , title: 'Inline image' }, + { icon: 'editor-strikethrough', title: 'Strikethrough' }, + ] } /> - - ); - }; + + + + ); +}; - return ; -} +export const toolbarButton = () => { + const icon = text( 'Icon', 'wordpress' ); + const title = text( 'Title', 'WordPress' ); + return ; +}; + +export const withoutGroups = () => { + return ( + + + + + + ); +}; + +export const withSlotFill = () => { + const { Slot, Fill } = createToolbarSlotFill( 'toolbar' ); + + return ( + + + + + + + + + + + + ); +}; diff --git a/packages/components/src/toolbar/style.scss b/packages/components/src/toolbar/style.scss index 8017bb0c3c7ab1..2dc89b9b4e5f8f 100644 --- a/packages/components/src/toolbar/style.scss +++ b/packages/components/src/toolbar/style.scss @@ -1,8 +1,4 @@ .components-accessible-toolbar { - margin: 0; - border: $border-width solid $light-gray-500; - background-color: $white; - // Required for IE11. display: inline-flex; diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index a13b7c0f60190d..bba82774d967ad 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -3,20 +3,56 @@ */ import { useToolbarState, Toolbar } from 'reakit/Toolbar'; +/** + * WordPress dependencies + */ +import { forwardRef, useEffect, useRef, useCallback } from '@wordpress/element'; + /** * Internal dependencies */ import ToolbarContext from './toolbar-context'; -function ToolbarContainer( { accessibilityLabel, ...props } ) { +// When using slots, register/unregister are called with delay, and there's a +// chance that the toolbar is already unmounted when this happens. +function useMountedToolbarState( initialState ) { + const toolbar = useToolbarState( initialState ); + const mounted = useRef( true ); + + useEffect( () => () => { + mounted.current = false; + }, [] ); + + return { + ...toolbar, + register: useCallback( ( ...args ) => { + if ( mounted.current ) { + toolbar.register( ...args ); + } + }, [ toolbar.register ] ), + unregister: useCallback( ( ...args ) => { + if ( mounted.current ) { + toolbar.unregister( ...args ); + } + }, [ toolbar.unregister ] ), + }; +} + +function ToolbarContainer( { accessibilityLabel, ...props }, ref ) { // https://reakit.io/docs/basic-concepts/#state-hooks - const toolbarState = useToolbarState( { loop: true } ); + const toolbarState = useMountedToolbarState( { loop: true } ); return ( + // This will provide state for `ToolbarButton`'s - + ); } -export default ToolbarContainer; +export default forwardRef( ToolbarContainer ); From 8d4e0ffbbf311305822527db2df1d7877bfa662f Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 26 Oct 2019 23:01:59 -0600 Subject: [PATCH 39/75] Move data-toolbar-button to the accessible toolbar button --- .../accessible-toolbar-button-container.js | 9 ++++++++- packages/components/src/toolbar-button/index.js | 5 ----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js index 5451c782f36a20..5aa5d1a2a075e9 100644 --- a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js +++ b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js @@ -18,7 +18,14 @@ function AccessibleToolbarButtonContainer( props ) { const childButton = Children.only( props.children ); // https://reakit.io/docs/composition/#props-hooks - const itemHTMLProps = useToolbarItem( accessibleToolbarState, childButton.props ); + const itemHTMLProps = useToolbarItem( accessibleToolbarState, { + ...childButton.props, + // With this attribute, can check if `ToolbarButton` is used within the + // tree and then decide whether to use the accessible Toolbar (which only + // accepts `ToolbarButton` as toolbar items) or fallback to the legacy + // `NavigableToolbar`, which accepts any tabbable element. + 'data-toolbar-button': true, + } ); return
    { cloneElement( childButton, itemHTMLProps ) }
    ; } diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index e4777c7e1895e9..936bb00282a363 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -53,11 +53,6 @@ function ToolbarButton( { ) } aria-pressed={ isActive } disabled={ isDisabled } - // With this attribute, can check if `ToolbarButton` is used within the - // tree and then decide whether to use the accessible Toolbar (which only - // accepts `ToolbarButton` as toolbar items) or fallback to the legacy - // `NavigableToolbar` - data-toolbar-button={ true } { ...extraProps } { ...otherProps } /> From c095e1971dccc0d08633f2fef35ce8a28a879378 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 26 Oct 2019 23:10:14 -0600 Subject: [PATCH 40/75] Update Toolbar stories --- .../components/src/toolbar/stories/index.js | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 0b19ffc94850d0..19a652425f5232 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { text } from '@storybook/addon-knobs'; - /** * Internal dependencies */ @@ -63,7 +58,7 @@ export const _default = () => { title="Change text alignment" isCollapsed controls={ [ - { icon: 'editor-bold', title: 'Align left', isActive: true }, + { icon: 'editor-alignleft', title: 'Align left', isActive: true }, { icon: 'editor-aligncenter', title: 'Align center' }, { icon: 'editor-alignright', title: 'Align right' }, ] } @@ -72,13 +67,7 @@ export const _default = () => { ); }; -export const toolbarButton = () => { - const icon = text( 'Icon', 'wordpress' ); - const title = text( 'Title', 'WordPress' ); - return ; -}; - -export const withoutGroups = () => { +export const withoutGroup = () => { return ( @@ -105,3 +94,35 @@ export const withSlotFill = () => { ); }; + +export const standaloneToolbarGroup = () => { + return ( + + + + + + ); +}; + +export const standaloneToolbarGroupWithControlsProp = () => { + return ( + + ); +}; + +export const legacyToolbar = () => { + return ( + + + + + + ); +}; From bc97f6dbee54559b3bfadecf530304023ec18f1a Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 27 Oct 2019 00:58:24 -0600 Subject: [PATCH 41/75] Update BlockControls and BlockFormatControls slots --- packages/block-editor/src/components/block-controls/index.js | 4 ++-- .../src/components/block-format-controls/index.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-controls/index.js b/packages/block-editor/src/components/block-controls/index.js index dbbc0c62a9981d..1eaf948ac56f14 100644 --- a/packages/block-editor/src/components/block-controls/index.js +++ b/packages/block-editor/src/components/block-controls/index.js @@ -1,14 +1,14 @@ /** * WordPress dependencies */ -import { createSlotFill, ToolbarGroup } from '@wordpress/components'; +import { createToolbarSlotFill, ToolbarGroup } from '@wordpress/components'; /** * Internal dependencies */ import { ifBlockEditSelected } from '../block-edit/context'; -const { Fill, Slot } = createSlotFill( 'BlockControls' ); +const { Fill, Slot } = createToolbarSlotFill( 'BlockControls' ); const BlockControlsFill = ( { controls, children } ) => ( diff --git a/packages/block-editor/src/components/block-format-controls/index.js b/packages/block-editor/src/components/block-format-controls/index.js index 2ab83843846ad4..3ad50829c69e04 100644 --- a/packages/block-editor/src/components/block-format-controls/index.js +++ b/packages/block-editor/src/components/block-format-controls/index.js @@ -1,14 +1,14 @@ /** * WordPress dependencies */ -import { createSlotFill } from '@wordpress/components'; +import { createToolbarSlotFill } from '@wordpress/components'; /** * Internal dependencies */ import { ifBlockEditSelected } from '../block-edit/context'; -const { Fill, Slot } = createSlotFill( 'BlockFormatControls' ); +const { Fill, Slot } = createToolbarSlotFill( 'BlockFormatControls' ); const BlockFormatControls = ifBlockEditSelected( Fill ); From 5a03322508cacab6d02956c6567ddcfc9e8b24ad Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 27 Oct 2019 01:01:37 -0600 Subject: [PATCH 42/75] Use ToolbarButton instead of IconButton? Not sure about this --- .../components/block-navigation/dropdown.js | 4 +- .../components/block-settings-menu/index.js | 159 +++++++++--------- .../src/components/block-switcher/index.js | 6 +- .../test/__snapshots__/index.js.snap | 2 +- .../components/block-switcher/test/index.js | 4 +- .../src/components/block-toolbar/style.scss | 6 + .../src/components/inserter/index.js | 12 +- .../src/components/navigable-toolbar/index.js | 88 ++++++---- .../rich-text/format-toolbar/index.js | 5 +- packages/block-library/src/audio/edit.js | 4 +- packages/block-library/src/cover/edit.js | 4 +- .../block-library/src/embed/embed-controls.js | 4 +- packages/block-library/src/file/edit.js | 4 +- packages/block-library/src/html/edit.js | 1 + packages/block-library/src/image/edit.js | 8 +- .../src/legacy-widget/edit/index.js | 5 +- .../src/media-text/media-container.js | 5 +- .../navigation-menu/use-block-navigator.js | 5 +- packages/block-library/src/table/edit.js | 15 +- packages/block-library/src/video/edit.js | 4 +- .../components/src/toolbar-group/style.scss | 8 +- .../components/header/header-toolbar/index.js | 12 +- .../src/components/editor-history/redo.js | 4 +- .../src/components/editor-history/undo.js | 4 +- .../src/components/table-of-contents/index.js | 4 +- 25 files changed, 212 insertions(+), 165 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/dropdown.js b/packages/block-editor/src/components/block-navigation/dropdown.js index b53dd49bdf4bdc..c4c76aa77a5051 100644 --- a/packages/block-editor/src/components/block-navigation/dropdown.js +++ b/packages/block-editor/src/components/block-navigation/dropdown.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { IconButton, Dropdown, SVG, Path, KeyboardShortcuts } from '@wordpress/components'; +import { ToolbarButton, Dropdown, SVG, Path, KeyboardShortcuts } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { rawShortcut, displayShortcut } from '@wordpress/keycodes'; import { withSelect } from '@wordpress/data'; @@ -32,7 +32,7 @@ function BlockNavigationDropdown( { hasBlocks, isDisabled } ) { } } /> } - ( - - - { ( { onClose } ) => ( - <> - - <__experimentalBlockSettingsMenuFirstItem.Slot - fillProps={ { onClose } } + + { ( { onClose } ) => ( + <> + + <__experimentalBlockSettingsMenuFirstItem.Slot + fillProps={ { onClose } } + /> + { count === 1 && ( + - { count === 1 && ( - - ) } - { count === 1 && ( - - ) } - { canDuplicate && ( - - { __( 'Duplicate' ) } - - ) } - { canInsertDefaultBlock && ( - <> - - { __( 'Insert Before' ) } - - - { __( 'Insert After' ) } - - - ) } - { count === 1 && ( - - ) } - <__experimentalBlockSettingsMenuPluginsExtension.Slot - fillProps={ { clientIds, onClose } } + ) } + { count === 1 && ( + - - - { ! isLocked && ( - - { _n( 'Remove Block', 'Remove Blocks', count ) } - - ) } - - - ) } - + ) } + { canDuplicate && ( + + { __( 'Duplicate' ) } + + ) } + { canInsertDefaultBlock && ( + <> + + { __( 'Insert Before' ) } + + + { __( 'Insert After' ) } + + + ) } + { count === 1 && ( + + ) } + <__experimentalBlockSettingsMenuPluginsExtension.Slot + fillProps={ { clientIds, onClose } } + /> + + + { ! isLocked && ( + + { _n( 'Remove Block', 'Remove Blocks', count ) } + + ) } + + + ) } ) } diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js index 4f7b343c03c914..a0c0b645f8cc7b 100644 --- a/packages/block-editor/src/components/block-switcher/index.js +++ b/packages/block-editor/src/components/block-switcher/index.js @@ -7,7 +7,7 @@ import { castArray, filter, first, mapKeys, orderBy, uniq, map } from 'lodash'; * WordPress dependencies */ import { __, _n, sprintf } from '@wordpress/i18n'; -import { Dropdown, IconButton, ToolbarGroup, PanelBody, Path, SVG } from '@wordpress/components'; +import { Dropdown, ToolbarButton, ToolbarGroup, PanelBody, Path, SVG } from '@wordpress/components'; import { getBlockType, getPossibleBlockTransformations, switchToBlockType, cloneBlock, getBlockFromExample } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; import { DOWN } from '@wordpress/keycodes'; @@ -72,7 +72,7 @@ export class BlockSwitcher extends Component { if ( ! hasBlockStyles && ! possibleBlockTransformations.length ) { return ( - - - { test( 'should simulate a keydown event, which should call onToggle and open transform toggle.', () => { const toggleClosed = shallow( getDropdown().props().renderToggle( { onToggle: onToggleStub, isOpen: false } ) ); - const iconButtonClosed = toggleClosed.find( 'ForwardRef(IconButton)' ); + const iconButtonClosed = toggleClosed.find( 'ToolbarButton' ); iconButtonClosed.simulate( 'keydown', mockKeyDown ); @@ -180,7 +180,7 @@ describe( 'BlockSwitcher', () => { test( 'should simulate a click event, which should call onToggle.', () => { const toggleOpen = shallow( getDropdown().props().renderToggle( { onToggle: onToggleStub, isOpen: true } ) ); - const iconButtonOpen = toggleOpen.find( 'ForwardRef(IconButton)' ); + const iconButtonOpen = toggleOpen.find( 'ToolbarButton' ); iconButtonOpen.simulate( 'keydown', mockKeyDown ); diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index e83f34bc54eadd..fb9733a12cbd7a 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -35,6 +35,12 @@ line-height: 0; } + .components-toolbar-group { + border-color: $light-gray-800; + border-left: 0; + margin: 0; + } + // Add a left border and adjust the color for Top Toolbar mode. .has-fixed-toolbar & { box-shadow: none; diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js index 5abb1ae4ef9230..a25a7e55b9a8e5 100644 --- a/packages/block-editor/src/components/inserter/index.js +++ b/packages/block-editor/src/components/inserter/index.js @@ -12,8 +12,13 @@ import { compose, ifCondition } from '@wordpress/compose'; */ import InserterMenu from './menu'; -const defaultRenderToggle = ( { onToggle, disabled, isOpen } ) => ( - ( + { + if ( ! this.toolbar.current ) { + return; + } + + // If there are tabbable elements within this component that aren't + // ToolbarButton, we'll fallback to the old NavigableToolbar and emit a + // warning + const hasNonAccessibleTabbable = focus.tabbable + .find( this.toolbar.current ) + .some( ( el ) => ! el.hasAttribute( 'data-toolbar-button' ) ); - componentwillUnmount() { - this.toolbar.current.removeEventListener( 'keydown', this.switchOnKeyDown ); + if ( hasNonAccessibleTabbable ) { + this.setState( { hasNonAccessibleTabbable } ); + } + } ); } render() { - const { children, ...props } = this.props; - return ( - - + + { children } + + ), + }; + + if ( this.state.hasNonAccessibleTabbable ) { + deprecated( 'Using tabbable controls without `ToolbarButton`', { + alternative: '`ToolbarButton` for toolbar items', + } ); + + return ( + - { children } - + ); + } + + return ( + ); } } diff --git a/packages/block-editor/src/components/rich-text/format-toolbar/index.js b/packages/block-editor/src/components/rich-text/format-toolbar/index.js index 0aec6a8c7fb90f..dc9eaefa12a082 100644 --- a/packages/block-editor/src/components/rich-text/format-toolbar/index.js +++ b/packages/block-editor/src/components/rich-text/format-toolbar/index.js @@ -9,7 +9,7 @@ import { orderBy } from 'lodash'; */ import { __ } from '@wordpress/i18n'; -import { ToolbarGroup, Slot, DropdownMenu } from '@wordpress/components'; +import { ToolbarGroup, Slot } from '@wordpress/components'; const POPOVER_PROPS = { position: 'bottom left', @@ -24,7 +24,8 @@ const FormatToolbar = () => { ) } { ( fills ) => fills.length !== 0 && - props ), 'title' ) } diff --git a/packages/block-library/src/audio/edit.js b/packages/block-library/src/audio/edit.js index 319040b718cc00..d3c4f5ab211125 100644 --- a/packages/block-library/src/audio/edit.js +++ b/packages/block-library/src/audio/edit.js @@ -5,7 +5,7 @@ import { getBlobByURL, isBlobURL } from '@wordpress/blob'; import { compose } from '@wordpress/compose'; import { Disabled, - IconButton, + ToolbarButton, PanelBody, SelectControl, ToggleControl, @@ -155,7 +155,7 @@ class AudioEdit extends Component { <> - ( - { @@ -20,7 +20,7 @@ const EmbedControls = ( props ) => { { showEditButton && ( - ( - diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index 857cccf28be25c..e1ac109daac9ba 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -20,7 +20,7 @@ import { Button, ButtonGroup, ExternalLink, - IconButton, + ToolbarButton, MenuItem, NavigableMenu, PanelBody, @@ -199,7 +199,7 @@ const ImageURLInputUI = ( { ).title; return ( <> - - - { ! widgetObject.isHidden && ( - ( - setIsNavigationListOpen( true ) } diff --git a/packages/block-library/src/table/edit.js b/packages/block-library/src/table/edit.js index 1e31efbd62626a..fb9cd2081cf9e5 100644 --- a/packages/block-library/src/table/edit.js +++ b/packages/block-library/src/table/edit.js @@ -23,7 +23,6 @@ import { TextControl, Button, ToolbarGroup, - DropdownMenu, Placeholder, } from '@wordpress/components'; @@ -528,14 +527,12 @@ export class TableEdit extends Component { return ( <> - - - + -
    - + - { __( 'Welcome to the wonderful world of blocks! Click the “+” (“Add block”) button to add a new block. There are blocks available for all kinds of content: you can insert text, headings, images, lists, and lots more!' ) } + { __( + 'Welcome to the wonderful world of blocks! Click the “+” (“Add block”) button to add a new block. There are blocks available for all kinds of content: you can insert text, headings, images, lists, and lots more!' + ) }
    diff --git a/packages/editor/src/components/editor-history/redo.js b/packages/editor/src/components/editor-history/redo.js index de6c3e46a26daf..400f85a71d0d69 100644 --- a/packages/editor/src/components/editor-history/redo.js +++ b/packages/editor/src/components/editor-history/redo.js @@ -2,14 +2,14 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { IconButton } from '@wordpress/components'; +import { ToolbarButton } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { displayShortcut } from '@wordpress/keycodes'; function EditorHistoryRedo( { hasRedo, redo } ) { return ( - ( - Date: Sun, 27 Oct 2019 01:16:42 -0600 Subject: [PATCH 43/75] Revert "Use ToolbarButton instead of IconButton?" This reverts commit 5a03322508cacab6d02956c6567ddcfc9e8b24ad. --- .../components/block-navigation/dropdown.js | 4 +- .../components/block-settings-menu/index.js | 159 +++++++++--------- .../src/components/block-switcher/index.js | 6 +- .../test/__snapshots__/index.js.snap | 2 +- .../components/block-switcher/test/index.js | 4 +- .../src/components/block-toolbar/style.scss | 6 - .../src/components/inserter/index.js | 12 +- .../src/components/navigable-toolbar/index.js | 88 ++++------ .../rich-text/format-toolbar/index.js | 5 +- packages/block-library/src/audio/edit.js | 4 +- packages/block-library/src/cover/edit.js | 4 +- .../block-library/src/embed/embed-controls.js | 4 +- packages/block-library/src/file/edit.js | 4 +- packages/block-library/src/html/edit.js | 1 - packages/block-library/src/image/edit.js | 8 +- .../src/legacy-widget/edit/index.js | 5 +- .../src/media-text/media-container.js | 5 +- .../navigation-menu/use-block-navigator.js | 5 +- packages/block-library/src/table/edit.js | 15 +- packages/block-library/src/video/edit.js | 4 +- .../components/src/toolbar-group/style.scss | 8 +- .../components/header/header-toolbar/index.js | 12 +- .../src/components/editor-history/redo.js | 4 +- .../src/components/editor-history/undo.js | 4 +- .../src/components/table-of-contents/index.js | 4 +- 25 files changed, 165 insertions(+), 212 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/dropdown.js b/packages/block-editor/src/components/block-navigation/dropdown.js index c4c76aa77a5051..b53dd49bdf4bdc 100644 --- a/packages/block-editor/src/components/block-navigation/dropdown.js +++ b/packages/block-editor/src/components/block-navigation/dropdown.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { ToolbarButton, Dropdown, SVG, Path, KeyboardShortcuts } from '@wordpress/components'; +import { IconButton, Dropdown, SVG, Path, KeyboardShortcuts } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { rawShortcut, displayShortcut } from '@wordpress/keycodes'; import { withSelect } from '@wordpress/data'; @@ -32,7 +32,7 @@ function BlockNavigationDropdown( { hasBlocks, isDisabled } ) { } } /> } - ( - - { ( { onClose } ) => ( - <> - - <__experimentalBlockSettingsMenuFirstItem.Slot - fillProps={ { onClose } } - /> - { count === 1 && ( - + + { ( { onClose } ) => ( + <> + + <__experimentalBlockSettingsMenuFirstItem.Slot + fillProps={ { onClose } } /> - ) } - { count === 1 && ( - + ) } + { count === 1 && ( + + ) } + { canDuplicate && ( + + { __( 'Duplicate' ) } + + ) } + { canInsertDefaultBlock && ( + <> + + { __( 'Insert Before' ) } + + + { __( 'Insert After' ) } + + + ) } + { count === 1 && ( + + ) } + <__experimentalBlockSettingsMenuPluginsExtension.Slot + fillProps={ { clientIds, onClose } } /> - ) } - { canDuplicate && ( - - { __( 'Duplicate' ) } - - ) } - { canInsertDefaultBlock && ( - <> - - { __( 'Insert Before' ) } - - - { __( 'Insert After' ) } - - - ) } - { count === 1 && ( - - ) } - <__experimentalBlockSettingsMenuPluginsExtension.Slot - fillProps={ { clientIds, onClose } } - /> - - - { ! isLocked && ( - - { _n( 'Remove Block', 'Remove Blocks', count ) } - - ) } - - - ) } + + + { ! isLocked && ( + + { _n( 'Remove Block', 'Remove Blocks', count ) } + + ) } + + + ) } +
    ) } diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js index a0c0b645f8cc7b..4f7b343c03c914 100644 --- a/packages/block-editor/src/components/block-switcher/index.js +++ b/packages/block-editor/src/components/block-switcher/index.js @@ -7,7 +7,7 @@ import { castArray, filter, first, mapKeys, orderBy, uniq, map } from 'lodash'; * WordPress dependencies */ import { __, _n, sprintf } from '@wordpress/i18n'; -import { Dropdown, ToolbarButton, ToolbarGroup, PanelBody, Path, SVG } from '@wordpress/components'; +import { Dropdown, IconButton, ToolbarGroup, PanelBody, Path, SVG } from '@wordpress/components'; import { getBlockType, getPossibleBlockTransformations, switchToBlockType, cloneBlock, getBlockFromExample } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; import { DOWN } from '@wordpress/keycodes'; @@ -72,7 +72,7 @@ export class BlockSwitcher extends Component { if ( ! hasBlockStyles && ! possibleBlockTransformations.length ) { return ( - - - { test( 'should simulate a keydown event, which should call onToggle and open transform toggle.', () => { const toggleClosed = shallow( getDropdown().props().renderToggle( { onToggle: onToggleStub, isOpen: false } ) ); - const iconButtonClosed = toggleClosed.find( 'ToolbarButton' ); + const iconButtonClosed = toggleClosed.find( 'ForwardRef(IconButton)' ); iconButtonClosed.simulate( 'keydown', mockKeyDown ); @@ -180,7 +180,7 @@ describe( 'BlockSwitcher', () => { test( 'should simulate a click event, which should call onToggle.', () => { const toggleOpen = shallow( getDropdown().props().renderToggle( { onToggle: onToggleStub, isOpen: true } ) ); - const iconButtonOpen = toggleOpen.find( 'ToolbarButton' ); + const iconButtonOpen = toggleOpen.find( 'ForwardRef(IconButton)' ); iconButtonOpen.simulate( 'keydown', mockKeyDown ); diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index fb9733a12cbd7a..e83f34bc54eadd 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -35,12 +35,6 @@ line-height: 0; } - .components-toolbar-group { - border-color: $light-gray-800; - border-left: 0; - margin: 0; - } - // Add a left border and adjust the color for Top Toolbar mode. .has-fixed-toolbar & { box-shadow: none; diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js index a25a7e55b9a8e5..5abb1ae4ef9230 100644 --- a/packages/block-editor/src/components/inserter/index.js +++ b/packages/block-editor/src/components/inserter/index.js @@ -12,13 +12,8 @@ import { compose, ifCondition } from '@wordpress/compose'; */ import InserterMenu from './menu'; -const defaultRenderToggle = ( { - toggleComponent: ToggleComponent, - onToggle, - disabled, - isOpen, -} ) => ( - ( + { - if ( ! this.toolbar.current ) { - return; - } - - // If there are tabbable elements within this component that aren't - // ToolbarButton, we'll fallback to the old NavigableToolbar and emit a - // warning - const hasNonAccessibleTabbable = focus.tabbable - .find( this.toolbar.current ) - .some( ( el ) => ! el.hasAttribute( 'data-toolbar-button' ) ); + // We use DOM event listeners instead of React event listeners + // because we want to catch events from the underlying DOM tree + // The React Tree can be different from the DOM tree when using + // portals. Block Toolbars for instance are rendered in a separate + // React Tree. + this.toolbar.current.addEventListener( 'keydown', this.switchOnKeyDown ); + } - if ( hasNonAccessibleTabbable ) { - this.setState( { hasNonAccessibleTabbable } ); - } - } ); + componentwillUnmount() { + this.toolbar.current.removeEventListener( 'keydown', this.switchOnKeyDown ); } render() { - const { children, accessibilityLabel, ...props } = this.props; - const label = accessibilityLabel || props[ 'aria-label' ]; - - const toolbarProps = { - ...omit( props, [ 'focusOnMount' ] ), - ref: this.toolbar, - children: ( - <> - - { children } - - ), - }; - - if ( this.state.hasNonAccessibleTabbable ) { - deprecated( 'Using tabbable controls without `ToolbarButton`', { - alternative: '`ToolbarButton` for toolbar items', - } ); - - return ( - - ); - } - + const { children, ...props } = this.props; return ( - + + + { children } + ); } } diff --git a/packages/block-editor/src/components/rich-text/format-toolbar/index.js b/packages/block-editor/src/components/rich-text/format-toolbar/index.js index dc9eaefa12a082..0aec6a8c7fb90f 100644 --- a/packages/block-editor/src/components/rich-text/format-toolbar/index.js +++ b/packages/block-editor/src/components/rich-text/format-toolbar/index.js @@ -9,7 +9,7 @@ import { orderBy } from 'lodash'; */ import { __ } from '@wordpress/i18n'; -import { ToolbarGroup, Slot } from '@wordpress/components'; +import { ToolbarGroup, Slot, DropdownMenu } from '@wordpress/components'; const POPOVER_PROPS = { position: 'bottom left', @@ -24,8 +24,7 @@ const FormatToolbar = () => { ) } { ( fills ) => fills.length !== 0 && - props ), 'title' ) } diff --git a/packages/block-library/src/audio/edit.js b/packages/block-library/src/audio/edit.js index d3c4f5ab211125..319040b718cc00 100644 --- a/packages/block-library/src/audio/edit.js +++ b/packages/block-library/src/audio/edit.js @@ -5,7 +5,7 @@ import { getBlobByURL, isBlobURL } from '@wordpress/blob'; import { compose } from '@wordpress/compose'; import { Disabled, - ToolbarButton, + IconButton, PanelBody, SelectControl, ToggleControl, @@ -155,7 +155,7 @@ class AudioEdit extends Component { <> - ( - { @@ -20,7 +20,7 @@ const EmbedControls = ( props ) => { { showEditButton && ( - ( - diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index e1ac109daac9ba..857cccf28be25c 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -20,7 +20,7 @@ import { Button, ButtonGroup, ExternalLink, - ToolbarButton, + IconButton, MenuItem, NavigableMenu, PanelBody, @@ -199,7 +199,7 @@ const ImageURLInputUI = ( { ).title; return ( <> - - - { ! widgetObject.isHidden && ( - ( - setIsNavigationListOpen( true ) } diff --git a/packages/block-library/src/table/edit.js b/packages/block-library/src/table/edit.js index fb9cd2081cf9e5..1e31efbd62626a 100644 --- a/packages/block-library/src/table/edit.js +++ b/packages/block-library/src/table/edit.js @@ -23,6 +23,7 @@ import { TextControl, Button, ToolbarGroup, + DropdownMenu, Placeholder, } from '@wordpress/components'; @@ -527,12 +528,14 @@ export class TableEdit extends Component { return ( <> - + + + -
    - + - { __( - 'Welcome to the wonderful world of blocks! Click the “+” (“Add block”) button to add a new block. There are blocks available for all kinds of content: you can insert text, headings, images, lists, and lots more!' - ) } + { __( 'Welcome to the wonderful world of blocks! Click the “+” (“Add block”) button to add a new block. There are blocks available for all kinds of content: you can insert text, headings, images, lists, and lots more!' ) }
    diff --git a/packages/editor/src/components/editor-history/redo.js b/packages/editor/src/components/editor-history/redo.js index 400f85a71d0d69..de6c3e46a26daf 100644 --- a/packages/editor/src/components/editor-history/redo.js +++ b/packages/editor/src/components/editor-history/redo.js @@ -2,14 +2,14 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { ToolbarButton } from '@wordpress/components'; +import { IconButton } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { displayShortcut } from '@wordpress/keycodes'; function EditorHistoryRedo( { hasRedo, redo } ) { return ( - ( - Date: Sun, 27 Oct 2019 01:23:51 -0600 Subject: [PATCH 44/75] Update styles --- .../block-editor/src/components/block-toolbar/style.scss | 6 ++++++ packages/components/src/toolbar-group/style.scss | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index e83f34bc54eadd..fb9733a12cbd7a 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -35,6 +35,12 @@ line-height: 0; } + .components-toolbar-group { + border-color: $light-gray-800; + border-left: 0; + margin: 0; + } + // Add a left border and adjust the color for Top Toolbar mode. .has-fixed-toolbar & { box-shadow: none; diff --git a/packages/components/src/toolbar-group/style.scss b/packages/components/src/toolbar-group/style.scss index c6680a689bcfe5..e03bb5a1a7a640 100644 --- a/packages/components/src/toolbar-group/style.scss +++ b/packages/components/src/toolbar-group/style.scss @@ -3,14 +3,14 @@ background-color: $white; display: flex; flex-shrink: 0; - - & + & { - border-left-width: 0; - } + margin-right: -$border-width; & & { border-width: 0; + margin: 0; } + + line-height: 0; } // Legacy toolbar group From a812fd9c68899c3b5015742bccaa6be3658eb931 Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 27 Oct 2019 18:53:38 -0600 Subject: [PATCH 45/75] Extend toolbar context to accept any button --- .../src/components/block-toolbar/style.scss | 6 -- .../src/components/navigable-toolbar/index.js | 88 +++++++++++++------ packages/components/src/button/index.js | 22 +++-- .../components/src/button/toolbar-button.js | 26 ++++++ packages/components/src/popover/index.js | 75 ++++++++-------- .../accessible-toolbar-button-container.js | 33 ------- ...essible-toolbar-button-container.native.js | 6 -- .../components/src/toolbar-button/index.js | 64 ++++---------- .../components/src/toolbar-group/index.js | 67 ++++++-------- .../components/src/toolbar-group/style.scss | 25 ++---- .../toolbar-group/toolbar-group-collapsed.js | 52 ----------- .../toolbar-group-collapsed.native.js | 18 ---- .../components/src/toolbar/stories/index.js | 6 +- .../src/components/visual-editor/style.scss | 1 + 14 files changed, 194 insertions(+), 295 deletions(-) create mode 100644 packages/components/src/button/toolbar-button.js delete mode 100644 packages/components/src/toolbar-button/accessible-toolbar-button-container.js delete mode 100644 packages/components/src/toolbar-button/accessible-toolbar-button-container.native.js delete mode 100644 packages/components/src/toolbar-group/toolbar-group-collapsed.js delete mode 100644 packages/components/src/toolbar-group/toolbar-group-collapsed.native.js diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index fb9733a12cbd7a..e83f34bc54eadd 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -35,12 +35,6 @@ line-height: 0; } - .components-toolbar-group { - border-color: $light-gray-800; - border-left: 0; - margin: 0; - } - // Add a left border and adjust the color for Top Toolbar mode. .has-fixed-toolbar & { box-shadow: none; diff --git a/packages/block-editor/src/components/navigable-toolbar/index.js b/packages/block-editor/src/components/navigable-toolbar/index.js index 26984e217f3d07..642e95d12bf204 100644 --- a/packages/block-editor/src/components/navigable-toolbar/index.js +++ b/packages/block-editor/src/components/navigable-toolbar/index.js @@ -6,9 +6,10 @@ import { omit } from 'lodash'; /** * WordPress dependencies */ -import { NavigableMenu, KeyboardShortcuts } from '@wordpress/components'; +import { NavigableMenu, Toolbar, KeyboardShortcuts } from '@wordpress/components'; import { Component, createRef } from '@wordpress/element'; import { focus } from '@wordpress/dom'; +import deprecated from '@wordpress/deprecated'; class NavigableToolbar extends Component { constructor() { @@ -17,6 +18,10 @@ class NavigableToolbar extends Component { this.focusToolbar = this.focusToolbar.bind( this ); this.toolbar = createRef(); + + this.state = { + hasNonAccessibleTabbable: false, + }; } focusToolbar() { @@ -31,39 +36,64 @@ class NavigableToolbar extends Component { this.focusToolbar(); } - // We use DOM event listeners instead of React event listeners - // because we want to catch events from the underlying DOM tree - // The React Tree can be different from the DOM tree when using - // portals. Block Toolbars for instance are rendered in a separate - // React Tree. - this.toolbar.current.addEventListener( 'keydown', this.switchOnKeyDown ); - } + // Toolbar items added via Portal (Slot bubblesVirtually) aren't added + // to the DOM right away + window.requestAnimationFrame( () => { + if ( ! this.toolbar.current ) { + return; + } + + // If there are tabbable elements within this component that aren't + // ToolbarButton, we'll fallback to the old NavigableToolbar and emit a + // warning + const hasNonAccessibleTabbable = focus.tabbable + .find( this.toolbar.current ) + .some( ( el ) => ! el.hasAttribute( 'data-toolbar-button' ) ); - componentwillUnmount() { - this.toolbar.current.removeEventListener( 'keydown', this.switchOnKeyDown ); + if ( hasNonAccessibleTabbable ) { + this.setState( { hasNonAccessibleTabbable } ); + } + } ); } render() { - const { children, ...props } = this.props; - return ( - - + + { children } + + ), + }; + + if ( this.state.hasNonAccessibleTabbable ) { + deprecated( 'Using tabbable controls without `ToolbarButton`', { + alternative: '`ToolbarButton` for toolbar items', + } ); + + return ( + - { children } - + ); + } + + return ( + ); } } diff --git a/packages/components/src/button/index.js b/packages/components/src/button/index.js index da3441e7f2982a..165c5dfc9052db 100644 --- a/packages/components/src/button/index.js +++ b/packages/components/src/button/index.js @@ -6,7 +6,12 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { createElement, forwardRef } from '@wordpress/element'; +import { createElement, forwardRef, useContext } from '@wordpress/element'; +/** + * Internal dependencies + */ +import { ToolbarContext } from '../toolbar'; +import ToolbarButton from './toolbar-button'; export function Button( props, ref ) { const { @@ -41,13 +46,16 @@ export function Button( props, ref ) { const tag = href !== undefined && ! disabled ? 'a' : 'button'; const tagProps = tag === 'a' ? { href, target } : { type: 'button', disabled }; + const allProps = { ...tagProps, ...additionalProps, className: classes, ref }; - return createElement( tag, { - ...tagProps, - ...additionalProps, - className: classes, - ref, - } ); + const toolbarContext = useContext( ToolbarContext ); + + // If it's within a toolbar, render with toolbar context instead. + if ( toolbarContext ) { + return ; + } + + return createElement( tag, allProps ); } export default forwardRef( Button ); diff --git a/packages/components/src/button/toolbar-button.js b/packages/components/src/button/toolbar-button.js new file mode 100644 index 00000000000000..f41cd552a045d1 --- /dev/null +++ b/packages/components/src/button/toolbar-button.js @@ -0,0 +1,26 @@ +/** + * External dependencies + */ +import { useToolbarItem } from 'reakit/Toolbar'; + +/** + * WordPress dependencies + */ +import { forwardRef } from '@wordpress/element'; + +function ToolbarButton( { context, as: Component = 'button', ...props }, ref ) { + // https://reakit.io/docs/composition/#props-hooks + const itemHTMLProps = useToolbarItem( context, { + ref, + ...props, + // With this attribute, can check if `ToolbarButton` is used within the + // tree and then decide whether to use the accessible Toolbar (which only + // accepts `ToolbarButton` as toolbar items) or fallback to the legacy + // `NavigableToolbar`, which accepts any tabbable element. + 'data-toolbar-button': true, + } ); + + return ; +} + +export default forwardRef( ToolbarButton ); diff --git a/packages/components/src/popover/index.js b/packages/components/src/popover/index.js index 420ac78b76bdbf..fa9f9842d6bc7b 100644 --- a/packages/components/src/popover/index.js +++ b/packages/components/src/popover/index.js @@ -24,6 +24,7 @@ import ScrollLock from '../scroll-lock'; import IsolatedEventContainer from '../isolated-event-container'; import { Slot, Fill, Consumer } from '../slot-fill'; import Animate from '../animate'; +import { ToolbarContext } from '../toolbar'; const FocusManaged = withConstrainedTabbing( withFocusReturn( ( { children } ) => children ) ); @@ -381,45 +382,47 @@ const Popover = ( { // within popover as inferring close intent. let content = ( - - - { ( { className: animateClassName } ) => ( - - { popoverPosition.isMobile && ( -
    - - { headerTitle } - - -
    - ) } -
    + + + { ( { className: animateClassName } ) => ( + - { children } -
    -
    - ) } -
    -
    + { popoverPosition.isMobile && ( +
    + + { headerTitle } + + +
    + ) } +
    + { children } +
    + + ) } + + + ); // Apply focus to element as long as focusOnMount is truthy; false is diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js b/packages/components/src/toolbar-button/accessible-toolbar-button-container.js deleted file mode 100644 index 5aa5d1a2a075e9..00000000000000 --- a/packages/components/src/toolbar-button/accessible-toolbar-button-container.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * External dependencies - */ -import { useToolbarItem } from 'reakit/Toolbar'; - -/** - * WordPress dependencies - */ -import { Children, cloneElement, useContext } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { ToolbarContext } from '../toolbar'; - -function AccessibleToolbarButtonContainer( props ) { - const accessibleToolbarState = useContext( ToolbarContext ); - const childButton = Children.only( props.children ); - - // https://reakit.io/docs/composition/#props-hooks - const itemHTMLProps = useToolbarItem( accessibleToolbarState, { - ...childButton.props, - // With this attribute, can check if `ToolbarButton` is used within the - // tree and then decide whether to use the accessible Toolbar (which only - // accepts `ToolbarButton` as toolbar items) or fallback to the legacy - // `NavigableToolbar`, which accepts any tabbable element. - 'data-toolbar-button': true, - } ); - - return
    { cloneElement( childButton, itemHTMLProps ) }
    ; -} - -export default AccessibleToolbarButtonContainer; diff --git a/packages/components/src/toolbar-button/accessible-toolbar-button-container.native.js b/packages/components/src/toolbar-button/accessible-toolbar-button-container.native.js deleted file mode 100644 index 7fe5e6657bcd10..00000000000000 --- a/packages/components/src/toolbar-button/accessible-toolbar-button-container.native.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Internal dependencies - */ -import ToolbarButtonContainer from './toolbar-button-container'; - -export default ToolbarButtonContainer; diff --git a/packages/components/src/toolbar-button/index.js b/packages/components/src/toolbar-button/index.js index 936bb00282a363..8c2374b0284d1d 100644 --- a/packages/components/src/toolbar-button/index.js +++ b/packages/components/src/toolbar-button/index.js @@ -3,17 +3,10 @@ */ import classnames from 'classnames'; -/** - * WordPress dependencies - */ -import { useContext } from '@wordpress/element'; - /** * Internal dependencies */ import IconButton from '../icon-button'; -import { ToolbarContext } from '../toolbar'; -import AccessibleToolbarButtonContainer from './accessible-toolbar-button-container'; import ToolbarButtonContainer from './toolbar-button-container'; function ToolbarButton( { @@ -28,48 +21,27 @@ function ToolbarButton( { isDisabled, extraProps, children, - ...rest } ) { - // It'll contain state if `ToolbarButton` is being used within - // `` - const accessibleToolbarState = useContext( ToolbarContext ); - - const renderButton = ( otherProps ) => ( - { - event.stopPropagation(); - if ( onClick ) { - onClick( event ); - } - } } - className={ classnames( - 'components-toolbar__control', - className, - { 'is-active': isActive } - ) } - aria-pressed={ isActive } - disabled={ isDisabled } - { ...extraProps } - { ...otherProps } - /> - ); - - if ( accessibleToolbarState ) { - return ( - - { renderButton( rest ) } - - ); - } - - // ToolbarButton is being used outside of the accessible Toolbar return ( - { renderButton() } + { + event.stopPropagation(); + onClick(); + } } + className={ classnames( + 'components-toolbar__control', + className, + { 'is-active': isActive } + ) } + aria-pressed={ isActive } + disabled={ isDisabled } + { ...extraProps } + /> { children } ); diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index 28854d05248d77..73fbdae4668381 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -4,21 +4,15 @@ import classnames from 'classnames'; import { flatMap } from 'lodash'; -/** - * WordPress dependencies - */ -import { useContext } from '@wordpress/element'; - /** * Internal dependencies */ import ToolbarButton from '../toolbar-button'; -import ToolbarGroupContainer from './toolbar-group-container'; -import ToolbarGroupCollapsed from './toolbar-group-collapsed'; -import { ToolbarContext } from '../toolbar'; +import DropdownMenu from '../dropdown-menu'; +import ToolbarContainer from './toolbar-group-container'; /** - * Renders a collapsible group of controls + * Renders a collapsible group of controls. * * The `controls` prop accepts an array of sets. A set is an array of controls. * Controls have the following shape: @@ -39,31 +33,23 @@ import { ToolbarContext } from '../toolbar'; * * Either `controls` or `children` is required, otherwise this components * renders nothing. + * + * @param {Object} props + * @param {Array} [props.controls] The controls to render in this toolbar. + * @param {WPElement} [props.children] Any other things to render inside the + * toolbar besides the controls. + * @param {string} [props.className] Class to set on the container div. + * + * @return {WPComponent} The rendered component. */ -function ToolbarGroup( { - controls = [], - children, - className, - isCollapsed, - icon, - title, - ...otherProps -} ) { - // It'll contain state if `ToolbarGroup` is being used within - // `` - const accessibleToolbarState = useContext( ToolbarContext ); - - if ( ( ! controls || ! controls.length ) && ! children ) { +function ToolbarGroup( { controls = [], children, className, isCollapsed, icon, label, ...otherProps } ) { + if ( + ( ! controls || ! controls.length ) && + ! children + ) { return null; } - const finalClassName = classnames( - // Unfortunately, there's legacy code referencing to `.components-toolbar` - // So we can't get rid of it - accessibleToolbarState ? 'components-toolbar-group' : 'components-toolbar', - className - ); - // Normalize controls to nested array of objects (sets of controls) let controlSets = controls; if ( ! Array.isArray( controlSets[ 0 ] ) ) { @@ -72,32 +58,29 @@ function ToolbarGroup( { if ( isCollapsed ) { return ( - ); } return ( - - { flatMap( controlSets, ( controlSet, indexOfSet ) => + + { flatMap( controlSets, ( controlSet, indexOfSet ) => ( controlSet.map( ( control, indexOfControl ) => ( 0 && indexOfControl === 0 ? 'has-left-divider' : null - } + containerClassName={ indexOfSet > 0 && indexOfControl === 0 ? 'has-left-divider' : null } { ...control } /> ) ) - ) } + ) ) } { children } - + ); } diff --git a/packages/components/src/toolbar-group/style.scss b/packages/components/src/toolbar-group/style.scss index e03bb5a1a7a640..f073721663d00a 100644 --- a/packages/components/src/toolbar-group/style.scss +++ b/packages/components/src/toolbar-group/style.scss @@ -1,25 +1,16 @@ -.components-toolbar-group { +.components-toolbar { + margin: 0; border: $border-width solid $light-gray-500; background-color: $white; - display: flex; - flex-shrink: 0; - margin-right: -$border-width; - & & { - border-width: 0; - margin: 0; - } + // Required for IE11. + display: inline-flex; - line-height: 0; -} + // IE11 doesn't read rules inside this query. They are applied only to modern browsers. + @supports (position: sticky) { + display: flex; + } -// Legacy toolbar group -// External code references to it, so we can't change it? -.components-toolbar { - margin: 0; - border: $border-width solid $light-gray-500; - background-color: $white; - display: flex; flex-shrink: 0; } diff --git a/packages/components/src/toolbar-group/toolbar-group-collapsed.js b/packages/components/src/toolbar-group/toolbar-group-collapsed.js deleted file mode 100644 index 700f64fb138b65..00000000000000 --- a/packages/components/src/toolbar-group/toolbar-group-collapsed.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * External dependencies - */ -import { ToolbarItem } from 'reakit/Toolbar'; - -/** - * WordPress dependencies - */ -import { useContext } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import DropdownMenu from '../dropdown-menu'; -import { ToolbarContext } from '../toolbar'; - -function ToolbarGroupCollapsed( { - controls = [], - className, - icon, - label, - ...props -} ) { - // It'll contain state if `ToolbarGroup` is being used within - // `` - const accessibleToolbarState = useContext( ToolbarContext ); - - const renderDropdownMenu = ( toggleProps ) => ( - - ); - - if ( accessibleToolbarState ) { - return ( - // https://reakit.io/docs/composition/#render-props - - { ( toolbarItemHTMLProps ) => renderDropdownMenu( toolbarItemHTMLProps ) } - - ); - } - - return renderDropdownMenu(); -} - -export default ToolbarGroupCollapsed; diff --git a/packages/components/src/toolbar-group/toolbar-group-collapsed.native.js b/packages/components/src/toolbar-group/toolbar-group-collapsed.native.js deleted file mode 100644 index 718e1238792c43..00000000000000 --- a/packages/components/src/toolbar-group/toolbar-group-collapsed.native.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Internal dependencies - */ -import DropdownMenu from '../dropdown-menu'; - -function ToolbarGroupCollapsed( { controls = [], className, icon, label } ) { - return ( - - ); -} - -export default ToolbarGroupCollapsed; diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 19a652425f5232..0a4107b910b154 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -29,7 +29,7 @@ export const _default = () => {
    { , title: 'Inline image' }, @@ -55,7 +55,7 @@ export const _default = () => { .block-editor-block-list__block-edit > .block-editor-block-contextual-toolbar, &[data-align="full"] > .block-editor-block-list__block-edit > .block-editor-block-contextual-toolbar { + display: block; height: 0; // This collapses the container to an invisible element without margin. width: calc(100% - #{$block-side-ui-width * 3} - #{$grid-size-small * 1.5}); // -90px to account for inner element left position value causing overflow-x scrollbars margin-left: 0; From 2fce0823cd9d335c748e5c4515f1f615452df7d5 Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 27 Oct 2019 19:02:36 -0600 Subject: [PATCH 46/75] Separate toolbar context --- packages/components/src/button/index.js | 2 +- packages/components/src/popover/index.js | 2 +- .../{toolbar/toolbar-context.js => toolbar-context/index.js} | 0 packages/components/src/toolbar-slot-fill/index.js | 2 +- packages/components/src/toolbar/index.js | 3 --- packages/components/src/toolbar/toolbar-container.js | 2 +- 6 files changed, 4 insertions(+), 7 deletions(-) rename packages/components/src/{toolbar/toolbar-context.js => toolbar-context/index.js} (100%) diff --git a/packages/components/src/button/index.js b/packages/components/src/button/index.js index 165c5dfc9052db..b952e6a3b4fa19 100644 --- a/packages/components/src/button/index.js +++ b/packages/components/src/button/index.js @@ -10,7 +10,7 @@ import { createElement, forwardRef, useContext } from '@wordpress/element'; /** * Internal dependencies */ -import { ToolbarContext } from '../toolbar'; +import ToolbarContext from '../toolbar-context'; import ToolbarButton from './toolbar-button'; export function Button( props, ref ) { diff --git a/packages/components/src/popover/index.js b/packages/components/src/popover/index.js index fa9f9842d6bc7b..fad0bad73d4827 100644 --- a/packages/components/src/popover/index.js +++ b/packages/components/src/popover/index.js @@ -24,7 +24,7 @@ import ScrollLock from '../scroll-lock'; import IsolatedEventContainer from '../isolated-event-container'; import { Slot, Fill, Consumer } from '../slot-fill'; import Animate from '../animate'; -import { ToolbarContext } from '../toolbar'; +import ToolbarContext from '../toolbar-context'; const FocusManaged = withConstrainedTabbing( withFocusReturn( ( { children } ) => children ) ); diff --git a/packages/components/src/toolbar/toolbar-context.js b/packages/components/src/toolbar-context/index.js similarity index 100% rename from packages/components/src/toolbar/toolbar-context.js rename to packages/components/src/toolbar-context/index.js diff --git a/packages/components/src/toolbar-slot-fill/index.js b/packages/components/src/toolbar-slot-fill/index.js index 18e5e784995dab..4f35125ba482fd 100644 --- a/packages/components/src/toolbar-slot-fill/index.js +++ b/packages/components/src/toolbar-slot-fill/index.js @@ -12,7 +12,7 @@ import { useContext } from '@wordpress/element'; * Internal dependencies */ import { Slot, Fill } from '../slot-fill'; -import { ToolbarContext } from '../toolbar'; +import ToolbarContext from '../toolbar-context'; export function ToolbarSlot( { className, ...props } ) { const accessibleToolbarState = useContext( ToolbarContext ); diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index ea6b1e870ac5d2..577cf69f6198c8 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -14,9 +14,6 @@ import deprecated from '@wordpress/deprecated'; */ import ToolbarGroup from '../toolbar-group'; import ToolbarContainer from './toolbar-container'; -import ToolbarContext from './toolbar-context'; - -export { ToolbarContext }; /** * Renders an accessible toolbar that follows the diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index bba82774d967ad..9dea69765f8524 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -11,7 +11,7 @@ import { forwardRef, useEffect, useRef, useCallback } from '@wordpress/element'; /** * Internal dependencies */ -import ToolbarContext from './toolbar-context'; +import ToolbarContext from '../toolbar-context'; // When using slots, register/unregister are called with delay, and there's a // chance that the toolbar is already unmounted when this happens. From 798c7080fbc2fb97ed87db4f85a8b876d44bd785 Mon Sep 17 00:00:00 2001 From: Haz Date: Sun, 27 Oct 2019 19:13:53 -0600 Subject: [PATCH 47/75] Update Toolbar Group styles --- .../src/components/block-toolbar/style.scss | 6 +++++ .../components/src/toolbar-group/index.js | 22 +++++++++++++++++-- .../components/src/toolbar-group/style.scss | 15 +++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index e83f34bc54eadd..fb9733a12cbd7a 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -35,6 +35,12 @@ line-height: 0; } + .components-toolbar-group { + border-color: $light-gray-800; + border-left: 0; + margin: 0; + } + // Add a left border and adjust the color for Top Toolbar mode. .has-fixed-toolbar & { box-shadow: none; diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index 73fbdae4668381..1f96c069f6e5dc 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -4,12 +4,18 @@ import classnames from 'classnames'; import { flatMap } from 'lodash'; +/** + * WordPress dependencies + */ +import { useContext } from '@wordpress/element'; + /** * Internal dependencies */ import ToolbarButton from '../toolbar-button'; import DropdownMenu from '../dropdown-menu'; import ToolbarContainer from './toolbar-group-container'; +import ToolbarContext from '../toolbar-context'; /** * Renders a collapsible group of controls. @@ -43,6 +49,10 @@ import ToolbarContainer from './toolbar-group-container'; * @return {WPComponent} The rendered component. */ function ToolbarGroup( { controls = [], children, className, isCollapsed, icon, label, ...otherProps } ) { + // It'll contain state if `ToolbarGroup` is being used within + // `` + const accessibleToolbarState = useContext( ToolbarContext ); + if ( ( ! controls || ! controls.length ) && ! children @@ -50,6 +60,13 @@ function ToolbarGroup( { controls = [], children, className, isCollapsed, icon, return null; } + const finalClassName = classnames( + // Unfortunately, there's legacy code referencing to `.components-toolbar` + // So we can't get rid of it + accessibleToolbarState ? 'components-toolbar-group' : 'components-toolbar', + className + ); + // Normalize controls to nested array of objects (sets of controls) let controlSets = controls; if ( ! Array.isArray( controlSets[ 0 ] ) ) { @@ -63,13 +80,14 @@ function ToolbarGroup( { controls = [], children, className, isCollapsed, icon, icon={ icon } label={ label } controls={ controlSets } - className={ classnames( 'components-toolbar', className ) } + className={ finalClassName } + { ...otherProps } /> ); } return ( - + { flatMap( controlSets, ( controlSet, indexOfSet ) => ( controlSet.map( ( control, indexOfControl ) => ( Date: Sun, 27 Oct 2019 19:19:44 -0600 Subject: [PATCH 48/75] Revert DropdownMenu to master version --- packages/components/src/dropdown-menu/index.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/components/src/dropdown-menu/index.js b/packages/components/src/dropdown-menu/index.js index 768e2edff5209d..e94b246231ad1b 100644 --- a/packages/components/src/dropdown-menu/index.js +++ b/packages/components/src/dropdown-menu/index.js @@ -98,18 +98,8 @@ function DropdownMenu( { { - onToggle( event ); - if ( mergedToggleProps.onClick ) { - mergedToggleProps.onClick( event ); - } - } } - onKeyDown={ ( event ) => { - openOnArrowDown( event ); - if ( mergedToggleProps.onKeyDown ) { - mergedToggleProps.onKeyDown( event ); - } - } } + onClick={ onToggle } + onKeyDown={ openOnArrowDown } aria-haspopup="true" aria-expanded={ isOpen } label={ label } From d3fe09f6f6764185becffee6e14e1f42a29e63f8 Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 31 Oct 2019 05:27:46 -0300 Subject: [PATCH 49/75] Check if button is descendant of Toolbar --- packages/components/src/button/index.js | 13 ++-- .../components/src/button/toolbar-button.js | 11 ++- packages/components/src/popover/index.js | 75 +++++++++---------- .../src/toolbar/toolbar-container.js | 1 + .../src/toolbar/use-is-within-toolbar.js | 35 +++++++++ 5 files changed, 87 insertions(+), 48 deletions(-) create mode 100644 packages/components/src/toolbar/use-is-within-toolbar.js diff --git a/packages/components/src/button/index.js b/packages/components/src/button/index.js index b952e6a3b4fa19..3bc2630a0af6ef 100644 --- a/packages/components/src/button/index.js +++ b/packages/components/src/button/index.js @@ -6,12 +6,12 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { createElement, forwardRef, useContext } from '@wordpress/element'; +import { createElement, forwardRef } from '@wordpress/element'; /** * Internal dependencies */ -import ToolbarContext from '../toolbar-context'; import ToolbarButton from './toolbar-button'; +import useIsWithinToolbar from '../toolbar/use-is-within-toolbar'; export function Button( props, ref ) { const { @@ -46,13 +46,12 @@ export function Button( props, ref ) { const tag = href !== undefined && ! disabled ? 'a' : 'button'; const tagProps = tag === 'a' ? { href, target } : { type: 'button', disabled }; - const allProps = { ...tagProps, ...additionalProps, className: classes, ref }; - - const toolbarContext = useContext( ToolbarContext ); + const { isWithinToolbar, ref: finalRef } = useIsWithinToolbar( ref ); + const allProps = { ...tagProps, ...additionalProps, className: classes, ref: finalRef }; // If it's within a toolbar, render with toolbar context instead. - if ( toolbarContext ) { - return ; + if ( isWithinToolbar ) { + return ; } return createElement( tag, allProps ); diff --git a/packages/components/src/button/toolbar-button.js b/packages/components/src/button/toolbar-button.js index f41cd552a045d1..614d03eac8cc38 100644 --- a/packages/components/src/button/toolbar-button.js +++ b/packages/components/src/button/toolbar-button.js @@ -1,14 +1,21 @@ /** * External dependencies */ -import { useToolbarItem } from 'reakit/Toolbar'; +import { useToolbarItem, useContext } from 'reakit/Toolbar'; /** * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; -function ToolbarButton( { context, as: Component = 'button', ...props }, ref ) { +/** + * Internal dependencies + */ +import ToolbarContext from '../toolbar-context'; + +function ToolbarButton( { as: Component = 'button', ...props }, ref ) { + const context = useContext( ToolbarContext ); + // https://reakit.io/docs/composition/#props-hooks const itemHTMLProps = useToolbarItem( context, { ref, diff --git a/packages/components/src/popover/index.js b/packages/components/src/popover/index.js index fad0bad73d4827..420ac78b76bdbf 100644 --- a/packages/components/src/popover/index.js +++ b/packages/components/src/popover/index.js @@ -24,7 +24,6 @@ import ScrollLock from '../scroll-lock'; import IsolatedEventContainer from '../isolated-event-container'; import { Slot, Fill, Consumer } from '../slot-fill'; import Animate from '../animate'; -import ToolbarContext from '../toolbar-context'; const FocusManaged = withConstrainedTabbing( withFocusReturn( ( { children } ) => children ) ); @@ -382,47 +381,45 @@ const Popover = ( { // within popover as inferring close intent. let content = ( - - - - { ( { className: animateClassName } ) => ( - + + { ( { className: animateClassName } ) => ( + + { popoverPosition.isMobile && ( +
    + + { headerTitle } + + +
    + ) } +
    - { popoverPosition.isMobile && ( -
    - - { headerTitle } - - -
    - ) } -
    - { children } -
    - - ) } - - - + { children } +
    +
    + ) } +
    +
    ); // Apply focus to element as long as focusOnMount is truthy; false is diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index 9dea69765f8524..b122fdb975f820 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -48,6 +48,7 @@ function ToolbarContainer( { accessibilityLabel, ...props }, ref ) { diff --git a/packages/components/src/toolbar/use-is-within-toolbar.js b/packages/components/src/toolbar/use-is-within-toolbar.js new file mode 100644 index 00000000000000..4adad3c9818d99 --- /dev/null +++ b/packages/components/src/toolbar/use-is-within-toolbar.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies + */ +import { useContext, useRef, useEffect, useState } from '@wordpress/element'; +/** + * Internal dependencies + */ +import ToolbarContext from '../toolbar-context'; + +function useIsWithinToolbar( ref ) { + const context = useContext( ToolbarContext ); + const [ isWithinToolbar, setIsWithinToolbar ] = useState( Boolean( context ) ); + const internalRef = useRef(); + + const finalRef = ref ? ( ( instance ) => { + if ( typeof ref === 'function' ) { + ref( instance ); + } else { + ref.current = instance; + } + internalRef.current = instance; + } ) : internalRef; + + useEffect( () => { + if ( isWithinToolbar && internalRef.current ) { + if ( ! internalRef.current.closest( '[data-toolbar="true"]' ) ) { + setIsWithinToolbar( false ); + } + } + }, [ isWithinToolbar ] ); + + return { isWithinToolbar, ref: finalRef }; +} + +export default useIsWithinToolbar; From 0a1fa12a606059b4032e856711eb1e35032b0058 Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 31 Oct 2019 05:39:34 -0300 Subject: [PATCH 50/75] Fix toolbar button --- packages/components/src/button/toolbar-button.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/button/toolbar-button.js b/packages/components/src/button/toolbar-button.js index 614d03eac8cc38..3e3f9899d64116 100644 --- a/packages/components/src/button/toolbar-button.js +++ b/packages/components/src/button/toolbar-button.js @@ -1,12 +1,12 @@ /** * External dependencies */ -import { useToolbarItem, useContext } from 'reakit/Toolbar'; +import { useToolbarItem } from 'reakit/Toolbar'; /** * WordPress dependencies */ -import { forwardRef } from '@wordpress/element'; +import { forwardRef, useContext } from '@wordpress/element'; /** * Internal dependencies From a80ec3d402f41ccbb2daed6bb4ea8c9978e8a4ca Mon Sep 17 00:00:00 2001 From: Haz Date: Thu, 31 Oct 2019 06:20:39 -0300 Subject: [PATCH 51/75] Try some weird stuff --- .../block-list/block-contextual-toolbar.js | 2 ++ .../src/components/navigable-toolbar/index.js | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block-contextual-toolbar.js b/packages/block-editor/src/components/block-list/block-contextual-toolbar.js index 36575d90f9ab59..cb89d98001dc8e 100644 --- a/packages/block-editor/src/components/block-list/block-contextual-toolbar.js +++ b/packages/block-editor/src/components/block-list/block-contextual-toolbar.js @@ -13,6 +13,8 @@ function BlockContextualToolbar( { focusOnMount } ) { return ( { + if ( tabbables.length ) { + tabbables[ 0 ].focus(); + } + window.requestAnimationFrame( () => { + if ( this.props.highPriorityOnFocus ) { + if ( tabbables.length ) { + tabbables[ 0 ].focus(); + } + } + } ); + } ); } } From 755a0086330b6b74f57d272afd81c7f63e9ecb26 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 2 Nov 2019 03:19:44 -0300 Subject: [PATCH 52/75] Tweaks --- package-lock.json | 46 ++++++++++--------- .../block-list/block-contextual-toolbar.js | 2 - .../src/components/navigable-toolbar/index.js | 18 +------- packages/block-library/src/html/edit.js | 10 ++-- packages/components/package.json | 2 +- packages/components/src/slot-fill/fill.js | 6 +-- packages/components/src/slot-fill/slot.js | 11 +++++ 7 files changed, 46 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8dd5c87a6fbfcd..74762a9cba465b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7370,10 +7370,33 @@ "re-resizable": "^6.0.0", "react-dates": "^17.1.1", "react-spring": "^8.0.20", - "reakit": "^1.0.0-beta.9", + "reakit": "^1.0.0-beta.10", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "uuid": "^3.3.2" + }, + "dependencies": { + "reakit": { + "version": "1.0.0-beta.10", + "resolved": "https://registry.npmjs.org/reakit/-/reakit-1.0.0-beta.10.tgz", + "integrity": "sha512-cMzkKVOYYIxjc7TuDu0WrSodg43SXyRmC1YZroIFY5XDSR36j8xKOaEzAM0lz0bd9Jn972LCeCKJtPUqtcNvNA==", + "requires": { + "body-scroll-lock": "^2.6.4", + "popper.js": "^1.15.0", + "reakit-system": "^0.6.7", + "reakit-utils": "^0.6.7" + } + }, + "reakit-system": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/reakit-system/-/reakit-system-0.6.7.tgz", + "integrity": "sha512-Ih2S6VL3UFUPsZ1rjKJbiSjWTveEwNBxjqQFENWOx2WtHEGcBM3+ZuvNwjxAmnYngAoUN8QtABcrbFvDYHGGXA==" + }, + "reakit-utils": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/reakit-utils/-/reakit-utils-0.6.7.tgz", + "integrity": "sha512-S/0PB9Oh7/oTVwbTnPU3y/ztG6rhFsUMX6C781MfJ5GYlgfiGBtqhLxcu5mbpj8EoLtwq/pSs3J8Y6nK10Sn0w==" + } } }, "@wordpress/compose": { @@ -28625,27 +28648,6 @@ "readable-stream": "^2.0.2" } }, - "reakit": { - "version": "1.0.0-beta.9", - "resolved": "https://registry.npmjs.org/reakit/-/reakit-1.0.0-beta.9.tgz", - "integrity": "sha512-rWHW3QnuKDrxfgL3hv/ah/nqbhEozpApLWtrwNpWPMZOoRAuzydgKM2l6rsmxS1aCVR7SmOt6Bv4iwl651SuIg==", - "requires": { - "body-scroll-lock": "^2.6.4", - "popper.js": "^1.15.0", - "reakit-system": "^0.6.6", - "reakit-utils": "^0.6.6" - } - }, - "reakit-system": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/reakit-system/-/reakit-system-0.6.6.tgz", - "integrity": "sha512-EzhnYAuYTqRci7xkv68PiquHRJpRGDWGEJKTO+0s5cr1f4soooJ3ms0tE7/dQRSSseGon+79jJ4bvfVDvCNing==" - }, - "reakit-utils": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/reakit-utils/-/reakit-utils-0.6.6.tgz", - "integrity": "sha512-YbyHQ310biVmDkhl4wRhXMvn2cl4ooL2nlpfI753dwPJxEsSqvyayhqLth/j+a4+44J0YeqBCCH3LDoYBrGg2Q==" - }, "realpath-native": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", diff --git a/packages/block-editor/src/components/block-list/block-contextual-toolbar.js b/packages/block-editor/src/components/block-list/block-contextual-toolbar.js index cb89d98001dc8e..36575d90f9ab59 100644 --- a/packages/block-editor/src/components/block-list/block-contextual-toolbar.js +++ b/packages/block-editor/src/components/block-list/block-contextual-toolbar.js @@ -13,8 +13,6 @@ function BlockContextualToolbar( { focusOnMount } ) { return ( { - if ( tabbables.length ) { - tabbables[ 0 ].focus(); - } - window.requestAnimationFrame( () => { - if ( this.props.highPriorityOnFocus ) { - if ( tabbables.length ) { - tabbables[ 0 ].focus(); - } - } - } ); - } ); + if ( tabbables.length ) { + tabbables[ 0 ].focus(); } } diff --git a/packages/block-library/src/html/edit.js b/packages/block-library/src/html/edit.js index 4f1ab45a073dfe..55c6084270b7a9 100644 --- a/packages/block-library/src/html/edit.js +++ b/packages/block-library/src/html/edit.js @@ -8,7 +8,7 @@ import { PlainText, transformStyles, } from '@wordpress/block-editor'; -import { Disabled, SandBox } from '@wordpress/components'; +import { Disabled, SandBox, Button } from '@wordpress/components'; import { withSelect } from '@wordpress/data'; class HTMLEdit extends Component { @@ -58,18 +58,18 @@ class HTMLEdit extends Component {
    - - +
    diff --git a/packages/components/package.json b/packages/components/package.json index d8df39a12df560..2f0a562ba1f458 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -42,7 +42,7 @@ "re-resizable": "^6.0.0", "react-dates": "^17.1.1", "react-spring": "^8.0.20", - "reakit": "^1.0.0-beta.9", + "reakit": "^1.0.0-beta.10", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "uuid": "^3.3.2" diff --git a/packages/components/src/slot-fill/fill.js b/packages/components/src/slot-fill/fill.js index ac1be6b15853a7..3570a3a75e5db4 100644 --- a/packages/components/src/slot-fill/fill.js +++ b/packages/components/src/slot-fill/fill.js @@ -6,7 +6,7 @@ import { isFunction } from 'lodash'; /** * WordPress dependencies */ -import { createPortal, useLayoutEffect, useRef, useState } from '@wordpress/element'; +import { createPortal, useLayoutEffect, useRef, useReducer } from '@wordpress/element'; /** * Internal dependencies @@ -17,12 +17,12 @@ let occurrences = 0; function FillComponent( { name, children, registerFill, unregisterFill } ) { const slot = useSlot( name ); - const [ , setState ] = useState( {} ); + const [ , forceUpdate ] = useReducer( () => ( {} ), {} ); const ref = useRef( { name, children, - forceUpdate: () => setState( {} ), + forceUpdate, } ); if ( ! ref.current.occurrence ) { diff --git a/packages/components/src/slot-fill/slot.js b/packages/components/src/slot-fill/slot.js index e1195b7d4317d5..8d7ec45284b5c2 100644 --- a/packages/components/src/slot-fill/slot.js +++ b/packages/components/src/slot-fill/slot.js @@ -33,12 +33,14 @@ class SlotComponent extends Component { componentDidMount() { const { registerSlot } = this.props; + this._isMounted = true; registerSlot( this.props.name, this ); } componentWillUnmount() { const { unregisterSlot } = this.props; + this._isMounted = false; unregisterSlot( this.props.name, this ); } @@ -56,6 +58,15 @@ class SlotComponent extends Component { this.node = node; } + forceUpdate() { + // Fill may call this when the Slot has been already unmounted. + // The overall Slot/Fill technique needs to be revisited. + // This only avoids React warnings. + if ( this._isMounted ) { + return super.forceUpdate(); + } + } + render() { const { children, name, bubblesVirtually = false, fillProps = {}, getFills, className } = this.props; From 3bada40db969b258ce065d1f04d4688bfcc4c255 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 2 Nov 2019 03:40:55 -0300 Subject: [PATCH 53/75] Update package-lock.json --- package-lock.json | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74762a9cba465b..d8a8e590137cd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7374,29 +7374,6 @@ "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "uuid": "^3.3.2" - }, - "dependencies": { - "reakit": { - "version": "1.0.0-beta.10", - "resolved": "https://registry.npmjs.org/reakit/-/reakit-1.0.0-beta.10.tgz", - "integrity": "sha512-cMzkKVOYYIxjc7TuDu0WrSodg43SXyRmC1YZroIFY5XDSR36j8xKOaEzAM0lz0bd9Jn972LCeCKJtPUqtcNvNA==", - "requires": { - "body-scroll-lock": "^2.6.4", - "popper.js": "^1.15.0", - "reakit-system": "^0.6.7", - "reakit-utils": "^0.6.7" - } - }, - "reakit-system": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/reakit-system/-/reakit-system-0.6.7.tgz", - "integrity": "sha512-Ih2S6VL3UFUPsZ1rjKJbiSjWTveEwNBxjqQFENWOx2WtHEGcBM3+ZuvNwjxAmnYngAoUN8QtABcrbFvDYHGGXA==" - }, - "reakit-utils": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/reakit-utils/-/reakit-utils-0.6.7.tgz", - "integrity": "sha512-S/0PB9Oh7/oTVwbTnPU3y/ztG6rhFsUMX6C781MfJ5GYlgfiGBtqhLxcu5mbpj8EoLtwq/pSs3J8Y6nK10Sn0w==" - } } }, "@wordpress/compose": { @@ -28648,6 +28625,27 @@ "readable-stream": "^2.0.2" } }, + "reakit": { + "version": "1.0.0-beta.10", + "resolved": "https://registry.npmjs.org/reakit/-/reakit-1.0.0-beta.10.tgz", + "integrity": "sha512-cMzkKVOYYIxjc7TuDu0WrSodg43SXyRmC1YZroIFY5XDSR36j8xKOaEzAM0lz0bd9Jn972LCeCKJtPUqtcNvNA==", + "requires": { + "body-scroll-lock": "^2.6.4", + "popper.js": "^1.15.0", + "reakit-system": "^0.6.7", + "reakit-utils": "^0.6.7" + } + }, + "reakit-system": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/reakit-system/-/reakit-system-0.6.7.tgz", + "integrity": "sha512-Ih2S6VL3UFUPsZ1rjKJbiSjWTveEwNBxjqQFENWOx2WtHEGcBM3+ZuvNwjxAmnYngAoUN8QtABcrbFvDYHGGXA==" + }, + "reakit-utils": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/reakit-utils/-/reakit-utils-0.6.7.tgz", + "integrity": "sha512-S/0PB9Oh7/oTVwbTnPU3y/ztG6rhFsUMX6C781MfJ5GYlgfiGBtqhLxcu5mbpj8EoLtwq/pSs3J8Y6nK10Sn0w==" + }, "realpath-native": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", From da8049f858f1c46a93e8f20e9d51666c783ae017 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 2 Nov 2019 04:58:58 -0300 Subject: [PATCH 54/75] Migrate missing Toolbar --- packages/block-library/src/navigation-menu-item/edit.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/navigation-menu-item/edit.js b/packages/block-library/src/navigation-menu-item/edit.js index 2755acf775b0bf..ecfe34d88ae064 100644 --- a/packages/block-library/src/navigation-menu-item/edit.js +++ b/packages/block-library/src/navigation-menu-item/edit.js @@ -16,7 +16,7 @@ import { SVG, TextareaControl, TextControl, - Toolbar, + ToolbarGroup, ToggleControl, ToolbarButton, } from '@wordpress/components'; @@ -88,7 +88,7 @@ function NavigationMenuItemEdit( { return ( - + } - + { isLinkOpen && <> Date: Sat, 2 Nov 2019 05:17:20 -0300 Subject: [PATCH 55/75] Delay Toolbar focus on mount --- .../block-editor/src/components/navigable-toolbar/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/navigable-toolbar/index.js b/packages/block-editor/src/components/navigable-toolbar/index.js index 642e95d12bf204..7705af421579c3 100644 --- a/packages/block-editor/src/components/navigable-toolbar/index.js +++ b/packages/block-editor/src/components/navigable-toolbar/index.js @@ -33,7 +33,9 @@ class NavigableToolbar extends Component { componentDidMount() { if ( this.props.focusOnMount ) { - this.focusToolbar(); + window.requestAnimationFrame( () => { + this.focusToolbar(); + } ); } // Toolbar items added via Portal (Slot bubblesVirtually) aren't added From 21ad7afe6867dc79bcf53a79dd12d7a7d7323805 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 2 Nov 2019 20:31:49 -0300 Subject: [PATCH 56/75] Fix Toolbar styling --- packages/block-editor/src/components/block-list/style.scss | 4 ++++ packages/edit-post/src/components/visual-editor/style.scss | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index f70a789f4e704d..00e6178668c2e3 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -1,3 +1,7 @@ +.block-editor-block-contextual-toolbar { + display: block; +} + .block-editor-block-list__layout .components-draggable__clone { // Hide the Block UI when dragging the block. // This ensures the page scroll properly (no sticky elements). diff --git a/packages/edit-post/src/components/visual-editor/style.scss b/packages/edit-post/src/components/visual-editor/style.scss index 5cc06fd7276d87..cfa17d6d3e5175 100644 --- a/packages/edit-post/src/components/visual-editor/style.scss +++ b/packages/edit-post/src/components/visual-editor/style.scss @@ -34,7 +34,6 @@ // Use specific selector to not affect nested block toolbars. &[data-align="wide"] > .block-editor-block-list__block-edit > .block-editor-block-contextual-toolbar, &[data-align="full"] > .block-editor-block-list__block-edit > .block-editor-block-contextual-toolbar { - display: block; height: 0; // This collapses the container to an invisible element without margin. width: calc(100% - #{$block-side-ui-width * 3} - #{$grid-size-small * 1.5}); // -90px to account for inner element left position value causing overflow-x scrollbars margin-left: 0; From 5c6baf7f50c29701bce097ef0528a47c36b9e250 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 2 Nov 2019 21:49:19 -0300 Subject: [PATCH 57/75] Fix tabThroughBlockToolbar e2e test --- .../editor/various/keyboard-navigable-blocks.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js index a8ebe96c34fcd2..54264776899316 100644 --- a/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js @@ -76,15 +76,15 @@ const tabThroughBlockToolbar = async () => { ); await expect( isFocusedBlockSwitcherControl ).toBe( true ); - // Tab to focus on the 'Change text alignment' dropdown control - await page.keyboard.press( 'Tab' ); + // ArrowRight to focus on the 'Change text alignment' dropdown control + await page.keyboard.press( 'ArrowRight' ); const isFocusedChangeTextAlignmentControl = await page.evaluate( () => document.activeElement.classList.contains( 'components-dropdown-menu__toggle' ) ); await expect( isFocusedChangeTextAlignmentControl ).toBe( true ); - // Tab to focus on the 'More formatting' dropdown toggle - await page.keyboard.press( 'Tab' ); + // ArrowRight to focus on the 'More formatting' dropdown toggle + await page.keyboard.press( 'ArrowRight' ); const isFocusedBlockSettingsDropdown = await page.evaluate( () => document.activeElement.classList.contains( 'components-dropdown-menu__toggle' ) ); From 81161458c68df3405f8a13c42929d3a36e74e0b9 Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 2 Nov 2019 22:19:53 -0300 Subject: [PATCH 58/75] Fix Tab navigation on block-hierarchy navigation test --- .../editor/various/block-hierarchy-navigation.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js b/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js index efba2e1d6afc5e..90a12bac1430da 100644 --- a/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js +++ b/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js @@ -24,7 +24,7 @@ describe( 'Navigating the block hierarchy', () => { await page.click( '[aria-label="Two columns; equal split"]' ); // Add a paragraph in the first column. - await pressKeyTimes( 'Tab', 3 ); // Tab to inserter. + await pressKeyTimes( 'Tab', 1 ); // Tab to inserter. await page.keyboard.press( 'Enter' ); // Activate inserter. await page.keyboard.type( 'Paragraph' ); await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. @@ -51,7 +51,7 @@ describe( 'Navigating the block hierarchy', () => { await lastColumnsBlockMenuItem.click(); // Insert text in the last column block. - await pressKeyTimes( 'Tab', 3 ); // Tab to inserter. + await pressKeyTimes( 'Tab', 1 ); // Tab to inserter. await page.keyboard.press( 'Enter' ); // Activate inserter. await page.keyboard.type( 'Paragraph' ); await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. @@ -66,7 +66,7 @@ describe( 'Navigating the block hierarchy', () => { await page.click( '[aria-label="Two columns; equal split"]' ); // Add a paragraph in the first column. - await pressKeyTimes( 'Tab', 3 ); // Tab to inserter. + await pressKeyTimes( 'Tab', 1 ); // Tab to inserter. await page.keyboard.press( 'Enter' ); // Activate inserter. await page.keyboard.type( 'Paragraph' ); await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. @@ -95,7 +95,7 @@ describe( 'Navigating the block hierarchy', () => { await page.waitForSelector( '.is-selected[data-type="core/column"]' ); // Insert text in the last column block - await pressKeyTimes( 'Tab', 3 ); // Tab to inserter. + await pressKeyTimes( 'Tab', 1 ); // Tab to inserter. await page.keyboard.press( 'Enter' ); // Activate inserter. await page.keyboard.type( 'Paragraph' ); await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. From 1d7e9c977f051ba271de2d5f289ab5a3ea4e908e Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 2 Nov 2019 22:37:12 -0300 Subject: [PATCH 59/75] Try without isMounted on Slot --- packages/components/src/slot-fill/slot.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/components/src/slot-fill/slot.js b/packages/components/src/slot-fill/slot.js index 8d7ec45284b5c2..e1195b7d4317d5 100644 --- a/packages/components/src/slot-fill/slot.js +++ b/packages/components/src/slot-fill/slot.js @@ -33,14 +33,12 @@ class SlotComponent extends Component { componentDidMount() { const { registerSlot } = this.props; - this._isMounted = true; registerSlot( this.props.name, this ); } componentWillUnmount() { const { unregisterSlot } = this.props; - this._isMounted = false; unregisterSlot( this.props.name, this ); } @@ -58,15 +56,6 @@ class SlotComponent extends Component { this.node = node; } - forceUpdate() { - // Fill may call this when the Slot has been already unmounted. - // The overall Slot/Fill technique needs to be revisited. - // This only avoids React warnings. - if ( this._isMounted ) { - return super.forceUpdate(); - } - } - render() { const { children, name, bubblesVirtually = false, fillProps = {}, getFills, className } = this.props; From a1328b21902fc36abd7e028fd874250f636cde9c Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 2 Nov 2019 23:35:40 -0300 Subject: [PATCH 60/75] Try without useMountedToolbarState --- .../src/toolbar/toolbar-container.js | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index b122fdb975f820..3abeb7ffec137e 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -6,41 +6,16 @@ import { useToolbarState, Toolbar } from 'reakit/Toolbar'; /** * WordPress dependencies */ -import { forwardRef, useEffect, useRef, useCallback } from '@wordpress/element'; +import { forwardRef } from '@wordpress/element'; /** * Internal dependencies */ import ToolbarContext from '../toolbar-context'; -// When using slots, register/unregister are called with delay, and there's a -// chance that the toolbar is already unmounted when this happens. -function useMountedToolbarState( initialState ) { - const toolbar = useToolbarState( initialState ); - const mounted = useRef( true ); - - useEffect( () => () => { - mounted.current = false; - }, [] ); - - return { - ...toolbar, - register: useCallback( ( ...args ) => { - if ( mounted.current ) { - toolbar.register( ...args ); - } - }, [ toolbar.register ] ), - unregister: useCallback( ( ...args ) => { - if ( mounted.current ) { - toolbar.unregister( ...args ); - } - }, [ toolbar.unregister ] ), - }; -} - function ToolbarContainer( { accessibilityLabel, ...props }, ref ) { // https://reakit.io/docs/basic-concepts/#state-hooks - const toolbarState = useMountedToolbarState( { loop: true } ); + const toolbarState = useToolbarState( { loop: true } ); return ( // This will provide state for `ToolbarButton`'s From 7f489856f98bb51207a90a69aef94c5774fe9faf Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 5 Nov 2019 19:53:31 -0300 Subject: [PATCH 61/75] Remove ToolbarSlotFill --- .../src/components/block-controls/index.js | 4 +- .../components/block-format-controls/index.js | 4 +- .../src/components/block-list/block.js | 168 +++++++++--------- .../src/components/navigable-toolbar/index.js | 14 +- packages/components/src/index.js | 4 +- packages/components/src/index.native.js | 4 +- packages/components/src/slot-fill/context.js | 7 - packages/components/src/slot-fill/fill.js | 4 +- packages/components/src/slot-fill/slot.js | 5 +- .../components/src/slot-fill/test/slot.js | 55 +----- packages/components/src/style.scss | 1 - .../components/src/toolbar-context/index.js | 15 ++ .../components/src/toolbar-slot-fill/index.js | 59 ------ .../src/toolbar-slot-fill/style.scss | 4 - packages/components/src/toolbar/index.js | 24 ++- .../components/src/toolbar/stories/index.js | 20 --- .../src/toolbar/toolbar-container.js | 30 ++-- 17 files changed, 148 insertions(+), 274 deletions(-) delete mode 100644 packages/components/src/toolbar-slot-fill/index.js delete mode 100644 packages/components/src/toolbar-slot-fill/style.scss diff --git a/packages/block-editor/src/components/block-controls/index.js b/packages/block-editor/src/components/block-controls/index.js index 1eaf948ac56f14..dbbc0c62a9981d 100644 --- a/packages/block-editor/src/components/block-controls/index.js +++ b/packages/block-editor/src/components/block-controls/index.js @@ -1,14 +1,14 @@ /** * WordPress dependencies */ -import { createToolbarSlotFill, ToolbarGroup } from '@wordpress/components'; +import { createSlotFill, ToolbarGroup } from '@wordpress/components'; /** * Internal dependencies */ import { ifBlockEditSelected } from '../block-edit/context'; -const { Fill, Slot } = createToolbarSlotFill( 'BlockControls' ); +const { Fill, Slot } = createSlotFill( 'BlockControls' ); const BlockControlsFill = ( { controls, children } ) => ( diff --git a/packages/block-editor/src/components/block-format-controls/index.js b/packages/block-editor/src/components/block-format-controls/index.js index 3ad50829c69e04..2ab83843846ad4 100644 --- a/packages/block-editor/src/components/block-format-controls/index.js +++ b/packages/block-editor/src/components/block-format-controls/index.js @@ -1,14 +1,14 @@ /** * WordPress dependencies */ -import { createToolbarSlotFill } from '@wordpress/components'; +import { createSlotFill } from '@wordpress/components'; /** * Internal dependencies */ import { ifBlockEditSelected } from '../block-edit/context'; -const { Fill, Slot } = createToolbarSlotFill( 'BlockFormatControls' ); +const { Fill, Slot } = createSlotFill( 'BlockFormatControls' ); const BlockFormatControls = ifBlockEditSelected( Fill ); diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 48b5506e452f2f..a848a53da2aa86 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -22,7 +22,7 @@ import { isUnmodifiedDefaultBlock, getUnregisteredTypeHandlerName, } from '@wordpress/blocks'; -import { KeyboardShortcuts, withFilters } from '@wordpress/components'; +import { KeyboardShortcuts, withFilters, ToolbarProvider } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { withDispatch, @@ -515,48 +515,43 @@ function BlockListBlock( { animationStyle } > - { shouldShowInsertionPoint && ( - - ) } - - { isFirstMultiSelected && ( - - ) } -
    - { shouldRenderMovers && ( moverDirection === 'vertical' ) && blockMover } - { shouldShowBreadcrumb && ( - + { shouldShowInsertionPoint && ( + ) } - { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ( - + { isFirstMultiSelected && ( + ) } - { - ! isNavigationMode && - ! shouldShowContextualToolbar && - isSelected && - ! hasFixedToolbar && - ! isEmptyDefaultBlock && ( +
    + { shouldRenderMovers && moverDirection === 'vertical' && blockMover } + { shouldShowBreadcrumb && ( + + ) } + { ( shouldShowContextualToolbar || + isForcingContextualToolbar.current ) && ( + + ) } + { ! isNavigationMode && + ! shouldShowContextualToolbar && + isSelected && + ! hasFixedToolbar && + ! isEmptyDefaultBlock && ( - ) - } - - - { isValid && blockEdit } - { isValid && mode === 'html' && ( - - ) } - { shouldRenderMovers && ( moverDirection === 'horizontal' ) && blockMover } - { ! isValid && [ - , -
    - { getSaveElement( blockType, attributes ) } -
    , - ] } -
    - { !! hasError && } - { shouldShowMobileToolbar && ( - ) } -
    -
    - { showInserterShortcuts && ( -
    - -
    - ) } - { showEmptyBlockSideInserter && ( -
    - + + + { isValid && blockEdit } + { isValid && mode === 'html' && } + { shouldRenderMovers && + moverDirection === 'horizontal' && + blockMover } + { ! isValid && [ + , +
    + { getSaveElement( blockType, attributes ) } +
    , + ] } +
    + { !! hasError && } + { shouldShowMobileToolbar && ( + + ) } +
    - ) } + { showInserterShortcuts && ( +
    + +
    + ) } + { showEmptyBlockSideInserter && ( +
    + +
    + ) } + ); } diff --git a/packages/block-editor/src/components/navigable-toolbar/index.js b/packages/block-editor/src/components/navigable-toolbar/index.js index 7705af421579c3..a5dec45d8dd3d4 100644 --- a/packages/block-editor/src/components/navigable-toolbar/index.js +++ b/packages/block-editor/src/components/navigable-toolbar/index.js @@ -6,7 +6,13 @@ import { omit } from 'lodash'; /** * WordPress dependencies */ -import { NavigableMenu, Toolbar, KeyboardShortcuts } from '@wordpress/components'; +import { + NavigableMenu, + KeyboardShortcuts, + Toolbar, + ToolbarContext, + ToolbarContainer, +} from '@wordpress/components'; import { Component, createRef } from '@wordpress/element'; import { focus } from '@wordpress/dom'; import deprecated from '@wordpress/deprecated'; @@ -94,10 +100,16 @@ class NavigableToolbar extends Component { ); } + if ( this.context ) { + return ; + } + return ( ); } } +NavigableToolbar.contextType = ToolbarContext; + export default NavigableToolbar; diff --git a/packages/components/src/index.js b/packages/components/src/index.js index cc474a125d8aea..6a7e6cb4c6bd83 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -59,10 +59,10 @@ export { default as TextControl } from './text-control'; export { default as TextareaControl } from './textarea-control'; export { default as Tip } from './tip'; export { default as ToggleControl } from './toggle-control'; -export { default as Toolbar } from './toolbar'; +export { default as Toolbar, ToolbarContainer } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; +export { default as ToolbarContext, ToolbarProvider } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; -export { createToolbarSlotFill, ToolbarSlot, ToolbarFill } from './toolbar-slot-fill'; export { default as Tooltip } from './tooltip'; export { default as TreeSelect } from './tree-select'; export { default as VisuallyHidden } from './visually-hidden'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 8fa534f63ad922..8c8eae7cd33806 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -4,10 +4,10 @@ export { default as ColorIndicator } from './color-indicator'; export { default as ColorPalette } from './color-palette'; export { default as Dashicon } from './dashicon'; export { default as Dropdown } from './dropdown'; -export { default as Toolbar } from './toolbar'; +export { default as Toolbar, ToolbarContainer } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; +export { default as ToolbarContext, ToolbarProvider } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; -export { createToolbarSlotFill, ToolbarSlot, ToolbarFill } from './toolbar-slot-fill'; export { default as Icon } from './icon'; export { default as IconButton } from './icon-button'; export { default as Spinner } from './spinner'; diff --git a/packages/components/src/slot-fill/context.js b/packages/components/src/slot-fill/context.js index 5e407988a0abd0..587c1ccab5ccb8 100644 --- a/packages/components/src/slot-fill/context.js +++ b/packages/components/src/slot-fill/context.js @@ -60,9 +60,6 @@ class SlotFillProvider extends Component { // will be empty (the new Slot will subsume all fills for this name). if ( previousSlot ) { previousSlot.forceUpdate(); - - // See 'should re-render Fill when fillProps are updated' test. - this.forceUpdateFills( name ); } } @@ -122,10 +119,6 @@ class SlotFillProvider extends Component { } } - forceUpdateFills( name ) { - forEach( this.fills[ name ], ( instance ) => instance.forceUpdate() ); - } - triggerListeners() { this.listeners.forEach( ( listener ) => listener() ); } diff --git a/packages/components/src/slot-fill/fill.js b/packages/components/src/slot-fill/fill.js index 3570a3a75e5db4..3600e177e5434f 100644 --- a/packages/components/src/slot-fill/fill.js +++ b/packages/components/src/slot-fill/fill.js @@ -6,7 +6,7 @@ import { isFunction } from 'lodash'; /** * WordPress dependencies */ -import { createPortal, useLayoutEffect, useRef, useReducer } from '@wordpress/element'; +import { createPortal, useLayoutEffect, useRef } from '@wordpress/element'; /** * Internal dependencies @@ -17,12 +17,10 @@ let occurrences = 0; function FillComponent( { name, children, registerFill, unregisterFill } ) { const slot = useSlot( name ); - const [ , forceUpdate ] = useReducer( () => ( {} ), {} ); const ref = useRef( { name, children, - forceUpdate, } ); if ( ! ref.current.occurrence ) { diff --git a/packages/components/src/slot-fill/slot.js b/packages/components/src/slot-fill/slot.js index e1195b7d4317d5..4578656b5efaa9 100644 --- a/packages/components/src/slot-fill/slot.js +++ b/packages/components/src/slot-fill/slot.js @@ -11,7 +11,6 @@ import { /** * WordPress dependencies */ -import isShallowEqual from '@wordpress/is-shallow-equal'; import { Children, Component, @@ -44,9 +43,9 @@ class SlotComponent extends Component { } componentDidUpdate( prevProps ) { - const { name, fillProps, unregisterSlot, registerSlot } = this.props; + const { name, unregisterSlot, registerSlot } = this.props; - if ( prevProps.name !== name || ! isShallowEqual( prevProps.fillProps, fillProps ) ) { + if ( prevProps.name !== name ) { unregisterSlot( prevProps.name ); registerSlot( name, this ); } diff --git a/packages/components/src/slot-fill/test/slot.js b/packages/components/src/slot-fill/test/slot.js index 5d9c7b438b1edb..c4a61e8e2a9a55 100644 --- a/packages/components/src/slot-fill/test/slot.js +++ b/packages/components/src/slot-fill/test/slot.js @@ -2,8 +2,6 @@ * External dependencies */ import { isEmpty } from 'lodash'; -import { unmountComponentAtNode, render } from 'react-dom'; -import { act } from 'react-dom/test-utils'; import ReactTestRenderer from 'react-test-renderer'; /** @@ -16,7 +14,7 @@ import Provider from '../context'; /** * WordPress dependencies */ -import { Component, useState } from '@wordpress/element'; +import { Component } from '@wordpress/element'; class Filler extends Component { constructor() { @@ -254,57 +252,6 @@ describe( 'Slot', () => { expect( testRenderer.getInstance().slots ).toHaveProperty( 'egg' ); } ); - - it( 'should re-render Fill when fillProps are updated', () => { - // react-test-renderer doesn't support portal - const container = document.createElement( 'div' ); - document.body.appendChild( container ); - - // Creating a separate component so only this one will be re-rendered - // when its internal state (children) changes. App will not. - // This is necessary so we can ensure that Fill components will re-render - // when fillProps change even when they're in a separate tree. - const StatefulSlot = () => { - const [ children, setChildren ] = useState( 'a' ); - return ( - <> - - - - ); - }; - - const App = () => ( - - - - { ( props ) =>
    { props.children }
    } -
    -
    - ); - - act( () => { - render( , container ); - } ); - - const fill = container.querySelector( '[data-testid="fill"]' ); - const button = container.querySelector( '[data-testid="change"]' ); - - expect( fill.innerHTML ).toBe( 'a' ); - - button.click(); - - expect( fill.innerHTML ).toBe( 'b' ); - - unmountComponentAtNode( container ); - container.remove(); - } ); } ); } ); diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index 42fe7c09a08bed..bba2e5e0c5fc2f 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -45,6 +45,5 @@ @import "./toolbar/style.scss"; @import "./toolbar-button/style.scss"; @import "./toolbar-group/style.scss"; -@import "./toolbar-slot-fill/style.scss"; @import "./tooltip/style.scss"; @import "./visually-hidden/style.scss"; diff --git a/packages/components/src/toolbar-context/index.js b/packages/components/src/toolbar-context/index.js index a5095c503f6ece..ddae1a5d261355 100644 --- a/packages/components/src/toolbar-context/index.js +++ b/packages/components/src/toolbar-context/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { useToolbarState } from 'reakit'; + /** * WordPress dependencies */ @@ -5,4 +10,14 @@ import { createContext } from '@wordpress/element'; const ToolbarContext = createContext(); +export function ToolbarProvider( { children } ) { + // https://reakit.io/docs/basic-concepts/#state-hooks + const state = useToolbarState( { loop: true } ); + return ( + + { children } + + ); +} + export default ToolbarContext; diff --git a/packages/components/src/toolbar-slot-fill/index.js b/packages/components/src/toolbar-slot-fill/index.js deleted file mode 100644 index 4f35125ba482fd..00000000000000 --- a/packages/components/src/toolbar-slot-fill/index.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ -import { useContext } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { Slot, Fill } from '../slot-fill'; -import ToolbarContext from '../toolbar-context'; - -export function ToolbarSlot( { className, ...props } ) { - const accessibleToolbarState = useContext( ToolbarContext ); - const finalClassName = classnames( 'components-toolbar-slot-fill', className ); - return ( - - ); -} - -export function ToolbarFill( props ) { - return ( - - { ( fillProps ) => ( - - { props.children } - - ) } - - ); -} - -/** - * Toolbar requires context to be passed to its children (ToolbarButton's), - * which doesn't happen when using Slot/Fill with bubblesVirtually prop set to - * true. Toolbar Slot/Fill will pass the context automatically. - * - * @param {string} name - */ -export function createToolbarSlotFill( name ) { - const FillComponent = ( props ) => ; - FillComponent.displayName = name + 'Fill'; - - const SlotComponent = ( props ) => ; - SlotComponent.displayName = name + 'Slot'; - - return { - Fill: FillComponent, - Slot: SlotComponent, - }; -} diff --git a/packages/components/src/toolbar-slot-fill/style.scss b/packages/components/src/toolbar-slot-fill/style.scss deleted file mode 100644 index 297a3cbe51acf2..00000000000000 --- a/packages/components/src/toolbar-slot-fill/style.scss +++ /dev/null @@ -1,4 +0,0 @@ -.components-toolbar-slot-fill { - display: flex; - flex-shrink: 0; -} diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 577cf69f6198c8..4e2adf346bf6db 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ @@ -14,6 +9,9 @@ import deprecated from '@wordpress/deprecated'; */ import ToolbarGroup from '../toolbar-group'; import ToolbarContainer from './toolbar-container'; +import { ToolbarProvider } from '../toolbar-context'; + +export { ToolbarContainer }; /** * Renders an accessible toolbar that follows the @@ -28,16 +26,16 @@ import ToolbarContainer from './toolbar-container'; * @param {string} [props.className] * @param {Object} ref */ -const Toolbar = forwardRef( ( { className, accessibilityLabel, ...props }, ref ) => { +const Toolbar = forwardRef( ( { accessibilityLabel, ...props }, ref ) => { if ( accessibilityLabel ) { return ( - + + + ); } diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 0a4107b910b154..6678d7a3d7242a 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -7,8 +7,6 @@ import { Path, ToolbarButton, ToolbarGroup, - createToolbarSlotFill, - SlotFillProvider, } from '../../'; export default { title: 'Toolbar', component: Toolbar }; @@ -77,24 +75,6 @@ export const withoutGroup = () => { ); }; -export const withSlotFill = () => { - const { Slot, Fill } = createToolbarSlotFill( 'toolbar' ); - - return ( - - - - - - - - - - - - ); -}; - export const standaloneToolbarGroup = () => { return ( diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index 3abeb7ffec137e..bca66f98990705 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -1,33 +1,31 @@ /** * External dependencies */ -import { useToolbarState, Toolbar } from 'reakit/Toolbar'; +import classnames from 'classnames'; +import { Toolbar } from 'reakit/Toolbar'; /** * WordPress dependencies */ -import { forwardRef } from '@wordpress/element'; +import { forwardRef, useContext } from '@wordpress/element'; /** * Internal dependencies */ import ToolbarContext from '../toolbar-context'; -function ToolbarContainer( { accessibilityLabel, ...props }, ref ) { - // https://reakit.io/docs/basic-concepts/#state-hooks - const toolbarState = useToolbarState( { loop: true } ); - +function ToolbarContainer( { accessibilityLabel, className, ...props }, ref ) { + const accessibleToolbarState = useContext( ToolbarContext ); return ( - // This will provide state for `ToolbarButton`'s - - - + ); } From efe8ee08ceba9233c41b3a9b17217e46d43ae81c Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 5 Nov 2019 20:00:41 -0300 Subject: [PATCH 62/75] Put ToolbarProvider closer to the block contextual toolbar and the block edit --- .../src/components/block-list/block.js | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index a848a53da2aa86..4b0ec9737b640c 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -515,30 +515,30 @@ function BlockListBlock( { animationStyle } > - - { shouldShowInsertionPoint && ( - + { shouldShowInsertionPoint && ( + + ) } + + { isFirstMultiSelected && ( + + ) } +
    - { isFirstMultiSelected && ( - + > + { shouldRenderMovers && moverDirection === 'vertical' && blockMover } + { shouldShowBreadcrumb && ( + ) } -
    - { shouldRenderMovers && moverDirection === 'vertical' && blockMover } - { shouldShowBreadcrumb && ( - - ) } + { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ( ) } + +
    + { showInserterShortcuts && ( +
    +
    - { showInserterShortcuts && ( -
    - -
    - ) } - { showEmptyBlockSideInserter && ( -
    - -
    - ) } - + ) } + { showEmptyBlockSideInserter && ( +
    + +
    + ) } ); } From 7d61565227da549b8403bd794214587a9494d750 Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 5 Nov 2019 20:05:09 -0300 Subject: [PATCH 63/75] Update BlockListBlock format --- .../src/components/block-list/block.js | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 4b0ec9737b640c..15a3d968eceac7 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -521,7 +521,10 @@ function BlockListBlock( { rootClientId={ rootClientId } /> ) } - + { isFirstMultiSelected && ( - { shouldRenderMovers && moverDirection === 'vertical' && blockMover } + { shouldRenderMovers && ( moverDirection === 'vertical' ) && blockMover } { shouldShowBreadcrumb && ( - + ) } - { ( shouldShowContextualToolbar || - isForcingContextualToolbar.current ) && ( + { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ( ) } - { ! isNavigationMode && + { + ! isNavigationMode && ! shouldShowContextualToolbar && isSelected && ! hasFixedToolbar && ! isEmptyDefaultBlock && ( - - ) } + + ) + } { isValid && blockEdit } - { isValid && mode === 'html' && } - { shouldRenderMovers && - moverDirection === 'horizontal' && - blockMover } + { isValid && mode === 'html' && ( + + ) } + { shouldRenderMovers && ( moverDirection === 'horizontal' ) && blockMover } { ! isValid && [ { !! hasError && } { shouldShowMobileToolbar && ( - + ) } From c715bc3c968c9296dac19d894774450845af95aa Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 5 Nov 2019 20:47:01 -0300 Subject: [PATCH 64/75] Use Header Toolbar Provider when contextual toolbar is not shown --- .../src/components/block-list/block.js | 109 ++++++++++-------- .../src/components/navigable-toolbar/index.js | 18 +-- packages/edit-post/src/editor.js | 15 ++- 3 files changed, 69 insertions(+), 73 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 15a3d968eceac7..b46fca1c8a16ac 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -488,6 +488,60 @@ function BlockListBlock( { blockEdit =
    { blockEdit }
    ; } + const blockContent = ( + <> + { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ( + + ) } + { + ! isNavigationMode && + ! shouldShowContextualToolbar && + isSelected && + ! hasFixedToolbar && + ! isEmptyDefaultBlock && ( + + ) + } + + + { isValid && blockEdit } + { isValid && mode === 'html' && ( + + ) } + { shouldRenderMovers && ( moverDirection === 'horizontal' ) && blockMover } + { ! isValid && [ + , +
    + { getSaveElement( blockType, attributes ) } +
    , + ] } +
    + { !! hasError && } + { shouldShowMobileToolbar && ( + + ) } +
    + + ); + return ( ) } - - { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ( - - ) } - { - ! isNavigationMode && - ! shouldShowContextualToolbar && - isSelected && - ! hasFixedToolbar && - ! isEmptyDefaultBlock && ( - - ) - } - - - { isValid && blockEdit } - { isValid && mode === 'html' && ( - - ) } - { shouldRenderMovers && ( moverDirection === 'horizontal' ) && blockMover } - { ! isValid && [ - , -
    - { getSaveElement( blockType, attributes ) } -
    , - ] } -
    - { !! hasError && } - { shouldShowMobileToolbar && ( - - ) } -
    -
    + { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) ? + { blockContent } : + blockContent + }
    { showInserterShortcuts && (
    diff --git a/packages/block-editor/src/components/navigable-toolbar/index.js b/packages/block-editor/src/components/navigable-toolbar/index.js index a5dec45d8dd3d4..d177874dac3125 100644 --- a/packages/block-editor/src/components/navigable-toolbar/index.js +++ b/packages/block-editor/src/components/navigable-toolbar/index.js @@ -6,13 +6,7 @@ import { omit } from 'lodash'; /** * WordPress dependencies */ -import { - NavigableMenu, - KeyboardShortcuts, - Toolbar, - ToolbarContext, - ToolbarContainer, -} from '@wordpress/components'; +import { NavigableMenu, KeyboardShortcuts, ToolbarContainer } from '@wordpress/components'; import { Component, createRef } from '@wordpress/element'; import { focus } from '@wordpress/dom'; import deprecated from '@wordpress/deprecated'; @@ -100,16 +94,8 @@ class NavigableToolbar extends Component { ); } - if ( this.context ) { - return ; - } - - return ( - - ); + return ; } } -NavigableToolbar.contextType = ToolbarContext; - export default NavigableToolbar; diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js index eb6754e7cf665c..0c15ed6ed021d6 100644 --- a/packages/edit-post/src/editor.js +++ b/packages/edit-post/src/editor.js @@ -14,6 +14,7 @@ import { KeyboardShortcuts, SlotFillProvider, DropZoneProvider, + ToolbarProvider, } from '@wordpress/components'; import { compose } from '@wordpress/compose'; @@ -123,12 +124,14 @@ class Editor extends Component { useSubRegistry={ false } { ...props } > - - - - - - + + + + + + + + From 634156a0ab7893b25dda2f2138a4ccea4a9768cb Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 5 Nov 2019 21:10:04 -0300 Subject: [PATCH 65/75] Remove Contextual Toolbar context only when there is a fixed toolbar --- packages/block-editor/src/components/block-list/block.js | 5 +---- packages/components/src/index.js | 2 +- packages/components/src/index.native.js | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index b46fca1c8a16ac..b0e1f6f22b14f1 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -598,10 +598,7 @@ function BlockListBlock( { ref={ breadcrumb } /> ) } - { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) ? - { blockContent } : - blockContent - } + { hasFixedToolbar ? blockContent : { blockContent } }
    { showInserterShortcuts && (
    diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 6a7e6cb4c6bd83..0bda235879c1fa 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -61,7 +61,7 @@ export { default as Tip } from './tip'; export { default as ToggleControl } from './toggle-control'; export { default as Toolbar, ToolbarContainer } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; -export { default as ToolbarContext, ToolbarProvider } from './toolbar-context'; +export { ToolbarProvider } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; export { default as Tooltip } from './tooltip'; export { default as TreeSelect } from './tree-select'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 8c8eae7cd33806..33f085d03fc1c6 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -6,7 +6,7 @@ export { default as Dashicon } from './dashicon'; export { default as Dropdown } from './dropdown'; export { default as Toolbar, ToolbarContainer } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; -export { default as ToolbarContext, ToolbarProvider } from './toolbar-context'; +export { ToolbarProvider } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; export { default as Icon } from './icon'; export { default as IconButton } from './icon-button'; From b6e6b5701eb39a4e00dc46adfb74289fc93b185f Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 5 Nov 2019 21:50:06 -0300 Subject: [PATCH 66/75] Revert to a1328b2 --- .../src/components/block-controls/index.js | 4 +- .../components/block-format-controls/index.js | 4 +- .../src/components/block-list/block.js | 106 +++++++++--------- .../src/components/navigable-toolbar/index.js | 6 +- packages/components/src/index.js | 4 +- packages/components/src/index.native.js | 4 +- packages/components/src/slot-fill/context.js | 7 ++ packages/components/src/slot-fill/fill.js | 4 +- packages/components/src/slot-fill/slot.js | 5 +- .../components/src/slot-fill/test/slot.js | 55 ++++++++- packages/components/src/style.scss | 1 + .../components/src/toolbar-context/index.js | 15 --- .../components/src/toolbar-slot-fill/index.js | 59 ++++++++++ .../src/toolbar-slot-fill/style.scss | 4 + packages/components/src/toolbar/index.js | 24 ++-- .../components/src/toolbar/stories/index.js | 20 ++++ .../src/toolbar/toolbar-container.js | 30 ++--- packages/edit-post/src/editor.js | 15 +-- 18 files changed, 248 insertions(+), 119 deletions(-) create mode 100644 packages/components/src/toolbar-slot-fill/index.js create mode 100644 packages/components/src/toolbar-slot-fill/style.scss diff --git a/packages/block-editor/src/components/block-controls/index.js b/packages/block-editor/src/components/block-controls/index.js index dbbc0c62a9981d..1eaf948ac56f14 100644 --- a/packages/block-editor/src/components/block-controls/index.js +++ b/packages/block-editor/src/components/block-controls/index.js @@ -1,14 +1,14 @@ /** * WordPress dependencies */ -import { createSlotFill, ToolbarGroup } from '@wordpress/components'; +import { createToolbarSlotFill, ToolbarGroup } from '@wordpress/components'; /** * Internal dependencies */ import { ifBlockEditSelected } from '../block-edit/context'; -const { Fill, Slot } = createSlotFill( 'BlockControls' ); +const { Fill, Slot } = createToolbarSlotFill( 'BlockControls' ); const BlockControlsFill = ( { controls, children } ) => ( diff --git a/packages/block-editor/src/components/block-format-controls/index.js b/packages/block-editor/src/components/block-format-controls/index.js index 2ab83843846ad4..3ad50829c69e04 100644 --- a/packages/block-editor/src/components/block-format-controls/index.js +++ b/packages/block-editor/src/components/block-format-controls/index.js @@ -1,14 +1,14 @@ /** * WordPress dependencies */ -import { createSlotFill } from '@wordpress/components'; +import { createToolbarSlotFill } from '@wordpress/components'; /** * Internal dependencies */ import { ifBlockEditSelected } from '../block-edit/context'; -const { Fill, Slot } = createSlotFill( 'BlockFormatControls' ); +const { Fill, Slot } = createToolbarSlotFill( 'BlockFormatControls' ); const BlockFormatControls = ifBlockEditSelected( Fill ); diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index b0e1f6f22b14f1..48b5506e452f2f 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -22,7 +22,7 @@ import { isUnmodifiedDefaultBlock, getUnregisteredTypeHandlerName, } from '@wordpress/blocks'; -import { KeyboardShortcuts, withFilters, ToolbarProvider } from '@wordpress/components'; +import { KeyboardShortcuts, withFilters } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { withDispatch, @@ -488,60 +488,6 @@ function BlockListBlock( { blockEdit =
    { blockEdit }
    ; } - const blockContent = ( - <> - { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ( - - ) } - { - ! isNavigationMode && - ! shouldShowContextualToolbar && - isSelected && - ! hasFixedToolbar && - ! isEmptyDefaultBlock && ( - - ) - } - - - { isValid && blockEdit } - { isValid && mode === 'html' && ( - - ) } - { shouldRenderMovers && ( moverDirection === 'horizontal' ) && blockMover } - { ! isValid && [ - , -
    - { getSaveElement( blockType, attributes ) } -
    , - ] } -
    - { !! hasError && } - { shouldShowMobileToolbar && ( - - ) } -
    - - ); - return ( ) } - { hasFixedToolbar ? blockContent : { blockContent } } + { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ( + + ) } + { + ! isNavigationMode && + ! shouldShowContextualToolbar && + isSelected && + ! hasFixedToolbar && + ! isEmptyDefaultBlock && ( + + ) + } + + + { isValid && blockEdit } + { isValid && mode === 'html' && ( + + ) } + { shouldRenderMovers && ( moverDirection === 'horizontal' ) && blockMover } + { ! isValid && [ + , +
    + { getSaveElement( blockType, attributes ) } +
    , + ] } +
    + { !! hasError && } + { shouldShowMobileToolbar && ( + + ) } +
    { showInserterShortcuts && (
    diff --git a/packages/block-editor/src/components/navigable-toolbar/index.js b/packages/block-editor/src/components/navigable-toolbar/index.js index d177874dac3125..7705af421579c3 100644 --- a/packages/block-editor/src/components/navigable-toolbar/index.js +++ b/packages/block-editor/src/components/navigable-toolbar/index.js @@ -6,7 +6,7 @@ import { omit } from 'lodash'; /** * WordPress dependencies */ -import { NavigableMenu, KeyboardShortcuts, ToolbarContainer } from '@wordpress/components'; +import { NavigableMenu, Toolbar, KeyboardShortcuts } from '@wordpress/components'; import { Component, createRef } from '@wordpress/element'; import { focus } from '@wordpress/dom'; import deprecated from '@wordpress/deprecated'; @@ -94,7 +94,9 @@ class NavigableToolbar extends Component { ); } - return ; + return ( + + ); } } diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 0bda235879c1fa..cc474a125d8aea 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -59,10 +59,10 @@ export { default as TextControl } from './text-control'; export { default as TextareaControl } from './textarea-control'; export { default as Tip } from './tip'; export { default as ToggleControl } from './toggle-control'; -export { default as Toolbar, ToolbarContainer } from './toolbar'; +export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; -export { ToolbarProvider } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; +export { createToolbarSlotFill, ToolbarSlot, ToolbarFill } from './toolbar-slot-fill'; export { default as Tooltip } from './tooltip'; export { default as TreeSelect } from './tree-select'; export { default as VisuallyHidden } from './visually-hidden'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 33f085d03fc1c6..8fa534f63ad922 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -4,10 +4,10 @@ export { default as ColorIndicator } from './color-indicator'; export { default as ColorPalette } from './color-palette'; export { default as Dashicon } from './dashicon'; export { default as Dropdown } from './dropdown'; -export { default as Toolbar, ToolbarContainer } from './toolbar'; +export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; -export { ToolbarProvider } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; +export { createToolbarSlotFill, ToolbarSlot, ToolbarFill } from './toolbar-slot-fill'; export { default as Icon } from './icon'; export { default as IconButton } from './icon-button'; export { default as Spinner } from './spinner'; diff --git a/packages/components/src/slot-fill/context.js b/packages/components/src/slot-fill/context.js index 587c1ccab5ccb8..5e407988a0abd0 100644 --- a/packages/components/src/slot-fill/context.js +++ b/packages/components/src/slot-fill/context.js @@ -60,6 +60,9 @@ class SlotFillProvider extends Component { // will be empty (the new Slot will subsume all fills for this name). if ( previousSlot ) { previousSlot.forceUpdate(); + + // See 'should re-render Fill when fillProps are updated' test. + this.forceUpdateFills( name ); } } @@ -119,6 +122,10 @@ class SlotFillProvider extends Component { } } + forceUpdateFills( name ) { + forEach( this.fills[ name ], ( instance ) => instance.forceUpdate() ); + } + triggerListeners() { this.listeners.forEach( ( listener ) => listener() ); } diff --git a/packages/components/src/slot-fill/fill.js b/packages/components/src/slot-fill/fill.js index 3600e177e5434f..3570a3a75e5db4 100644 --- a/packages/components/src/slot-fill/fill.js +++ b/packages/components/src/slot-fill/fill.js @@ -6,7 +6,7 @@ import { isFunction } from 'lodash'; /** * WordPress dependencies */ -import { createPortal, useLayoutEffect, useRef } from '@wordpress/element'; +import { createPortal, useLayoutEffect, useRef, useReducer } from '@wordpress/element'; /** * Internal dependencies @@ -17,10 +17,12 @@ let occurrences = 0; function FillComponent( { name, children, registerFill, unregisterFill } ) { const slot = useSlot( name ); + const [ , forceUpdate ] = useReducer( () => ( {} ), {} ); const ref = useRef( { name, children, + forceUpdate, } ); if ( ! ref.current.occurrence ) { diff --git a/packages/components/src/slot-fill/slot.js b/packages/components/src/slot-fill/slot.js index 4578656b5efaa9..e1195b7d4317d5 100644 --- a/packages/components/src/slot-fill/slot.js +++ b/packages/components/src/slot-fill/slot.js @@ -11,6 +11,7 @@ import { /** * WordPress dependencies */ +import isShallowEqual from '@wordpress/is-shallow-equal'; import { Children, Component, @@ -43,9 +44,9 @@ class SlotComponent extends Component { } componentDidUpdate( prevProps ) { - const { name, unregisterSlot, registerSlot } = this.props; + const { name, fillProps, unregisterSlot, registerSlot } = this.props; - if ( prevProps.name !== name ) { + if ( prevProps.name !== name || ! isShallowEqual( prevProps.fillProps, fillProps ) ) { unregisterSlot( prevProps.name ); registerSlot( name, this ); } diff --git a/packages/components/src/slot-fill/test/slot.js b/packages/components/src/slot-fill/test/slot.js index c4a61e8e2a9a55..5d9c7b438b1edb 100644 --- a/packages/components/src/slot-fill/test/slot.js +++ b/packages/components/src/slot-fill/test/slot.js @@ -2,6 +2,8 @@ * External dependencies */ import { isEmpty } from 'lodash'; +import { unmountComponentAtNode, render } from 'react-dom'; +import { act } from 'react-dom/test-utils'; import ReactTestRenderer from 'react-test-renderer'; /** @@ -14,7 +16,7 @@ import Provider from '../context'; /** * WordPress dependencies */ -import { Component } from '@wordpress/element'; +import { Component, useState } from '@wordpress/element'; class Filler extends Component { constructor() { @@ -252,6 +254,57 @@ describe( 'Slot', () => { expect( testRenderer.getInstance().slots ).toHaveProperty( 'egg' ); } ); + + it( 'should re-render Fill when fillProps are updated', () => { + // react-test-renderer doesn't support portal + const container = document.createElement( 'div' ); + document.body.appendChild( container ); + + // Creating a separate component so only this one will be re-rendered + // when its internal state (children) changes. App will not. + // This is necessary so we can ensure that Fill components will re-render + // when fillProps change even when they're in a separate tree. + const StatefulSlot = () => { + const [ children, setChildren ] = useState( 'a' ); + return ( + <> + + + + ); + }; + + const App = () => ( + + + + { ( props ) =>
    { props.children }
    } +
    +
    + ); + + act( () => { + render( , container ); + } ); + + const fill = container.querySelector( '[data-testid="fill"]' ); + const button = container.querySelector( '[data-testid="change"]' ); + + expect( fill.innerHTML ).toBe( 'a' ); + + button.click(); + + expect( fill.innerHTML ).toBe( 'b' ); + + unmountComponentAtNode( container ); + container.remove(); + } ); } ); } ); diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index bba2e5e0c5fc2f..42fe7c09a08bed 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -45,5 +45,6 @@ @import "./toolbar/style.scss"; @import "./toolbar-button/style.scss"; @import "./toolbar-group/style.scss"; +@import "./toolbar-slot-fill/style.scss"; @import "./tooltip/style.scss"; @import "./visually-hidden/style.scss"; diff --git a/packages/components/src/toolbar-context/index.js b/packages/components/src/toolbar-context/index.js index ddae1a5d261355..a5095c503f6ece 100644 --- a/packages/components/src/toolbar-context/index.js +++ b/packages/components/src/toolbar-context/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { useToolbarState } from 'reakit'; - /** * WordPress dependencies */ @@ -10,14 +5,4 @@ import { createContext } from '@wordpress/element'; const ToolbarContext = createContext(); -export function ToolbarProvider( { children } ) { - // https://reakit.io/docs/basic-concepts/#state-hooks - const state = useToolbarState( { loop: true } ); - return ( - - { children } - - ); -} - export default ToolbarContext; diff --git a/packages/components/src/toolbar-slot-fill/index.js b/packages/components/src/toolbar-slot-fill/index.js new file mode 100644 index 00000000000000..4f35125ba482fd --- /dev/null +++ b/packages/components/src/toolbar-slot-fill/index.js @@ -0,0 +1,59 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { useContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { Slot, Fill } from '../slot-fill'; +import ToolbarContext from '../toolbar-context'; + +export function ToolbarSlot( { className, ...props } ) { + const accessibleToolbarState = useContext( ToolbarContext ); + const finalClassName = classnames( 'components-toolbar-slot-fill', className ); + return ( + + ); +} + +export function ToolbarFill( props ) { + return ( + + { ( fillProps ) => ( + + { props.children } + + ) } + + ); +} + +/** + * Toolbar requires context to be passed to its children (ToolbarButton's), + * which doesn't happen when using Slot/Fill with bubblesVirtually prop set to + * true. Toolbar Slot/Fill will pass the context automatically. + * + * @param {string} name + */ +export function createToolbarSlotFill( name ) { + const FillComponent = ( props ) => ; + FillComponent.displayName = name + 'Fill'; + + const SlotComponent = ( props ) => ; + SlotComponent.displayName = name + 'Slot'; + + return { + Fill: FillComponent, + Slot: SlotComponent, + }; +} diff --git a/packages/components/src/toolbar-slot-fill/style.scss b/packages/components/src/toolbar-slot-fill/style.scss new file mode 100644 index 00000000000000..297a3cbe51acf2 --- /dev/null +++ b/packages/components/src/toolbar-slot-fill/style.scss @@ -0,0 +1,4 @@ +.components-toolbar-slot-fill { + display: flex; + flex-shrink: 0; +} diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 4e2adf346bf6db..577cf69f6198c8 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -9,9 +14,6 @@ import deprecated from '@wordpress/deprecated'; */ import ToolbarGroup from '../toolbar-group'; import ToolbarContainer from './toolbar-container'; -import { ToolbarProvider } from '../toolbar-context'; - -export { ToolbarContainer }; /** * Renders an accessible toolbar that follows the @@ -26,16 +28,16 @@ export { ToolbarContainer }; * @param {string} [props.className] * @param {Object} ref */ -const Toolbar = forwardRef( ( { accessibilityLabel, ...props }, ref ) => { +const Toolbar = forwardRef( ( { className, accessibilityLabel, ...props }, ref ) => { if ( accessibilityLabel ) { return ( - - - + ); } diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 6678d7a3d7242a..0a4107b910b154 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -7,6 +7,8 @@ import { Path, ToolbarButton, ToolbarGroup, + createToolbarSlotFill, + SlotFillProvider, } from '../../'; export default { title: 'Toolbar', component: Toolbar }; @@ -75,6 +77,24 @@ export const withoutGroup = () => { ); }; +export const withSlotFill = () => { + const { Slot, Fill } = createToolbarSlotFill( 'toolbar' ); + + return ( + + + + + + + + + + + + ); +}; + export const standaloneToolbarGroup = () => { return ( diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index bca66f98990705..3abeb7ffec137e 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -1,31 +1,33 @@ /** * External dependencies */ -import classnames from 'classnames'; -import { Toolbar } from 'reakit/Toolbar'; +import { useToolbarState, Toolbar } from 'reakit/Toolbar'; /** * WordPress dependencies */ -import { forwardRef, useContext } from '@wordpress/element'; +import { forwardRef } from '@wordpress/element'; /** * Internal dependencies */ import ToolbarContext from '../toolbar-context'; -function ToolbarContainer( { accessibilityLabel, className, ...props }, ref ) { - const accessibleToolbarState = useContext( ToolbarContext ); +function ToolbarContainer( { accessibilityLabel, ...props }, ref ) { + // https://reakit.io/docs/basic-concepts/#state-hooks + const toolbarState = useToolbarState( { loop: true } ); + return ( - + // This will provide state for `ToolbarButton`'s + + + ); } diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js index 0c15ed6ed021d6..eb6754e7cf665c 100644 --- a/packages/edit-post/src/editor.js +++ b/packages/edit-post/src/editor.js @@ -14,7 +14,6 @@ import { KeyboardShortcuts, SlotFillProvider, DropZoneProvider, - ToolbarProvider, } from '@wordpress/components'; import { compose } from '@wordpress/compose'; @@ -124,14 +123,12 @@ class Editor extends Component { useSubRegistry={ false } { ...props } > - - - - - - - - + + + + + + From 228068c67faa8e3f0d2a8a5ae5bfdf9f718708c9 Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 5 Nov 2019 22:13:37 -0300 Subject: [PATCH 67/75] Remove ToolbarSlotFill from components package --- .../src/components/block-controls/index.js | 30 +++++++--- .../components/block-format-controls/index.js | 26 ++++++-- packages/components/src/index.js | 2 +- packages/components/src/index.native.js | 2 +- packages/components/src/style.scss | 1 - .../components/src/toolbar-slot-fill/index.js | 59 ------------------- .../src/toolbar-slot-fill/style.scss | 4 -- .../components/src/toolbar/stories/index.js | 20 ------- 8 files changed, 45 insertions(+), 99 deletions(-) delete mode 100644 packages/components/src/toolbar-slot-fill/index.js delete mode 100644 packages/components/src/toolbar-slot-fill/style.scss diff --git a/packages/block-editor/src/components/block-controls/index.js b/packages/block-editor/src/components/block-controls/index.js index 1eaf948ac56f14..4c265e2582d5d7 100644 --- a/packages/block-editor/src/components/block-controls/index.js +++ b/packages/block-editor/src/components/block-controls/index.js @@ -1,24 +1,36 @@ /** * WordPress dependencies */ -import { createToolbarSlotFill, ToolbarGroup } from '@wordpress/components'; +import { useContext } from '@wordpress/element'; +import { createSlotFill, ToolbarGroup, ToolbarContext } from '@wordpress/components'; /** * Internal dependencies */ import { ifBlockEditSelected } from '../block-edit/context'; -const { Fill, Slot } = createToolbarSlotFill( 'BlockControls' ); +const { Fill, Slot } = createSlotFill( 'BlockControls' ); -const BlockControlsFill = ( { controls, children } ) => ( - - - { children } - -); +function BlockControlsSlot( props ) { + const accessibleToolbarState = useContext( ToolbarContext ); + return ; +} + +function BlockControlsFill( { controls, children } ) { + return ( + + { ( fillProps ) => ( + + + { children } + + ) } + + ); +} const BlockControls = ifBlockEditSelected( BlockControlsFill ); -BlockControls.Slot = Slot; +BlockControls.Slot = BlockControlsSlot; export default BlockControls; diff --git a/packages/block-editor/src/components/block-format-controls/index.js b/packages/block-editor/src/components/block-format-controls/index.js index 3ad50829c69e04..ca2381e83d8bb3 100644 --- a/packages/block-editor/src/components/block-format-controls/index.js +++ b/packages/block-editor/src/components/block-format-controls/index.js @@ -1,17 +1,35 @@ /** * WordPress dependencies */ -import { createToolbarSlotFill } from '@wordpress/components'; +import { useContext } from '@wordpress/element'; +import { createSlotFill, ToolbarContext } from '@wordpress/components'; /** * Internal dependencies */ import { ifBlockEditSelected } from '../block-edit/context'; -const { Fill, Slot } = createToolbarSlotFill( 'BlockFormatControls' ); +const { Fill, Slot } = createSlotFill( 'BlockFormatControls' ); -const BlockFormatControls = ifBlockEditSelected( Fill ); +function BlockFormatControlsSlot( props ) { + const accessibleToolbarState = useContext( ToolbarContext ); + return ; +} -BlockFormatControls.Slot = Slot; +function BlockFormatControlsFill( props ) { + return ( + + { ( fillProps ) => ( + + { props.children } + + ) } + + ); +} + +const BlockFormatControls = ifBlockEditSelected( BlockFormatControlsFill ); + +BlockFormatControls.Slot = BlockFormatControlsSlot; export default BlockFormatControls; diff --git a/packages/components/src/index.js b/packages/components/src/index.js index cc474a125d8aea..6671190cab4da3 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -61,8 +61,8 @@ export { default as Tip } from './tip'; export { default as ToggleControl } from './toggle-control'; export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; +export { default as ToolbarContext } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; -export { createToolbarSlotFill, ToolbarSlot, ToolbarFill } from './toolbar-slot-fill'; export { default as Tooltip } from './tooltip'; export { default as TreeSelect } from './tree-select'; export { default as VisuallyHidden } from './visually-hidden'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 8fa534f63ad922..4827dc28c9ab26 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -6,8 +6,8 @@ export { default as Dashicon } from './dashicon'; export { default as Dropdown } from './dropdown'; export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; +export { default as ToolbarContext } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; -export { createToolbarSlotFill, ToolbarSlot, ToolbarFill } from './toolbar-slot-fill'; export { default as Icon } from './icon'; export { default as IconButton } from './icon-button'; export { default as Spinner } from './spinner'; diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index 42fe7c09a08bed..bba2e5e0c5fc2f 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -45,6 +45,5 @@ @import "./toolbar/style.scss"; @import "./toolbar-button/style.scss"; @import "./toolbar-group/style.scss"; -@import "./toolbar-slot-fill/style.scss"; @import "./tooltip/style.scss"; @import "./visually-hidden/style.scss"; diff --git a/packages/components/src/toolbar-slot-fill/index.js b/packages/components/src/toolbar-slot-fill/index.js deleted file mode 100644 index 4f35125ba482fd..00000000000000 --- a/packages/components/src/toolbar-slot-fill/index.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ -import { useContext } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { Slot, Fill } from '../slot-fill'; -import ToolbarContext from '../toolbar-context'; - -export function ToolbarSlot( { className, ...props } ) { - const accessibleToolbarState = useContext( ToolbarContext ); - const finalClassName = classnames( 'components-toolbar-slot-fill', className ); - return ( - - ); -} - -export function ToolbarFill( props ) { - return ( - - { ( fillProps ) => ( - - { props.children } - - ) } - - ); -} - -/** - * Toolbar requires context to be passed to its children (ToolbarButton's), - * which doesn't happen when using Slot/Fill with bubblesVirtually prop set to - * true. Toolbar Slot/Fill will pass the context automatically. - * - * @param {string} name - */ -export function createToolbarSlotFill( name ) { - const FillComponent = ( props ) => ; - FillComponent.displayName = name + 'Fill'; - - const SlotComponent = ( props ) => ; - SlotComponent.displayName = name + 'Slot'; - - return { - Fill: FillComponent, - Slot: SlotComponent, - }; -} diff --git a/packages/components/src/toolbar-slot-fill/style.scss b/packages/components/src/toolbar-slot-fill/style.scss deleted file mode 100644 index 297a3cbe51acf2..00000000000000 --- a/packages/components/src/toolbar-slot-fill/style.scss +++ /dev/null @@ -1,4 +0,0 @@ -.components-toolbar-slot-fill { - display: flex; - flex-shrink: 0; -} diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 0a4107b910b154..6678d7a3d7242a 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -7,8 +7,6 @@ import { Path, ToolbarButton, ToolbarGroup, - createToolbarSlotFill, - SlotFillProvider, } from '../../'; export default { title: 'Toolbar', component: Toolbar }; @@ -77,24 +75,6 @@ export const withoutGroup = () => { ); }; -export const withSlotFill = () => { - const { Slot, Fill } = createToolbarSlotFill( 'toolbar' ); - - return ( - - - - - - - - - - - - ); -}; - export const standaloneToolbarGroup = () => { return ( From d410b772fffe5a9aa3f0b5586b624d677506596b Mon Sep 17 00:00:00 2001 From: Haz Date: Mon, 11 Nov 2019 19:59:56 +0100 Subject: [PATCH 68/75] Fix toolbar group styling --- packages/block-editor/src/components/block-list/style.scss | 4 ++++ packages/block-editor/src/components/block-toolbar/style.scss | 1 + packages/block-editor/src/components/rich-text/style.scss | 1 + packages/block-editor/src/components/url-input/style.scss | 1 + .../edit-post/src/components/header/header-toolbar/style.scss | 2 ++ packages/edit-post/src/components/sidebar/style.scss | 2 ++ 6 files changed, 11 insertions(+) diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index d797e4a309bd26..ca148f485ca8dc 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -879,6 +879,7 @@ // Paint the borders on the toolbar itself on mobile. border-top: $border-width solid $light-gray-800; + .components-toolbar-group, .components-toolbar { border-top: none; border-bottom: none; @@ -886,6 +887,7 @@ @include break-small() { border-top: none; + .components-toolbar-group, .components-toolbar { border-top: $border-width solid $light-gray-800; border-bottom: $border-width solid $light-gray-800; @@ -1029,6 +1031,7 @@ left: -$block-padding - $block-left-border-width; top: (($block-padding * -2) - $block-left-border-width); + .components-toolbar-group, .components-toolbar { border: none; line-height: 1; @@ -1070,6 +1073,7 @@ left: -$block-padding; top: -$block-toolbar-height - $block-padding; + .components-toolbar-group, .components-toolbar { background: $white; border: $border-width solid $blue-medium-focus; diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index 1ef288a0d3d89a..bfa96237172c82 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -46,6 +46,7 @@ box-shadow: none; border-left: $border-width solid $light-gray-500; + .components-toolbar-group, .components-toolbar { border-color: $light-gray-500; } diff --git a/packages/block-editor/src/components/rich-text/style.scss b/packages/block-editor/src/components/rich-text/style.scss index 8d3eeb019834d2..a5481ae3f3808d 100644 --- a/packages/block-editor/src/components/rich-text/style.scss +++ b/packages/block-editor/src/components/rich-text/style.scss @@ -61,6 +61,7 @@ figcaption.block-editor-rich-text__editable [data-rich-text-placeholder]::before min-width: auto; } + .components-toolbar-group, .components-toolbar { // The popover already provides a border. border: none; diff --git a/packages/block-editor/src/components/url-input/style.scss b/packages/block-editor/src/components/url-input/style.scss index 5e6f34762a4605..d3540dbb4ce97e 100644 --- a/packages/block-editor/src/components/url-input/style.scss +++ b/packages/block-editor/src/components/url-input/style.scss @@ -101,6 +101,7 @@ $input-size: 300px; } // Toolbar button +.components-toolbar-group > .block-editor-url-input__button, .components-toolbar > .block-editor-url-input__button { position: inherit; // Let the dialog position according to parent. } diff --git a/packages/edit-post/src/components/header/header-toolbar/style.scss b/packages/edit-post/src/components/header/header-toolbar/style.scss index ceed4a0364ffdb..4f015f2f227e5e 100644 --- a/packages/edit-post/src/components/header/header-toolbar/style.scss +++ b/packages/edit-post/src/components/header/header-toolbar/style.scss @@ -33,6 +33,7 @@ min-height: $block-toolbar-height; border-bottom: $border-width solid $light-gray-500; + .block-editor-block-toolbar .components-toolbar-group, .block-editor-block-toolbar .components-toolbar { border-top: none; border-bottom: none; @@ -67,6 +68,7 @@ margin: -($grid-size + $border-width) 0; } + .block-editor-block-toolbar .components-toolbar-group, .block-editor-block-toolbar .components-toolbar { padding: ($grid-size + $border-width + $border-width) $grid-size-small ($grid-size + $border-width); } diff --git a/packages/edit-post/src/components/sidebar/style.scss b/packages/edit-post/src/components/sidebar/style.scss index 50ddd2efedbde5..80c74f3998895d 100644 --- a/packages/edit-post/src/components/sidebar/style.scss +++ b/packages/edit-post/src/components/sidebar/style.scss @@ -83,6 +83,7 @@ margin: 1.5em 0; } + div.components-toolbar-group, div.components-toolbar { box-shadow: none; margin-bottom: 1.5em; @@ -91,6 +92,7 @@ } } + p + div.components-toolbar-group, p + div.components-toolbar { margin-top: -1em; } From d8c0673ea2ba77f66b91c064b101df512c868e26 Mon Sep 17 00:00:00 2001 From: Haz Date: Mon, 11 Nov 2019 20:01:06 +0100 Subject: [PATCH 69/75] Remove subscript prop from ToolbarGroup tests --- packages/components/src/toolbar-group/test/index.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/components/src/toolbar-group/test/index.js b/packages/components/src/toolbar-group/test/index.js index f77a0158593d1a..972dca89499f78 100644 --- a/packages/components/src/toolbar-group/test/index.js +++ b/packages/components/src/toolbar-group/test/index.js @@ -26,7 +26,6 @@ describe( 'ToolbarGroup', () => { { icon: 'wordpress', title: 'WordPress', - subscript: 'wp', onClick: clickHandler, isActive: false, }, @@ -36,7 +35,6 @@ describe( 'ToolbarGroup', () => { expect( button.props() ).toMatchObject( { 'aria-label': 'WordPress', 'aria-pressed': false, - 'data-subscript': 'wp', type: 'button', } ); } ); @@ -47,7 +45,6 @@ describe( 'ToolbarGroup', () => { { icon: 'wordpress', title: 'WordPress', - subscript: 'wp', onClick: clickHandler, isActive: true, }, @@ -57,7 +54,6 @@ describe( 'ToolbarGroup', () => { expect( button.props() ).toMatchObject( { 'aria-label': 'WordPress', 'aria-pressed': true, - 'data-subscript': 'wp', type: 'button', } ); } ); @@ -92,7 +88,6 @@ describe( 'ToolbarGroup', () => { { icon: 'wordpress', title: 'WordPress', - subscript: 'wp', onClick: clickHandler, isActive: true, }, From 2eecd53492c9fa341d4c7c680f3fd527ef5cf997 Mon Sep 17 00:00:00 2001 From: Haz Date: Mon, 11 Nov 2019 20:06:16 +0100 Subject: [PATCH 70/75] Fix DropdownMenu items don't receiving focus on Storybook --- packages/components/src/popover/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/components/src/popover/index.js b/packages/components/src/popover/index.js index 420ac78b76bdbf..8bb922d15fe0d1 100644 --- a/packages/components/src/popover/index.js +++ b/packages/components/src/popover/index.js @@ -24,6 +24,7 @@ import ScrollLock from '../scroll-lock'; import IsolatedEventContainer from '../isolated-event-container'; import { Slot, Fill, Consumer } from '../slot-fill'; import Animate from '../animate'; +import ToolbarContext from '../toolbar-context'; const FocusManaged = withConstrainedTabbing( withFocusReturn( ( { children } ) => children ) ); @@ -435,6 +436,8 @@ const Popover = ( { // default to an in-place rendering. if ( getSlot && getSlot( SLOT_NAME ) ) { content = { content }; + } else { + content = { content }; } return ( From 3a9bf79416919df01b303a76ef58a0a11d9d55af Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 12 Nov 2019 12:42:26 +0100 Subject: [PATCH 71/75] ToolbarContext -> __experimentalToolbarContext --- .../src/components/block-controls/index.js | 8 +++--- .../components/block-format-controls/index.js | 8 +++--- packages/components/src/index.js | 2 +- packages/components/src/index.native.js | 2 +- .../src/toolbar/use-is-within-toolbar.js | 26 ++++++++++++------- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/packages/block-editor/src/components/block-controls/index.js b/packages/block-editor/src/components/block-controls/index.js index 4c265e2582d5d7..465e76a8318127 100644 --- a/packages/block-editor/src/components/block-controls/index.js +++ b/packages/block-editor/src/components/block-controls/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { useContext } from '@wordpress/element'; -import { createSlotFill, ToolbarGroup, ToolbarContext } from '@wordpress/components'; +import { createSlotFill, ToolbarGroup, __experimentalToolbarContext } from '@wordpress/components'; /** * Internal dependencies @@ -12,7 +12,7 @@ import { ifBlockEditSelected } from '../block-edit/context'; const { Fill, Slot } = createSlotFill( 'BlockControls' ); function BlockControlsSlot( props ) { - const accessibleToolbarState = useContext( ToolbarContext ); + const accessibleToolbarState = useContext( __experimentalToolbarContext ); return ; } @@ -20,10 +20,10 @@ function BlockControlsFill( { controls, children } ) { return ( { ( fillProps ) => ( - + <__experimentalToolbarContext.Provider value={ fillProps }> { children } - + ) } ); diff --git a/packages/block-editor/src/components/block-format-controls/index.js b/packages/block-editor/src/components/block-format-controls/index.js index ca2381e83d8bb3..79988df9ac564d 100644 --- a/packages/block-editor/src/components/block-format-controls/index.js +++ b/packages/block-editor/src/components/block-format-controls/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { useContext } from '@wordpress/element'; -import { createSlotFill, ToolbarContext } from '@wordpress/components'; +import { createSlotFill, __experimentalToolbarContext } from '@wordpress/components'; /** * Internal dependencies @@ -12,7 +12,7 @@ import { ifBlockEditSelected } from '../block-edit/context'; const { Fill, Slot } = createSlotFill( 'BlockFormatControls' ); function BlockFormatControlsSlot( props ) { - const accessibleToolbarState = useContext( ToolbarContext ); + const accessibleToolbarState = useContext( __experimentalToolbarContext ); return ; } @@ -20,9 +20,9 @@ function BlockFormatControlsFill( props ) { return ( { ( fillProps ) => ( - + <__experimentalToolbarContext.Provider value={ fillProps }> { props.children } - + ) } ); diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 6671190cab4da3..005643ac9f0824 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -61,7 +61,7 @@ export { default as Tip } from './tip'; export { default as ToggleControl } from './toggle-control'; export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; -export { default as ToolbarContext } from './toolbar-context'; +export { default as __experimentalToolbarContext } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; export { default as Tooltip } from './tooltip'; export { default as TreeSelect } from './tree-select'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 1e873e0f081413..b2a254824bb957 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -6,7 +6,7 @@ export { default as Dashicon } from './dashicon'; export { default as Dropdown } from './dropdown'; export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; -export { default as ToolbarContext } from './toolbar-context'; +export { default as __experimentalToolbarContext } from './toolbar-context'; export { default as ToolbarGroup } from './toolbar-group'; export { default as Icon } from './icon'; export { default as IconButton } from './icon-button'; diff --git a/packages/components/src/toolbar/use-is-within-toolbar.js b/packages/components/src/toolbar/use-is-within-toolbar.js index 4adad3c9818d99..6345068a0bb33e 100644 --- a/packages/components/src/toolbar/use-is-within-toolbar.js +++ b/packages/components/src/toolbar/use-is-within-toolbar.js @@ -9,19 +9,27 @@ import ToolbarContext from '../toolbar-context'; function useIsWithinToolbar( ref ) { const context = useContext( ToolbarContext ); + // Initially, it'll be true if there's toolbar context const [ isWithinToolbar, setIsWithinToolbar ] = useState( Boolean( context ) ); const internalRef = useRef(); - const finalRef = ref ? ( ( instance ) => { - if ( typeof ref === 'function' ) { - ref( instance ); - } else { - ref.current = instance; - } - internalRef.current = instance; - } ) : internalRef; + // Combine internal ref with ref argument + if ( ref ) { + ref = ( instance ) => { + if ( typeof ref === 'function' ) { + ref( instance ); + } else { + ref.current = instance; + } + internalRef.current = instance; + }; + } else { + ref = internalRef; + } useEffect( () => { + // Context propagates through React Portal, + // so we have to make sure this is really DOM descendant if ( isWithinToolbar && internalRef.current ) { if ( ! internalRef.current.closest( '[data-toolbar="true"]' ) ) { setIsWithinToolbar( false ); @@ -29,7 +37,7 @@ function useIsWithinToolbar( ref ) { } }, [ isWithinToolbar ] ); - return { isWithinToolbar, ref: finalRef }; + return { isWithinToolbar, ref }; } export default useIsWithinToolbar; From 5e033bc87a94913ab9c825d8aa7df4ee1789a1f6 Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 12 Nov 2019 13:06:43 +0100 Subject: [PATCH 72/75] Add missing JSDocs to ToolbarGroup --- packages/components/src/toolbar-group/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index 1f96c069f6e5dc..42887acd6fb490 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -40,11 +40,13 @@ import ToolbarContext from '../toolbar-context'; * Either `controls` or `children` is required, otherwise this components * renders nothing. * - * @param {Object} props - * @param {Array} [props.controls] The controls to render in this toolbar. - * @param {WPElement} [props.children] Any other things to render inside the - * toolbar besides the controls. - * @param {string} [props.className] Class to set on the container div. + * @param {Object} props + * @param {Array} [props.controls] The controls to render in this toolbar. + * @param {WPElement} [props.children] Any other things to render inside the toolbar besides the controls. + * @param {string} [props.className] Class to set on the container div. + * @param {boolean} [props.isCollapsed] Turns ToolbarGroup into a dropdown menu. + * @param {WPBlockTypeIconRender} [props.icon] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element. + * @param {string} [props.label] The menu item text. * * @return {WPComponent} The rendered component. */ From 413355154269e079b29f68fbe5e055aa962de84f Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 12 Nov 2019 13:10:54 +0100 Subject: [PATCH 73/75] Fix ref merging on useIsWithinToolbar --- .../src/toolbar/use-is-within-toolbar.js | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/components/src/toolbar/use-is-within-toolbar.js b/packages/components/src/toolbar/use-is-within-toolbar.js index 6345068a0bb33e..a2400bdd9e4303 100644 --- a/packages/components/src/toolbar/use-is-within-toolbar.js +++ b/packages/components/src/toolbar/use-is-within-toolbar.js @@ -14,18 +14,14 @@ function useIsWithinToolbar( ref ) { const internalRef = useRef(); // Combine internal ref with ref argument - if ( ref ) { - ref = ( instance ) => { - if ( typeof ref === 'function' ) { - ref( instance ); - } else { - ref.current = instance; - } - internalRef.current = instance; - }; - } else { - ref = internalRef; - } + const finalRef = ref ? ( ( instance ) => { + if ( typeof ref === 'function' ) { + ref( instance ); + } else { + ref.current = instance; + } + internalRef.current = instance; + } ) : internalRef; useEffect( () => { // Context propagates through React Portal, @@ -37,7 +33,7 @@ function useIsWithinToolbar( ref ) { } }, [ isWithinToolbar ] ); - return { isWithinToolbar, ref }; + return { isWithinToolbar, ref: finalRef }; } export default useIsWithinToolbar; From 36050557ba2e35b4141403549c2c40cd9107bd3e Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 12 Nov 2019 13:14:51 +0100 Subject: [PATCH 74/75] Add missing JSDoc descriptions --- packages/components/src/toolbar-group/index.js | 2 +- packages/components/src/toolbar/index.js | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/components/src/toolbar-group/index.js b/packages/components/src/toolbar-group/index.js index 42887acd6fb490..1a0c4038a6caff 100644 --- a/packages/components/src/toolbar-group/index.js +++ b/packages/components/src/toolbar-group/index.js @@ -40,7 +40,7 @@ import ToolbarContext from '../toolbar-context'; * Either `controls` or `children` is required, otherwise this components * renders nothing. * - * @param {Object} props + * @param {Object} props Component props. * @param {Array} [props.controls] The controls to render in this toolbar. * @param {WPElement} [props.children] Any other things to render inside the toolbar besides the controls. * @param {string} [props.className] Class to set on the container div. diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 577cf69f6198c8..e86dd8a91029c0 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -23,10 +23,9 @@ import ToolbarContainer from './toolbar-container'; * * To add controls, simply pass `ToolbarButton` components as children. * - * @param {Object} props + * @param {Object} props Component props. * @param {string} props.accessibilityLabel Required label for assistive technology users. - * @param {string} [props.className] - * @param {Object} ref + * @param {string} [props.className] Class to set on the container div. */ const Toolbar = forwardRef( ( { className, accessibilityLabel, ...props }, ref ) => { if ( accessibilityLabel ) { From d422d7eabbb99c2b81bd8d8e140de0d9b8abbd4c Mon Sep 17 00:00:00 2001 From: Haz Date: Tue, 12 Nov 2019 14:19:37 +0100 Subject: [PATCH 75/75] Update stories --- packages/components/src/toolbar/stories/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/toolbar/stories/index.js b/packages/components/src/toolbar/stories/index.js index 6678d7a3d7242a..329f0f82433b70 100644 --- a/packages/components/src/toolbar/stories/index.js +++ b/packages/components/src/toolbar/stories/index.js @@ -9,7 +9,7 @@ import { ToolbarGroup, } from '../../'; -export default { title: 'Toolbar', component: Toolbar }; +export default { title: 'Components|Toolbar', component: Toolbar }; function InlineImageIcon() { return (