From 12ee69658a2dc250cc112e27136e0af1f3fbd6b4 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:39:44 +1000 Subject: [PATCH 01/12] Section Styles: Clean up block style variation filters (#62858) Co-authored-by: aaronrobertshaw Co-authored-by: ramonjd Co-authored-by: andrewserong --- lib/block-supports/block-style-variations.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/lib/block-supports/block-style-variations.php b/lib/block-supports/block-style-variations.php index 12c2453681b419..1c049f4a0fee58 100644 --- a/lib/block-supports/block-style-variations.php +++ b/lib/block-supports/block-style-variations.php @@ -274,19 +274,3 @@ function gutenberg_register_block_style_variations_from_theme_json_partials( $va } } } - -// DO NOT BACKPORT TO CORE. -// To be removed when core has backported this PR. -if ( function_exists( 'wp_resolve_block_style_variations_from_styles_registry' ) ) { - remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_styles_registry' ); -} -if ( function_exists( 'wp_resolve_block_style_variations_from_primary_theme_json' ) ) { - remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_primary_theme_json' ); -} -if ( function_exists( 'wp_resolve_block_style_variations_from_theme_json_partials' ) ) { - remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_theme_json_partials' ); -} -if ( function_exists( 'wp_resolve_block_style_variations_from_theme_style_variation' ) ) { - remove_filter( 'wp_theme_json_data_user', 'wp_resolve_block_style_variations_from_theme_style_variation' ); -} -// END OF DO NOT BACKPORT TO CORE. From 7717c1e199732e9701c27db646cc59d28762dc68 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Thu, 27 Jun 2024 00:31:47 -0400 Subject: [PATCH 02/12] Fix minor typos in Interactivity API Reference (#62890) Co-authored-by: jffng Co-authored-by: fabiankaegy --- docs/reference-guides/interactivity-api/api-reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference-guides/interactivity-api/api-reference.md b/docs/reference-guides/interactivity-api/api-reference.md index ba93e7fc529ebd..a898e437b40de8 100644 --- a/docs/reference-guides/interactivity-api/api-reference.md +++ b/docs/reference-guides/interactivity-api/api-reference.md @@ -531,7 +531,7 @@ The `unique-id` doesn't need to be unique globally. It just needs to be differen See store used with the directive above ```js -import { store, useState, useEffect } from '@wordpress/interactivity'; +import { getElement, store, useState, useEffect } from '@wordpress/interactivity'; // Unlike `data-wp-init` and `data-wp-watch`, you can use any hooks inside // `data-wp-run` callbacks. @@ -1071,7 +1071,7 @@ Those attributes will contain the directives of that element. In the button exam ```js // store -import { store, getContext } from '@wordpress/interactivity'; +import { store, getElement } from '@wordpress/interactivity'; store( "myPlugin", { actions: { From 8758b5c3da8111cb100f11487d33a963778ec4ea Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 26 Jun 2024 22:01:47 -0700 Subject: [PATCH 03/12] Remove link to polyfill.io (#62883) Co-authored-by: westonruter Co-authored-by: ockham Co-authored-by: ellatrix --- docs/contributors/code/scripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributors/code/scripts.md b/docs/contributors/code/scripts.md index 1483a409a4d08f..bcbbdbabc15a1e 100644 --- a/docs/contributors/code/scripts.md +++ b/docs/contributors/code/scripts.md @@ -64,7 +64,7 @@ It is recommended to use the main `wp-polyfill` script handle which takes care o | [Fetch Polyfill](https://www.npmjs.com/package/whatwg-fetch) | wp-polyfill-fetch | Polyfill that implements a subset of the standard Fetch specification | | [Promise Polyfill](https://www.npmjs.com/package/promise-polyfill) | wp-polyfill-promise | Lightweight ES6 Promise polyfill for the browser and node | | [Formdata Polyfill](https://www.npmjs.com/package/formdata-polyfill) | wp-polyfill-formdata | Polyfill conditionally replaces the native implementation | -| [Node Contains Polyfill](https://polyfill.io) | wp-polyfill-node-contains | Polyfill for Node.contains | +| [Node Contains Polyfill](https://www.npmjs.com/package/polyfill-library) | wp-polyfill-node-contains | Polyfill for Node.contains | | [Element Closest Polyfill](https://www.npmjs.com/package/element-closest) | wp-polyfill-element-closest | Return the closest element matching a selector up the DOM tree | ## Bundling and code sharing From 69edb243e0652da57b5f1a4ecacc13246e27b85c Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 27 Jun 2024 15:49:51 +1000 Subject: [PATCH 04/12] Block supports: ensure tools panel dropdown are visible on mobile (#62896) * Remove the const `TOOLSPANEL_DROPDOWNMENU_PROPS` in favour of a hook that returns different popover props depending on the viewport width * Remove offset completely * Add to block support panels * Replace in block library. Co-authored-by: ramonjd Co-authored-by: talldan --- .../global-styles/background-panel.js | 5 ++-- .../components/global-styles/border-panel.js | 5 ++-- .../components/global-styles/color-panel.js | 5 ++-- .../global-styles/dimensions-panel.js | 5 ++-- .../components/global-styles/filters-panel.js | 5 ++-- .../global-styles/image-settings-panel.js | 5 ++-- .../global-styles/typography-panel.js | 5 ++-- .../src/components/global-styles/utils.js | 23 ++++++++++++++----- .../block-support-tools-panel.js | 6 ++--- packages/block-library/src/image/image.js | 8 ++++--- packages/block-library/src/media-text/edit.js | 5 ++-- .../query/edit/inspector-controls/index.js | 5 ++-- packages/block-library/src/utils/constants.js | 8 ------- packages/block-library/src/utils/hooks.js | 14 +++++++++++ 14 files changed, 66 insertions(+), 38 deletions(-) delete mode 100644 packages/block-library/src/utils/constants.js diff --git a/packages/block-editor/src/components/global-styles/background-panel.js b/packages/block-editor/src/components/global-styles/background-panel.js index 307c742befafda..d4b2468bf2a255 100644 --- a/packages/block-editor/src/components/global-styles/background-panel.js +++ b/packages/block-editor/src/components/global-styles/background-panel.js @@ -34,7 +34,7 @@ import { isBlobURL } from '@wordpress/blob'; /** * Internal dependencies */ -import { TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils'; +import { useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; import MediaReplaceFlow from '../media-replace-flow'; import { store as blockEditorStore } from '../../store'; @@ -600,6 +600,7 @@ function BackgroundToolsPanel( { children, headerLabel, } ) { + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const resetAll = () => { const updatedValue = resetAllFilter( value ); onChange( updatedValue ); @@ -612,7 +613,7 @@ function BackgroundToolsPanel( { label={ headerLabel } resetAll={ resetAll } panelId={ panelId } - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } > { children } diff --git a/packages/block-editor/src/components/global-styles/border-panel.js b/packages/block-editor/src/components/global-styles/border-panel.js index a20bb15c044c51..cc7a464f8634a9 100644 --- a/packages/block-editor/src/components/global-styles/border-panel.js +++ b/packages/block-editor/src/components/global-styles/border-panel.js @@ -18,7 +18,7 @@ import { __ } from '@wordpress/i18n'; */ import BorderRadiusControl from '../border-radius-control'; import { useColorsPerOrigin } from './hooks'; -import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils'; +import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; import { useBorderPanelLabel } from '../../hooks/border'; import { ShadowPopover, useShadowPresets } from './shadow-panel-components'; @@ -69,6 +69,7 @@ function BorderToolsPanel( { children, label, } ) { + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const resetAll = () => { const updatedValue = resetAllFilter( value ); onChange( updatedValue ); @@ -79,7 +80,7 @@ function BorderToolsPanel( { label={ label } resetAll={ resetAll } panelId={ panelId } - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } > { children } 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 7b49ab453bb453..957d68edaf8492 100644 --- a/packages/block-editor/src/components/global-styles/color-panel.js +++ b/packages/block-editor/src/components/global-styles/color-panel.js @@ -27,7 +27,7 @@ import { __, sprintf } from '@wordpress/i18n'; */ import ColorGradientControl from '../colors-gradients/control'; import { useColorsPerOrigin, useGradientsPerOrigin } from './hooks'; -import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils'; +import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; import { unlock } from '../../lock-unlock'; @@ -116,6 +116,7 @@ function ColorToolsPanel( { panelId, children, } ) { + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const resetAll = () => { const updatedValue = resetAllFilter( value ); onChange( updatedValue ); @@ -131,7 +132,7 @@ function ColorToolsPanel( { className="color-block-support-panel" __experimentalFirstVisibleItemClass="first" __experimentalLastVisibleItemClass="last" - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } >
{ children } 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 9718545795f7c8..5711e16ffadcb1 100644 --- a/packages/block-editor/src/components/global-styles/dimensions-panel.js +++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js @@ -22,7 +22,7 @@ import { useCallback, useState, Platform } from '@wordpress/element'; /** * Internal dependencies */ -import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils'; +import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils'; import SpacingSizesControl from '../spacing-sizes-control'; import HeightControl from '../height-control'; import ChildLayoutControl from '../child-layout-control'; @@ -175,6 +175,7 @@ function DimensionsToolsPanel( { panelId, children, } ) { + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const resetAll = () => { const updatedValue = resetAllFilter( value ); onChange( updatedValue ); @@ -185,7 +186,7 @@ function DimensionsToolsPanel( { label={ __( 'Dimensions' ) } resetAll={ resetAll } panelId={ panelId } - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } > { children } diff --git a/packages/block-editor/src/components/global-styles/filters-panel.js b/packages/block-editor/src/components/global-styles/filters-panel.js index c22891684278b7..9eee1b3ff0ec75 100644 --- a/packages/block-editor/src/components/global-styles/filters-panel.js +++ b/packages/block-editor/src/components/global-styles/filters-panel.js @@ -28,7 +28,7 @@ import { useCallback, useMemo } from '@wordpress/element'; /** * Internal dependencies */ -import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils'; +import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; const EMPTY_ARRAY = []; @@ -72,6 +72,7 @@ function FiltersToolsPanel( { panelId, children, } ) { + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const resetAll = () => { const updatedValue = resetAllFilter( value ); onChange( updatedValue ); @@ -82,7 +83,7 @@ function FiltersToolsPanel( { label={ _x( 'Filters', 'Name for applying graphical effects' ) } resetAll={ resetAll } panelId={ panelId } - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } > { children } diff --git a/packages/block-editor/src/components/global-styles/image-settings-panel.js b/packages/block-editor/src/components/global-styles/image-settings-panel.js index 17c5ac1dd3112e..f668e7e5efc244 100644 --- a/packages/block-editor/src/components/global-styles/image-settings-panel.js +++ b/packages/block-editor/src/components/global-styles/image-settings-panel.js @@ -11,7 +11,7 @@ import { __, _x } from '@wordpress/i18n'; /** * Internal dependencies */ -import { TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils'; +import { useToolsPanelDropdownMenuProps } from './utils'; export function useHasImageSettingsPanel( name, value, inheritedValue ) { // Note: If lightbox `value` exists, that means it was @@ -30,6 +30,7 @@ export default function ImageSettingsPanel( { inheritedValue, panelId, } ) { + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const resetLightbox = () => { onChange( undefined ); }; @@ -52,7 +53,7 @@ export default function ImageSettingsPanel( { label={ _x( 'Settings', 'Image settings' ) } resetAll={ resetLightbox } panelId={ panelId } - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } > { const updatedValue = resetAllFilter( value ); onChange( updatedValue ); @@ -145,7 +146,7 @@ function TypographyToolsPanel( { label={ __( 'Typography' ) } resetAll={ resetAll } panelId={ panelId } - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } > { children } diff --git a/packages/block-editor/src/components/global-styles/utils.js b/packages/block-editor/src/components/global-styles/utils.js index 52752596594833..bf84e6f0b5765c 100644 --- a/packages/block-editor/src/components/global-styles/utils.js +++ b/packages/block-editor/src/components/global-styles/utils.js @@ -3,6 +3,11 @@ */ import fastDeepEqual from 'fast-deep-equal/es6'; +/** + * WordPress dependencies + */ +import { useViewportMatch } from '@wordpress/compose'; + /** * Internal dependencies */ @@ -136,12 +141,18 @@ export const STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE = { 'typography.fontFamily': 'fontFamily', }; -export const TOOLSPANEL_DROPDOWNMENU_PROPS = { - popoverProps: { - placement: 'left-start', - offset: 259, // Inner sidebar width (248px) - button width (24px) - border (1px) + padding (16px) + spacing (20px) - }, -}; +export function useToolsPanelDropdownMenuProps() { + const isMobile = useViewportMatch( 'medium', '<' ); + return ! isMobile + ? { + popoverProps: { + placement: 'left-start', + // For non-mobile, inner sidebar width (248px) - button width (24px) - border (1px) + padding (16px) + spacing (20px) + offset: 259, + }, + } + : {}; +} function findInPresetsBy( features, diff --git a/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.js b/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.js index 3f67135ad3c104..3eee7bf1b09574 100644 --- a/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.js +++ b/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.js @@ -10,7 +10,7 @@ import { useCallback } from '@wordpress/element'; */ import { store as blockEditorStore } from '../../store'; import { cleanEmptyObject } from '../../hooks/utils'; -import { TOOLSPANEL_DROPDOWNMENU_PROPS } from '../global-styles/utils'; +import { useToolsPanelDropdownMenuProps } from '../global-styles/utils'; export default function BlockSupportToolsPanel( { children, group, label } ) { const { updateBlockAttributes } = useDispatch( blockEditorStore ); @@ -20,7 +20,7 @@ export default function BlockSupportToolsPanel( { children, group, label } ) { getSelectedBlockClientId, hasMultiSelection, } = useSelect( blockEditorStore ); - + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const panelId = getSelectedBlockClientId(); const resetAll = useCallback( ( resetFilters = [] ) => { @@ -72,7 +72,7 @@ export default function BlockSupportToolsPanel( { children, group, label } ) { shouldRenderPlaceholderItems // Required to maintain fills ordering. __experimentalFirstVisibleItemClass="first" __experimentalLastVisibleItemClass="last" - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } > { children } diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index c96eb4e45117d9..a491ed5f1dfaa0 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -49,7 +49,7 @@ import { Caption } from '../utils/caption'; /** * Module constants */ -import { TOOLSPANEL_DROPDOWNMENU_PROPS } from '../utils/constants'; +import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; import { MIN_SIZE, ALLOWED_MEDIA_TYPES } from './constants'; import { evalAspectRatio } from './utils'; @@ -373,6 +373,8 @@ export default function Image( { const lightboxChecked = !! lightbox?.enabled || ( ! lightbox && !! lightboxSetting?.enabled ); + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); + const dimensionsControl = ( { isResizable && dimensionsControl } @@ -691,7 +693,7 @@ export default function Image( { { isSingleSelected && ( @@ -223,7 +224,7 @@ export default function QueryInspectorControls( props ) { } ); setQuerySearch( '' ); } } - dropdownMenuProps={ TOOLSPANEL_DROPDOWNMENU_PROPS } + dropdownMenuProps={ dropdownMenuProps } > { showTaxControl && ( Date: Thu, 27 Jun 2024 15:52:26 +1000 Subject: [PATCH 05/12] Fix extra scrollbar when a popover extends past the viewport. (#62894) Co-authored-by: tellthemachines Co-authored-by: talldan --- packages/editor/src/components/visual-editor/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/components/visual-editor/style.scss b/packages/editor/src/components/visual-editor/style.scss index f340f9f1313e34..b7fbf882a897ba 100644 --- a/packages/editor/src/components/visual-editor/style.scss +++ b/packages/editor/src/components/visual-editor/style.scss @@ -19,7 +19,7 @@ // In the iframed canvas this keeps extra scrollbars from appearing (when block toolbars overflow). In the // legacy (non-iframed) canvas, overflow must not be hidden in order to maintain support for sticky positioning. - .is-iframed { + &.is-iframed { overflow: hidden; } From 2e41a461dbb45ed34224fa446dcff47c813d1d89 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 27 Jun 2024 08:34:27 +0200 Subject: [PATCH 06/12] DataViews: remove the AnyItem type (#62856) Co-authored-by: jsnajdr Co-authored-by: ellatrix Co-authored-by: youknowriad --- .../dataviews/src/bulk-actions-toolbar.tsx | 18 +++--- packages/dataviews/src/bulk-actions.tsx | 22 +++---- packages/dataviews/src/dataviews.tsx | 18 ++++-- .../src/filter-and-sort-data-view.ts | 4 +- packages/dataviews/src/filters.tsx | 6 +- packages/dataviews/src/item-actions.tsx | 29 ++++----- packages/dataviews/src/normalize-fields.ts | 8 ++- .../src/single-selection-checkbox.tsx | 6 +- packages/dataviews/src/types.ts | 64 ++++++++++--------- packages/dataviews/src/utils.ts | 6 +- packages/dataviews/src/view-actions.tsx | 14 ++-- packages/dataviews/src/view-grid.tsx | 8 +-- packages/dataviews/src/view-list.tsx | 10 ++- packages/dataviews/src/view-table.tsx | 17 +++-- .../src/dataviews/store/private-actions.ts | 4 +- 15 files changed, 119 insertions(+), 115 deletions(-) diff --git a/packages/dataviews/src/bulk-actions-toolbar.tsx b/packages/dataviews/src/bulk-actions-toolbar.tsx index 56a8aa58e7dc40..50a1386aadec0f 100644 --- a/packages/dataviews/src/bulk-actions-toolbar.tsx +++ b/packages/dataviews/src/bulk-actions-toolbar.tsx @@ -18,24 +18,24 @@ import { useRegistry } from '@wordpress/data'; * Internal dependencies */ import { ActionWithModal } from './item-actions'; -import type { Action, AnyItem } from './types'; +import type { Action } from './types'; import type { ActionTriggerProps } from './item-actions'; -interface ActionButtonProps< Item extends AnyItem > { +interface ActionButtonProps< Item > { action: Action< Item >; selectedItems: Item[]; actionInProgress: string | null; setActionInProgress: ( actionId: string | null ) => void; } -interface ToolbarContentProps< Item extends AnyItem > { +interface ToolbarContentProps< Item > { selection: string[]; actionsToShow: Action< Item >[]; selectedItems: Item[]; onSelectionChange: ( selection: Item[] ) => void; } -interface BulkActionsToolbarProps< Item extends AnyItem > { +interface BulkActionsToolbarProps< Item > { data: Item[]; selection: string[]; actions: Action< Item >[]; @@ -62,7 +62,7 @@ const SNACKBAR_VARIANTS = { }, }; -function ActionTrigger< Item extends AnyItem >( { +function ActionTrigger< Item >( { action, onClick, isBusy, @@ -87,7 +87,7 @@ function ActionTrigger< Item extends AnyItem >( { const EMPTY_ARRAY: [] = []; -function ActionButton< Item extends AnyItem >( { +function ActionButton< Item >( { action, selectedItems, actionInProgress, @@ -125,7 +125,7 @@ function ActionButton< Item extends AnyItem >( { ); } -function renderToolbarContent< Item extends AnyItem >( +function renderToolbarContent< Item >( selection: string[], actionsToShow: Action< Item >[], selectedItems: Item[], @@ -179,7 +179,7 @@ function renderToolbarContent< Item extends AnyItem >( ); } -function ToolbarContent< Item extends AnyItem >( { +function ToolbarContent< Item >( { selection, actionsToShow, selectedItems, @@ -214,7 +214,7 @@ function ToolbarContent< Item extends AnyItem >( { return buttons.current; } -export default function BulkActionsToolbar< Item extends AnyItem >( { +export default function BulkActionsToolbar< Item >( { data, selection, actions = EMPTY_ARRAY, diff --git a/packages/dataviews/src/bulk-actions.tsx b/packages/dataviews/src/bulk-actions.tsx index fd4d5c390948d4..7f743bbeea1a2d 100644 --- a/packages/dataviews/src/bulk-actions.tsx +++ b/packages/dataviews/src/bulk-actions.tsx @@ -14,7 +14,7 @@ import { useRegistry } from '@wordpress/data'; * Internal dependencies */ import { unlock } from './lock-unlock'; -import type { Action, ActionModal, AnyItem } from './types'; +import type { Action, ActionModal } from './types'; const { DropdownMenuV2: DropdownMenu, @@ -23,26 +23,26 @@ const { DropdownMenuSeparatorV2: DropdownMenuSeparator, } = unlock( componentsPrivateApis ); -interface ActionWithModalProps< Item extends AnyItem > { +interface ActionWithModalProps< Item > { action: ActionModal< Item >; selectedItems: Item[]; setActionWithModal: ( action?: ActionModal< Item > ) => void; onMenuOpenChange: ( isOpen: boolean ) => void; } -interface BulkActionsItemProps< Item extends AnyItem > { +interface BulkActionsItemProps< Item > { action: Action< Item >; selectedItems: Item[]; setActionWithModal: ( action?: ActionModal< Item > ) => void; } -interface ActionsMenuGroupProps< Item extends AnyItem > { +interface ActionsMenuGroupProps< Item > { actions: Action< Item >[]; selectedItems: Item[]; setActionWithModal: ( action?: ActionModal< Item > ) => void; } -interface BulkActionsProps< Item extends AnyItem > { +interface BulkActionsProps< Item > { data: Item[]; actions: Action< Item >[]; selection: string[]; @@ -50,7 +50,7 @@ interface BulkActionsProps< Item extends AnyItem > { getItemId: ( item: Item ) => string; } -export function useHasAPossibleBulkAction< Item extends AnyItem >( +export function useHasAPossibleBulkAction< Item >( actions: Action< Item >[], item: Item ) { @@ -64,7 +64,7 @@ export function useHasAPossibleBulkAction< Item extends AnyItem >( }, [ actions, item ] ); } -export function useSomeItemHasAPossibleBulkAction< Item extends AnyItem >( +export function useSomeItemHasAPossibleBulkAction< Item >( actions: Action< Item >[], data: Item[] ) { @@ -80,7 +80,7 @@ export function useSomeItemHasAPossibleBulkAction< Item extends AnyItem >( }, [ actions, data ] ); } -function ActionWithModal< Item extends AnyItem >( { +function ActionWithModal< Item >( { action, selectedItems, setActionWithModal, @@ -115,7 +115,7 @@ function ActionWithModal< Item extends AnyItem >( { ); } -function BulkActionItem< Item extends AnyItem >( { +function BulkActionItem< Item >( { action, selectedItems, setActionWithModal, @@ -150,7 +150,7 @@ function BulkActionItem< Item extends AnyItem >( { ); } -function ActionsMenuGroup< Item extends AnyItem >( { +function ActionsMenuGroup< Item >( { actions, selectedItems, setActionWithModal, @@ -172,7 +172,7 @@ function ActionsMenuGroup< Item extends AnyItem >( { ); } -export default function BulkActions< Item extends AnyItem >( { +export default function BulkActions< Item >( { data, actions, selection, diff --git a/packages/dataviews/src/dataviews.tsx b/packages/dataviews/src/dataviews.tsx index de4deb36659f40..476ed895ed5297 100644 --- a/packages/dataviews/src/dataviews.tsx +++ b/packages/dataviews/src/dataviews.tsx @@ -21,9 +21,11 @@ import { VIEW_LAYOUTS } from './layouts'; import BulkActions from './bulk-actions'; import { normalizeFields } from './normalize-fields'; import BulkActionsToolbar from './bulk-actions-toolbar'; -import type { Action, AnyItem, Field, View, ViewBaseProps } from './types'; +import type { Action, Field, View, ViewBaseProps } from './types'; -interface DataViewsProps< Item extends AnyItem > { +type ItemWithId = { id: string }; + +type DataViewsProps< Item > = { view: View; onChangeView: ( view: View ) => void; fields: Field< Item >[]; @@ -31,7 +33,6 @@ interface DataViewsProps< Item extends AnyItem > { searchLabel?: string; actions?: Action< Item >[]; data: Item[]; - getItemId?: ( item: Item ) => string; isLoading?: boolean; paginationInfo: { totalItems: number; @@ -41,12 +42,15 @@ interface DataViewsProps< Item extends AnyItem > { selection?: string[]; setSelection?: ( selection: string[] ) => void; onSelectionChange?: ( items: Item[] ) => void; -} +} & ( Item extends ItemWithId + ? { getItemId?: ( item: Item ) => string } + : { getItemId: ( item: Item ) => string } ); + +const defaultGetItemId = ( item: ItemWithId ) => item.id; -const defaultGetItemId = ( item: AnyItem ) => item.id; const defaultOnSelectionChange = () => {}; -function useSomeItemHasAPossibleBulkAction< Item extends AnyItem >( +function useSomeItemHasAPossibleBulkAction< Item >( actions: Action< Item >[], data: Item[] ) { @@ -62,7 +66,7 @@ function useSomeItemHasAPossibleBulkAction< Item extends AnyItem >( }, [ actions, data ] ); } -export default function DataViews< Item extends AnyItem >( { +export default function DataViews< Item >( { view, onChangeView, fields, diff --git a/packages/dataviews/src/filter-and-sort-data-view.ts b/packages/dataviews/src/filter-and-sort-data-view.ts index a2906fdc4869e3..a62cdcccf6b868 100644 --- a/packages/dataviews/src/filter-and-sort-data-view.ts +++ b/packages/dataviews/src/filter-and-sort-data-view.ts @@ -15,7 +15,7 @@ import { OPERATOR_IS_NOT_ALL, } from './constants'; import { normalizeFields } from './normalize-fields'; -import type { Field, AnyItem, View } from './types'; +import type { Field, View } from './types'; function normalizeSearchInput( input = '' ) { return removeAccents( input.trim().toLowerCase() ); @@ -32,7 +32,7 @@ const EMPTY_ARRAY: [] = []; * * @return Filtered, sorted and paginated data. */ -export function filterSortAndPaginate< Item extends AnyItem >( +export function filterSortAndPaginate< Item >( data: Item[], view: View, fields: Field< Item >[] diff --git a/packages/dataviews/src/filters.tsx b/packages/dataviews/src/filters.tsx index 0cf017fce191b1..187f34b532ddef 100644 --- a/packages/dataviews/src/filters.tsx +++ b/packages/dataviews/src/filters.tsx @@ -12,9 +12,9 @@ import AddFilter from './add-filter'; import ResetFilters from './reset-filters'; import { sanitizeOperators } from './utils'; import { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from './constants'; -import type { AnyItem, NormalizedField, NormalizedFilter, View } from './types'; +import type { NormalizedField, NormalizedFilter, View } from './types'; -interface FiltersProps< Item extends AnyItem > { +interface FiltersProps< Item > { fields: NormalizedField< Item >[]; view: View; onChangeView: ( view: View ) => void; @@ -22,7 +22,7 @@ interface FiltersProps< Item extends AnyItem > { setOpenedFilter: ( openedFilter: string | null ) => void; } -function _Filters< Item extends AnyItem >( { +function _Filters< Item >( { fields, view, onChangeView, diff --git a/packages/dataviews/src/item-actions.tsx b/packages/dataviews/src/item-actions.tsx index fa6aae3336464a..631bcbf485de33 100644 --- a/packages/dataviews/src/item-actions.tsx +++ b/packages/dataviews/src/item-actions.tsx @@ -21,7 +21,7 @@ import { useRegistry } from '@wordpress/data'; * Internal dependencies */ import { unlock } from './lock-unlock'; -import type { Action, ActionModal as ActionModalType, AnyItem } from './types'; +import type { Action, ActionModal as ActionModalType } from './types'; const { DropdownMenuV2: DropdownMenu, @@ -31,42 +31,41 @@ const { kebabCase, } = unlock( componentsPrivateApis ); -export interface ActionTriggerProps< Item extends AnyItem > { +export interface ActionTriggerProps< Item > { action: Action< Item >; onClick: MouseEventHandler; isBusy?: boolean; items: Item[]; } -interface ActionModalProps< Item extends AnyItem > { +interface ActionModalProps< Item > { action: ActionModalType< Item >; items: Item[]; closeModal?: () => void; } -interface ActionWithModalProps< Item extends AnyItem > - extends ActionModalProps< Item > { +interface ActionWithModalProps< Item > extends ActionModalProps< Item > { ActionTrigger: ( props: ActionTriggerProps< Item > ) => ReactElement; isBusy?: boolean; } -interface ActionsDropdownMenuGroupProps< Item extends AnyItem > { +interface ActionsDropdownMenuGroupProps< Item > { actions: Action< Item >[]; item: Item; } -interface ItemActionsProps< Item extends AnyItem > { +interface ItemActionsProps< Item > { item: Item; actions: Action< Item >[]; isCompact?: boolean; } -interface CompactItemActionsProps< Item extends AnyItem > { +interface CompactItemActionsProps< Item > { item: Item; actions: Action< Item >[]; } -function ButtonTrigger< Item extends AnyItem >( { +function ButtonTrigger< Item >( { action, onClick, items, @@ -84,7 +83,7 @@ function ButtonTrigger< Item extends AnyItem >( { ); } -function DropdownMenuItemTrigger< Item extends AnyItem >( { +function DropdownMenuItemTrigger< Item >( { action, onClick, items, @@ -101,7 +100,7 @@ function DropdownMenuItemTrigger< Item extends AnyItem >( { ); } -export function ActionModal< Item extends AnyItem >( { +export function ActionModal< Item >( { action, items, closeModal, @@ -124,7 +123,7 @@ export function ActionModal< Item extends AnyItem >( { ); } -export function ActionWithModal< Item extends AnyItem >( { +export function ActionWithModal< Item >( { action, items, ActionTrigger, @@ -153,7 +152,7 @@ export function ActionWithModal< Item extends AnyItem >( { ); } -export function ActionsDropdownMenuGroup< Item extends AnyItem >( { +export function ActionsDropdownMenuGroup< Item >( { actions, item, }: ActionsDropdownMenuGroupProps< Item > ) { @@ -186,7 +185,7 @@ export function ActionsDropdownMenuGroup< Item extends AnyItem >( { ); } -export default function ItemActions< Item extends AnyItem >( { +export default function ItemActions< Item >( { item, actions, isCompact, @@ -247,7 +246,7 @@ export default function ItemActions< Item extends AnyItem >( { ); } -function CompactItemActions< Item extends AnyItem >( { +function CompactItemActions< Item >( { item, actions, }: CompactItemActionsProps< Item > ) { diff --git a/packages/dataviews/src/normalize-fields.ts b/packages/dataviews/src/normalize-fields.ts index 2c5edd91070c7d..a7a9a47734a961 100644 --- a/packages/dataviews/src/normalize-fields.ts +++ b/packages/dataviews/src/normalize-fields.ts @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import type { Field, AnyItem, NormalizedField } from './types'; +import type { Field, NormalizedField, ItemRecord } from './types'; /** * Apply default values and normalize the fields config. @@ -9,11 +9,13 @@ import type { Field, AnyItem, NormalizedField } from './types'; * @param fields Fields config. * @return Normalized fields config. */ -export function normalizeFields< Item extends AnyItem >( +export function normalizeFields< Item >( fields: Field< Item >[] ): NormalizedField< Item >[] { return fields.map( ( field ) => { - const getValue = field.getValue || ( ( { item } ) => item[ field.id ] ); + const getValue = + field.getValue || + ( ( { item }: { item: ItemRecord } ) => item[ field.id ] ); return { ...field, diff --git a/packages/dataviews/src/single-selection-checkbox.tsx b/packages/dataviews/src/single-selection-checkbox.tsx index 7c61b8e4aaa83c..84b359508663b3 100644 --- a/packages/dataviews/src/single-selection-checkbox.tsx +++ b/packages/dataviews/src/single-selection-checkbox.tsx @@ -7,9 +7,9 @@ import { CheckboxControl } from '@wordpress/components'; /** * Internal dependencies */ -import type { Field, AnyItem } from './types'; +import type { Field } from './types'; -interface SingleSelectionCheckboxProps< Item extends AnyItem > { +interface SingleSelectionCheckboxProps< Item > { selection: string[]; onSelectionChange: ( selection: Item[] ) => void; item: Item; @@ -19,7 +19,7 @@ interface SingleSelectionCheckboxProps< Item extends AnyItem > { disabled: boolean; } -export default function SingleSelectionCheckbox< Item extends AnyItem >( { +export default function SingleSelectionCheckbox< Item >( { selection, onSelectionChange, item, diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index b01394c7f846a1..76b514755056a1 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -37,12 +37,12 @@ export type Operator = | 'isAll' | 'isNotAll'; -export type AnyItem = Record< string, any >; +export type ItemRecord = Record< string, unknown >; /** * A dataview field for a specific property of a data type. */ -export interface Field< Item extends AnyItem > { +export type Field< Item > = { /** * The unique identifier of the field. */ @@ -53,12 +53,6 @@ export interface Field< Item extends AnyItem > { */ header?: string; - /** - * Callback used to retrieve the value of the field from the item. - * Defaults to `item[ field.id ]`. - */ - getValue?: ( args: { item: Item } ) => any; - /** * Callback used to render the field. Defaults to `field.getValue`. */ @@ -103,17 +97,34 @@ export interface Field< Item extends AnyItem > { * Filter config for the field. */ filterBy?: FilterByConfig | undefined; -} - -export type NormalizedField< Item extends AnyItem > = Field< Item > & - Required< Pick< Field< Item >, 'header' | 'getValue' | 'render' > >; +} & ( Item extends ItemRecord + ? { + /** + * Callback used to retrieve the value of the field from the item. + * Defaults to `item[ field.id ]`. + */ + getValue?: ( args: { item: Item } ) => any; + } + : { + /** + * Callback used to retrieve the value of the field from the item. + * Defaults to `item[ field.id ]`. + */ + getValue: ( args: { item: Item } ) => any; + } ); + +export type NormalizedField< Item > = Field< Item > & { + header: string; + getValue: ( args: { item: Item } ) => any; + render: ( args: { item: Item } ) => ReactNode; +}; /** * A collection of dataview fields for a data type. */ -export type Fields< Item extends AnyItem > = Field< Item >[]; +export type Fields< Item > = Field< Item >[]; -export type Data< Item extends AnyItem > = Item[]; +export type Data< Item > = Item[]; /** * The filters applied to the dataset. @@ -279,7 +290,7 @@ export interface ViewGrid extends ViewBase { export type View = ViewList | ViewGrid | ViewTable; -interface ActionBase< Item extends AnyItem > { +interface ActionBase< Item > { /** * The unique identifier of the action. */ @@ -325,8 +336,7 @@ interface ActionBase< Item extends AnyItem > { supportsBulk?: boolean; } -export interface ActionModal< Item extends AnyItem > - extends ActionBase< Item > { +export interface ActionModal< Item > extends ActionBase< Item > { /** * Modal to render when the action is triggered. */ @@ -351,8 +361,7 @@ export interface ActionModal< Item extends AnyItem > modalHeader?: string; } -export interface ActionButton< Item extends AnyItem > - extends ActionBase< AnyItem > { +export interface ActionButton< Item > extends ActionBase< Item > { /** * The callback to execute when the action is triggered. */ @@ -365,11 +374,9 @@ export interface ActionButton< Item extends AnyItem > ) => void; } -export type Action< Item extends AnyItem > = - | ActionModal< Item > - | ActionButton< Item >; +export type Action< Item > = ActionModal< Item > | ActionButton< Item >; -export interface ViewBaseProps< Item extends AnyItem > { +export interface ViewBaseProps< Item > { actions: Action< Item >[]; data: Item[]; fields: NormalizedField< Item >[]; @@ -382,22 +389,19 @@ export interface ViewBaseProps< Item extends AnyItem > { view: View; } -export interface ViewTableProps< Item extends AnyItem > - extends ViewBaseProps< Item > { +export interface ViewTableProps< Item > extends ViewBaseProps< Item > { view: ViewTable; } -export interface ViewListProps< Item extends AnyItem > - extends ViewBaseProps< Item > { +export interface ViewListProps< Item > extends ViewBaseProps< Item > { view: ViewList; } -export interface ViewGridProps< Item extends AnyItem > - extends ViewBaseProps< Item > { +export interface ViewGridProps< Item > extends ViewBaseProps< Item > { view: ViewGrid; } -export type ViewProps< Item extends AnyItem > = +export type ViewProps< Item > = | ViewTableProps< Item > | ViewGridProps< Item > | ViewListProps< Item >; diff --git a/packages/dataviews/src/utils.ts b/packages/dataviews/src/utils.ts index d895289318da03..408288c5174897 100644 --- a/packages/dataviews/src/utils.ts +++ b/packages/dataviews/src/utils.ts @@ -8,11 +8,9 @@ import { OPERATOR_IS_ANY, OPERATOR_IS_NONE, } from './constants'; -import type { AnyItem, NormalizedField } from './types'; +import type { NormalizedField } from './types'; -export function sanitizeOperators< Item extends AnyItem >( - field: NormalizedField< Item > -) { +export function sanitizeOperators< Item >( field: NormalizedField< Item > ) { let operators = field.filterBy?.operators; // Assign default values. diff --git a/packages/dataviews/src/view-actions.tsx b/packages/dataviews/src/view-actions.tsx index 90098417575315..f83a5887065ec3 100644 --- a/packages/dataviews/src/view-actions.tsx +++ b/packages/dataviews/src/view-actions.tsx @@ -20,7 +20,7 @@ import { settings } from '@wordpress/icons'; import { unlock } from './lock-unlock'; import { SORTING_DIRECTIONS, sortLabels } from './constants'; import { VIEW_LAYOUTS } from './layouts'; -import type { AnyItem, NormalizedField, View } from './types'; +import type { NormalizedField, View } from './types'; const { DropdownMenuV2: DropdownMenu, @@ -42,19 +42,19 @@ interface PageSizeMenuProps { onChangeView: ( view: View ) => void; } -interface FieldsVisibilityMenuProps< Item extends AnyItem > { +interface FieldsVisibilityMenuProps< Item > { view: View; onChangeView: ( view: View ) => void; fields: NormalizedField< Item >[]; } -interface SortMenuProps< Item extends AnyItem > { +interface SortMenuProps< Item > { fields: NormalizedField< Item >[]; view: View; onChangeView: ( view: View ) => void; } -interface ViewActionsProps< Item extends AnyItem > { +interface ViewActionsProps< Item > { fields: NormalizedField< Item >[]; view: View; onChangeView: ( view: View ) => void; @@ -161,7 +161,7 @@ function PageSizeMenu( { view, onChangeView }: PageSizeMenuProps ) { ); } -function FieldsVisibilityMenu< Item extends AnyItem >( { +function FieldsVisibilityMenu< Item >( { view, onChangeView, fields, @@ -215,7 +215,7 @@ function FieldsVisibilityMenu< Item extends AnyItem >( { ); } -function SortMenu< Item extends AnyItem >( { +function SortMenu< Item >( { fields, view, onChangeView, @@ -303,7 +303,7 @@ function SortMenu< Item extends AnyItem >( { ); } -function _ViewActions< Item extends AnyItem >( { +function _ViewActions< Item >( { fields, view, onChangeView, diff --git a/packages/dataviews/src/view-grid.tsx b/packages/dataviews/src/view-grid.tsx index 4538ab145d2134..8fa9d6413d851d 100644 --- a/packages/dataviews/src/view-grid.tsx +++ b/packages/dataviews/src/view-grid.tsx @@ -22,9 +22,9 @@ import { __ } from '@wordpress/i18n'; import ItemActions from './item-actions'; import SingleSelectionCheckbox from './single-selection-checkbox'; import { useHasAPossibleBulkAction } from './bulk-actions'; -import type { Action, AnyItem, NormalizedField, ViewGridProps } from './types'; +import type { Action, NormalizedField, ViewGridProps } from './types'; -interface GridItemProps< Item extends AnyItem > { +interface GridItemProps< Item > { selection: string[]; data: Item[]; onSelectionChange: ( items: Item[] ) => void; @@ -38,7 +38,7 @@ interface GridItemProps< Item extends AnyItem > { columnFields?: string[]; } -function GridItem< Item extends AnyItem >( { +function GridItem< Item >( { selection, data, onSelectionChange, @@ -187,7 +187,7 @@ function GridItem< Item extends AnyItem >( { ); } -export default function ViewGrid< Item extends AnyItem >( { +export default function ViewGrid< Item >( { actions, data, fields, diff --git a/packages/dataviews/src/view-list.tsx b/packages/dataviews/src/view-list.tsx index eb2b9c6c077a6a..d6714a16e82df0 100644 --- a/packages/dataviews/src/view-list.tsx +++ b/packages/dataviews/src/view-list.tsx @@ -33,11 +33,11 @@ import { useRegistry } from '@wordpress/data'; * Internal dependencies */ import { unlock } from './lock-unlock'; -import type { Action, AnyItem, NormalizedField, ViewListProps } from './types'; +import type { Action, NormalizedField, ViewListProps } from './types'; import { ActionsDropdownMenuGroup, ActionModal } from './item-actions'; -interface ListViewItemProps< Item extends AnyItem > { +interface ListViewItemProps< Item > { actions: Action< Item >[]; id?: string; isSelected: boolean; @@ -57,7 +57,7 @@ const { DropdownMenuV2: DropdownMenu, } = unlock( componentsPrivateApis ); -function ListItem< Item extends AnyItem >( { +function ListItem< Item >( { actions, id, isSelected, @@ -303,9 +303,7 @@ function ListItem< Item extends AnyItem >( { ); } -export default function ViewList< Item extends AnyItem >( - props: ViewListProps< Item > -) { +export default function ViewList< Item >( props: ViewListProps< Item > ) { const { actions, data, diff --git a/packages/dataviews/src/view-table.tsx b/packages/dataviews/src/view-table.tsx index 66e59a8ebb4230..f09b46733e1a84 100644 --- a/packages/dataviews/src/view-table.tsx +++ b/packages/dataviews/src/view-table.tsx @@ -46,7 +46,6 @@ import { } from './bulk-actions'; import type { Action, - AnyItem, NormalizedField, SortDirection, ViewTable as ViewTableType, @@ -62,7 +61,7 @@ const { DropdownMenuSeparatorV2: DropdownMenuSeparator, } = unlock( componentsPrivateApis ); -interface HeaderMenuProps< Item extends AnyItem > { +interface HeaderMenuProps< Item > { field: NormalizedField< Item >; view: ViewTableType; onChangeView: ( view: ViewTableType ) => void; @@ -70,7 +69,7 @@ interface HeaderMenuProps< Item extends AnyItem > { setOpenedFilter: ( fieldId: string ) => void; } -interface BulkSelectionCheckboxProps< Item extends AnyItem > { +interface BulkSelectionCheckboxProps< Item > { selection: string[]; onSelectionChange: ( items: Item[] ) => void; data: Item[]; @@ -78,7 +77,7 @@ interface BulkSelectionCheckboxProps< Item extends AnyItem > { getItemId: ( item: Item ) => string; } -interface TableRowProps< Item extends AnyItem > { +interface TableRowProps< Item > { hasBulkActions: boolean; item: Item; actions: Action< Item >[]; @@ -102,7 +101,7 @@ function WithDropDownMenuSeparators( { children }: { children: ReactNode } ) { ) ); } -const _HeaderMenu = forwardRef( function HeaderMenu< Item extends AnyItem >( +const _HeaderMenu = forwardRef( function HeaderMenu< Item >( { field, view, @@ -240,12 +239,12 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item extends AnyItem >( } ); // @ts-expect-error Lift the `Item` type argument through the forwardRef. -const HeaderMenu: < Item extends AnyItem >( +const HeaderMenu: < Item >( props: PropsWithoutRef< HeaderMenuProps< Item > > & RefAttributes< HTMLButtonElement > ) => ReturnType< typeof _HeaderMenu > = _HeaderMenu; -function BulkSelectionCheckbox< Item extends AnyItem >( { +function BulkSelectionCheckbox< Item >( { selection, onSelectionChange, data, @@ -287,7 +286,7 @@ function BulkSelectionCheckbox< Item extends AnyItem >( { ); } -function TableRow< Item extends AnyItem >( { +function TableRow< Item >( { hasBulkActions, item, actions, @@ -425,7 +424,7 @@ function TableRow< Item extends AnyItem >( { ); } -function ViewTable< Item extends AnyItem >( { +function ViewTable< Item >( { actions, data, fields, diff --git a/packages/editor/src/dataviews/store/private-actions.ts b/packages/editor/src/dataviews/store/private-actions.ts index a74e1b5e79844a..562e4140ed806a 100644 --- a/packages/editor/src/dataviews/store/private-actions.ts +++ b/packages/editor/src/dataviews/store/private-actions.ts @@ -1,9 +1,9 @@ /** * WordPress dependencies */ -import type { Action, AnyItem } from '@wordpress/dataviews'; +import type { Action } from '@wordpress/dataviews'; -export function registerEntityAction< Item extends AnyItem >( +export function registerEntityAction< Item >( kind: string, name: string, config: Action< Item > From 49b9692a793a6d5cbc2eb23e5fd93a2f31091de0 Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 27 Jun 2024 16:34:34 +1000 Subject: [PATCH 07/12] Global styles revisions: ensure that user-defined variation styles CSS is generated (#62768) * Render styles after the variation style overrides have been saved to stage. Getting closer. But the overrides in state need to be merged with incoming revision styles. * For every current override, update the variation CSS with the incoming config from the revision. * Rename hook Destructure in hook so the consumer doesn't have to clone Only send the override overrides to EditorStyles that need to be overridden. * Fetching overrides in the hook * Feedback suggestions from review: add overrides to dep array in Editor Styles rename hook * Return getBlockStyles from the useSelect callback * Refactor so we don't have to change the EditorStyles props Register revision overrides with useStyleOverride * Adding some explanatory comments Add rudimentary E2E test covering block style partials, applying them, updating them and viewing styles revisions. * Removed unused style fixture Co-authored-by: ramonjd Co-authored-by: aaronrobertshaw Co-authored-by: ellatrix --- .../src/hooks/block-style-variation.js | 122 ++++++ packages/block-editor/src/hooks/index.js | 1 + packages/block-editor/src/hooks/utils.js | 2 + packages/block-editor/src/private-apis.js | 7 +- .../src/components/revisions/index.js | 10 +- .../block-style-variations.spec.js | 366 ++++++++++++++++++ .../block-templates/singular.html | 2 + .../styles/block-style-variation-a.json | 20 + .../styles/block-style-variation-b.json | 20 + 9 files changed, 548 insertions(+), 2 deletions(-) create mode 100644 test/e2e/specs/site-editor/block-style-variations.spec.js create mode 100644 test/gutenberg-test-themes/style-variations/block-templates/singular.html create mode 100644 test/gutenberg-test-themes/style-variations/styles/block-style-variation-a.json create mode 100644 test/gutenberg-test-themes/style-variations/styles/block-style-variation-b.json diff --git a/packages/block-editor/src/hooks/block-style-variation.js b/packages/block-editor/src/hooks/block-style-variation.js index 336db687558f52..21259966d8a63b 100644 --- a/packages/block-editor/src/hooks/block-style-variation.js +++ b/packages/block-editor/src/hooks/block-style-variation.js @@ -16,6 +16,7 @@ import { import { useStyleOverride } from './utils'; import { store as blockEditorStore } from '../store'; import { globalStylesDataKey } from '../store/private-keys'; +import { unlock } from '../lock-unlock'; const VARIATION_PREFIX = 'is-style-'; @@ -59,6 +60,126 @@ function getVariationNameFromClass( className, registeredStyles = [] ) { return null; } +// A helper component to apply a style override using the useStyleOverride hook. +function OverrideStyles( { override } ) { + useStyleOverride( override ); +} + +/** + * This component is used to generate new block style variation overrides + * based on an incoming theme config. If a matching style is found in the config, + * a new override is created and returned. The overrides can be used in conjunction with + * useStyleOverride to apply the new styles to the editor. Its use is + * subject to change. + * + * @param {Object} props Props. + * @param {Object} props.config A global styles object, containing settings and styles. + * @return {JSX.Element|undefined} An array of new block variation overrides. + */ +export function __unstableBlockStyleVariationOverridesWithConfig( { config } ) { + const { getBlockStyles, overrides } = useSelect( + ( select ) => ( { + getBlockStyles: select( blocksStore ).getBlockStyles, + overrides: unlock( select( blockEditorStore ) ).getStyleOverrides(), + } ), + [] + ); + const { getBlockName } = useSelect( blockEditorStore ); + + const overridesWithConfig = useMemo( () => { + if ( ! overrides?.length ) { + return; + } + const newOverrides = []; + const overriddenClientIds = []; + for ( const [ , override ] of overrides ) { + if ( + override?.variation && + override?.clientId && + /* + * Because this component overwrites existing style overrides, + * filter out any overrides that are already present in the store. + */ + ! overriddenClientIds.includes( override.clientId ) + ) { + const blockName = getBlockName( override.clientId ); + const configStyles = + config?.styles?.blocks?.[ blockName ]?.variations?.[ + override.variation + ]; + if ( configStyles ) { + const variationConfig = { + settings: config?.settings, + // The variation style data is all that is needed to generate + // the styles for the current application to a block. The variation + // name is updated to match the instance specific class name. + styles: { + blocks: { + [ blockName ]: { + variations: { + [ `${ override.variation }-${ override.clientId }` ]: + configStyles, + }, + }, + }, + }, + }; + const blockSelectors = getBlockSelectors( + getBlockTypes(), + getBlockStyles, + override.clientId + ); + const hasBlockGapSupport = false; + const hasFallbackGapSupport = true; + const disableLayoutStyles = true; + const disableRootPadding = true; + const variationStyles = toStyles( + variationConfig, + blockSelectors, + hasBlockGapSupport, + hasFallbackGapSupport, + disableLayoutStyles, + disableRootPadding, + { + blockGap: false, + blockStyles: true, + layoutStyles: false, + marginReset: false, + presets: false, + rootPadding: false, + variationStyles: true, + } + ); + newOverrides.push( { + id: `${ override.variation }-${ override.clientId }`, + css: variationStyles, + __unstableType: 'variation', + variation: override.variation, + // The clientId will be stored with the override and used to ensure + // the order of overrides matches the order of blocks so that the + // correct CSS cascade is maintained. + clientId: override.clientId, + } ); + overriddenClientIds.push( override.clientId ); + } + } + } + return newOverrides; + }, [ config, overrides, getBlockStyles, getBlockName ] ); + + if ( ! overridesWithConfig || ! overridesWithConfig.length ) { + return; + } + + return ( + <> + { overridesWithConfig.map( ( override ) => ( + + ) ) } + + ); +} + function useBlockStyleVariation( name, variation, clientId ) { // Prefer global styles data in GlobalStylesContext, which are available // if in the site editor. Otherwise fall back to whatever is in the @@ -157,6 +278,7 @@ function useBlockProps( { name, className, clientId } ) { id: `variation-${ clientId }`, css: variationStyles, __unstableType: 'variation', + variation, // The clientId will be stored with the override and used to ensure // the order of overrides matches the order of blocks so that the // correct CSS cascade is maintained. diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index 89e6819c1d0314..bd1835571fdd4a 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -88,3 +88,4 @@ export { getTypographyClassesAndStyles } from './use-typography-props'; export { getGapCSSValue } from './gap'; export { useCachedTruthy } from './use-cached-truthy'; export { useZoomOut } from './use-zoom-out'; +export { __unstableBlockStyleVariationOverridesWithConfig } from './block-style-variation'; diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index d4eb7df553d3c0..26700ecf7b3fab 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -140,6 +140,7 @@ export function useStyleOverride( { css, assets, __unstableType, + variation, clientId, } = {} ) { const { setStyleOverride, deleteStyleOverride } = unlock( @@ -159,6 +160,7 @@ export function useStyleOverride( { css, assets, __unstableType, + variation, clientId, }; // Batch updates to style overrides to avoid triggering cascading renders diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index e6f3fc4cc39d6a..bfa6ac0c90c846 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -20,7 +20,11 @@ import { cleanEmptyObject, useStyleOverride } from './hooks/utils'; import BlockQuickNavigation from './components/block-quick-navigation'; import { LayoutStyle } from './components/block-list/layout'; import { BlockRemovalWarningModal } from './components/block-removal-warning-modal'; -import { useLayoutClasses, useLayoutStyles } from './hooks'; +import { + useLayoutClasses, + useLayoutStyles, + __unstableBlockStyleVariationOverridesWithConfig, +} from './hooks'; import DimensionsTool from './components/dimensions-tool'; import ResolutionTool from './components/resolution-tool'; import TextAlignmentControl from './components/text-alignment-control'; @@ -88,4 +92,5 @@ lock( privateApis, { PrivatePublishDateTimePicker, useSpacingSizes, useBlockDisplayTitle, + __unstableBlockStyleVariationOverridesWithConfig, } ); diff --git a/packages/edit-site/src/components/revisions/index.js b/packages/edit-site/src/components/revisions/index.js index b726e79b15f2f7..d43b5e8d2ac025 100644 --- a/packages/edit-site/src/components/revisions/index.js +++ b/packages/edit-site/src/components/revisions/index.js @@ -25,6 +25,7 @@ const { ExperimentalBlockEditorProvider, GlobalStylesContext, useGlobalStylesOutputWithConfig, + __unstableBlockStyleVariationOverridesWithConfig, } = unlock( blockEditorPrivateApis ); const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); @@ -74,7 +75,6 @@ function Revisions( { userConfig, blocks } ) { name="revisions" tabIndex={ 0 } > -