From afdd6df2f14f0ee3fe3bcbdf273ab374cadf33b7 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 1 Nov 2019 11:01:37 +0000 Subject: [PATCH] Add experimental `ResponsiveBlockControl` component (#16790) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Basic component skeleton * Adds component demo to Group Block * Make toggle control label dynamic and translatable * Adds styles * Automatically render responsive controls based on default control Continue to allow full customisation of the responsive controls, but by default render the responsive fields using the same markup as the default control. This avoids duplication when consuming the component. * Wrap each field group in a `fieldset` element * Invert toggle and language to be on by default. Addresses points raised in https://github.com/WordPress/gutenberg/pull/16790/#issuecomment-517814542 * Adds initial tests * Update tests to resemble how a user interacts with Component * Add toggle state test * Update to switch toggle to preceed controls Addresses concern raised in https://github.com/WordPress/gutenberg/pull/16790/#issuecomment-531777090 * Update individual control labels to fully describe control for a11y Address concerns raised in https://github.com/WordPress/gutenberg/pull/16790/#issuecomment-531777090 * Fixes form ui to improve alignment Addresses https://github.com/WordPress/gutenberg/pull/16790#issuecomment-535255247 * Improves i18n of generated control label Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r330595669. Note that aria-describedby is the correct type of aria role for this use case https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-describedby_attribute * Improve a11y by leaving DOM intact between state changes. * Adds aria description to toggle control for improved a11y context * Update tests * Update toggle of responsive controls internal to the component * Adds test for responsiveControlsActive setting prop * Add tests to cover custom labels and custom device sets * Fix to only build default repsonsive controls when necessary * Adds tests to cover rendering of custom responsive controls * Update to make label component part of component’s internal API Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r331602172. Also updates tests. * Adds callback prop to fire when responsive mode state changes Addresses https://github.com/jorgefilipecosta * Update to utilise withInstanceId HOC Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r331589219 * Removes unused export This component is now provided by internal API and doesn’t need exposing. Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r332718859 * Mark as “experimental” Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r332716937 * Remove non exposed component API doc * Extracts label to be a dedicated component * Updates to completely switch out DOM when responsive mode is toggled Please see https://github.com/WordPress/gutenberg/pull/16790#discussion_r331599556 * Update to useCallback to avoid expensive DOM re-renders Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r331586167 See also https://github.com/WordPress/gutenberg/pull/16791#discussion_r331624709 * Updates i18n to provide better descriptions for translators Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r331587069 * Updates devices list to contain unique non-translatable slug This addresses concern that we need to be able to identify a “size” by a value that does not change due to translations. See https://github.com/WordPress/gutenberg/pull/16790#discussion_r331597378 * Ensure renderDefault controls render prop has access to device details Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r331598820 * Begin documentation Incomplete. * Rename “legend” prop to “title” This prop was named based on implementation details. Switching out for more agnostic term. * Fix incorrect usage of _x and replace with translator comments Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r340622289 Noting that this type of comment seems to be undocumented in Gutenberg… * Update internal nomenclature from “device” to “viewport” This is a small terminology change which could have a big change on the way developers think about repsonsive settings. We shouldn’t be thinking about “devices” but rather “viewports” / screen sizes. We can still present to the user as “devices” but the developer should not be tying layout changes to specific devices (or conceptual groups of devices - eg: “Mobile”). * Refactor to make component fully controlled Addresses https://github.com/WordPress/gutenberg/pull/16790#discussion_r340589849 by making the state of the responsive mode controlled by the consuming component. This completes the process of making the component fully controlled. * Adds custom hook useResponsiveBlockControl Attempt to relieve some of the overhead associated with having ResponsiveBlockControl be a fully controlled component. By consuming this hook, a developer can wire up a default handler for toggling responsive mode without having to worry about creating their own useState-based hooks. * Revert "Adds custom hook useResponsiveBlockControl" This reverts commit 48529f70a4f3d17ce2b5d1be57e8d08780f680bc. * Remove testing implementation in Group Block * Docs update --- packages/block-editor/src/components/index.js | 1 + .../responsive-block-control/README.md | 237 ++++++++++++ .../responsive-block-control/index.js | 97 +++++ .../responsive-block-control/label.js | 21 ++ .../responsive-block-control/style.scss | 47 +++ .../test/__snapshots__/index.js.snap | 3 + .../responsive-block-control/test/index.js | 354 ++++++++++++++++++ packages/block-editor/src/style.scss | 1 + 8 files changed, 761 insertions(+) create mode 100644 packages/block-editor/src/components/responsive-block-control/README.md create mode 100644 packages/block-editor/src/components/responsive-block-control/index.js create mode 100644 packages/block-editor/src/components/responsive-block-control/label.js create mode 100644 packages/block-editor/src/components/responsive-block-control/style.scss create mode 100644 packages/block-editor/src/components/responsive-block-control/test/__snapshots__/index.js.snap create mode 100644 packages/block-editor/src/components/responsive-block-control/test/index.js diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 9b094396d9d8b..c7792608518ac 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -32,6 +32,7 @@ export { default as MediaUpload } from './media-upload'; export { default as MediaUploadCheck } from './media-upload/check'; export { default as PanelColorSettings } from './panel-color-settings'; export { default as PlainText } from './plain-text'; +export { default as __experimentalResponsiveBlockControl } from './responsive-block-control'; export { default as RichText, RichTextShortcut, diff --git a/packages/block-editor/src/components/responsive-block-control/README.md b/packages/block-editor/src/components/responsive-block-control/README.md new file mode 100644 index 0000000000000..deabc0bc4af4c --- /dev/null +++ b/packages/block-editor/src/components/responsive-block-control/README.md @@ -0,0 +1,237 @@ +ResponsiveBlockControl +============================= + +`ResponsiveBlockControl` provides a standardised interface for the creation of Block controls that require **different settings per viewport** (ie: "responsive" settings). + +For example, imagine your Block provides a control which affords the ability to change a "padding" value used in the Block display. Consider that whilst this setting may work well on "large" screens, the same value may not work well on smaller screens (it may be too large for example). As a result, you now need to provide a padding control _per viewport/screensize_. + +`ResponsiveBlockControl` provides a standardised component for the creation of such interfaces within Gutenberg. + +Complete control over rendering the controls is provided and the viewport sizes used are entirely customisable. + +Note that `ResponsiveBlockControl` does not handle any persistence of your control values. The control you provide to `ResponsiveBlockControl` as the `renderDefaultControl` prop should take care of this. + +## Usage + +In a block's `edit` implementation, render a `` component passing the required props plus: + +1. a `renderDefaultControl` function which renders an interface control. +2. an boolean state for `isResponsive` (see "Props" below). +3. a handler function for `onIsResponsiveChange` (see "Props" below). + + +By default the default control will be used to render the default (ie: "All") setting _as well as_ the per-viewport responsive settings. + +```jsx +import { registerBlockType } from '@wordpress/blocks'; +import { + InspectorControls, + ResponsiveBlockControl, +} from '@wordpress/block-editor'; + +import { useState } from '@wordpress/element'; + +import { + DimensionControl, +} from '@wordpress/components'; + +registerBlockType( 'my-plugin/my-block', { + // ... + + edit( { attributes, setAttributes } ) { + + const [ isResponsive, setIsResponsive ] = useState( false ); + + // Used for example purposes only + const sizeOptions = [ + { + label: 'Small', + value: 'small', + }, + { + label: 'Medium', + value: 'medium', + }, + { + label: 'Large', + value: 'large', + }, + ]; + + const { paddingSize } = attributes; + + + // Your custom control can be anything you'd like to use. + // You are not restricted to `DimensionControl`s, but this + // makes life easier if dealing with standard CSS values. + // see `packages/components/src/dimension-control/README.md` + const paddingControl = ( labelComponent, viewport ) => { + return ( + + ); + }; + + return ( + <> + + { + setIsResponsive( ! isResponsive ); + } } + /> + +
+ // your Block here +
+ + ); + } +} ); +``` + +## Props + +### `title` +* **Type:** `String` +* **Default:** `undefined` +* **Required:** `true` + +The title of the control group used in the `fieldset`'s `legend` element to label the _entire_ set of controls. + +### `property` +* **Type:** `String` +* **Default:** `undefined` +* **Required:** `true` + +Used to build accessible labels and ARIA roles for the control group. Should represent the layout property which the component controls (eg: `padding`, `margin`...etc). + +### `isResponsive` +* **Type:** `Boolean` +* **Default:** `false` ) +* **Required:** `false` + +Determines whether the component displays the default or responsive controls. Updates the state of the toggle control. See also `onIsResponsiveChange` below. + +### `onIsResponsiveChange` +* **Type:** `Function` +* **Default:** `undefined` +* **Required:** `true` + +A callback function invoked when the component's toggle value is changed between responsive and non-responsive mode. Should be used to update the value of the `isResponsive` prop to reflect the current state of the toggle control. + +### `renderDefaultControl` +* **Type:** `Function` +* **Default:** `undefined` +* **Required:** `true` +* **Args:** + - **labelComponent:** (`Function`) - a rendered `ResponsiveBlockControlLabel` component for your control. + - **viewport:** (`Object`) - an object representing viewport attributes for your control. + +A render function (prop) used to render the control for which you would like to display per viewport settings. + +For example, if you have a `SelectControl` which controls padding size, then pass this component as `renderDefaultControl` and it will be used to render both default and "responsive" controls for "padding". + +The component you return from this function will be used to render the control displayed for the (default) "All" state and (if the `renderResponsiveControls` is not provided) the individual responsive controls when in "responsive" mode. + +It is passed a pre-created, accessible `