diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 00000000000000..e437ec744d3807 --- /dev/null +++ b/assets/README.md @@ -0,0 +1,7 @@ +## Gutenberg Plugin Assets + +The contents of this directory are synced from the [`assets/` directory in the Gutenberg repository on GitHub](https://github.com/WordPress/gutenberg/tree/trunk/assets) to the [`assets/` directory of the Gutenberg WordPress.org plugin repository](https://plugins.trac.wordpress.org/browser/gutenberg/assets). **Any changes committed directly to the plugin repository on WordPress.org will be overwritten.** + +The sync is performed by a [GitHub Actions workflow](https://github.com/WordPress/gutenberg/actions/workflows/sync-assets-to-plugin-repo.yml) that is triggered whenever a file in this directory is changed. + +Since that workflow requires access to WP.org plugin repository credentials, it needs to be approved manually by a member of the Gutenberg Core team. If you don't have the necessary permissions, please ask someone in [#core-editor](https://wordpress.slack.com/archives/C02QB2JS7). diff --git a/backport-changelog/6.8/8015.md b/backport-changelog/6.8/8015.md new file mode 100644 index 00000000000000..214705518a0e72 --- /dev/null +++ b/backport-changelog/6.8/8015.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/8015 + +* https://github.com/WordPress/gutenberg/pull/68058 diff --git a/docs/how-to-guides/themes/global-settings-and-styles.md b/docs/how-to-guides/themes/global-settings-and-styles.md index f71bd67bfaf2ec..205a3ee862ce6b 100644 --- a/docs/how-to-guides/themes/global-settings-and-styles.md +++ b/docs/how-to-guides/themes/global-settings-and-styles.md @@ -1053,16 +1053,16 @@ Pseudo selectors `:hover`, `:focus`, `:visited`, `:active`, `:link`, `:any-link` #### Variations -A block can have a "style variation", as defined per the [block.json specification](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/#styles-optional). Theme authors can define the style attributes for an existing style variation using the theme.json file. Styles for unregistered style variations will be ignored. +A block can have a "style variation," as defined in the [block.json specification](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/#styles-optional). Theme authors can define the style attributes for an existing style variation using the `theme.json` file. Styles for unregistered style variations will be ignored. -Note that variations are a "block concept", they only exist bound to blocks. The `theme.json` specification respects that distinction by only allowing `variations` at the block-level but not at the top-level. It's also worth highlighting that only variations defined in the `block.json` file of the block are considered "registered": so far, the style variations added via `register_block_style` or in the client are ignored, see [this issue](https://github.com/WordPress/gutenberg/issues/49602) for more information. +Note that variations are a "block concept"—they only exist when bound to blocks. The `theme.json` specification respects this distinction by only allowing `variations` at the block level, not the top level. It’s also worth highlighting that only variations defined in the `block.json` file of the block or via `register_block_style` on the server are considered "registered" for `theme.json` styling purposes. For example, this is how to provide styles for the existing `plain` variation for the `core/quote` block: ```json { "version": 3, - "styles":{ + "styles": { "blocks": { "core/quote": { "variations": { @@ -1078,7 +1078,7 @@ For example, this is how to provide styles for the existing `plain` variation fo } ``` -The resulting CSS output is this: +The resulting CSS output is: ```css .wp-block-quote.is-style-plain { @@ -1086,6 +1086,99 @@ The resulting CSS output is this: } ``` +It is also possible for multiple block types to share the same variation styles. There are two recommended ways to define such shared styles: + +1. `theme.json` partial files +2. programmatically, using `register_block_style` + +##### Variation Theme.json Partials + +Like theme style variation partials, those for block style variations reside within a theme's `/styles` directory. However, they are differentiated from theme style variations by the introduction of a top-level property called `blockTypes`. The `blockTypes` property is an array of block types for which the block style variation has been registered. + +Additionally, a `slug` property is available to provide consistency between the different sources that may define block style variations and to decouple the `slug` from the translatable `title` property. + +The following is an example of a `theme.json` partial that defines styles for the "Variation A" block style for the Group, Columns, and Media & Text block types: + +```json +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 3, + "title": "Variation A", + "slug": "variation-a", + "blockTypes": [ "core/group", "core/columns", "core/media-text" ], + "styles": { + "color": { + "background": "#eed8d3", + "text": "#201819" + }, + "elements": { + "heading": { + "color": { + "text": "#201819" + } + } + }, + "blocks": { + "core/group": { + "color": { + "background": "#825f58", + "text": "#eed8d3" + }, + "elements": { + "heading": { + "color": { + "text": "#eed8d3" + } + } + } + } + } + } +} +``` + +##### Programmatically Registering Variation Styles + +As an alternative to `theme.json` partials, you can register variation styles at the same time as registering the variation itself through `register_block_style`. This is done by registering the block style for an array of block types while also passing a "style object" within the `style_data` option. + +The example below registers a "Green" variation for the Group and Columns blocks. Note that the style object passed via `style_data` follows the same shape as the `styles` property of a `theme.json` partial. + +```php +register_block_style( + array( 'core/group', 'core/columns' ), + array( + 'name' => 'green', + 'label' => __( 'Green' ), + 'style_data' => array( + 'color' => array( + 'background' => '#4f6f52', + 'text' => '#d2e3c8', + ), + 'blocks' => array( + 'core/group' => array( + 'color' => array( + 'background' => '#739072', + 'text' => '#e3eedd', + ), + ), + ), + 'elements' => array( + 'link' => array( + 'color' => array( + 'text' => '#ead196', + ), + ':hover' => array( + 'color' => array( + 'text' => '#ebd9b4', + ), + ), + ), + ), + ), + ) +); +``` + ### customTemplates
Supported in WordPress from version 5.9.
diff --git a/lib/compat/wordpress-6.8/blocks.php b/lib/compat/wordpress-6.8/blocks.php index 8e176e58c8d7f5..dc8747c04aec2f 100644 --- a/lib/compat/wordpress-6.8/blocks.php +++ b/lib/compat/wordpress-6.8/blocks.php @@ -170,7 +170,7 @@ function gutenberg_apply_block_hooks_to_post_content( $content ) { * @return WP_REST_Response The response object. */ function gutenberg_insert_hooked_blocks_into_rest_response( $response, $post ) { - if ( empty( $response->data['content']['raw'] ) || empty( $response->data['content']['rendered'] ) ) { + if ( empty( $response->data['content']['raw'] ) ) { return $response; } @@ -185,6 +185,8 @@ function gutenberg_insert_hooked_blocks_into_rest_response( $response, $post ) { if ( 'wp_navigation' === $post->post_type ) { $wrapper_block_type = 'core/navigation'; + } elseif ( 'wp_block' === $post->post_type ) { + $wrapper_block_type = 'core/block'; } else { $wrapper_block_type = 'core/post-content'; } @@ -206,6 +208,11 @@ function gutenberg_insert_hooked_blocks_into_rest_response( $response, $post ) { $response->data['content']['raw'] = $content; + // If the rendered content was previously empty, we leave it like that. + if ( empty( $response->data['content']['rendered'] ) ) { + return $response; + } + // No need to inject hooked blocks twice. $priority = has_filter( 'the_content', 'apply_block_hooks_to_content' ); if ( false !== $priority ) { @@ -224,6 +231,7 @@ function gutenberg_insert_hooked_blocks_into_rest_response( $response, $post ) { } add_filter( 'rest_prepare_page', 'gutenberg_insert_hooked_blocks_into_rest_response', 10, 2 ); add_filter( 'rest_prepare_post', 'gutenberg_insert_hooked_blocks_into_rest_response', 10, 2 ); +add_filter( 'rest_prepare_wp_block', 'gutenberg_insert_hooked_blocks_into_rest_response', 10, 2 ); /** * Updates the wp_postmeta with the list of ignored hooked blocks @@ -272,6 +280,8 @@ function gutenberg_update_ignored_hooked_blocks_postmeta( $post ) { if ( 'wp_navigation' === $post->post_type ) { $wrapper_block_type = 'core/navigation'; + } elseif ( 'wp_block' === $post->post_type ) { + $wrapper_block_type = 'core/block'; } else { $wrapper_block_type = 'core/post-content'; } @@ -311,3 +321,4 @@ function gutenberg_update_ignored_hooked_blocks_postmeta( $post ) { } add_filter( 'rest_pre_insert_page', 'gutenberg_update_ignored_hooked_blocks_postmeta' ); add_filter( 'rest_pre_insert_post', 'gutenberg_update_ignored_hooked_blocks_postmeta' ); +add_filter( 'rest_pre_insert_wp_block', 'gutenberg_update_ignored_hooked_blocks_postmeta' ); diff --git a/package-lock.json b/package-lock.json index edfa7bfa80024f..bd901baca24a96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51237,6 +51237,7 @@ "@wordpress/icons": "*", "@wordpress/keyboard-shortcuts": "*", "@wordpress/keycodes": "*", + "@wordpress/media-utils": "5.14.0", "@wordpress/notices": "*", "@wordpress/patterns": "*", "@wordpress/plugins": "*", diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 13dffce114f59a..8fe2c5f1179dcd 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -713,10 +713,50 @@ Undocumented declaration. ### PlainText +Render an auto-growing textarea allow users to fill any textual content. + _Related_ - +_Usage_ + +```jsx +import { registerBlockType } from '@wordpress/blocks'; +import { PlainText } from '@wordpress/block-editor'; + +registerBlockType( 'my-plugin/example-block', { + // ... + + attributes: { + content: { + type: 'string', + }, + }, + + edit( { className, attributes, setAttributes } ) { + return ( + setAttributes( { content } ) } + /> + ); + }, +} ); +``` + +_Parameters_ + +- _props_ `Object`: Component props. +- _props.value_ `string`: String value of the textarea. +- _props.onChange_ `Function`: Function called when the text value changes. +- _props.ref_ `[Object]`: The component forwards the `ref` property to the `TextareaAutosize` component. + +_Returns_ + +- `Element`: Plain text component + ### privateApis Private @wordpress/block-editor APIs. diff --git a/packages/block-editor/src/components/plain-text/README.md b/packages/block-editor/src/components/plain-text/README.md index aa15758118afdc..1e0a7888ed1e4d 100644 --- a/packages/block-editor/src/components/plain-text/README.md +++ b/packages/block-editor/src/components/plain-text/README.md @@ -6,11 +6,11 @@ Render an auto-growing textarea allow users to fill any textual content. ### `value: string` -_Required._ String value of the textarea +_Required._ String value of the textarea. ### `onChange( value: string ): Function` -_Required._ Called when the value changes. +_Required._ Function called when the text value changes. You can also pass any extra prop to the textarea rendered by this component. diff --git a/packages/block-editor/src/components/plain-text/index.js b/packages/block-editor/src/components/plain-text/index.js index 4bd6681f4eb079..d28aabebf7a140 100644 --- a/packages/block-editor/src/components/plain-text/index.js +++ b/packages/block-editor/src/components/plain-text/index.js @@ -15,7 +15,41 @@ import { forwardRef } from '@wordpress/element'; import EditableText from '../editable-text'; /** + * Render an auto-growing textarea allow users to fill any textual content. + * * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/plain-text/README.md + * + * @example + * ```jsx + * import { registerBlockType } from '@wordpress/blocks'; + * import { PlainText } from '@wordpress/block-editor'; + * + * registerBlockType( 'my-plugin/example-block', { + * // ... + * + * attributes: { + * content: { + * type: 'string', + * }, + * }, + * + * edit( { className, attributes, setAttributes } ) { + * return ( + * <PlainText + * className={ className } + * value={ attributes.content } + * onChange={ ( content ) => setAttributes( { content } ) } + * /> + * ); + * }, + * } ); + * ```` + * + * @param {Object} props Component props. + * @param {string} props.value String value of the textarea. + * @param {Function} props.onChange Function called when the text value changes. + * @param {Object} [props.ref] The component forwards the `ref` property to the `TextareaAutosize` component. + * @return {Element} Plain text component */ const PlainText = forwardRef( ( { __experimentalVersion, ...props }, ref ) => { if ( __experimentalVersion === 2 ) { diff --git a/packages/block-editor/src/components/plain-text/stories/index.story.js b/packages/block-editor/src/components/plain-text/stories/index.story.js new file mode 100644 index 00000000000000..d1a6253c0870a7 --- /dev/null +++ b/packages/block-editor/src/components/plain-text/stories/index.story.js @@ -0,0 +1,75 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import PlainText from '..'; + +const meta = { + title: 'BlockEditor/PlainText', + component: PlainText, + parameters: { + docs: { + canvas: { sourceState: 'shown' }, + description: { + component: + 'PlainText renders an auto-growing textarea that allows users to enter any textual content.', + }, + }, + }, + argTypes: { + value: { + control: { + type: null, + }, + table: { + type: { + summary: 'string', + }, + }, + description: 'String value of the textarea.', + }, + onChange: { + action: 'onChange', + control: { + type: null, + }, + table: { + type: { + summary: 'function', + }, + }, + description: 'Function called when the text value changes.', + }, + className: { + control: 'text', + table: { + type: { + summary: 'string', + }, + }, + description: 'Additional class name for the PlainText.', + }, + }, +}; + +export default meta; + +export const Default = { + render: function Template( { onChange, ...args } ) { + const [ value, setValue ] = useState(); + return ( + <PlainText + { ...args } + onChange={ ( ...changeArgs ) => { + onChange( ...changeArgs ); + setValue( ...changeArgs ); + } } + value={ value } + /> + ); + }, +}; diff --git a/packages/block-editor/src/components/text-decoration-control/README.md b/packages/block-editor/src/components/text-decoration-control/README.md index a606140baa330e..87fb6e89bd5712 100644 --- a/packages/block-editor/src/components/text-decoration-control/README.md +++ b/packages/block-editor/src/components/text-decoration-control/README.md @@ -28,7 +28,6 @@ Then, you can use the component in your block editor UI: ### `value` - **Type:** `String` -- **Default:** `none` - **Options:** `none`, `underline`, `line-through` The current value of the Text Decoration setting. You may only choose from the `Options` listed above. diff --git a/packages/block-editor/src/components/text-decoration-control/stories/index.story.js b/packages/block-editor/src/components/text-decoration-control/stories/index.story.js index 2212b484185cde..d139b30a2bb4b5 100644 --- a/packages/block-editor/src/components/text-decoration-control/stories/index.story.js +++ b/packages/block-editor/src/components/text-decoration-control/stories/index.story.js @@ -8,26 +8,61 @@ import { useState } from '@wordpress/element'; */ import TextDecorationControl from '../'; -export default { +const meta = { title: 'BlockEditor/TextDecorationControl', component: TextDecorationControl, + parameters: { + docs: { + canvas: { sourceState: 'shown' }, + description: { + component: 'Control to facilitate text decoration selections.', + }, + }, + }, argTypes: { - onChange: { action: 'onChange' }, + value: { + control: { type: null }, + description: 'Currently selected text decoration.', + table: { + type: { + summary: 'string', + }, + }, + }, + onChange: { + action: 'onChange', + control: { type: null }, + description: 'Handles change in text decoration selection.', + table: { + type: { + summary: 'function', + }, + }, + }, + className: { + control: 'text', + description: 'Additional class name to apply.', + table: { + type: { summary: 'string' }, + }, + }, }, }; -const Template = ( { onChange, ...args } ) => { - const [ value, setValue ] = useState(); - return ( - <TextDecorationControl - { ...args } - onChange={ ( ...changeArgs ) => { - onChange( ...changeArgs ); - setValue( ...changeArgs ); - } } - value={ value } - /> - ); -}; +export default meta; -export const Default = Template.bind( {} ); +export const Default = { + render: function Template( { onChange, ...args } ) { + const [ value, setValue ] = useState(); + return ( + <TextDecorationControl + { ...args } + onChange={ ( ...changeArgs ) => { + onChange( ...changeArgs ); + setValue( ...changeArgs ); + } } + value={ value } + /> + ); + }, +}; diff --git a/packages/block-editor/src/components/text-transform-control/README.md b/packages/block-editor/src/components/text-transform-control/README.md index 2d40cc16ba86f8..3ed8f1da8cd6e9 100644 --- a/packages/block-editor/src/components/text-transform-control/README.md +++ b/packages/block-editor/src/components/text-transform-control/README.md @@ -1,7 +1,7 @@ # TextTransformControl The `TextTransformControl` component is responsible for rendering a control element that allows users to select and apply text transformation options to blocks or elements in the Gutenberg editor. It provides an intuitive interface for changing the text appearance by applying different transformations such as `none`, `uppercase`, `lowercase`, `capitalize`. - + ![TextTransformConrol Element in Inspector Control](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/assets/text-transform-component.png?raw=true) ## Development guidelines @@ -28,7 +28,6 @@ const MyTextTransformControlComponent = () => ( ### `value` - **Type:** `String` -- **Default:** `none` - **Options:** `none`, `uppercase`, `lowercase`, `capitalize` The current value of the Text Transform setting. You may only choose from the `Options` listed above. @@ -37,4 +36,4 @@ The current value of the Text Transform setting. You may only choose from the `O - **Type:** `Function` -A callback function invoked when the Text Transform value is changed via an interaction with any of the buttons. Called with the Text Transform value (`none`, `uppercase`, `lowercase`, `capitalize`) as the only argument. \ No newline at end of file +A callback function invoked when the Text Transform value is changed via an interaction with any of the buttons. Called with the Text Transform value (`none`, `uppercase`, `lowercase`, `capitalize`) as the only argument. diff --git a/packages/block-editor/src/components/text-transform-control/stories/index.story.js b/packages/block-editor/src/components/text-transform-control/stories/index.story.js index 96dd8ed479dc4e..77dc550368da19 100644 --- a/packages/block-editor/src/components/text-transform-control/stories/index.story.js +++ b/packages/block-editor/src/components/text-transform-control/stories/index.story.js @@ -8,26 +8,63 @@ import { useState } from '@wordpress/element'; */ import TextTransformControl from '../'; -export default { +const meta = { title: 'BlockEditor/TextTransformControl', component: TextTransformControl, + parameters: { + docs: { + canvas: { sourceState: 'shown' }, + description: { + component: + 'Control to facilitate text transformation selections.', + }, + }, + }, argTypes: { - onChange: { action: 'onChange' }, + onChange: { + action: 'onChange', + control: { + type: null, + }, + description: 'Handles change in text transform selection.', + table: { + type: { + summary: 'function', + }, + }, + }, + className: { + control: { type: 'text' }, + description: 'Class name to add to the control.', + table: { + type: { summary: 'string' }, + }, + }, + value: { + control: { type: null }, + description: 'Currently selected text transform.', + table: { + type: { summary: 'string' }, + }, + }, }, }; -const Template = ( { onChange, ...args } ) => { - const [ value, setValue ] = useState(); - return ( - <TextTransformControl - { ...args } - onChange={ ( ...changeArgs ) => { - onChange( ...changeArgs ); - setValue( ...changeArgs ); - } } - value={ value } - /> - ); -}; +export default meta; + +export const Default = { + render: function Template( { onChange, ...args } ) { + const [ value, setValue ] = useState(); -export const Default = Template.bind( {} ); + return ( + <TextTransformControl + { ...args } + onChange={ ( ...changeArgs ) => { + onChange( ...changeArgs ); + setValue( ...changeArgs ); + } } + value={ value } + /> + ); + }, +}; diff --git a/packages/block-library/src/block/index.php b/packages/block-library/src/block/index.php index 8beef975fad6f3..e8075115cabda4 100644 --- a/packages/block-library/src/block/index.php +++ b/packages/block-library/src/block/index.php @@ -87,6 +87,26 @@ function render_block_core_block( $attributes ) { add_filter( 'render_block_context', $filter_block_context, 1 ); } + $ignored_hooked_blocks = get_post_meta( $attributes['ref'], '_wp_ignored_hooked_blocks', true ); + if ( ! empty( $ignored_hooked_blocks ) ) { + $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); + $attributes['metadata'] = array( + 'ignoredHookedBlocks' => $ignored_hooked_blocks, + ); + } + + // Wrap in "Block" block so the Block Hooks algorithm can insert blocks + // that are hooked as first or last child of `core/block`. + $content = get_comment_delimited_block_content( + 'core/block', + $attributes, + $content + ); + // Apply Block Hooks. + $content = apply_block_hooks_to_content( $content, $reusable_block ); + // Remove block wrapper. + $content = remove_serialized_parent_block( $content ); + $content = do_blocks( $content ); unset( $seen_refs[ $attributes['ref'] ] ); diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 39073b848d3ca8..5966739aa61a61 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -9,7 +9,8 @@ import clsx from 'clsx'; import { createBlock } from '@wordpress/blocks'; import { useSelect, useDispatch } from '@wordpress/data'; import { - PanelBody, + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, TextControl, TextareaControl, ToolbarButton, @@ -161,71 +162,110 @@ function getMissingText( type ) { function Controls( { attributes, setAttributes, setIsLabelFieldFocused } ) { const { label, url, description, title, rel } = attributes; return ( - <PanelBody title={ __( 'Settings' ) }> - <TextControl - __nextHasNoMarginBottom - __next40pxDefaultSize - value={ label ? stripHTML( label ) : '' } - onChange={ ( labelValue ) => { - setAttributes( { label: labelValue } ); - } } + <ToolsPanel label={ __( 'Settings' ) }> + <ToolsPanelItem + hasValue={ () => !! label } label={ __( 'Text' ) } - autoComplete="off" - onFocus={ () => setIsLabelFieldFocused( true ) } - onBlur={ () => setIsLabelFieldFocused( false ) } - /> - <TextControl - __nextHasNoMarginBottom - __next40pxDefaultSize - value={ url ? safeDecodeURI( url ) : '' } - onChange={ ( urlValue ) => { - updateAttributes( - { url: urlValue }, - setAttributes, - attributes - ); - } } + onDeselect={ () => setAttributes( { label: '' } ) } + isShownByDefault + > + <TextControl + __nextHasNoMarginBottom + __next40pxDefaultSize + label={ __( 'Text' ) } + value={ label ? stripHTML( label ) : '' } + onChange={ ( labelValue ) => { + setAttributes( { label: labelValue } ); + } } + autoComplete="off" + onFocus={ () => setIsLabelFieldFocused( true ) } + onBlur={ () => setIsLabelFieldFocused( false ) } + /> + </ToolsPanelItem> + + <ToolsPanelItem + hasValue={ () => !! url } label={ __( 'Link' ) } - autoComplete="off" - /> - <TextareaControl - __nextHasNoMarginBottom - value={ description || '' } - onChange={ ( descriptionValue ) => { - setAttributes( { description: descriptionValue } ); - } } + onDeselect={ () => setAttributes( { url: '' } ) } + isShownByDefault + > + <TextControl + __nextHasNoMarginBottom + __next40pxDefaultSize + label={ __( 'Link' ) } + value={ url ? safeDecodeURI( url ) : '' } + onChange={ ( urlValue ) => { + updateAttributes( + { url: urlValue }, + setAttributes, + attributes + ); + } } + autoComplete="off" + /> + </ToolsPanelItem> + + <ToolsPanelItem + hasValue={ () => !! description } label={ __( 'Description' ) } - help={ __( - 'The description will be displayed in the menu if the current theme supports it.' - ) } - /> - <TextControl - __nextHasNoMarginBottom - __next40pxDefaultSize - value={ title || '' } - onChange={ ( titleValue ) => { - setAttributes( { title: titleValue } ); - } } + onDeselect={ () => setAttributes( { description: '' } ) } + isShownByDefault + > + <TextareaControl + __nextHasNoMarginBottom + label={ __( 'Description' ) } + value={ description || '' } + onChange={ ( descriptionValue ) => { + setAttributes( { description: descriptionValue } ); + } } + help={ __( + 'The description will be displayed in the menu if the current theme supports it.' + ) } + /> + </ToolsPanelItem> + + <ToolsPanelItem + hasValue={ () => !! title } label={ __( 'Title attribute' ) } - autoComplete="off" - help={ __( - 'Additional information to help clarify the purpose of the link.' - ) } - /> - <TextControl - __nextHasNoMarginBottom - __next40pxDefaultSize - value={ rel || '' } - onChange={ ( relValue ) => { - setAttributes( { rel: relValue } ); - } } + onDeselect={ () => setAttributes( { title: '' } ) } + isShownByDefault + > + <TextControl + __nextHasNoMarginBottom + __next40pxDefaultSize + label={ __( 'Title attribute' ) } + value={ title || '' } + onChange={ ( titleValue ) => { + setAttributes( { title: titleValue } ); + } } + autoComplete="off" + help={ __( + 'Additional information to help clarify the purpose of the link.' + ) } + /> + </ToolsPanelItem> + + <ToolsPanelItem + hasValue={ () => !! rel } label={ __( 'Rel attribute' ) } - autoComplete="off" - help={ __( - 'The relationship of the linked URL as space-separated link types.' - ) } - /> - </PanelBody> + onDeselect={ () => setAttributes( { rel: '' } ) } + isShownByDefault + > + <TextControl + __nextHasNoMarginBottom + __next40pxDefaultSize + label={ __( 'Rel attribute' ) } + value={ rel || '' } + onChange={ ( relValue ) => { + setAttributes( { rel: relValue } ); + } } + autoComplete="off" + help={ __( + 'The relationship of the linked URL as space-separated link types.' + ) } + /> + </ToolsPanelItem> + </ToolsPanel> ); } diff --git a/packages/block-library/src/site-title/edit.js b/packages/block-library/src/site-title/edit.js index 644629a96fe4e1..44b29173e06b03 100644 --- a/packages/block-library/src/site-title/edit.js +++ b/packages/block-library/src/site-title/edit.js @@ -25,6 +25,11 @@ import { import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; import { decodeEntities } from '@wordpress/html-entities'; +/** + * Internal dependencies + */ +import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; + export default function SiteTitleEdit( { attributes, setAttributes, @@ -47,6 +52,7 @@ export default function SiteTitleEdit( { }; }, [] ); const { editEntityRecord } = useDispatch( coreStore ); + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); function setTitle( newTitle ) { editEntityRecord( 'root', 'site', undefined, { @@ -121,6 +127,7 @@ export default function SiteTitleEdit( { linkTarget: '_self', } ); } } + dropdownMenuProps={ dropdownMenuProps } > <ToolsPanelItem hasValue={ () => isLink !== false } diff --git a/packages/block-library/src/spacer/controls.js b/packages/block-library/src/spacer/controls.js index 1e899e15aff0de..fde06d3ee8c339 100644 --- a/packages/block-library/src/spacer/controls.js +++ b/packages/block-library/src/spacer/controls.js @@ -10,10 +10,11 @@ import { privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { - PanelBody, __experimentalUseCustomUnits as useCustomUnits, __experimentalUnitControl as UnitControl, __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; import { View } from '@wordpress/primitives'; @@ -94,28 +95,54 @@ export default function SpacerControls( { } ) { return ( <InspectorControls> - <PanelBody title={ __( 'Settings' ) }> + <ToolsPanel + label={ __( 'Settings' ) } + resetAll={ () => { + setAttributes( { + width: undefined, + height: '100px', + } ); + } } + > { orientation === 'horizontal' && ( - <DimensionInput + <ToolsPanelItem label={ __( 'Width' ) } - value={ width } - onChange={ ( nextWidth ) => - setAttributes( { width: nextWidth } ) + isShownByDefault + hasValue={ () => width !== undefined } + onDeselect={ () => + setAttributes( { width: undefined } ) } - isResizing={ isResizing } - /> + > + <DimensionInput + label={ __( 'Width' ) } + value={ width } + onChange={ ( nextWidth ) => + setAttributes( { width: nextWidth } ) + } + isResizing={ isResizing } + /> + </ToolsPanelItem> ) } { orientation !== 'horizontal' && ( - <DimensionInput + <ToolsPanelItem label={ __( 'Height' ) } - value={ height } - onChange={ ( nextHeight ) => - setAttributes( { height: nextHeight } ) + isShownByDefault + hasValue={ () => height !== '100px' } + onDeselect={ () => + setAttributes( { height: '100px' } ) } - isResizing={ isResizing } - /> + > + <DimensionInput + label={ __( 'Height' ) } + value={ height } + onChange={ ( nextHeight ) => + setAttributes( { height: nextHeight } ) + } + isResizing={ isResizing } + /> + </ToolsPanelItem> ) } - </PanelBody> + </ToolsPanel> </InspectorControls> ); } diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 51045be503de31..299f0a67da9b7a 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -56,6 +56,7 @@ "@wordpress/icons": "*", "@wordpress/keyboard-shortcuts": "*", "@wordpress/keycodes": "*", + "@wordpress/media-utils": "5.14.0", "@wordpress/notices": "*", "@wordpress/patterns": "*", "@wordpress/plugins": "*", diff --git a/packages/edit-site/src/components/sidebar-navigation-item/style.scss b/packages/edit-site/src/components/sidebar-navigation-item/style.scss index 230967c4c7e0ed..57b7e84bd57a8b 100644 --- a/packages/edit-site/src/components/sidebar-navigation-item/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-item/style.scss @@ -18,6 +18,7 @@ &[aria-current="true"] { background: $gray-800; color: $white; + font-weight: $font-weight-medium; } // Make sure the focus style is drawn on top of the current item background. diff --git a/packages/edit-site/src/components/style-book/index.js b/packages/edit-site/src/components/style-book/index.js index 4c93d2ed8db895..28a693e76958d7 100644 --- a/packages/edit-site/src/components/style-book/index.js +++ b/packages/edit-site/src/components/style-book/index.js @@ -35,6 +35,8 @@ import { useEffect, } from '@wordpress/element'; import { ENTER, SPACE } from '@wordpress/keycodes'; +import { uploadMedia } from '@wordpress/media-utils'; +import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -361,10 +363,22 @@ export const StyleBookPreview = ( { userConfig = {}, isStatic = false } ) => { [] ); + const canUserUploadMedia = useSelect( + ( select ) => + select( coreStore ).canUser( 'create', { + kind: 'root', + name: 'media', + } ), + [] + ); + // Update block editor settings because useMultipleOriginColorsAndGradients fetch colours from there. useEffect( () => { - dispatch( blockEditorStore ).updateSettings( siteEditorSettings ); - }, [ siteEditorSettings ] ); + dispatch( blockEditorStore ).updateSettings( { + ...siteEditorSettings, + mediaUpload: canUserUploadMedia ? uploadMedia : undefined, + } ); + }, [ siteEditorSettings, canUserUploadMedia ] ); const [ section, onChangeSection ] = useSection(); diff --git a/packages/editor/src/components/post-card-panel/index.js b/packages/editor/src/components/post-card-panel/index.js index 7849f014ab49c8..78f9522ba5f444 100644 --- a/packages/editor/src/components/post-card-panel/index.js +++ b/packages/editor/src/components/post-card-panel/index.js @@ -6,6 +6,7 @@ import { __experimentalHStack as HStack, __experimentalVStack as VStack, __experimentalText as Text, + privateApis as componentsPrivateApis, } from '@wordpress/components'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; @@ -25,6 +26,7 @@ import { unlock } from '../../lock-unlock'; import PostActions from '../post-actions'; import usePageTypeBadge from '../../utils/pageTypeBadge'; import { getTemplateInfo } from '../../utils/get-template-info'; +const { Badge } = unlock( componentsPrivateApis ); /** * Renders a title of the post type and the available quick actions available within a 3-dot dropdown. @@ -109,11 +111,11 @@ export default function PostCardPanel( { className="editor-post-card-panel__title" as="h2" > - { title } + <span className="editor-post-card-panel__title-name"> + { title } + </span> { pageTypeBadge && postIds.length === 1 && ( - <span className="editor-post-card-panel__title-badge"> - { pageTypeBadge } - </span> + <Badge>{ pageTypeBadge }</Badge> ) } </Text> <PostActions diff --git a/packages/editor/src/components/post-card-panel/style.scss b/packages/editor/src/components/post-card-panel/style.scss index c3638b313a8285..5fa54c67f14e55 100644 --- a/packages/editor/src/components/post-card-panel/style.scss +++ b/packages/editor/src/components/post-card-panel/style.scss @@ -9,7 +9,6 @@ &.editor-post-card-panel__title { @include heading-medium(); margin: 0; - padding: 2px 0; display: flex; align-items: center; flex-wrap: wrap; @@ -34,19 +33,11 @@ margin-bottom: $grid-unit-10; } + .editor-post-card-panel__title-name { + padding: 2px 0; + } + .editor-post-card-panel__description { color: $gray-700; } } - -.editor-post-card-panel__title-badge { - background: $gray-100; - color: $gray-800; - padding: 0 $grid-unit-05; - border-radius: $radius-small; - font-size: 12px; - font-weight: 400; - flex-shrink: 0; - line-height: $grid-unit-05 * 5; - display: inline-block; -} diff --git a/packages/fields/src/fields/page-title/style.scss b/packages/fields/src/fields/page-title/style.scss deleted file mode 100644 index def56aa466a8a1..00000000000000 --- a/packages/fields/src/fields/page-title/style.scss +++ /dev/null @@ -1,10 +0,0 @@ -.fields-field__page-title__badge { - background: $gray-100; - color: $gray-800; - padding: 0 $grid-unit-05; - border-radius: $radius-small; - font-size: 12px; - font-weight: 400; - flex-shrink: 0; - line-height: $grid-unit-05 * 5; -} diff --git a/packages/fields/src/fields/page-title/view.tsx b/packages/fields/src/fields/page-title/view.tsx index 0be4c16d5d29ae..eb5184362ec82b 100644 --- a/packages/fields/src/fields/page-title/view.tsx +++ b/packages/fields/src/fields/page-title/view.tsx @@ -5,12 +5,15 @@ import { __ } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import type { Settings } from '@wordpress/core-data'; +import { privateApis as componentsPrivateApis } from '@wordpress/components'; /** * Internal dependencies */ import type { CommonPost } from '../../types'; import { BaseTitleView } from '../title/view'; +import { unlock } from '../../lock-unlock'; +const { Badge } = unlock( componentsPrivateApis ); export default function PageTitleView( { item }: { item: CommonPost } ) { const { frontPageId, postsPageId } = useSelect( ( select ) => { @@ -27,11 +30,11 @@ export default function PageTitleView( { item }: { item: CommonPost } ) { return ( <BaseTitleView item={ item } className="fields-field__page-title"> { [ frontPageId, postsPageId ].includes( item.id as number ) && ( - <span className="fields-field__page-title__badge"> + <Badge> { item.id === frontPageId ? __( 'Homepage' ) : __( 'Posts Page' ) } - </span> + </Badge> ) } </BaseTitleView> ); diff --git a/packages/fields/src/style.scss b/packages/fields/src/style.scss index d9a571270fbb68..96b1f816de5b61 100644 --- a/packages/fields/src/style.scss +++ b/packages/fields/src/style.scss @@ -3,5 +3,4 @@ @import "./fields/featured-image/style.scss"; @import "./fields/template/style.scss"; @import "./fields/title/style.scss"; -@import "./fields/page-title/style.scss"; @import "./fields/pattern-title/style.scss"; diff --git a/storybook/preview.js b/storybook/preview.js index 8372103cd99444..b74640d9bcfbcf 100644 --- a/storybook/preview.js +++ b/storybook/preview.js @@ -108,6 +108,9 @@ export const parameters = { sort: 'requiredFirst', }, docs: { + controls: { + sort: 'requiredFirst', + }, // Flips the order of the description and the primary component story // so the component is always visible before the fold. page: () => (