diff --git a/lib/patterns/two-buttons.php b/lib/patterns/two-buttons.php index 90564c4c30487d..8cd24997ce20ed 100644 --- a/lib/patterns/two-buttons.php +++ b/lib/patterns/two-buttons.php @@ -7,7 +7,7 @@ return array( 'title' => __( 'Two buttons', 'gutenberg' ), - 'content' => "\n
\n
" . __( 'Download now', 'gutenberg' ) . "
\n\n\n\n
" . __( 'About Cervantes', 'gutenberg' ) . "
\n
\n", + 'content' => "\n
\n
" . __( 'Download now', 'gutenberg' ) . "
\n\n\n\n
" . __( 'About Cervantes', 'gutenberg' ) . "
\n
\n", 'viewportWidth' => 500, 'categories' => array( 'buttons' ), 'description' => _x( 'Two buttons, one filled and one outlined, side by side.', 'Block pattern description', 'gutenberg' ), diff --git a/packages/block-library/src/buttons/block.json b/packages/block-library/src/buttons/block.json index a1e4c33c2ed3eb..b534316ce8f970 100644 --- a/packages/block-library/src/buttons/block.json +++ b/packages/block-library/src/buttons/block.json @@ -2,9 +2,13 @@ "apiVersion": 2, "name": "core/buttons", "category": "design", + "attributes": { + "contentJustification": { + "type": "string" + } + }, "supports": { "anchor": true, - "align": true, - "alignWide": false + "align": [ "wide", "full" ] } } diff --git a/packages/block-library/src/buttons/content-justification-dropdown.js b/packages/block-library/src/buttons/content-justification-dropdown.js new file mode 100644 index 00000000000000..10c02c5524bfa6 --- /dev/null +++ b/packages/block-library/src/buttons/content-justification-dropdown.js @@ -0,0 +1,73 @@ +/** + * WordPress dependencies + */ +import { DropdownMenu } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { + contentJustificationCenterIcon, + contentJustificationLeftIcon, + contentJustificationRightIcon, +} from './icons'; + +const DEFAULT_ALLOWED_VALUES = [ 'left', 'center', 'right' ]; + +const CONTROLS = { + left: { + icon: contentJustificationLeftIcon, + title: __( 'Justify content left' ), + }, + center: { + icon: contentJustificationCenterIcon, + title: __( 'Justify content center' ), + }, + right: { + icon: contentJustificationRightIcon, + title: __( 'Justify content right' ), + }, +}; + +const DEFAULT_ICON = CONTROLS.center.icon; + +/** + * Dropdown for selecting a content justification option. + * + * @param {Object} props Component props. + * @param {string[]} [props.allowedValues] List of options to include. Default: + * ['left', 'center', 'right']. + * @param {()=>void} props.onChange Callback to run when an option is + * selected in the dropdown. + * @param {Object} props.toggleProps Props to pass to the dropdown toggle. + * @param {string} props.value The current content justification + * value. + * + * @return {WPComponent} The component. + */ +export default function ContentJustificationDropdown( { + onChange, + allowedValues = DEFAULT_ALLOWED_VALUES, + toggleProps, + value, +} ) { + return ( + { + return { + ...CONTROLS[ allowedValue ], + isActive: value === allowedValue, + role: 'menuitemradio', + onClick: () => + onChange( + value === allowedValue ? undefined : allowedValue + ), + }; + } ) } + toggleProps={ toggleProps } + /> + ); +} diff --git a/packages/block-library/src/buttons/deprecated.js b/packages/block-library/src/buttons/deprecated.js new file mode 100644 index 00000000000000..47d049578ea58a --- /dev/null +++ b/packages/block-library/src/buttons/deprecated.js @@ -0,0 +1,38 @@ +/** + * WordPress dependencies + */ +import { InnerBlocks } from '@wordpress/block-editor'; + +const deprecated = [ + { + supports: { + align: [ 'center', 'left', 'right' ], + anchor: true, + }, + save() { + return ( +
+ +
+ ); + }, + isEligible( { align } ) { + return align && [ 'center', 'left', 'right' ].includes( align ); + }, + migrate( attributes ) { + return { + ...attributes, + align: undefined, + // Floating Buttons blocks shouldn't have been supported in the + // first place. Most users using them probably expected them to + // act like content justification controls, so these blocks are + // migrated to use content justification. + // As for center-aligned Buttons blocks, the content justification + // equivalent will create an identical end result in most cases. + contentJustification: attributes.align, + }; + }, + }, +]; + +export default deprecated; diff --git a/packages/block-library/src/buttons/edit.js b/packages/block-library/src/buttons/edit.js index 5e1abc01582a0e..a77f1bf335fd5c 100644 --- a/packages/block-library/src/buttons/edit.js +++ b/packages/block-library/src/buttons/edit.js @@ -1,21 +1,36 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ import { - __experimentalUseInnerBlocksProps as useInnerBlocksProps, + BlockControls, useBlockProps, + __experimentalUseInnerBlocksProps as useInnerBlocksProps, } from '@wordpress/block-editor'; +import { ToolbarGroup, ToolbarItem } from '@wordpress/components'; /** * Internal dependencies */ -import { name as buttonBlockName } from '../button/'; +import { name as buttonBlockName } from '../button'; +import ContentJustificationDropdown from './content-justification-dropdown'; const ALLOWED_BLOCKS = [ buttonBlockName ]; const BUTTONS_TEMPLATE = [ [ 'core/button' ] ]; -function ButtonsEdit() { - const blockProps = useBlockProps(); +function ButtonsEdit( { + attributes: { contentJustification }, + setAttributes, +} ) { + const blockProps = useBlockProps( { + className: classnames( { + [ `is-content-justification-${ contentJustification }` ]: contentJustification, + } ), + } ); const innerBlocksProps = useInnerBlocksProps( blockProps, { allowedBlocks: ALLOWED_BLOCKS, template: BUTTONS_TEMPLATE, @@ -25,7 +40,28 @@ function ButtonsEdit() { alignments: [], }, } ); - return
; + return ( + <> + + + + { ( toggleProps ) => ( + { + setAttributes( { + contentJustification: updatedValue, + } ); + } } + /> + ) } + + + +
+ + ); } export default ButtonsEdit; diff --git a/packages/block-library/src/buttons/edit.native.js b/packages/block-library/src/buttons/edit.native.js index 7c35a762935c98..b190907b7b1eeb 100644 --- a/packages/block-library/src/buttons/edit.native.js +++ b/packages/block-library/src/buttons/edit.native.js @@ -7,25 +7,28 @@ import { View } from 'react-native'; /** * WordPress dependencies */ -import { InnerBlocks } from '@wordpress/block-editor'; +import { BlockControls, InnerBlocks } from '@wordpress/block-editor'; import { createBlock } from '@wordpress/blocks'; import { useResizeObserver } from '@wordpress/compose'; import { useDispatch, useSelect } from '@wordpress/data'; import { useState, useEffect, useRef } from '@wordpress/element'; +import { ToolbarGroup, ToolbarItem } from '@wordpress/components'; /** * Internal dependencies */ import { name as buttonBlockName } from '../button/'; import styles from './editor.scss'; +import ContentJustificationDropdown from './content-justification-dropdown'; const ALLOWED_BLOCKS = [ buttonBlockName ]; const BUTTONS_TEMPLATE = [ [ 'core/button' ] ]; export default function ButtonsEdit( { - attributes: { align }, + attributes: { contentJustification }, clientId, isSelected, + setAttributes, } ) { const [ resizeObserver, sizes ] = useResizeObserver(); const [ maxWidth, setMaxWidth ] = useState( 0 ); @@ -85,6 +88,12 @@ export default function ButtonsEdit( { selectBlock( insertedBlock.clientId ); }, 200 ); + function onChangeContentJustification( updatedValue ) { + setAttributes( { + contentJustification: updatedValue, + } ); + } + const renderFooterAppender = useRef( () => ( + + + + { ( toggleProps ) => ( + + ) } + + + { resizeObserver } removeBlock( clientId ) : undefined } diff --git a/packages/block-library/src/buttons/editor.scss b/packages/block-library/src/buttons/editor.scss index 121ba5f5af5cf2..d10bedb1318ec3 100644 --- a/packages/block-library/src/buttons/editor.scss +++ b/packages/block-library/src/buttons/editor.scss @@ -16,5 +16,6 @@ } .wp-block-buttons > .block-list-appender { - display: inline-block; + display: inline-flex; + align-items: center; } diff --git a/packages/block-library/src/buttons/icons.js b/packages/block-library/src/buttons/icons.js new file mode 100644 index 00000000000000..38378b3afaa325 --- /dev/null +++ b/packages/block-library/src/buttons/icons.js @@ -0,0 +1,37 @@ +/** + * WordPress dependencies + */ +import { Path, SVG } from '@wordpress/components'; + +export const contentJustificationLeftIcon = ( + + + +); + +export const contentJustificationCenterIcon = ( + + + +); + +export const contentJustificationRightIcon = ( + + + +); diff --git a/packages/block-library/src/buttons/index.js b/packages/block-library/src/buttons/index.js index a9070759325a20..81653d1e12d686 100644 --- a/packages/block-library/src/buttons/index.js +++ b/packages/block-library/src/buttons/index.js @@ -7,6 +7,7 @@ import { button as icon } from '@wordpress/icons'; /** * Internal dependencies */ +import deprecated from './deprecated'; import transforms from './transforms'; import edit from './edit'; import metadata from './block.json'; @@ -35,6 +36,7 @@ export const settings = { }, ], }, + deprecated, transforms, edit, save, diff --git a/packages/block-library/src/buttons/save.js b/packages/block-library/src/buttons/save.js index 000acdcd4a6055..5c64fc110e0d57 100644 --- a/packages/block-library/src/buttons/save.js +++ b/packages/block-library/src/buttons/save.js @@ -1,11 +1,22 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; -export default function save() { +export default function save( { attributes: { contentJustification } } ) { return ( -
+
); diff --git a/packages/block-library/src/buttons/style.scss b/packages/block-library/src/buttons/style.scss index 45a0d6aed21b1b..324e3e3c5adddf 100644 --- a/packages/block-library/src/buttons/style.scss +++ b/packages/block-library/src/buttons/style.scss @@ -1,37 +1,69 @@ -// Increased specificity to override blocks default margin. -.wp-block-buttons .wp-block-button { - display: inline-block; - margin-right: 0.5em; - margin-bottom: 0.5em; +.wp-block-buttons { + display: flex; + flex-direction: row; + flex-wrap: wrap; - &:last-child { - margin-right: 0; + // Increased specificity to override blocks default margin. + > .wp-block-button { + display: inline-block; + /*rtl:ignore*/ + margin-right: 0.5em; + margin-bottom: 0.5em; + + &:last-child { + /*rtl:ignore*/ + margin-right: 0; + } } -} -.wp-block-buttons.alignright .wp-block-button { - /*rtl:ignore*/ - margin-right: 0; - /*rtl:ignore*/ - margin-left: 0.5em; + &.is-content-justification-left { + justify-content: flex-start; + } - &:first-child { - margin-left: 0; + &.is-content-justification-center { + justify-content: center; } -} -.wp-block-buttons.alignleft .wp-block-button { - /*rtl:ignore*/ - margin-left: 0; - /*rtl:ignore*/ - margin-right: 0.5em; + &.is-content-justification-right { + justify-content: flex-end; - &:last-child { - margin-right: 0; + > .wp-block-button { + /*rtl:ignore*/ + margin-left: 0.5em; + /*rtl:ignore*/ + margin-right: 0; + + &:first-child { + /*rtl:ignore*/ + margin-left: 0; + } + } } -} -.wp-block-button.aligncenter, // This is to support the legacy Button block. -.wp-block-buttons.aligncenter { - text-align: center; + // Kept for backward compatibiity. + &.aligncenter { + text-align: center; + } + &.alignleft .wp-block-button { + /*rtl:ignore*/ + margin-left: 0; + /*rtl:ignore*/ + margin-right: 0.5em; + + &:last-child { + /*rtl:ignore*/ + margin-right: 0; + } + } + &.alignright .wp-block-button { + /*rtl:ignore*/ + margin-right: 0; + /*rtl:ignore*/ + margin-left: 0.5em; + + &:first-child { + /*rtl:ignore*/ + margin-left: 0; + } + } } diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.html b/packages/e2e-tests/fixtures/blocks/core__buttons.html index e70af7acc72ad4..c6cc0e3bf76c29 100644 --- a/packages/e2e-tests/fixtures/blocks/core__buttons.html +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.html @@ -1,5 +1,5 @@ - -
+ +
diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.json b/packages/e2e-tests/fixtures/blocks/core__buttons.json index 044daeb82101ac..29df40b187fd34 100644 --- a/packages/e2e-tests/fixtures/blocks/core__buttons.json +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.json @@ -3,7 +3,10 @@ "clientId": "_clientId_0", "name": "core/buttons", "isValid": true, - "attributes": {}, + "attributes": { + "contentJustification": "center", + "align": "wide" + }, "innerBlocks": [ { "clientId": "_clientId_0", @@ -26,6 +29,6 @@ "originalContent": "" } ], - "originalContent": "
\n\t\n\n\t\n
" + "originalContent": "
\n\t\n\n\t\n
" } ] diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.parsed.json b/packages/e2e-tests/fixtures/blocks/core__buttons.parsed.json index b96b1f50db1fc9..06f7ac796e189a 100644 --- a/packages/e2e-tests/fixtures/blocks/core__buttons.parsed.json +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.parsed.json @@ -1,7 +1,10 @@ [ { "blockName": "core/buttons", - "attrs": {}, + "attrs": { + "align": "wide", + "contentJustification": "center" + }, "innerBlocks": [ { "blockName": "core/button", @@ -22,9 +25,9 @@ ] } ], - "innerHTML": "\n
\n\t\n\n\t\n
\n", + "innerHTML": "\n
\n\t\n\n\t\n
\n", "innerContent": [ - "\n
\n\t", + "\n
\n\t", null, "\n\n\t", null, diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.serialized.html b/packages/e2e-tests/fixtures/blocks/core__buttons.serialized.html index baf0a0226c066c..34141befc4da2c 100644 --- a/packages/e2e-tests/fixtures/blocks/core__buttons.serialized.html +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.serialized.html @@ -1,5 +1,5 @@ - -
+ +
diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.html b/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.html new file mode 100644 index 00000000000000..29411d54215873 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.html @@ -0,0 +1,11 @@ + +
+ + + + + + + +
+ diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.json b/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.json new file mode 100644 index 00000000000000..12acc3c2acca8f --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.json @@ -0,0 +1,33 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/buttons", + "isValid": true, + "attributes": { + "contentJustification": "center" + }, + "innerBlocks": [ + { + "clientId": "_clientId_0", + "name": "core/button", + "isValid": true, + "attributes": { + "text": "My button 1" + }, + "innerBlocks": [], + "originalContent": "" + }, + { + "clientId": "_clientId_1", + "name": "core/button", + "isValid": true, + "attributes": { + "text": "My button 2" + }, + "innerBlocks": [], + "originalContent": "" + } + ], + "originalContent": "
\n\t\n\n\t\n
" + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.parsed.json b/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.parsed.json new file mode 100644 index 00000000000000..6e9acab3132426 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.parsed.json @@ -0,0 +1,45 @@ +[ + { + "blockName": "core/buttons", + "attrs": { + "align": "center" + }, + "innerBlocks": [ + { + "blockName": "core/button", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\t\n\t", + "innerContent": [ + "\n\t\n\t" + ] + }, + { + "blockName": "core/button", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\t\n\t", + "innerContent": [ + "\n\t\n\t" + ] + } + ], + "innerHTML": "\n
\n\t\n\n\t\n
\n", + "innerContent": [ + "\n
\n\t", + null, + "\n\n\t", + null, + "\n
\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n", + "innerContent": [ + "\n" + ] + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.serialized.html b/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.serialized.html new file mode 100644 index 00000000000000..b311167cbfed0e --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__buttons__deprecated-1.serialized.html @@ -0,0 +1,9 @@ + + + diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/adding-patterns.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/adding-patterns.test.js.snap index bf3dee614c9c76..d3894f3030d376 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/adding-patterns.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/adding-patterns.test.js.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`adding patterns should insert a block pattern 1`] = ` -" -