From 6058323845a92811293a613c88a7d874c7aa5622 Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Tue, 3 Sep 2024 10:26:57 +0200 Subject: [PATCH 001/131] Layout content and wide width controls: remove confusing icon and clarify labels (#64891) * Remove confusing icons. * Used 40px input size. * Improve content and wide widths labeling. * Remove confusing icons from block settings constrained layout widths. * Try icons in input prefix. * Adjust vertical spacing. Co-authored-by: afercia Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: ciampo Co-authored-by: t-hamano Co-authored-by: tyxla Co-authored-by: jameskoster Co-authored-by: richtabor Co-authored-by: MaggieCabrera --- .../global-styles/dimensions-panel.js | 79 +++++++------- packages/block-editor/src/hooks/layout.scss | 14 +-- .../block-editor/src/layouts/constrained.js | 101 +++++++++--------- 3 files changed, 89 insertions(+), 105 deletions(-) diff --git a/packages/block-editor/src/components/global-styles/dimensions-panel.js b/packages/block-editor/src/components/global-styles/dimensions-panel.js index 241d3bb93d1b13..8430703aec966d 100644 --- a/packages/block-editor/src/components/global-styles/dimensions-panel.js +++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js @@ -11,12 +11,11 @@ import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalBoxControl as BoxControl, - __experimentalHStack as HStack, __experimentalUnitControl as UnitControl, __experimentalUseCustomUnits as useCustomUnits, - __experimentalView as View, + __experimentalInputControlPrefixWrapper as InputControlPrefixWrapper, } from '@wordpress/components'; -import { Icon, positionCenter, stretchWide } from '@wordpress/icons'; +import { Icon, alignNone, stretchWide } from '@wordpress/icons'; import { useCallback, useState, Platform } from '@wordpress/element'; /** @@ -252,7 +251,7 @@ export default function DimensionsPanel( { const minimumMargin = -Infinity; const [ minMarginValue, setMinMarginValue ] = useState( minimumMargin ); - // Content Size + // Content Width const showContentSizeControl = useHasContentSize( settings ) && includeLayoutControls; const contentSizeValue = decodeValue( inheritedValue?.layout?.contentSize ); @@ -268,7 +267,7 @@ export default function DimensionsPanel( { const hasUserSetContentSizeValue = () => !! value?.layout?.contentSize; const resetContentSizeValue = () => setContentSizeValue( undefined ); - // Wide Size + // Wide Width const showWideSizeControl = useHasWideSize( settings ) && includeLayoutControls; const wideSizeValue = decodeValue( inheritedValue?.layout?.wideSize ); @@ -464,8 +463,7 @@ export default function DimensionsPanel( { ) } { showContentSizeControl && ( - - { - setContentSizeValue( nextContentSize ); - } } - units={ units } - /> - - - - + { + setContentSizeValue( nextContentSize ); + } } + units={ units } + prefix={ + + + + } + /> ) } { showWideSizeControl && ( - - { - setWideSizeValue( nextWideSize ); - } } - units={ units } - /> - - - - + { + setWideSizeValue( nextWideSize ); + } } + units={ units } + prefix={ + + + + } + /> ) } { showPaddingControl && ( diff --git a/packages/block-editor/src/hooks/layout.scss b/packages/block-editor/src/hooks/layout.scss index 83a044e3cdca75..3eedd1f629a633 100644 --- a/packages/block-editor/src/hooks/layout.scss +++ b/packages/block-editor/src/hooks/layout.scss @@ -1,15 +1,7 @@ -.block-editor-hooks__layout-controls { +.block-editor-hooks__layout-controls-units { display: flex; - margin-bottom: $grid-unit-10; - - .block-editor-hooks__layout-controls-unit { - display: flex; - margin-right: $grid-unit-30; - - svg { - margin: auto 0 $grid-unit-05 $grid-unit-10; - } - } + flex-direction: column; + gap: $grid-unit-20; } .block-editor-block-inspector .block-editor-hooks__layout-controls-unit-input { diff --git a/packages/block-editor/src/layouts/constrained.js b/packages/block-editor/src/layouts/constrained.js index b8dcde637a4d30..21aca422a315ff 100644 --- a/packages/block-editor/src/layouts/constrained.js +++ b/packages/block-editor/src/layouts/constrained.js @@ -6,11 +6,12 @@ import { __experimentalUnitControl as UnitControl, __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, + __experimentalInputControlPrefixWrapper as InputControlPrefixWrapper, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { Icon, - positionCenter, + alignNone, stretchWide, justifyLeft, justifyCenter, @@ -71,61 +72,59 @@ export default { return ( <> { allowCustomContentAndWideSize && ( - <> -
-
- { - nextWidth = - 0 > parseFloat( nextWidth ) - ? '0' - : nextWidth; - onChange( { - ...layout, - contentSize: nextWidth, - } ); - } } - units={ units } - /> - -
-
- { - nextWidth = - 0 > parseFloat( nextWidth ) - ? '0' - : nextWidth; - onChange( { - ...layout, - wideSize: nextWidth, - } ); - } } - units={ units } - /> - -
-
+
+ { + nextWidth = + 0 > parseFloat( nextWidth ) + ? '0' + : nextWidth; + onChange( { + ...layout, + contentSize: nextWidth, + } ); + } } + units={ units } + prefix={ + + + + } + /> + { + nextWidth = + 0 > parseFloat( nextWidth ) + ? '0' + : nextWidth; + onChange( { + ...layout, + wideSize: nextWidth, + } ); + } } + units={ units } + prefix={ + + + + } + />

{ __( 'Customize the width for all elements that are assigned to the center or wide columns.' ) }

- +
) } { allowJustification && ( Date: Tue, 3 Sep 2024 12:03:02 +0200 Subject: [PATCH 002/131] Try: Update block warnings. (#64997) Co-authored-by: jasmussen Co-authored-by: SantosGuillamot --- .../components/block-list/block-invalid-warning.js | 4 ++-- .../block-editor/src/components/warning/content.scss | 11 ++--------- packages/block-library/src/missing/edit.js | 4 ++-- test/e2e/specs/editor/various/invalid-block.spec.js | 2 +- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block-invalid-warning.js b/packages/block-editor/src/components/block-list/block-invalid-warning.js index 42ec5f9deb84d0..fb235cbce9ca21 100644 --- a/packages/block-editor/src/components/block-list/block-invalid-warning.js +++ b/packages/block-editor/src/components/block-list/block-invalid-warning.js @@ -107,12 +107,12 @@ export default function BlockInvalidWarning( { clientId } ) { onClick={ convert.toRecoveredBlock } variant="primary" > - { __( 'Attempt Block Recovery' ) } + { __( 'Attempt recovery' ) } , ] } secondaryActions={ secondaryActions } > - { __( 'This block contains unexpected or invalid content.' ) } + { __( 'Block contains unexpected or invalid content.' ) } { compare && ( { await expect( editor.canvas .getByRole( 'document', { name: 'Block: Paragraph' } ) - .getByRole( 'button', { name: 'Attempt Block Recovery' } ) + .getByRole( 'button', { name: 'Attempt recovery' } ) ).toBeVisible(); expect( hasAlert ).toBe( false ); From 7bd8f8e0256cb106066faa1af9e582cb47e265aa Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Tue, 3 Sep 2024 12:42:13 +0200 Subject: [PATCH 003/131] Fix the post summary Status toggle button accessibility (#63988) * Fix Change status label. * Add missing aria-expanded attribute to Status setting. Co-authored-by: afercia Co-authored-by: Mamaduka --- packages/editor/src/components/post-status/index.js | 5 +++-- test/e2e/specs/editor/various/change-detection.spec.js | 4 +--- test/e2e/specs/editor/various/post-visibility.spec.js | 6 ++---- test/e2e/specs/editor/various/preview.spec.js | 4 +--- test/e2e/specs/editor/various/switch-to-draft.spec.js | 2 +- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/editor/src/components/post-status/index.js b/packages/editor/src/components/post-status/index.js index ca89e40366b238..1d3050e7e3dd6b 100644 --- a/packages/editor/src/components/post-status/index.js +++ b/packages/editor/src/components/post-status/index.js @@ -171,7 +171,7 @@ export default function PostStatus() { contentClassName="editor-change-status__content" popoverProps={ popoverProps } focusOnMount - renderToggle={ ( { onToggle } ) => ( + renderToggle={ ( { onToggle, isOpen } ) => ( diff --git a/test/e2e/specs/editor/various/change-detection.spec.js b/test/e2e/specs/editor/various/change-detection.spec.js index 30b2287a8d9439..12d13b4890e368 100644 --- a/test/e2e/specs/editor/various/change-detection.spec.js +++ b/test/e2e/specs/editor/various/change-detection.spec.js @@ -80,9 +80,7 @@ test.describe( 'Change detection', () => { // Toggle post as needing review (not persisted for autosave). await editor.openDocumentSettingsSidebar(); - await page - .getByRole( 'button', { name: 'Change post status:' } ) - .click(); + await page.getByRole( 'button', { name: 'Change status:' } ).click(); await page.getByRole( 'radio', { name: 'Pending' } ).click(); // Force autosave to occur immediately. await Promise.all( [ diff --git a/test/e2e/specs/editor/various/post-visibility.spec.js b/test/e2e/specs/editor/various/post-visibility.spec.js index 61cd5e65f01015..67c8597953815b 100644 --- a/test/e2e/specs/editor/various/post-visibility.spec.js +++ b/test/e2e/specs/editor/various/post-visibility.spec.js @@ -18,7 +18,7 @@ test.describe( 'Post visibility', () => { await editor.openDocumentSettingsSidebar(); await page - .getByRole( 'button', { name: 'Change post status:' } ) + .getByRole( 'button', { name: 'Change status:' } ) .click(); await page.getByRole( 'radio', { name: 'Private' } ).click(); @@ -57,9 +57,7 @@ test.describe( 'Post visibility', () => { name: 'Close', } ) .click(); - await page - .getByRole( 'button', { name: 'Change post status:' } ) - .click(); + await page.getByRole( 'button', { name: 'Change status:' } ).click(); await page.getByRole( 'radio', { name: 'Private' } ).click(); await page .getByRole( 'region', { name: 'Editor top bar' } ) diff --git a/test/e2e/specs/editor/various/preview.spec.js b/test/e2e/specs/editor/various/preview.spec.js index b25d1da48122ce..e15f143983b742 100644 --- a/test/e2e/specs/editor/various/preview.spec.js +++ b/test/e2e/specs/editor/various/preview.spec.js @@ -183,9 +183,7 @@ test.describe( 'Preview', () => { // Return to editor and switch to Draft. await editorPage.bringToFront(); - await page - .getByRole( 'button', { name: 'Change post status:' } ) - .click(); + await page.getByRole( 'button', { name: 'Change status:' } ).click(); await page.getByRole( 'radio', { name: 'Draft' } ).click(); await page .getByRole( 'region', { name: 'Editor top bar' } ) diff --git a/test/e2e/specs/editor/various/switch-to-draft.spec.js b/test/e2e/specs/editor/various/switch-to-draft.spec.js index 516de6d0c85cc5..81652d9c11e2bb 100644 --- a/test/e2e/specs/editor/various/switch-to-draft.spec.js +++ b/test/e2e/specs/editor/various/switch-to-draft.spec.js @@ -47,7 +47,7 @@ test.describe( 'Clicking "Switch to draft" on a published/scheduled post/page', await editor.openDocumentSettingsSidebar(); await page - .getByRole( 'button', { name: 'Change post status:' } ) + .getByRole( 'button', { name: 'Change status:' } ) .click(); await page.getByRole( 'radio', { name: 'Draft' } ).click(); From 9c625558d034de78768df74a8c922430db19d83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:05:19 +0200 Subject: [PATCH 004/131] DataViews: add story about combining fields (#64984) Co-authored-by: oandregal Co-authored-by: youknowriad --- .../components/dataviews/stories/fixtures.js | 261 ------- .../components/dataviews/stories/fixtures.tsx | 690 ++++++++++++++++++ .../dataviews/stories/index.story.js | 111 --- .../dataviews/stories/index.story.tsx | 164 +++++ 4 files changed, 854 insertions(+), 372 deletions(-) delete mode 100644 packages/dataviews/src/components/dataviews/stories/fixtures.js create mode 100644 packages/dataviews/src/components/dataviews/stories/fixtures.tsx delete mode 100644 packages/dataviews/src/components/dataviews/stories/index.story.js create mode 100644 packages/dataviews/src/components/dataviews/stories/index.story.tsx diff --git a/packages/dataviews/src/components/dataviews/stories/fixtures.js b/packages/dataviews/src/components/dataviews/stories/fixtures.js deleted file mode 100644 index 14c9718a50d76b..00000000000000 --- a/packages/dataviews/src/components/dataviews/stories/fixtures.js +++ /dev/null @@ -1,261 +0,0 @@ -/** - * WordPress dependencies - */ -import { trash, image, Icon, category } from '@wordpress/icons'; -import { - Button, - __experimentalText as Text, - __experimentalHStack as HStack, - __experimentalVStack as VStack, -} from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { LAYOUT_TABLE } from '../../../constants'; - -export const data = [ - { - id: 1, - title: 'Apollo', - description: 'Apollo description', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Not a planet', - categories: [ 'Space', 'NASA' ], - satellites: 0, - date: '2021-01-01T00:00:00Z', - }, - { - id: 2, - title: 'Space', - description: 'Space description', - image: 'https://live.staticflickr.com/5678/21911065441_92e2d44708_b.jpg', - type: 'Not a planet', - categories: [ 'Space' ], - satellites: 0, - date: '2019-01-02T00:00:00Z', - }, - { - id: 3, - title: 'NASA', - description: 'NASA photo', - image: 'https://live.staticflickr.com/742/21712365770_8f70a2c91e_b.jpg', - type: 'Not a planet', - categories: [ 'NASA' ], - satellites: 0, - date: '2025-01-03T00:00:00Z', - }, - { - id: 4, - title: 'Neptune', - description: 'Neptune description', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Ice giant', - categories: [ 'Space', 'Planet', 'Solar system' ], - satellites: 14, - date: '2020-01-01T00:00:00Z', - }, - { - id: 5, - title: 'Mercury', - description: 'Mercury description', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Terrestrial', - categories: [ 'Space', 'Planet', 'Solar system' ], - satellites: 0, - date: '2020-01-02T01:00:00Z', - }, - { - id: 6, - title: 'Venus', - description: 'La planète Vénus', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Terrestrial', - categories: [ 'Space', 'Planet', 'Solar system' ], - satellites: 0, - date: '2020-01-02T00:00:00Z', - }, - { - id: 7, - title: 'Earth', - description: 'Earth description', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Terrestrial', - categories: [ 'Space', 'Planet', 'Solar system' ], - satellites: 1, - date: '2023-01-03T00:00:00Z', - }, - { - id: 8, - title: 'Mars', - description: 'Mars description', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Terrestrial', - categories: [ 'Space', 'Planet', 'Solar system' ], - satellites: 2, - date: '2020-01-01T00:00:00Z', - }, - { - id: 9, - title: 'Jupiter', - description: 'Jupiter description', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Gas giant', - categories: [ 'Space', 'Planet', 'Solar system' ], - satellites: 95, - date: '2017-01-01T00:01:00Z', - }, - { - id: 10, - title: 'Saturn', - description: 'Saturn description', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Gas giant', - categories: [ 'Space', 'Planet', 'Solar system' ], - satellites: 146, - date: '2020-02-01T00:02:00Z', - }, - { - id: 11, - title: 'Uranus', - description: 'Uranus description', - image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', - type: 'Ice giant', - categories: [ 'Space', 'Ice giant', 'Solar system' ], - satellites: 28, - date: '2020-03-01T00:00:00Z', - }, -]; - -export const DEFAULT_VIEW = { - type: LAYOUT_TABLE, - search: '', - page: 1, - perPage: 10, - fields: [ 'title', 'description', 'categories' ], - layout: {}, - filters: [], -}; - -export const actions = [ - { - id: 'delete', - label: 'Delete item', - isPrimary: true, - icon: trash, - hideModalHeader: true, - RenderModal: ( { items, closeModal } ) => { - return ( - - - { `Are you sure you want to delete "${ items[ 0 ].title }"?` } - - - - - - - ); - }, - }, - { - id: 'secondary', - label: 'Secondary action', - callback() {}, - }, -]; - -export const fields = [ - { - label: 'Image', - id: 'image', - header: ( - - - Image - - ), - render: ( { item } ) => { - return ( - - ); - }, - enableSorting: false, - }, - { - label: 'Title', - id: 'title', - enableHiding: false, - enableGlobalSearch: true, - render: ( { item } ) => { - return { item.title }; - }, - }, - { - id: 'date', - label: 'Date', - type: 'datetime', - }, - { - label: 'Type', - id: 'type', - enableHiding: false, - elements: [ - { value: 'Not a planet', label: 'Not a planet' }, - { value: 'Ice giant', label: 'Ice giant' }, - { value: 'Terrestrial', label: 'Terrestrial' }, - { value: 'Gas giant', label: 'Gas giant' }, - ], - }, - { - label: 'Satellites', - id: 'satellites', - type: 'integer', - enableSorting: true, - }, - { - label: 'Description', - id: 'description', - enableSorting: false, - enableGlobalSearch: true, - }, - { - label: 'Categories', - id: 'categories', - header: ( - - - Categories - - ), - elements: [ - { value: 'Space', label: 'Space' }, - { value: 'NASA', label: 'NASA' }, - { value: 'Planet', label: 'Planet' }, - { value: 'Solar system', label: 'Solar system' }, - { value: 'Ice giant', label: 'Ice giant' }, - ], - filterBy: { - operators: [ 'isAny', 'isNone', 'isAll', 'isNotAll' ], - }, - getValue: ( { item } ) => { - return item.categories; - }, - render: ( { item } ) => { - return item.categories.join( ',' ); - }, - enableSorting: false, - }, -]; diff --git a/packages/dataviews/src/components/dataviews/stories/fixtures.tsx b/packages/dataviews/src/components/dataviews/stories/fixtures.tsx new file mode 100644 index 00000000000000..2ab02ec728e5e0 --- /dev/null +++ b/packages/dataviews/src/components/dataviews/stories/fixtures.tsx @@ -0,0 +1,690 @@ +/** + * WordPress dependencies + */ +import { trash, image, Icon, category } from '@wordpress/icons'; +import { + Button, + __experimentalText as Text, + __experimentalHStack as HStack, + __experimentalVStack as VStack, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import type { Field, Action } from '../../../types'; + +export type Theme = { + slug: string; + name: string; + description: string; + requires: string; + tested: string; + tags: string[]; +}; + +export type SpaceObject = { + id: number; + title: string; + description: string; + image: string; + type: string; + categories: string[]; + satellites: number; + date: string; +}; + +export const data: SpaceObject[] = [ + { + id: 1, + title: 'Apollo', + description: 'Apollo description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Not a planet', + categories: [ 'Space', 'NASA' ], + satellites: 0, + date: '2021-01-01T00:00:00Z', + }, + { + id: 2, + title: 'Space', + description: 'Space description', + image: 'https://live.staticflickr.com/5678/21911065441_92e2d44708_b.jpg', + type: 'Not a planet', + categories: [ 'Space' ], + satellites: 0, + date: '2019-01-02T00:00:00Z', + }, + { + id: 3, + title: 'NASA', + description: 'NASA photo', + image: 'https://live.staticflickr.com/742/21712365770_8f70a2c91e_b.jpg', + type: 'Not a planet', + categories: [ 'NASA' ], + satellites: 0, + date: '2025-01-03T00:00:00Z', + }, + { + id: 4, + title: 'Neptune', + description: 'Neptune description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Ice giant', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 14, + date: '2020-01-01T00:00:00Z', + }, + { + id: 5, + title: 'Mercury', + description: 'Mercury description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Terrestrial', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 0, + date: '2020-01-02T01:00:00Z', + }, + { + id: 6, + title: 'Venus', + description: 'La planète Vénus', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Terrestrial', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 0, + date: '2020-01-02T00:00:00Z', + }, + { + id: 7, + title: 'Earth', + description: 'Earth description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Terrestrial', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 1, + date: '2023-01-03T00:00:00Z', + }, + { + id: 8, + title: 'Mars', + description: 'Mars description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Terrestrial', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 2, + date: '2020-01-01T00:00:00Z', + }, + { + id: 9, + title: 'Jupiter', + description: 'Jupiter description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Gas giant', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 95, + date: '2017-01-01T00:01:00Z', + }, + { + id: 10, + title: 'Saturn', + description: 'Saturn description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Gas giant', + categories: [ 'Space', 'Planet', 'Solar system' ], + satellites: 146, + date: '2020-02-01T00:02:00Z', + }, + { + id: 11, + title: 'Uranus', + description: 'Uranus description', + image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg', + type: 'Ice giant', + categories: [ 'Space', 'Ice giant', 'Solar system' ], + satellites: 28, + date: '2020-03-01T00:00:00Z', + }, +]; + +export const themeData: Theme[] = [ + { + slug: 'twentyeleven', + name: 'Twenty Eleven', + description: + 'The 2011 theme for WordPress is sophisticated, lightweight, and adaptable. Make it yours with a custom menu, header image, and background -- then go further with available theme options for light or dark color scheme, custom link colors, and three layout choices. Twenty Eleven comes equipped with a Showcase page template that transforms your front page into a showcase to show off your best content, widget support galore (sidebar, three footer areas, and a Showcase page widget area), and a custom "Ephemera" widget to display your Aside, Link, Quote, or Status posts. Included are styles for print and for the admin editor, support for featured images (as custom header images on posts and pages and as large images on featured "sticky" posts), and special styles for six different post formats.', + requires: '3.2', + tested: '6.6', + tags: [ + 'blog', + 'one-column', + 'two-columns', + 'left-sidebar', + 'right-sidebar', + 'custom-background', + 'custom-colors', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-image-header', + 'featured-images', + 'flexible-header', + 'footer-widgets', + 'full-width-template', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentyfifteen', + name: 'Twenty Fifteen', + description: + "Our 2015 default theme is clean, blog-focused, and designed for clarity. Twenty Fifteen's simple, straightforward typography is readable on a wide variety of screen sizes, and suitable for multiple languages. We designed it using a mobile-first approach, meaning your content takes center-stage, regardless of whether your visitors arrive by smartphone, tablet, laptop, or desktop computer.", + requires: '4.1', + tested: '6.6', + tags: [ + 'blog', + 'two-columns', + 'left-sidebar', + 'accessibility-ready', + 'custom-background', + 'custom-colors', + 'custom-header', + 'custom-logo', + 'custom-menu', + 'editor-style', + 'featured-images', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentyfourteen', + name: 'Twenty Fourteen', + description: + "In 2014, our default theme lets you create a responsive magazine website with a sleek, modern design. Feature your favorite homepage content in either a grid or a slider. Use the three widget areas to customize your website, and change your content's layout with a full-width page template and a contributor page to show off your authors. Creating a magazine website with WordPress has never been easier.", + requires: '3.6', + tested: '6.6', + tags: [ + 'blog', + 'news', + 'two-columns', + 'three-columns', + 'left-sidebar', + 'right-sidebar', + 'custom-background', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-images', + 'flexible-header', + 'footer-widgets', + 'full-width-template', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'translation-ready', + 'accessibility-ready', + 'block-patterns', + ], + }, + { + slug: 'twentynineteen', + name: 'Twenty Nineteen', + description: + "Our 2019 default theme is designed to show off the power of the block editor. It features custom styles for all the default blocks, and is built so that what you see in the editor looks like what you'll see on your website. Twenty Nineteen is designed to be adaptable to a wide range of websites, whether you’re running a photo blog, launching a new business, or supporting a non-profit. Featuring ample whitespace and modern sans-serif headlines paired with classic serif body text, it's built to be beautiful on all screen sizes.", + requires: '4.7', + tested: '6.6', + tags: [ + 'one-column', + 'accessibility-ready', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentyseventeen', + name: 'Twenty Seventeen', + description: + 'Twenty Seventeen brings your site to life with header video and immersive featured images. With a focus on business sites, it features multiple sections on the front page as well as widgets, navigation and social menus, a logo, and more. Personalize its asymmetrical grid with a custom color scheme and showcase your multimedia content with post formats. Our default theme for 2017 works great in many languages, for any abilities, and on any device.', + requires: '4.7', + tested: '6.6', + tags: [ + 'one-column', + 'two-columns', + 'right-sidebar', + 'flexible-header', + 'accessibility-ready', + 'custom-colors', + 'custom-header', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'threaded-comments', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentysixteen', + name: 'Twenty Sixteen', + description: + 'Twenty Sixteen is a modernized take on an ever-popular WordPress layout — the horizontal masthead with an optional right sidebar that works perfectly for blogs and websites. It has custom color options with beautiful default color schemes, a harmonious fluid grid using a mobile-first approach, and impeccable polish in every detail. Twenty Sixteen will make your WordPress look beautiful everywhere.', + requires: '4.4', + tested: '6.6', + tags: [ + 'one-column', + 'two-columns', + 'right-sidebar', + 'accessibility-ready', + 'custom-background', + 'custom-colors', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-images', + 'flexible-header', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'blog', + 'block-patterns', + ], + }, + { + slug: 'twentyten', + name: 'Twenty Ten', + description: + 'The 2010 theme for WordPress is stylish, customizable, simple, and readable -- make it yours with a custom menu, header image, and background. Twenty Ten supports six widgetized areas (two in the sidebar, four in the footer) and featured images (thumbnails for gallery posts and custom header images for posts and pages). It includes stylesheets for print and the admin Visual Editor, special styles for posts in the "Asides" and "Gallery" categories, and has an optional one-column page template that removes the sidebar.', + requires: '5.6', + tested: '6.6', + tags: [ + 'blog', + 'two-columns', + 'custom-header', + 'custom-background', + 'threaded-comments', + 'sticky-post', + 'translation-ready', + 'microformats', + 'rtl-language-support', + 'editor-style', + 'custom-menu', + 'flexible-header', + 'featured-images', + 'footer-widgets', + 'featured-image-header', + 'block-patterns', + ], + }, + { + slug: 'twentythirteen', + name: 'Twenty Thirteen', + description: + 'The 2013 theme for WordPress takes us back to the blog, featuring a full range of post formats, each displayed beautifully in their own unique way. Design details abound, starting with a vibrant color scheme and matching header images, beautiful typography and icons, and a flexible layout that looks great on any device, big or small.', + requires: '3.6', + tested: '6.6', + tags: [ + 'blog', + 'one-column', + 'two-columns', + 'right-sidebar', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'translation-ready', + 'accessibility-ready', + 'block-patterns', + ], + }, + { + slug: 'twentytwelve', + name: 'Twenty Twelve', + description: + 'The 2012 theme for WordPress is a fully responsive theme that looks great on any device. Features include a front page template with its own widgets, an optional display font, styling for post formats on both index and single views, and an optional no-sidebar page template. Make it yours with a custom menu, header image, and background.', + requires: '3.5', + tested: '6.6', + tags: [ + 'blog', + 'one-column', + 'two-columns', + 'right-sidebar', + 'custom-background', + 'custom-header', + 'custom-menu', + 'editor-style', + 'featured-images', + 'flexible-header', + 'footer-widgets', + 'full-width-template', + 'microformats', + 'post-formats', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'translation-ready', + 'block-patterns', + ], + }, + { + slug: 'twentytwenty', + name: 'Twenty Twenty', + description: + 'Our default theme for 2020 is designed to take full advantage of the flexibility of the block editor. Organizations and businesses have the ability to create dynamic landing pages with endless layouts using the group and column blocks. The centered content column and fine-tuned typography also makes it perfect for traditional blogs. Complete editor styles give you a good idea of what your content will look like, even before you publish. You can give your site a personal touch by changing the background colors and the accent color in the Customizer. The colors of all elements on your site are automatically calculated based on the colors you pick, ensuring a high, accessible color contrast for your visitors.', + requires: '4.7', + tested: '6.6', + tags: [ + 'blog', + 'one-column', + 'custom-background', + 'custom-colors', + 'custom-logo', + 'custom-menu', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'full-width-template', + 'rtl-language-support', + 'sticky-post', + 'theme-options', + 'threaded-comments', + 'translation-ready', + 'block-patterns', + 'block-styles', + 'wide-blocks', + 'accessibility-ready', + ], + }, + { + slug: 'twentytwentyfour', + name: 'Twenty Twenty-Four', + description: + 'Twenty Twenty-Four is designed to be flexible, versatile and applicable to any website. Its collection of templates and patterns tailor to different needs, such as presenting a business, blogging and writing or showcasing work. A multitude of possibilities open up with just a few adjustments to color and typography. Twenty Twenty-Four comes with style variations and full page designs to help speed up the site building process, is fully compatible with the site editor, and takes advantage of new design tools introduced in WordPress 6.4.', + requires: '6.4', + tested: '6.6', + tags: [ + 'one-column', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'full-site-editing', + 'block-patterns', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'wide-blocks', + 'block-styles', + 'style-variations', + 'accessibility-ready', + 'blog', + 'portfolio', + 'news', + ], + }, + { + slug: 'twentytwentyone', + name: 'Twenty Twenty-One', + description: + 'Twenty Twenty-One is a blank canvas for your ideas and it makes the block editor your best brush. With new block patterns, which allow you to create a beautiful layout in a matter of seconds, this theme’s soft colors and eye-catching — yet timeless — design will let your work shine. Take it for a spin! See how Twenty Twenty-One elevates your portfolio, business website, or personal blog.', + requires: '5.3', + tested: '6.6', + tags: [ + 'one-column', + 'accessibility-ready', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'footer-widgets', + 'block-patterns', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'blog', + 'portfolio', + ], + }, + { + slug: 'twentytwentythree', + name: 'Twenty Twenty-Three', + description: + 'Twenty Twenty-Three is designed to take advantage of the new design tools introduced in WordPress 6.1. With a clean, blank base as a starting point, this default theme includes ten diverse style variations created by members of the WordPress community. Whether you want to build a complex or incredibly simple website, you can do it quickly and intuitively through the bundled styles or dive into creation and full customization yourself.', + requires: '6.1', + tested: '6.6', + tags: [ + 'one-column', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'full-site-editing', + 'block-patterns', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'translation-ready', + 'wide-blocks', + 'block-styles', + 'style-variations', + 'accessibility-ready', + 'blog', + 'portfolio', + 'news', + ], + }, + { + slug: 'twentytwentytwo', + name: 'Twenty Twenty-Two', + description: + 'Built on a solidly designed foundation, Twenty Twenty-Two embraces the idea that everyone deserves a truly unique website. The theme’s subtle styles are inspired by the diversity and versatility of birds: its typography is lightweight yet strong, its color palette is drawn from nature, and its layout elements sit gently on the page. The true richness of Twenty Twenty-Two lies in its opportunity for customization. The theme is built to take advantage of the Site Editor features introduced in WordPress 5.9, which means that colors, typography, and the layout of every single page on your site can be customized to suit your vision. It also includes dozens of block patterns, opening the door to a wide range of professionally designed layouts in just a few clicks. Whether you’re building a single-page website, a blog, a business website, or a portfolio, Twenty Twenty-Two will help you create a site that is uniquely yours.', + requires: '5.9', + tested: '6.6', + tags: [ + 'one-column', + 'custom-colors', + 'custom-menu', + 'custom-logo', + 'editor-style', + 'featured-images', + 'full-site-editing', + 'block-patterns', + 'rtl-language-support', + 'sticky-post', + 'threaded-comments', + 'style-variations', + 'wide-blocks', + 'block-styles', + 'accessibility-ready', + 'blog', + 'portfolio', + 'news', + ], + }, +]; + +export const themeFields: Field< Theme >[] = [ + { id: 'slug', label: 'Slug' }, + { id: 'name', label: 'Name' }, + { id: 'description', label: 'Description' }, + { id: 'requires', label: 'Requires at least' }, + { id: 'tested', label: 'Tested up to' }, + { + id: 'tags', + label: 'Tags', + render: ( { item } ) => item.tags.join( ', ' ), + }, +]; + +export const DEFAULT_VIEW = { + type: 'table' as const, + search: '', + page: 1, + perPage: 10, + layout: {}, + filters: [], +}; + +export const actions: Action< SpaceObject >[] = [ + { + id: 'delete', + label: 'Delete item', + isPrimary: true, + icon: trash, + hideModalHeader: true, + RenderModal: ( { items, closeModal } ) => { + return ( + + + { `Are you sure you want to delete "${ items[ 0 ].title }"?` } + + + + + + + ); + }, + }, + { + id: 'secondary', + label: 'Secondary action', + callback() {}, + }, +]; + +export const fields: Field< SpaceObject >[] = [ + { + label: 'Image', + id: 'image', + header: ( + + + Image + + ), + render: ( { item } ) => { + return ( + + ); + }, + enableSorting: false, + }, + { + label: 'Title', + id: 'title', + enableHiding: false, + enableGlobalSearch: true, + render: ( { item } ) => { + return { item.title }; + }, + }, + { + id: 'date', + label: 'Date', + type: 'datetime', + }, + { + label: 'Type', + id: 'type', + enableHiding: false, + elements: [ + { value: 'Not a planet', label: 'Not a planet' }, + { value: 'Ice giant', label: 'Ice giant' }, + { value: 'Terrestrial', label: 'Terrestrial' }, + { value: 'Gas giant', label: 'Gas giant' }, + ], + }, + { + label: 'Satellites', + id: 'satellites', + type: 'integer', + enableSorting: true, + }, + { + label: 'Description', + id: 'description', + enableSorting: false, + enableGlobalSearch: true, + }, + { + label: 'Categories', + id: 'categories', + header: ( + + + Categories + + ), + elements: [ + { value: 'Space', label: 'Space' }, + { value: 'NASA', label: 'NASA' }, + { value: 'Planet', label: 'Planet' }, + { value: 'Solar system', label: 'Solar system' }, + { value: 'Ice giant', label: 'Ice giant' }, + ], + filterBy: { + operators: [ 'isAny', 'isNone', 'isAll', 'isNotAll' ], + }, + getValue: ( { item } ) => { + return item.categories; + }, + render: ( { item } ) => { + return item.categories.join( ',' ); + }, + enableSorting: false, + }, +]; diff --git a/packages/dataviews/src/components/dataviews/stories/index.story.js b/packages/dataviews/src/components/dataviews/stories/index.story.js deleted file mode 100644 index 75fb0786ecfee6..00000000000000 --- a/packages/dataviews/src/components/dataviews/stories/index.story.js +++ /dev/null @@ -1,111 +0,0 @@ -/** - * WordPress dependencies - */ -import { useState, useMemo } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import DataViews from '../index'; -import { DEFAULT_VIEW, actions, data, fields } from './fixtures'; -import { LAYOUT_GRID, LAYOUT_LIST, LAYOUT_TABLE } from '../../../constants'; -import { filterSortAndPaginate } from '../../../filter-and-sort-data-view'; - -const meta = { - title: 'DataViews/DataViews', - component: DataViews, -}; -export default meta; - -export const Default = ( props ) => { - const [ view, setView ] = useState( DEFAULT_VIEW ); - const { data: shownData, paginationInfo } = useMemo( () => { - return filterSortAndPaginate( data, view, fields ); - }, [ view ] ); - return ( - - ); -}; - -export const Empty = ( props ) => { - const [ view, setView ] = useState( DEFAULT_VIEW ); - - return ( - - ); -}; - -export const FieldsNoSortableNoHidable = ( props ) => { - const [ view, setView ] = useState( DEFAULT_VIEW ); - const { data: shownData, paginationInfo } = useMemo( () => { - return filterSortAndPaginate( data, view, fields ); - }, [ view ] ); - - const _fields = fields.map( ( field ) => ( { - ...field, - enableSorting: false, - enableHiding: false, - } ) ); - - return ( - - ); -}; - -Default.args = { - actions, - defaultLayouts: { - [ LAYOUT_TABLE ]: { - layout: { - primaryField: 'title', - styles: { - image: { - width: 50, - }, - title: { - maxWidth: 400, - }, - type: { - maxWidth: 400, - }, - description: { - maxWidth: 200, - }, - }, - }, - }, - [ LAYOUT_GRID ]: { - layout: { - mediaField: 'image', - primaryField: 'title', - }, - }, - [ LAYOUT_LIST ]: { - layout: { - mediaField: 'image', - primaryField: 'title', - }, - }, - }, -}; diff --git a/packages/dataviews/src/components/dataviews/stories/index.story.tsx b/packages/dataviews/src/components/dataviews/stories/index.story.tsx new file mode 100644 index 00000000000000..645c6d7ddcd922 --- /dev/null +++ b/packages/dataviews/src/components/dataviews/stories/index.story.tsx @@ -0,0 +1,164 @@ +/** + * WordPress dependencies + */ +import { useState, useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import DataViews from '../index'; +import { + DEFAULT_VIEW, + actions, + data, + fields, + themeData, + themeFields, +} from './fixtures'; +import { LAYOUT_GRID, LAYOUT_LIST, LAYOUT_TABLE } from '../../../constants'; +import { filterSortAndPaginate } from '../../../filter-and-sort-data-view'; +import type { View } from '../../../types'; + +const meta = { + title: 'DataViews/DataViews', + component: DataViews, +}; +export default meta; + +const defaultLayouts = { + [ LAYOUT_TABLE ]: { + layout: { + primaryField: 'title', + styles: { + image: { + width: 50, + }, + title: { + maxWidth: 400, + }, + type: { + maxWidth: 400, + }, + description: { + maxWidth: 200, + }, + }, + }, + }, + [ LAYOUT_GRID ]: { + layout: { + mediaField: 'image', + primaryField: 'title', + }, + }, + [ LAYOUT_LIST ]: { + layout: { + mediaField: 'image', + primaryField: 'title', + }, + }, +}; + +export const Default = () => { + const [ view, setView ] = useState< View >( { + ...DEFAULT_VIEW, + fields: [ 'title', 'description', 'categories' ], + } ); + const { data: shownData, paginationInfo } = useMemo( () => { + return filterSortAndPaginate( data, view, fields ); + }, [ view ] ); + return ( + item.id.toString() } + paginationInfo={ paginationInfo } + data={ shownData } + view={ view } + fields={ fields } + onChangeView={ setView } + actions={ actions } + defaultLayouts={ defaultLayouts } + /> + ); +}; + +export const Empty = () => { + const [ view, setView ] = useState< View >( { + ...DEFAULT_VIEW, + fields: [ 'title', 'description', 'categories' ], + } ); + + return ( + item.id.toString() } + paginationInfo={ { totalItems: 0, totalPages: 0 } } + data={ [] } + view={ view } + fields={ fields } + onChangeView={ setView } + actions={ actions } + defaultLayouts={ defaultLayouts } + /> + ); +}; + +export const FieldsNoSortableNoHidable = () => { + const [ view, setView ] = useState< View >( { + ...DEFAULT_VIEW, + fields: [ 'title', 'description', 'categories' ], + } ); + const { data: shownData, paginationInfo } = useMemo( () => { + return filterSortAndPaginate( data, view, fields ); + }, [ view ] ); + + const _fields = fields.map( ( field ) => ( { + ...field, + enableSorting: false, + enableHiding: false, + } ) ); + + return ( + item.id.toString() } + paginationInfo={ paginationInfo } + data={ shownData } + view={ view } + fields={ _fields } + onChangeView={ setView } + defaultLayouts={ { + table: {}, + } } + /> + ); +}; + +export const CombinedFields = () => { + const [ view, setView ] = useState< View >( { + ...DEFAULT_VIEW, + fields: [ 'theme', 'requires', 'tested' ], + layout: { + combinedFields: [ + { + id: 'theme', + label: 'Theme', + children: [ 'name', 'description' ], + direction: 'vertical', + }, + ], + }, + } ); + const { data: shownData, paginationInfo } = useMemo( () => { + return filterSortAndPaginate( themeData, view, themeFields ); + }, [ view ] ); + + return ( + item.name } + paginationInfo={ paginationInfo } + data={ shownData } + view={ view } + fields={ themeFields } + onChangeView={ setView } + defaultLayouts={ { table: {} } } + /> + ); +}; From 9d4f918cd107cda355802ddffd1eb96358d1162f Mon Sep 17 00:00:00 2001 From: Miguel Lezama Date: Tue, 3 Sep 2024 08:06:28 -0300 Subject: [PATCH 005/131] Editor: Add extensibility to PreviewOptions v2 (#64644) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add plugin-preview-dropdown-item * Add slot * export PluginPreviewDropdownItem * add registerPlugin example Co-authored-by: Greg Ziółkowski * example import from @wordpress/editor Co-authored-by: Greg Ziółkowski * rename to PluginPreviewMenuItem * add tests tests --------- Co-authored-by: Greg Ziółkowski Co-authored-by: lezama Co-authored-by: Mamaduka Co-authored-by: gziolo Co-authored-by: simison Co-authored-by: fabiankaegy Co-authored-by: youknowriad Co-authored-by: fumikito Co-authored-by: westonruter --- packages/e2e-tests/plugins/plugins-api.php | 13 +++++ .../plugins/plugins-api/preview-menu.js | 14 +++++ packages/editor/README.md | 37 +++++++++++++ packages/editor/src/components/index.js | 1 + .../plugin-preview-menu-item/index.js | 52 +++++++++++++++++++ .../src/components/preview-dropdown/index.js | 6 +++ .../specs/editor/plugins/plugins-api.spec.js | 21 ++++++++ 7 files changed, 144 insertions(+) create mode 100644 packages/e2e-tests/plugins/plugins-api/preview-menu.js create mode 100644 packages/editor/src/components/plugin-preview-menu-item/index.js diff --git a/packages/e2e-tests/plugins/plugins-api.php b/packages/e2e-tests/plugins/plugins-api.php index fb8054924cebb0..10e35f16226f2a 100644 --- a/packages/e2e-tests/plugins/plugins-api.php +++ b/packages/e2e-tests/plugins/plugins-api.php @@ -86,6 +86,19 @@ function enqueue_plugins_api_plugin_scripts() { filemtime( plugin_dir_path( __FILE__ ) . 'plugins-api/document-setting.js' ), true ); + + wp_enqueue_script( + 'gutenberg-test-plugins-api-preview-menu', + plugins_url( 'plugins-api/preview-menu.js', __FILE__ ), + array( + 'wp-editor', + 'wp-element', + 'wp-i18n', + 'wp-plugins', + ), + filemtime( plugin_dir_path( __FILE__ ) . 'plugins-api/preview-menu.js' ), + true + ); } add_action( 'init', 'enqueue_plugins_api_plugin_scripts' ); diff --git a/packages/e2e-tests/plugins/plugins-api/preview-menu.js b/packages/e2e-tests/plugins/plugins-api/preview-menu.js new file mode 100644 index 00000000000000..1aa53b2e8509ac --- /dev/null +++ b/packages/e2e-tests/plugins/plugins-api/preview-menu.js @@ -0,0 +1,14 @@ +( function () { + const { __ } = wp.i18n; + const { registerPlugin } = wp.plugins; + const PluginPreviewMenuItem = wp.editor.PluginPreviewMenuItem; + const el = wp.element.createElement; + + function CustomPreviewMenuItem() { + return el( PluginPreviewMenuItem, {}, __( 'Custom Preview' ) ); + } + + registerPlugin( 'custom-preview-menu-item', { + render: CustomPreviewMenuItem, + } ); +} )(); diff --git a/packages/editor/README.md b/packages/editor/README.md index 89ea15ef378495..ebd4af31e287d8 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -862,6 +862,43 @@ _Returns_ - `Component`: The component to be rendered. +### PluginPreviewMenuItem + +Renders a menu item in the Preview dropdown, which can be used as a button or link depending on the props provided. The text within the component appears as the menu item label. + +_Usage_ + +```jsx +import { __ } from '@wordpress/i18n'; +import { PluginPreviewMenuItem } from '@wordpress/editor'; +import { external } from '@wordpress/icons'; + +function onPreviewClick() { + // Handle preview action +} + +const ExternalPreviewMenuItem = () => ( + + { __( 'Preview in new tab' ) } + +); +registerPlugin( 'external-preview-menu-item', { + render: ExternalPreviewMenuItem, +} ); +``` + +_Parameters_ + +- _props_ `Object`: Component properties. +- _props.href_ `[string]`: When `href` is provided, the menu item is rendered as an anchor instead of a button. It corresponds to the `href` attribute of the anchor. +- _props.icon_ `[WPBlockTypeIconRender]`: The icon to be rendered to the left of the menu item label. Can be a Dashicon slug or an SVG WP element. +- _props.onClick_ `[Function]`: The callback function to be executed when the user clicks the menu item. +- _props.other_ `[...*]`: Any additional props are passed through to the underlying MenuItem component. + +_Returns_ + +- `Component`: The rendered menu item component. + ### PluginSidebar Renders a sidebar when activated. The contents within the `PluginSidebar` will appear as content within the sidebar. It also automatically renders a corresponding `PluginSidebarMenuItem` component when `isPinnable` flag is set to `true`. If you wish to display the sidebar, you can with use the `PluginSidebarMoreMenuItem` component or the `wp.data.dispatch` API: diff --git a/packages/editor/src/components/index.js b/packages/editor/src/components/index.js index 91dcc883d661b2..b42566aac653be 100644 --- a/packages/editor/src/components/index.js +++ b/packages/editor/src/components/index.js @@ -32,6 +32,7 @@ export { default as PluginMoreMenuItem } from './plugin-more-menu-item'; export { default as PluginPostPublishPanel } from './plugin-post-publish-panel'; export { default as PluginPostStatusInfo } from './plugin-post-status-info'; export { default as PluginPrePublishPanel } from './plugin-pre-publish-panel'; +export { default as PluginPreviewMenuItem } from './plugin-preview-menu-item'; export { default as PluginSidebar } from './plugin-sidebar'; export { default as PluginSidebarMoreMenuItem } from './plugin-sidebar-more-menu-item'; export { default as PostTemplatePanel } from './post-template/panel'; diff --git a/packages/editor/src/components/plugin-preview-menu-item/index.js b/packages/editor/src/components/plugin-preview-menu-item/index.js new file mode 100644 index 00000000000000..422248e17b88e1 --- /dev/null +++ b/packages/editor/src/components/plugin-preview-menu-item/index.js @@ -0,0 +1,52 @@ +/** + * WordPress dependencies + */ +import { compose } from '@wordpress/compose'; +import { MenuItem } from '@wordpress/components'; +import { withPluginContext } from '@wordpress/plugins'; +import { ActionItem } from '@wordpress/interface'; + +/** + * Renders a menu item in the Preview dropdown, which can be used as a button or link depending on the props provided. + * The text within the component appears as the menu item label. + * + * @param {Object} props Component properties. + * @param {string} [props.href] When `href` is provided, the menu item is rendered as an anchor instead of a button. It corresponds to the `href` attribute of the anchor. + * @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The icon to be rendered to the left of the menu item label. Can be a Dashicon slug or an SVG WP element. + * @param {Function} [props.onClick] The callback function to be executed when the user clicks the menu item. + * @param {...*} [props.other] Any additional props are passed through to the underlying MenuItem component. + * + * @example + * ```jsx + * import { __ } from '@wordpress/i18n'; + * import { PluginPreviewMenuItem } from '@wordpress/editor'; + * import { external } from '@wordpress/icons'; + * + * function onPreviewClick() { + * // Handle preview action + * } + * + * const ExternalPreviewMenuItem = () => ( + * + * { __( 'Preview in new tab' ) } + * + * ); + * registerPlugin( 'external-preview-menu-item', { + * render: ExternalPreviewMenuItem, + * } ); + * ``` + * + * @return {Component} The rendered menu item component. + */ +export default compose( + withPluginContext( ( context, ownProps ) => { + return { + as: ownProps.as ?? MenuItem, + icon: ownProps.icon || context.icon, + name: 'core/plugin-preview-menu', + }; + } ) +)( ActionItem ); diff --git a/packages/editor/src/components/preview-dropdown/index.js b/packages/editor/src/components/preview-dropdown/index.js index ec30d55cf0f17b..8b51bb79bc8873 100644 --- a/packages/editor/src/components/preview-dropdown/index.js +++ b/packages/editor/src/components/preview-dropdown/index.js @@ -22,6 +22,7 @@ import { store as coreStore } from '@wordpress/core-data'; import { useEffect, useRef } from '@wordpress/element'; import { store as preferencesStore } from '@wordpress/preferences'; import { store as blockEditorStore } from '@wordpress/block-editor'; +import { ActionItem } from '@wordpress/interface'; /** * Internal dependencies @@ -206,6 +207,11 @@ export default function PreviewDropdown( { forceIsAutosaveable, disabled } ) { /> ) } + ) } diff --git a/test/e2e/specs/editor/plugins/plugins-api.spec.js b/test/e2e/specs/editor/plugins/plugins-api.spec.js index c71b49e3c4d815..c7f3a655e14240 100644 --- a/test/e2e/specs/editor/plugins/plugins-api.spec.js +++ b/test/e2e/specs/editor/plugins/plugins-api.spec.js @@ -230,4 +230,25 @@ test.describe( 'Plugins API', () => { ).toBeVisible(); } ); } ); + + test.describe( 'Preview Menu Item', () => { + test( 'Should render and interact with PluginPreviewMenuItem', async ( { + page, + } ) => { + await page + .getByRole( 'region', { name: 'Editor top bar' } ) + .locator( '.editor-preview-dropdown__toggle' ) + .click(); + + const customPreviewItem = page.getByRole( 'menuitem', { + name: 'Custom Preview', + } ); + + await expect( customPreviewItem ).toBeVisible(); + + await customPreviewItem.click(); + + await expect( customPreviewItem ).toBeHidden(); + } ); + } ); } ); From 7b7196f06b6bbfc2d138fb313b9c1d84e2a5f066 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 3 Sep 2024 15:53:24 +0200 Subject: [PATCH 006/131] Dataviews Filter search widget: do not use Composite store (#64985) * Dataviews Filter search widget: do not use Composite store * Use internal CompositeHover and CompositeTypeahead version * Better comment * Refactor generateCompositeItemId arguments * Export Composite.Typeahead and Composite.Hover as private APIs * CHANGELOG --- Co-authored-by: ciampo Co-authored-by: tyxla --- packages/components/CHANGELOG.md | 1 + packages/components/src/private-apis.ts | 2 + .../dataviews-filters/search-widget.tsx | 60 ++++++++++++++----- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 3b8d45be5861bc..5a58ddfda38b99 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -66,6 +66,7 @@ ### Internal +- `DropdownMenu` v2: expose CompositeTypeaheadV2 and CompositeHoverV2 via private APIs ([#64985](https://github.com/WordPress/gutenberg/pull/64985)). - `DropdownMenu` v2: fix flashing menu item styles when using keyboard ([#64873](https://github.com/WordPress/gutenberg/pull/64873), [#64942](https://github.com/WordPress/gutenberg/pull/64942)). - `DropdownMenu` v2: refactor to overloaded naming convention ([#64654](https://github.com/WordPress/gutenberg/pull/64654)). - `DropdownMenu` v2: add `GroupLabel` subcomponent ([#64854](https://github.com/WordPress/gutenberg/pull/64854)). diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts index fa9fece048617e..7bcab0e052e022 100644 --- a/packages/components/src/private-apis.ts +++ b/packages/components/src/private-apis.ts @@ -18,6 +18,8 @@ lock( privateApis, { CompositeGroupV2: Composite.Group, CompositeItemV2: Composite.Item, CompositeRowV2: Composite.Row, + CompositeTypeaheadV2: Composite.Typeahead, + CompositeHoverV2: Composite.Hover, useCompositeStoreV2: useCompositeStore, __experimentalPopoverLegacyPositionToPlacement, createPrivateSlotFill, diff --git a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx index 1b369222b8f28c..24ef3b5594b413 100644 --- a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx +++ b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx @@ -8,6 +8,7 @@ import removeAccents from 'remove-accents'; /** * WordPress dependencies */ +import { useInstanceId } from '@wordpress/compose'; import { __, sprintf } from '@wordpress/i18n'; import { useState, useMemo, useDeferredValue } from '@wordpress/element'; import { @@ -27,7 +28,8 @@ import type { Filter, NormalizedFilter, View } from '../../types'; const { CompositeV2: Composite, CompositeItemV2: CompositeItem, - useCompositeStoreV2: useCompositeStore, + CompositeHoverV2: CompositeHover, + CompositeTypeaheadV2: CompositeTypeahead, } = unlock( componentsPrivateApis ); interface SearchWidgetProps { @@ -84,22 +86,37 @@ const getNewValue = ( return [ value ]; }; +function generateFilterElementCompositeItemId( + prefix: string, + filterElementValue: string +) { + return `${ prefix }-${ filterElementValue }`; +} + function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) { - const compositeStore = useCompositeStore( { - virtualFocus: true, - focusLoop: true, - // When we have no or just one operator, we can set the first item as active. - // We do that by passing `undefined` to `defaultActiveId`. Otherwise, we set it to `null`, - // so the first item is not selected, since the focus is on the operators control. - defaultActiveId: filter.operators?.length === 1 ? undefined : null, - } ); + const baseId = useInstanceId( ListBox, 'dataviews-filter-list-box' ); + + const [ activeCompositeId, setActiveCompositeId ] = useState< + string | null | undefined + >( + // When there are one or less operators, the first item is set as active + // (by setting the initial `activeId` to `undefined`). + // With 2 or more operators, the focus is moved on the operators control + // (by setting the initial `activeId` to `null`), meaning that there won't + // be an active item initially. Focus is then managed via the + // `onFocusVisible` callback. + filter.operators?.length === 1 ? undefined : null + ); const currentFilter = view.filters?.find( ( f ) => f.field === filter.field ); const currentValue = getCurrentValue( filter, currentFilter ); return ( { - if ( ! compositeStore.getState().activeId ) { - compositeStore.move( compositeStore.first() ); + // `onFocusVisible` needs the `Composite` component to be focusable, + // which is implicitly achieved via the `virtualFocus: true` option + // in the `useCompositeStore` hook. + if ( ! activeCompositeId && filter.elements.length ) { + setActiveCompositeId( + generateFilterElementCompositeItemId( + baseId, + filter.elements[ 0 ].value + ) + ); } } } - render={ } + render={ } > { filter.elements.map( ( element ) => ( - { element.label } - + ) ) } ); From 66eeb388745456156d21cfc4733933a731a6c883 Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:57:23 +0200 Subject: [PATCH 007/131] Block Bindings: Add warning in attributes connected to invalid sources (#65002) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add warning with invalid sources * Change variable name Co-authored-by: Greg Ziółkowski * Use `isSourceInvalid` variable --------- Co-authored-by: SantosGuillamot Co-authored-by: gziolo Co-authored-by: ndiego Co-authored-by: jasmussen --- packages/block-editor/src/hooks/block-bindings.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index 3b90ecc1a0c178..ea069c15a028fe 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -96,16 +96,20 @@ function BlockBindingsAttribute( { attribute, binding } ) { const { source: sourceName, args } = binding || {}; const sourceProps = unlock( blocksPrivateApis ).getBlockBindingsSource( sourceName ); + const isSourceInvalid = ! sourceProps; return ( { attribute } { !! binding && ( - { args?.key || sourceProps?.label || sourceName } + { isSourceInvalid + ? __( 'Invalid source' ) + : args?.key || sourceProps?.label || sourceName } ) } From 84b48f7256dceae44432c8ee3b54433ed0674035 Mon Sep 17 00:00:00 2001 From: "Joen A." <1204802+jasmussen@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:00:50 +0200 Subject: [PATCH 008/131] Icons: Add thumbs up and down icons. (#65004) Co-authored-by: jasmussen Co-authored-by: jameskoster --- packages/icons/src/index.js | 2 ++ packages/icons/src/library/thumbs-down.js | 12 ++++++++++++ packages/icons/src/library/thumbs-up.js | 12 ++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 packages/icons/src/library/thumbs-down.js create mode 100644 packages/icons/src/library/thumbs-up.js diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js index 9250f413a8572b..11b3da927f3644 100644 --- a/packages/icons/src/index.js +++ b/packages/icons/src/index.js @@ -255,6 +255,8 @@ export { default as tableRowBefore } from './library/table-row-before'; export { default as tableRowDelete } from './library/table-row-delete'; export { default as table } from './library/table'; export { default as tag } from './library/tag'; +export { default as thumbsDown } from './library/thumbs-down'; +export { default as thumbsUp } from './library/thumbs-up'; export { default as symbolFilled } from './library/symbol-filled'; export { default as termDescription } from './library/term-description'; export { default as footer } from './library/footer'; diff --git a/packages/icons/src/library/thumbs-down.js b/packages/icons/src/library/thumbs-down.js new file mode 100644 index 00000000000000..94e11ee8f06386 --- /dev/null +++ b/packages/icons/src/library/thumbs-down.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const thumbsDown = ( + + + +); + +export default thumbsDown; diff --git a/packages/icons/src/library/thumbs-up.js b/packages/icons/src/library/thumbs-up.js new file mode 100644 index 00000000000000..f65b6445359e25 --- /dev/null +++ b/packages/icons/src/library/thumbs-up.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const thumbsUp = ( + + + +); + +export default thumbsUp; From 559edaedc00efd3cd9f47418904fb2214d970124 Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 4 Sep 2024 00:09:03 +1000 Subject: [PATCH 009/131] Color panel hook: rename to remove ambiguity (#64993) Co-authored-by: ramonjd Co-authored-by: ciampo --- .../src/components/global-styles/color-panel.js | 6 +++--- .../src/components/global-styles/color-panel.native.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/global-styles/color-panel.js b/packages/block-editor/src/components/global-styles/color-panel.js index 15f06310176c70..87c19adedbb27b 100644 --- a/packages/block-editor/src/components/global-styles/color-panel.js +++ b/packages/block-editor/src/components/global-styles/color-panel.js @@ -33,7 +33,7 @@ import { unlock } from '../../lock-unlock'; export function useHasColorPanel( settings ) { const hasTextPanel = useHasTextPanel( settings ); - const hasBackgroundPanel = useHasBackgroundPanel( settings ); + const hasBackgroundPanel = useHasBackgroundColorPanel( settings ); const hasLinkPanel = useHasLinkPanel( settings ); const hasHeadingPanel = useHasHeadingPanel( settings ); const hasButtonPanel = useHasButtonPanel( settings ); @@ -97,7 +97,7 @@ export function useHasButtonPanel( settings ) { ); } -export function useHasBackgroundPanel( settings ) { +export function useHasBackgroundColorPanel( settings ) { const colors = useColorsPerOrigin( settings ); const gradients = useGradientsPerOrigin( settings ); return ( @@ -347,7 +347,7 @@ export default function ColorPanel( { }; // BackgroundColor - const showBackgroundPanel = useHasBackgroundPanel( settings ); + const showBackgroundPanel = useHasBackgroundColorPanel( settings ); const backgroundColor = decodeValue( inheritedValue?.color?.background ); const userBackgroundColor = decodeValue( value?.color?.background ); const gradient = decodeValue( inheritedValue?.color?.gradient ); diff --git a/packages/block-editor/src/components/global-styles/color-panel.native.js b/packages/block-editor/src/components/global-styles/color-panel.native.js index fcbff4e5a07d78..87002b5fa3e22f 100644 --- a/packages/block-editor/src/components/global-styles/color-panel.native.js +++ b/packages/block-editor/src/components/global-styles/color-panel.native.js @@ -18,7 +18,7 @@ import InspectorControls from '../inspector-controls'; import { useHasColorPanel, useHasTextPanel, - useHasBackgroundPanel, + useHasBackgroundColorPanel, } from './color-panel.js'; import { useGlobalStyles } from './use-global-styles-context'; @@ -95,7 +95,7 @@ const ColorPanel = ( { ); // BackgroundColor - const showBackgroundPanel = useHasBackgroundPanel( settings ); + const showBackgroundPanel = useHasBackgroundColorPanel( settings ); const backgroundColor = decodeValue( inheritedValue?.color?.background ); const gradient = decodeValue( inheritedValue?.color?.gradient ); const setBackgroundColor = useCallback( From 7c0638b5a5ad278da458ff5d3fa4cd8a753e301d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:18:38 +0200 Subject: [PATCH 010/131] DataViews: fix field reordering and visibility logic (#64999) Co-authored-by: oandregal Co-authored-by: youknowriad Co-authored-by: jorgefilipecosta --- .../dataviews-view-config/index.tsx | 251 ++++++++---------- .../dataviews/src/dataviews-layouts/index.ts | 65 ++++- .../table/column-header-menu.tsx | 105 ++++---- .../src/dataviews-layouts/table/index.tsx | 5 +- packages/dataviews/src/normalize-fields.ts | 2 + packages/dataviews/src/types.ts | 2 + 6 files changed, 238 insertions(+), 192 deletions(-) diff --git a/packages/dataviews/src/components/dataviews-view-config/index.tsx b/packages/dataviews/src/components/dataviews-view-config/index.tsx index 331d0d62dca00a..c01c72d2ebc699 100644 --- a/packages/dataviews/src/components/dataviews-view-config/index.tsx +++ b/packages/dataviews/src/components/dataviews-view-config/index.tsx @@ -38,8 +38,13 @@ import { sortIcons, sortLabels, } from '../../constants'; -import { VIEW_LAYOUTS, getMandatoryFields } from '../../dataviews-layouts'; -import type { NormalizedField, SupportedLayouts, View } from '../../types'; +import { + VIEW_LAYOUTS, + getNotHidableFieldIds, + getVisibleFieldIds, + getHiddenFieldIds, +} from '../../dataviews-layouts'; +import type { SupportedLayouts, View, Field } from '../../types'; import DataViewsContext from '../dataviews-context'; import { unlock } from '../../lock-unlock'; import DensityPicker from '../../dataviews-layouts/grid/density-picker'; @@ -232,50 +237,34 @@ function ItemsPerPageControl() { ); } +interface FieldItemProps { + id: any; + label: string; + index: number; + isVisible: boolean; + isHidable: boolean; +} + function FieldItem( { + field: { id, label, index, isVisible, isHidable }, fields, - fieldId, - mandatoryFields, - viewFields, view, onChangeView, }: { - fields: NormalizedField< any >[]; - fieldId: string; - mandatoryFields: string | any[]; - viewFields: string[]; + field: FieldItemProps; + fields: Field< any >[]; view: View; onChangeView: ( view: View ) => void; } ) { - let fieldLabel; - let fieldIsHidable; - const fieldObject = fields.find( - ( f ) => f.id === fieldId - ) as NormalizedField< any >; - if ( fieldObject ) { - fieldLabel = fieldObject.label; - fieldIsHidable = - fieldObject.enableHiding !== false && - ! mandatoryFields.includes( fieldId ); - } else if ( view.type === LAYOUT_TABLE ) { - const combinedFieldObject = view.layout?.combinedFields?.find( - ( f ) => f.id === fieldId - ); - if ( combinedFieldObject ) { - fieldLabel = combinedFieldObject.label; - fieldIsHidable = ! mandatoryFields.includes( fieldId ); - } - } + const visibleFieldIds = getVisibleFieldIds( view, fields ); - const index = view.fields?.indexOf( fieldId ) as number; - const isVisible = viewFields.includes( fieldId ); return ( - + - { fieldLabel } + { label } , ); - - if ( isToggleButton || hasSingleBlockType ) { - inserterButton = ( - { inserterButton } - ); - } - return inserterButton; } } isAppender /> From 95c19951b11667fabc12b90c3f6affa3a73dd5bd Mon Sep 17 00:00:00 2001 From: Maggie Date: Fri, 6 Sep 2024 14:56:13 +0100 Subject: [PATCH 078/131] Zoom out: Try vertical displacement when dragging a pattern between existing patterns/sections (#63896) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create a function to check when we can add the separators Attempt to define sectionClientIds Pass sectionClientIds to isSectionBlock Rename sectionClientIds to sectionRootClientIds check if it's zoom out mode, add some CSS to the separator expand separators when the insertion point is changed fix animation only open the first separator if the insertion point index is 0 conditionally render the separators and animate them using framer instead of on class change remove unused code refactor separators do displacement on drag * Don’t show seperator if opertation is not insert * Fix bug where rootClientId was not within sectionRoot * Ignore Group operations on drag in Zoom Out mode * Check for ZoomOut separators when firing onDragEnd * Refine function to detect zoom out separator * Update detection based on data attribute * Make generic * Add comment for clarity * Improve code comments * Remove blue line inserter in ZoomOutmode * Move logic into consuming hook * Consume new API for getting root * Move pure function * Fix background showing through * Add UI feedback on dragover separator --------- Co-authored-by: Dave Smith Co-authored-by: MaggieCabrera Co-authored-by: getdave Co-authored-by: Mamaduka Co-authored-by: scruffian Co-authored-by: andrewserong Co-authored-by: richtabor --- .../src/components/block-list/content.scss | 21 ++++ .../src/components/block-list/index.js | 97 +++++++++------ .../block-list/zoom-out-separator.js | 110 ++++++++++++++++++ .../src/components/block-tools/index.js | 2 +- .../block-tools/zoom-out-mode-inserters.js | 12 -- .../components/use-block-drop-zone/index.js | 34 +++++- .../compose/src/hooks/use-drop-zone/index.js | 2 + 7 files changed, 226 insertions(+), 52 deletions(-) create mode 100644 packages/block-editor/src/components/block-list/zoom-out-separator.js diff --git a/packages/block-editor/src/components/block-list/content.scss b/packages/block-editor/src/components/block-list/content.scss index 95bb610da9967c..3f4b4c508aeb02 100644 --- a/packages/block-editor/src/components/block-list/content.scss +++ b/packages/block-editor/src/components/block-list/content.scss @@ -453,3 +453,24 @@ _::-webkit-full-page-media, _:future, :root .has-multi-selection .block-editor-b margin-bottom: auto; } } + +.block-editor-block-list__zoom-out-separator { + /* same color as the iframe's background */ + background: $gray-300; + // Additional -1px is required to avoid sub pixel rounding errors allowing background to show. + margin-left: -1px; + margin-right: -1px; + transition: background-color 0.3s ease; + + &.is-dragged-over { + background: $gray-400; + } +} + +// In Post Editor allow the separator to occupy the full width by ignoring (cancelling out) the global padding. +.has-global-padding > .block-editor-block-list__zoom-out-separator, +.block-editor-block-list__layout.is-root-container.has-global-padding > .block-editor-block-list__zoom-out-separator { + max-width: none; + // Additional -1px is required to avoid sub pixel rounding errors allowing background to show. + margin: 0 calc(-1 * var(--wp--style--root--padding-right) - 1px) 0 calc(-1 * var(--wp--style--root--padding-left) - 1px) !important; +} diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index 37dba80511d920..ea6128f1534642 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -39,6 +39,7 @@ import { DEFAULT_BLOCK_EDIT_CONTEXT, } from '../block-edit/context'; import { useTypingObserver } from '../observe-typing'; +import { ZoomOutSeparator } from './zoom-out-separator'; import { unlock } from '../../lock-unlock'; export const IntersectionObserver = createContext(); @@ -174,49 +175,55 @@ function Items( { // function on every render. const hasAppender = CustomAppender !== false; const hasCustomAppender = !! CustomAppender; - const { order, selectedBlocks, visibleBlocks, shouldRenderAppender } = - useSelect( - ( select ) => { - const { - getSettings, - getBlockOrder, - getSelectedBlockClientId, - getSelectedBlockClientIds, - __unstableGetVisibleBlocks, - getTemplateLock, - getBlockEditingMode, - __unstableGetEditorMode, - } = select( blockEditorStore ); - - const _order = getBlockOrder( rootClientId ); + const { + order, + isZoomOut, + selectedBlocks, + visibleBlocks, + shouldRenderAppender, + } = useSelect( + ( select ) => { + const { + getSettings, + getBlockOrder, + getSelectedBlockClientId, + getSelectedBlockClientIds, + __unstableGetVisibleBlocks, + getTemplateLock, + getBlockEditingMode, + __unstableGetEditorMode, + } = select( blockEditorStore ); - if ( getSettings().__unstableIsPreviewMode ) { - return { - order: _order, - selectedBlocks: EMPTY_ARRAY, - visibleBlocks: EMPTY_SET, - }; - } + const _order = getBlockOrder( rootClientId ); - const selectedBlockClientId = getSelectedBlockClientId(); + if ( getSettings().__unstableIsPreviewMode ) { return { order: _order, - selectedBlocks: getSelectedBlockClientIds(), - visibleBlocks: __unstableGetVisibleBlocks(), - shouldRenderAppender: - hasAppender && - __unstableGetEditorMode() !== 'zoom-out' && - ( hasCustomAppender - ? ! getTemplateLock( rootClientId ) && - getBlockEditingMode( rootClientId ) !== 'disabled' - : rootClientId === selectedBlockClientId || - ( ! rootClientId && - ! selectedBlockClientId && - ! _order.length ) ), + selectedBlocks: EMPTY_ARRAY, + visibleBlocks: EMPTY_SET, }; - }, - [ rootClientId, hasAppender, hasCustomAppender ] - ); + } + + const selectedBlockClientId = getSelectedBlockClientId(); + return { + order: _order, + selectedBlocks: getSelectedBlockClientIds(), + visibleBlocks: __unstableGetVisibleBlocks(), + isZoomOut: __unstableGetEditorMode() === 'zoom-out', + shouldRenderAppender: + hasAppender && + __unstableGetEditorMode() !== 'zoom-out' && + ( hasCustomAppender + ? ! getTemplateLock( rootClientId ) && + getBlockEditingMode( rootClientId ) !== 'disabled' + : rootClientId === selectedBlockClientId || + ( ! rootClientId && + ! selectedBlockClientId && + ! _order.length ) ), + }; + }, + [ rootClientId, hasAppender, hasCustomAppender ] + ); return ( @@ -230,10 +237,24 @@ function Items( { ! selectedBlocks.includes( clientId ) } > + { isZoomOut && ( + + ) } + { isZoomOut && ( + + ) } ) ) } { order.length < 1 && placeholder } diff --git a/packages/block-editor/src/components/block-list/zoom-out-separator.js b/packages/block-editor/src/components/block-list/zoom-out-separator.js new file mode 100644 index 00000000000000..be5af549630607 --- /dev/null +++ b/packages/block-editor/src/components/block-list/zoom-out-separator.js @@ -0,0 +1,110 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { + __unstableMotion as motion, + __unstableAnimatePresence as AnimatePresence, +} from '@wordpress/components'; +import { useReducedMotion } from '@wordpress/compose'; +import { useSelect } from '@wordpress/data'; +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; + +export function ZoomOutSeparator( { + clientId, + rootClientId = '', + position = 'top', +} ) { + const [ isDraggedOver, setIsDraggedOver ] = useState( false ); + const { + sectionRootClientId, + sectionClientIds, + blockInsertionPoint, + blockInsertionPointVisible, + } = useSelect( ( select ) => { + const { + getBlockInsertionPoint, + getBlockOrder, + isBlockInsertionPointVisible, + getSectionRootClientId, + } = unlock( select( blockEditorStore ) ); + + const root = getSectionRootClientId(); + const sectionRootClientIds = getBlockOrder( root ); + return { + sectionRootClientId: root, + sectionClientIds: sectionRootClientIds, + blockOrder: getBlockOrder( root ), + blockInsertionPoint: getBlockInsertionPoint(), + blockInsertionPointVisible: isBlockInsertionPointVisible(), + }; + }, [] ); + + const isReducedMotion = useReducedMotion(); + + if ( ! clientId ) { + return; + } + + let isVisible = false; + + const isSectionBlock = + rootClientId === sectionRootClientId && + sectionClientIds && + sectionClientIds.includes( clientId ); + + if ( ! isSectionBlock ) { + return null; + } + + if ( position === 'top' ) { + isVisible = + blockInsertionPointVisible && + blockInsertionPoint.index === 0 && + clientId === sectionClientIds[ blockInsertionPoint.index ]; + } + + if ( position === 'bottom' ) { + isVisible = + blockInsertionPointVisible && + clientId === sectionClientIds[ blockInsertionPoint.index - 1 ]; + } + + return ( + + { isVisible && ( + setIsDraggedOver( true ) } + onDragLeave={ () => setIsDraggedOver( false ) } + > + ) } + + ); +} diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index 163c35b54e4381..24f60dbbf970aa 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -202,7 +202,7 @@ export default function BlockTools( { // eslint-disable-next-line jsx-a11y/no-static-element-interactions
- { ! isTyping && ( + { ! isTyping && ! isZoomOutMode && ( diff --git a/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js b/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js index 30d51654b77cf4..79f8be3f9cfe97 100644 --- a/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js +++ b/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js @@ -88,18 +88,6 @@ function ZoomOutModeInserters() { previousClientId={ previousClientId } nextClientId={ nextClientId } > - { shouldRenderInsertionPoint && ( -
- ) } { ! shouldRenderInsertionPoint && ( Date: Fri, 6 Sep 2024 21:08:44 +0530 Subject: [PATCH 079/131] Fix: Button: Replace remaining 40px default size violations [Block library 1] (#65033) * Fix legacy comments to use 40px default button size * Fix the embed placeholder button to use 40px default button size * Fix freeform block to use 40px default button size * Fix missing block to use 40px default button size * Revert the instance for modal fullscreen button size This is being reverted because we need to resolve this first - https://github.com/WordPress/gutenberg/pull/65033#discussion_r1746056089 Co-authored-by: hbhalodia Co-authored-by: mirka <0mirka00@git.wordpress.org> --- packages/block-library/src/comments/edit/comments-legacy.js | 3 +-- packages/block-library/src/embed/embed-placeholder.js | 6 ++---- packages/block-library/src/freeform/modal.js | 6 ++---- packages/block-library/src/missing/edit.js | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/block-library/src/comments/edit/comments-legacy.js b/packages/block-library/src/comments/edit/comments-legacy.js index b5a12e34343dac..dbe89a4e3a83e1 100644 --- a/packages/block-library/src/comments/edit/comments-legacy.js +++ b/packages/block-library/src/comments/edit/comments-legacy.js @@ -29,8 +29,7 @@ export default function CommentsLegacy( { const actions = [ { ' ' }
) } - { shouldShowVisualToolbar && isMultiToolbar && ( - - ) } + { ! hasContentOnlyLocking && + shouldShowVisualToolbar && + isMultiToolbar && } { shouldShowVisualToolbar && ( <> Date: Mon, 9 Sep 2024 09:41:03 +1000 Subject: [PATCH 082/131] Patterns: add opt out preference to the 'Choose a Pattern' modal when adding a page (#65026) * Adds a preference to disable the loading of the Choose a pattern modal when adding new pages. * Return an empty array from the memoized value of useStartPatterns() so that the preferences modal has a value to test. * Don't reset the `isClosed` value in the same editor session. This prevents the modal popping open between page > template part > page navigation. Co-authored-by: ramonjd Co-authored-by: andrewserong Co-authored-by: kevin940726 Co-authored-by: noisysocks Co-authored-by: jasmussen Co-authored-by: jakobtrost Co-authored-by: johnstonphilip Co-authored-by: annezazu Co-authored-by: MattrCoUk Co-authored-by: jasonbahl Co-authored-by: colorful-tones Co-authored-by: Blindmikey Co-authored-by: richtabor Co-authored-by: luminuu Co-authored-by: stokesman Co-authored-by: jordesign --- packages/edit-post/src/index.js | 1 + packages/edit-site/src/index.js | 1 + packages/edit-site/src/posts.js | 1 + .../src/components/preferences-modal/index.js | 13 +++++ .../components/start-page-options/index.js | 54 ++++++++++--------- 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 51abac654da295..daf789cb0a2ec9 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -70,6 +70,7 @@ export function initializeEditor( showBlockBreadcrumbs: true, showIconLabels: false, showListViewByDefault: false, + enableChoosePatternModal: true, isPublishSidebarEnabled: true, } ); diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 4ed5b96e3844d1..1aceecc4d8b1fc 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -79,6 +79,7 @@ export function initializeEditor( id, settings ) { openPanels: [ 'post-status' ], showBlockBreadcrumbs: true, showListViewByDefault: false, + enableChoosePatternModal: true, } ); if ( window.__experimentalMediaProcessing ) { diff --git a/packages/edit-site/src/posts.js b/packages/edit-site/src/posts.js index 9e2582ac23328a..78d823b56c4c11 100644 --- a/packages/edit-site/src/posts.js +++ b/packages/edit-site/src/posts.js @@ -72,6 +72,7 @@ export function initializePostsDashboard( id, settings ) { openPanels: [ 'post-status' ], showBlockBreadcrumbs: true, showListViewByDefault: false, + enableChoosePatternModal: true, } ); dispatch( editSiteStore ).updateSettings( settings ); diff --git a/packages/editor/src/components/preferences-modal/index.js b/packages/editor/src/components/preferences-modal/index.js index e7cc637dd0aed0..a8cfd8245522cd 100644 --- a/packages/editor/src/components/preferences-modal/index.js +++ b/packages/editor/src/components/preferences-modal/index.js @@ -26,6 +26,7 @@ import PageAttributesCheck from '../page-attributes/check'; import PostTypeSupportCheck from '../post-type-support-check'; import { store as editorStore } from '../../store'; import { unlock } from '../../lock-unlock'; +import { useStartPatterns } from '../start-page-options'; const { PreferencesModal, @@ -57,6 +58,7 @@ export default function EditorPreferencesModal( { extraSections = {} } ) { const { setIsListViewOpened, setIsInserterOpened } = useDispatch( editorStore ); const { set: setPreference } = useDispatch( preferencesStore ); + const hasStarterPatterns = !! useStartPatterns().length; const sections = useMemo( () => @@ -97,6 +99,16 @@ export default function EditorPreferencesModal( { extraSections = {} } ) { 'Allow right-click contextual menus' ) } /> + { hasStarterPatterns && ( + + ) } { - // filter patterns without postTypes declared if the current postType is page - // or patterns that declare the current postType in its post type array. + if ( ! blockPatternsWithPostContentBlockType?.length ) { + return []; + } + + /* + * Filter patterns without postTypes declared if the current postType is page + * or patterns that declare the current postType in its post type array. + */ return blockPatternsWithPostContentBlockType.filter( ( pattern ) => { return ( ( postType === 'page' && ! pattern.postTypes ) || @@ -110,30 +118,24 @@ function StartPageOptionsModal( { onClose } ) { export default function StartPageOptions() { const [ isClosed, setIsClosed ] = useState( false ); - const { shouldEnableModal, postType, postId } = useSelect( ( select ) => { - const { - isEditedPostDirty, - isEditedPostEmpty, - getCurrentPostType, - getCurrentPostId, - } = select( editorStore ); - const _postType = getCurrentPostType(); - - return { - shouldEnableModal: - ! isEditedPostDirty() && - isEditedPostEmpty() && - TEMPLATE_POST_TYPE !== _postType, - postType: _postType, - postId: getCurrentPostId(), - }; + const shouldEnableModal = useSelect( ( select ) => { + const { isEditedPostDirty, isEditedPostEmpty, getCurrentPostType } = + select( editorStore ); + const preferencesModalActive = + select( interfaceStore ).isModalActive( 'editor/preferences' ); + const choosePatternModalEnabled = select( preferencesStore ).get( + 'core', + 'enableChoosePatternModal' + ); + return ( + choosePatternModalEnabled && + ! preferencesModalActive && + ! isEditedPostDirty() && + isEditedPostEmpty() && + TEMPLATE_POST_TYPE !== getCurrentPostType() + ); }, [] ); - useEffect( () => { - // Should reset the modal state when navigating to a new page/post. - setIsClosed( false ); - }, [ postType, postId ] ); - if ( ! shouldEnableModal || isClosed ) { return null; } From 82bc72dedd2815fbc0d407782fde2a12c4139a7e Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:57:21 +1000 Subject: [PATCH 083/131] Preferences: Fix back button on mobile (#65141) Co-authored-by: andrewserong Co-authored-by: ramonjd Co-authored-by: t-hamano --- .../src/components/preferences-modal-tabs/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/preferences/src/components/preferences-modal-tabs/index.js b/packages/preferences/src/components/preferences-modal-tabs/index.js index f70430367cc617..d87e565f5e3364 100644 --- a/packages/preferences/src/components/preferences-modal-tabs/index.js +++ b/packages/preferences/src/components/preferences-modal-tabs/index.js @@ -110,7 +110,7 @@ export default function PreferencesModalTabs( { sections } ) { return ( @@ -142,7 +142,7 @@ export default function PreferencesModalTabs( { sections } ) { return ( Date: Mon, 9 Sep 2024 13:47:19 +0530 Subject: [PATCH 084/131] Fix: Button: Replace remaining 40px default size violation [Block library 3] (#65110) * Fix the modal button for page list to use default 40px button size * Fix convert page list to navigation links edit button to use 40px default size * Fix enable comments form to use 40px default button size * Fix featured image block open media library button to use 40px default sizee * Fix site-logo block to use 40px default button size * Fix social link apply link button to use 40px default size * Revert social link apply button size to todo state --- .../block-library/src/page-list/convert-to-links-modal.js | 6 ++---- packages/block-library/src/page-list/edit.js | 3 +-- packages/block-library/src/post-comments-form/form.js | 3 +-- packages/block-library/src/post-featured-image/edit.js | 3 +-- packages/block-library/src/site-logo/edit.js | 6 ++---- packages/block-library/src/social-link/edit.js | 2 +- 6 files changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/block-library/src/page-list/convert-to-links-modal.js b/packages/block-library/src/page-list/convert-to-links-modal.js index b56b3bf7c6b4f1..f2ab425a1ef10a 100644 --- a/packages/block-library/src/page-list/convert-to-links-modal.js +++ b/packages/block-library/src/page-list/convert-to-links-modal.js @@ -32,16 +32,14 @@ export function ConvertToLinksModal( { onClick, onClose, disabled } ) {

- +
diff --git a/packages/editor/src/components/editor/index.js b/packages/editor/src/components/editor/index.js index b094c3ceb44376..21becea43cd7ee 100644 --- a/packages/editor/src/components/editor/index.js +++ b/packages/editor/src/components/editor/index.js @@ -74,11 +74,11 @@ function Editor( { { extraContent } + { children } - { children } ) } diff --git a/packages/editor/src/components/header/index.js b/packages/editor/src/components/header/index.js index b8eda30c721860..2e045ad7329428 100644 --- a/packages/editor/src/components/header/index.js +++ b/packages/editor/src/components/header/index.js @@ -142,6 +142,9 @@ function Header( { forceIsAutosaveable={ forceIsDirty } /> + { ( isWideViewport || ! showIconLabels ) && ( + + ) } { ! customSaveButton && ( ) } { customSaveButton } - { ( isWideViewport || ! showIconLabels ) && ( - - ) } From 537fb1800ce2784388ec3d130a37bd26f6f2f62f Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Mon, 9 Sep 2024 23:56:59 +0200 Subject: [PATCH 092/131] Add `useEvent` and `useObserveElementSize` to `@wordpress/compose` (#64943) * Simplify useResizeObserver * Loop through all resize entries * Add `useEvent` util. * Add `useObserveElementSize` util. * Simplify `useResizeObserver` by using `useEvent` and `useObserveElementSize`. * Switch to layout effect and accept refs too. * Prevent initial re-render in ResizeElement. * Better error message. * Improved example of useEvent. * Update packages/compose/src/hooks/use-event/index.ts Co-authored-by: Marin Atanasov <8436925+tyxla@users.noreply.github.com> * Sync docs. * Avoid redundant resize listener calls. * Switch to structural check. * Improve example. * Fix docs. * Make `useObserveElementSize` generic. * New API that returns a ref. * Make utility private for now. * Mark legacy `useResizeObserver` as such. * Rename `useObserveElementSize` to `useResizeObserver`. * Add return type. * Add signature as overload. * Add support for legacy API. * Move into subdirectory. * Minor import fix. * Fix docgen to support overloads (will pick up the first function signature). * Replace legacy utility with the new one. * Apply feedback. * Clean up and document. * Added changelog entries. --------- Co-authored-by: jsnajdr Co-authored-by: DaniGuardiola Co-authored-by: tyxla Co-authored-by: ciampo Co-authored-by: youknowriad --- packages/compose/CHANGELOG.md | 9 +- packages/compose/README.md | 52 ++++++-- packages/compose/src/hooks/use-event/index.ts | 51 ++++++++ .../{ => _legacy}/index.native.js | 0 .../{ => _legacy}/index.tsx | 44 +++---- .../{ => _legacy}/test/index.native.js | 2 +- .../src/hooks/use-resize-observer/index.ts | 119 ++++++++++++++++++ packages/compose/src/index.js | 1 + packages/docgen/lib/get-type-annotation.js | 4 + 9 files changed, 238 insertions(+), 44 deletions(-) create mode 100644 packages/compose/src/hooks/use-event/index.ts rename packages/compose/src/hooks/use-resize-observer/{ => _legacy}/index.native.js (100%) rename packages/compose/src/hooks/use-resize-observer/{ => _legacy}/index.tsx (86%) rename packages/compose/src/hooks/use-resize-observer/{ => _legacy}/test/index.native.js (96%) create mode 100644 packages/compose/src/hooks/use-resize-observer/index.ts diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md index 2683fe8d79be70..cc0d7ef333f0d6 100644 --- a/packages/compose/CHANGELOG.md +++ b/packages/compose/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### New Features + +- `useEvent`: a new utility that creates a stable callback function that has access to the latest state and can be used within event handlers and effect callbacks ([#64943](https://github.com/WordPress/gutenberg/pull/64943)). +- `useResizeObserver`: new and improved version of the utility (legacy API is still supported) ([#64943](https://github.com/WordPress/gutenberg/pull/64943)). + ## 7.7.0 (2024-09-05) ## 7.6.0 (2024-08-21) @@ -205,8 +210,8 @@ ### Breaking Changes -- Drop support for Internet Explorer 11 ([#31110](https://github.com/WordPress/gutenberg/pull/31110)). Learn more at https://make.wordpress.org/core/2021/04/22/ie-11-support-phase-out-plan/. -- Increase the minimum Node.js version to v12 matching Long Term Support releases ([#31270](https://github.com/WordPress/gutenberg/pull/31270)). Learn more at https://nodejs.org/en/about/releases/. +- Drop support for Internet Explorer 11 ([#31110](https://github.com/WordPress/gutenberg/pull/31110)). Learn more at . +- Increase the minimum Node.js version to v12 matching Long Term Support releases ([#31270](https://github.com/WordPress/gutenberg/pull/31270)). Learn more at . ## 3.25.0 (2021-03-17) diff --git a/packages/compose/README.md b/packages/compose/README.md index 0da853ad75c5af..b4e20a79bab0cc 100644 --- a/packages/compose/README.md +++ b/packages/compose/README.md @@ -305,6 +305,29 @@ _Returns_ - `import('react').RefCallback`: Element Ref. +### useEvent + +Creates a stable callback function that has access to the latest state and can be used within event handlers and effect callbacks. Throws when used in the render phase. + +_Usage_ + +```tsx +function Component( props ) { + const onClick = useEvent( props.onClick ); + useEffect( () => { + onClick(); + // Won't trigger the effect again when props.onClick is updated. + }, [ onClick ] ); + // Won't re-render Button when props.onClick is updated (if `Button` is + // wrapped in `React.memo`). + return ' - ), - { - button: ( - ' + ), + { + button: ( +