From 6435b27f13f54a0442a1ec8f7d5dbb144cc26233 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 21 Dec 2023 21:04:49 +0400 Subject: [PATCH 01/63] Block API: Fix the block 'edit' property validation (#57193) * Block API: Fix the block 'edit' property validation * Update unit test * Don't pin package version --- package-lock.json | 12 ++++++++++++ packages/blocks/package.json | 1 + packages/blocks/src/api/test/registration.js | 4 ++-- packages/blocks/src/store/process-block-type.js | 5 +++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d048fae712ed..4ecc9173085c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54651,6 +54651,7 @@ "hpq": "^1.3.0", "is-plain-object": "^5.0.0", "memize": "^2.1.0", + "react-is": "^18.2.0", "rememo": "^4.0.2", "remove-accents": "^0.5.0", "showdown": "^1.9.1", @@ -54664,6 +54665,11 @@ "react": "^18.0.0" } }, + "packages/blocks/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "packages/blocks/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -70104,6 +70110,7 @@ "hpq": "^1.3.0", "is-plain-object": "^5.0.0", "memize": "^2.1.0", + "react-is": "^18.2.0", "rememo": "^4.0.2", "remove-accents": "^0.5.0", "showdown": "^1.9.1", @@ -70111,6 +70118,11 @@ "uuid": "^9.0.1" }, "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "uuid": { "version": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 5f124f9d4e7d7..a2aff291dbda3 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -50,6 +50,7 @@ "hpq": "^1.3.0", "is-plain-object": "^5.0.0", "memize": "^2.1.0", + "react-is": "^18.2.0", "rememo": "^4.0.2", "remove-accents": "^0.5.0", "showdown": "^1.9.1", diff --git a/packages/blocks/src/api/test/registration.js b/packages/blocks/src/api/test/registration.js index 6347912c0402c..8433bbcb04ca3 100644 --- a/packages/blocks/src/api/test/registration.js +++ b/packages/blocks/src/api/test/registration.js @@ -168,7 +168,7 @@ describe( 'blocks', () => { it( 'should reject blocks with an invalid edit function', () => { const blockType = { save: noop, - edit: 'not-a-function', + edit: {}, category: 'text', title: 'block title', }, @@ -177,7 +177,7 @@ describe( 'blocks', () => { blockType ); expect( console ).toHaveErroredWith( - 'The "edit" property must be a valid function.' + 'The "edit" property must be a valid component.' ); expect( block ).toBeUndefined(); } ); diff --git a/packages/blocks/src/store/process-block-type.js b/packages/blocks/src/store/process-block-type.js index 59b48979b07eb..889f59b55e392 100644 --- a/packages/blocks/src/store/process-block-type.js +++ b/packages/blocks/src/store/process-block-type.js @@ -2,6 +2,7 @@ * External dependencies */ import { isPlainObject } from 'is-plain-object'; +import { isValidElementType } from 'react-is'; /** * WordPress dependencies @@ -113,8 +114,8 @@ export const processBlockType = error( 'The "save" property must be a valid function.' ); return; } - if ( 'edit' in settings && typeof settings.edit !== 'function' ) { - error( 'The "edit" property must be a valid function.' ); + if ( 'edit' in settings && ! isValidElementType( settings.edit ) ) { + error( 'The "edit" property must be a valid component.' ); return; } From 73954e3db152f962a130b3b067820c6b5837ac20 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 21 Dec 2023 19:15:35 +0100 Subject: [PATCH 02/63] Refactor experimental dropdown menu usages to latest version (#55625) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * First attempt at refactoring the view actions dropdown * note * Force all menus to have "left-start" placement. This helps having a more coherent keyboard navigation behaviour * Port changes missed in rebase due to file renames * ViewActions: remove unnecessary icon management * ViewTable: remove SubMenu, SubMenuTrigger, and use RadioItem * Use Ariakit versions in FilterSummary as well * AddFilter: migrate to Ariakit * Update sort behavior to match RadioItem (cannot be unset once set) * Remove old add-filter file * Use ariakit components * remove submenu trigger * DropdownMenuItem: substitute onSelect by onClick * Remove old filter-summary file * FilterSummary: use the ariakit versions * Substitute trigger by menuitem * DropdownMenuItem: substitute onSelect by onClick * Remove old view-table file * ViewTable: update to ariakit * Remove trigger * DropdownMenuItem: substitute onSelect by onClick * Remove DropdownSubMenu in favor of DropdownMenu across dataviews package * Remove suffix chevronrightsmall across dataviews * Fix rebase for view-actions * Remove unnecessary label * Use DropdownMenuItemLabel component * Hide suffix contents to assistive tech * Refactor conditions to use radio menu items * Hide suffix contents to assistive technology * Fix checked check * Remove unnecessary preventDefault() calls * Improve isActive check, remove extra icons, remove extra preventDefault(), add import * Fix label not appearing * Use filter name instead of field, as done for other menus in the file * Rename isActive to isChecked * Remove unneeded radio semantics from hide button * Refactor to DropdownMenuRadioItem * Refactor to DrodownMenuRadioItem * Refactor OPERATORS * Refactor filter summery operators to radio items * Fix OPERATORS object && checks * Remove comment * Refactor operators code in view table * Add custom spacing for custom menu radio items * Remove comment * Replace preventDefault with hideOnClick={false} * Move OPERATORS object to common constants file * Use the `onChange` event instead of `onClick` on radio items + the event value * Extract custom dropdown radio item implementation * Swap actual menu radio items with custom implementation * Fix warning when onChange prop is not defined * Add a few min widths to dropdowns to avoid jumping and for better while space balance * Extract sorting directions to constants * Refactor more code to use the OPERATORS constant instead of individual operators * Remove un-needed workaround, now that Truncate has been updated * Remove hardcoded left placements * Use dot icon instead of check for custom radios * Use real DropdownMenuRadioItem where possible * Fix e2e test * Sort dependencies * Update package.lock --------- Co-authored-by: André Maneiro <583546+oandregal@users.noreply.github.com> --- package-lock.json | 2 + packages/dataviews/package.json | 1 + packages/dataviews/src/add-filter.js | 240 ++++++++--------- packages/dataviews/src/constants.js | 16 ++ .../dataviews/src/dropdown-menu-helper.js | 58 ++++ packages/dataviews/src/filter-summary.js | 134 ++++------ packages/dataviews/src/style.scss | 5 + packages/dataviews/src/view-actions.js | 201 +++++++------- packages/dataviews/src/view-table.js | 248 +++++++----------- .../site-editor/new-templates-list.spec.js | 2 +- 10 files changed, 436 insertions(+), 471 deletions(-) create mode 100644 packages/dataviews/src/dropdown-menu-helper.js diff --git a/package-lock.json b/package-lock.json index 4ecc9173085c3..c52010a3347fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55042,6 +55042,7 @@ "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", "@wordpress/keycodes": "file:../keycodes", + "@wordpress/primitives": "file:../primitives", "@wordpress/private-apis": "file:../private-apis", "classnames": "^2.3.1", "remove-accents": "^0.5.0" @@ -70393,6 +70394,7 @@ "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", "@wordpress/keycodes": "file:../keycodes", + "@wordpress/primitives": "file:../primitives", "@wordpress/private-apis": "file:../private-apis", "classnames": "^2.3.1", "remove-accents": "^0.5.0" diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index fd506e6f821c4..4dfb35948af46 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -35,6 +35,7 @@ "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", "@wordpress/keycodes": "file:../keycodes", + "@wordpress/primitives": "file:../primitives", "@wordpress/private-apis": "file:../private-apis", "classnames": "^2.3.1", "remove-accents": "^0.5.0" diff --git a/packages/dataviews/src/add-filter.js b/packages/dataviews/src/add-filter.js index c403baa9c13d3..eb244bff4782b 100644 --- a/packages/dataviews/src/add-filter.js +++ b/packages/dataviews/src/add-filter.js @@ -4,9 +4,8 @@ import { privateApis as componentsPrivateApis, Button, - Icon, } from '@wordpress/components'; -import { chevronRightSmall, funnel, check } from '@wordpress/icons'; +import { funnel } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; import { Children, Fragment } from '@wordpress/element'; @@ -14,15 +13,16 @@ import { Children, Fragment } from '@wordpress/element'; * Internal dependencies */ import { unlock } from './lock-unlock'; -import { LAYOUT_LIST, OPERATOR_IN, OPERATOR_NOT_IN } from './constants'; +import { LAYOUT_LIST, OPERATORS } from './constants'; +import { DropdownMenuRadioItemCustom } from './dropdown-menu-helper'; const { - DropdownMenuV2: DropdownMenu, - DropdownMenuGroupV2: DropdownMenuGroup, - DropdownSubMenuV2: DropdownSubMenu, - DropdownSubMenuTriggerV2: DropdownSubMenuTrigger, - DropdownMenuItemV2: DropdownMenuItem, - DropdownMenuSeparatorV2: DropdownMenuSeparator, + DropdownMenuV2Ariakit: DropdownMenu, + DropdownMenuGroupV2Ariakit: DropdownMenuGroup, + DropdownMenuItemV2Ariakit: DropdownMenuItem, + DropdownMenuRadioItemV2Ariakit: DropdownMenuRadioItem, + DropdownMenuSeparatorV2Ariakit: DropdownMenuSeparator, + DropdownMenuItemLabelV2Ariakit: DropdownMenuItemLabel, } = unlock( componentsPrivateApis ); function WithSeparators( { children } ) { @@ -50,7 +50,6 @@ export default function AddFilter( { filters, view, onChangeView } ) { return ( } + style={ { + minWidth: '230px', + } } > @@ -82,31 +84,29 @@ export default function AddFilter( { filters, view, onChangeView } ) { const activeOperator = filterInView?.operator || filter.operators[ 0 ]; return ( - - { activeElement && - activeOperator === - OPERATOR_IN && - __( 'Is' ) } - { activeElement && - activeOperator === - OPERATOR_NOT_IN && - __( 'Is not' ) } - { activeElement && ' ' } - { activeElement?.label } - - + activeElement && ( + + ) } > - { filter.name } - + + { filter.name } + + } + style={ { + minWidth: '200px', + } } > @@ -115,19 +115,12 @@ export default function AddFilter( { filters, view, onChangeView } ) { activeElement?.value === element.value; return ( - - ) - } - onSelect={ ( event ) => { - event.preventDefault(); + name={ `add-filter-${ filter.field.id }` } + value={ element.value } + checked={ isActive } + onClick={ () => { onChangeView( { ...view, page: 1, @@ -145,106 +138,77 @@ export default function AddFilter( { filters, view, onChangeView } ) { } ); } } > - { element.label } - + + { element.label } + + ); } ) } { filter.operators.length > 1 && ( - - { activeOperator === - OPERATOR_IN && - __( 'Is' ) } - { activeOperator === - OPERATOR_NOT_IN && - __( 'Is not' ) } - { ' ' } - + } > - { __( 'Conditions' ) } - + + { __( 'Conditions' ) } + + } > - - ) - } - onSelect={ ( event ) => { - event.preventDefault(); - onChangeView( { - ...view, - page: 1, - filters: [ - ...otherFilters, - { - field: filter.field, - operator: - OPERATOR_IN, - value: filterInView?.value, - }, - ], - } ); - } } - > - { __( 'Is' ) } - - - ) - } - onSelect={ ( event ) => { - event.preventDefault(); - onChangeView( { - ...view, - page: 1, - filters: [ - ...otherFilters, - { - field: filter.field, - operator: - OPERATOR_NOT_IN, - value: filterInView?.value, - }, - ], - } ); - } } - > - { __( 'Is not' ) } - - + { Object.entries( OPERATORS ).map( + ( [ + operator, + { label, key }, + ] ) => ( + { + onChangeView( { + ...view, + page: 1, + filters: [ + ...otherFilters, + { + field: filter.field, + operator: + e + .target + .value, + value: filterInView?.value, + }, + ], + } ); + } } + > + + { label } + + + ) + ) } + ) } { - event.preventDefault(); + hideOnClick={ false } + onClick={ () => { onChangeView( ( currentView ) => ( { ...currentView, page: 1, @@ -257,14 +221,16 @@ export default function AddFilter( { filters, view, onChangeView } ) { } ) ); } } > - { sprintf( - /* translators: 1: Filter name. e.g.: "Reset Author". */ - __( 'Reset %1$s' ), - filter.name.toLowerCase() - ) } + + { sprintf( + /* translators: 1: Filter name. e.g.: "Reset Author". */ + __( 'Reset %1$s' ), + filter.name.toLowerCase() + ) } + - + ); } ) } @@ -272,8 +238,8 @@ export default function AddFilter( { filters, view, onChangeView } ) { disabled={ view.search === '' && view.filters?.length === 0 } - onSelect={ ( event ) => { - event.preventDefault(); + hideOnClick={ false } + onClick={ () => { onChangeView( ( currentView ) => ( { ...currentView, page: 1, @@ -281,7 +247,9 @@ export default function AddFilter( { filters, view, onChangeView } ) { } ) ); } } > - { __( 'Reset filters' ) } + + { __( 'Reset filters' ) } + diff --git a/packages/dataviews/src/constants.js b/packages/dataviews/src/constants.js index 387050a1dca5b..b3d17d7fd1145 100644 --- a/packages/dataviews/src/constants.js +++ b/packages/dataviews/src/constants.js @@ -22,6 +22,22 @@ export const ENUMERATION_TYPE = 'enumeration'; // Filter operators. export const OPERATOR_IN = 'in'; export const OPERATOR_NOT_IN = 'notIn'; +export const OPERATORS = { + [ OPERATOR_IN ]: { + key: 'in-filter', + label: __( 'Is' ), + }, + [ OPERATOR_NOT_IN ]: { + key: 'not-in-filter', + label: __( 'Is not' ), + }, +}; + +// Sorting +export const SORTING_DIRECTIONS = { + asc: { label: __( 'Sort ascending' ) }, + desc: { label: __( 'Sort descending' ) }, +}; // View layouts. export const LAYOUT_TABLE = 'table'; diff --git a/packages/dataviews/src/dropdown-menu-helper.js b/packages/dataviews/src/dropdown-menu-helper.js new file mode 100644 index 0000000000000..ab59e7c764ae5 --- /dev/null +++ b/packages/dataviews/src/dropdown-menu-helper.js @@ -0,0 +1,58 @@ +/** + * WordPress dependencies + */ +import { + Icon, + privateApis as componentsPrivateApis, +} from '@wordpress/components'; +import { forwardRef } from '@wordpress/element'; +import { SVG, Circle } from '@wordpress/primitives'; + +/** + * Internal dependencies + */ +import { unlock } from './lock-unlock'; + +const { DropdownMenuItemV2Ariakit: DropdownMenuItem } = unlock( + componentsPrivateApis +); + +const radioCheck = ( + + + +); + +/** + * A custom implementation of a radio menu item using the standard menu item + * component, which allows deselecting selected values. + */ +export const DropdownMenuRadioItemCustom = forwardRef( + ( { checked, name, value, onChange, onClick, ...props }, ref ) => { + const onClickHandler = ( e ) => { + onClick?.( e ); + onChange?.( { ...e, target: { ...e.target, value } } ); + }; + return ( + + ) : ( + + ) + } + onClick={ onClickHandler } + { ...props } + /> + ); + } +); diff --git a/packages/dataviews/src/filter-summary.js b/packages/dataviews/src/filter-summary.js index 098e73db5eeb3..99eb796f0a8bf 100644 --- a/packages/dataviews/src/filter-summary.js +++ b/packages/dataviews/src/filter-summary.js @@ -6,23 +6,23 @@ import { privateApis as componentsPrivateApis, Icon, } from '@wordpress/components'; -import { chevronDown, chevronRightSmall, check } from '@wordpress/icons'; +import { chevronDown } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; import { Children, Fragment } from '@wordpress/element'; /** * Internal dependencies */ -import { OPERATOR_IN, OPERATOR_NOT_IN } from './constants'; +import { OPERATOR_IN, OPERATOR_NOT_IN, OPERATORS } from './constants'; import { unlock } from './lock-unlock'; +import { DropdownMenuRadioItemCustom } from './dropdown-menu-helper'; const { - DropdownMenuV2: DropdownMenu, - DropdownMenuGroupV2: DropdownMenuGroup, - DropdownMenuItemV2: DropdownMenuItem, - DropdownMenuSeparatorV2: DropdownMenuSeparator, - DropdownSubMenuV2: DropdownSubMenu, - DropdownSubMenuTriggerV2: DropdownSubMenuTrigger, + DropdownMenuV2Ariakit: DropdownMenu, + DropdownMenuGroupV2Ariakit: DropdownMenuGroup, + DropdownMenuItemV2Ariakit: DropdownMenuItem, + DropdownMenuSeparatorV2Ariakit: DropdownMenuSeparator, + DropdownMenuItemLabelV2Ariakit: DropdownMenuItemLabel, } = unlock( componentsPrivateApis ); const FilterText = ( { activeElement, filterInView, filter } ) => { @@ -101,12 +101,12 @@ export default function FilterSummary( { filter, view, onChangeView } ) { { filter.elements.map( ( element ) => { const isActive = activeElement?.value === element.value; return ( - } - onSelect={ () => + name={ `filter-summary-${ filter.field.id }` } + value={ element.value } + checked={ isActive } + onClick={ () => onChangeView( { ...view, page: 1, @@ -123,82 +123,58 @@ export default function FilterSummary( { filter, view, onChangeView } ) { } ) } > - { element.label } - + + { element.label } + + ); } ) } { filter.operators.length > 1 && ( - - { activeOperator === OPERATOR_IN && - __( 'Is' ) } - { activeOperator === OPERATOR_NOT_IN && - __( 'Is not' ) } - { ' ' } - + } > - { __( 'Conditions' ) } - + + { __( 'Conditions' ) } + + } > - - ) - } - onSelect={ () => - onChangeView( { - ...view, - page: 1, - filters: [ - ...otherFilters, - { - field: filter.field, - operator: OPERATOR_IN, - value: filterInView?.value, - }, - ], - } ) - } - > - { __( 'Is' ) } - - - ) - } - onSelect={ () => - onChangeView( { - ...view, - page: 1, - filters: [ - ...otherFilters, - { - field: filter.field, - operator: OPERATOR_NOT_IN, - value: filterInView?.value, - }, - ], - } ) - } - > - { __( 'Is not' ) } - - + { Object.entries( OPERATORS ).map( + ( [ operator, { label, key } ] ) => ( + { + onChangeView( { + ...view, + page: 1, + filters: [ + ...otherFilters, + { + field: filter.field, + operator: e.target.value, + value: filterInView?.value, + }, + ], + } ); + } } + > + + { label } + + + ) + ) } + ) } diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index b35e11deae7f4..3076162dc8de8 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -286,3 +286,8 @@ .dataviews-loading { padding: 0 $grid-unit-40; } + +.dataviews__filters-custom-menu-radio-item-prefix { + display: block; + width: 24px; +} diff --git a/packages/dataviews/src/view-actions.js b/packages/dataviews/src/view-actions.js index f9a07e2a1830a..b7ae1da63c1ec 100644 --- a/packages/dataviews/src/view-actions.js +++ b/packages/dataviews/src/view-actions.js @@ -3,24 +3,23 @@ */ import { Button, - Icon, privateApis as componentsPrivateApis, } from '@wordpress/components'; -import { chevronRightSmall, check, arrowUp, arrowDown } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import { unlock } from './lock-unlock'; -import { VIEW_LAYOUTS, LAYOUT_TABLE } from './constants'; +import { VIEW_LAYOUTS, LAYOUT_TABLE, SORTING_DIRECTIONS } from './constants'; +import { DropdownMenuRadioItemCustom } from './dropdown-menu-helper'; const { - DropdownMenuV2: DropdownMenu, - DropdownMenuGroupV2: DropdownMenuGroup, - DropdownMenuItemV2: DropdownMenuItem, - DropdownSubMenuV2: DropdownSubMenu, - DropdownSubMenuTriggerV2: DropdownSubMenuTrigger, + DropdownMenuV2Ariakit: DropdownMenu, + DropdownMenuGroupV2Ariakit: DropdownMenuGroup, + DropdownMenuItemV2Ariakit: DropdownMenuItem, + DropdownMenuCheckboxItemV2Ariakit: DropdownMenuCheckboxItem, + DropdownMenuItemLabelV2Ariakit: DropdownMenuItemLabel, } = unlock( componentsPrivateApis ); function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) { @@ -35,84 +34,78 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) { } const activeView = _availableViews.find( ( v ) => view.type === v.type ); return ( - - { activeView.label } - - + } > - { __( 'Layout' ) } - + + { __( 'Layout' ) } + + } > { _availableViews.map( ( availableView ) => { return ( - - ) - } - onSelect={ () => { + value={ availableView.type } + name="view-actions-available-view" + checked={ availableView.type === view.type } + onChange={ ( e ) => { onChangeView( { ...view, - type: availableView.type, + type: e.target.value, } ); } } > - { availableView.label } - + + { availableView.label } + + ); } ) } - + ); } const PAGE_SIZE_VALUES = [ 10, 20, 50, 100 ]; function PageSizeMenu( { view, onChangeView } ) { return ( - - { view.perPage } - - - } + } > { PAGE_SIZE_VALUES.map( ( size ) => { return ( - - } - onSelect={ ( event ) => { - // We need to handle this on DropDown component probably.. - event.preventDefault(); - onChangeView( { ...view, perPage: size, page: 1 } ); + value={ size } + name="view-actions-page-size" + checked={ view.perPage === size } + onChange={ ( e ) => { + onChangeView( { + ...view, + perPage: e.target.value, + page: 1, + } ); } } > - { size } - + { size } + ); } ) } - + ); } @@ -125,27 +118,22 @@ function FieldsVisibilityMenu( { view, onChangeView, fields } ) { return null; } return ( - } - > - { __( 'Fields' ) } - + + + { __( 'Fields' ) } + + } > { hidableFields?.map( ( field ) => { return ( - - ) - } - onSelect={ ( event ) => { - event.preventDefault(); + value={ field.id } + checked={ ! view.hiddenFields?.includes( field.id ) } + onChange={ () => { onChangeView( { ...view, hiddenFields: view.hiddenFields?.includes( @@ -161,19 +149,16 @@ function FieldsVisibilityMenu( { view, onChangeView, fields } ) { } ); } } > - { field.header } - + + { field.header } + + ); } ) } - + ); } -// This object is used to construct the sorting options per sortable field. -const sortingItemsInfo = { - asc: { icon: arrowUp, label: __( 'Sort ascending' ) }, - desc: { icon: arrowDown, label: __( 'Sort descending' ) }, -}; function SortMenu( { fields, view, onChangeView } ) { const sortableFields = fields.filter( ( field ) => field.enableSorting !== false @@ -185,69 +170,71 @@ function SortMenu( { fields, view, onChangeView } ) { ( field ) => field.id === view.sort?.field ); return ( - + } > - { __( 'Sort by' ) } - + + { __( 'Sort by' ) } + + } > { sortableFields?.map( ( field ) => { const sortedDirection = view.sort?.direction; return ( - } - > - { field.header } - + + + { field.header } + + } - side="left" + style={ { + minWidth: '220px', + } } > - { Object.entries( sortingItemsInfo ).map( + { Object.entries( SORTING_DIRECTIONS ).map( ( [ direction, info ] ) => { - const isActive = - currentSortedField && + const isChecked = + currentSortedField !== undefined && sortedDirection === direction && field.id === currentSortedField.id; + return ( - } - suffix={ - isActive && - } - onSelect={ ( event ) => { - event.preventDefault(); + value={ direction } + name={ `view-actions-sorting-${ field.id }` } + checked={ isChecked } + onChange={ ( e ) => { onChangeView( { ...view, sort: { field: field.id, - direction, + direction: e.target.value, }, } ); } } > - { info.label } - + + { info.label } + + ); } ) } - + ); } ) } - + ); } diff --git a/packages/dataviews/src/view-table.js b/packages/dataviews/src/view-table.js index d676ceaf14748..775bae97af4e6 100644 --- a/packages/dataviews/src/view-table.js +++ b/packages/dataviews/src/view-table.js @@ -3,14 +3,7 @@ */ import { __ } from '@wordpress/i18n'; import { useAsyncList } from '@wordpress/compose'; -import { - unseen, - check, - arrowUp, - arrowDown, - chevronRightSmall, - funnel, -} from '@wordpress/icons'; +import { unseen, funnel } from '@wordpress/icons'; import { Button, Icon, @@ -23,30 +16,26 @@ import { Children, Fragment } from '@wordpress/element'; */ import { unlock } from './lock-unlock'; import ItemActions from './item-actions'; -import { ENUMERATION_TYPE, OPERATOR_IN, OPERATOR_NOT_IN } from './constants'; +import { ENUMERATION_TYPE, OPERATORS, SORTING_DIRECTIONS } from './constants'; +import { DropdownMenuRadioItemCustom } from './dropdown-menu-helper'; const { - DropdownMenuV2: DropdownMenu, - DropdownMenuGroupV2: DropdownMenuGroup, - DropdownMenuItemV2: DropdownMenuItem, - DropdownMenuSeparatorV2: DropdownMenuSeparator, - DropdownSubMenuV2: DropdownSubMenu, - DropdownSubMenuTriggerV2: DropdownSubMenuTrigger, + DropdownMenuV2Ariakit: DropdownMenu, + DropdownMenuGroupV2Ariakit: DropdownMenuGroup, + DropdownMenuItemV2Ariakit: DropdownMenuItem, + DropdownMenuSeparatorV2Ariakit: DropdownMenuSeparator, + DropdownMenuItemLabelV2Ariakit: DropdownMenuItemLabel, } = unlock( componentsPrivateApis ); -const sortingItemsInfo = { - asc: { icon: arrowUp, label: __( 'Sort ascending' ) }, - desc: { icon: arrowDown, label: __( 'Sort descending' ) }, -}; const sortArrows = { asc: '↑', desc: '↓' }; const sanitizeOperators = ( field ) => { let operators = field.filterBy?.operators; if ( ! operators || ! Array.isArray( operators ) ) { - operators = [ OPERATOR_IN, OPERATOR_NOT_IN ]; + operators = Object.keys( OPERATORS ); } return operators.filter( ( operator ) => - [ OPERATOR_IN, OPERATOR_NOT_IN ].includes( operator ) + Object.keys( OPERATORS ).includes( operator ) ); }; @@ -94,37 +83,36 @@ function HeaderMenu( { field, view, onChangeView } ) { ) } } + style={ { minWidth: '240px' } } > { isSortable && ( - { Object.entries( sortingItemsInfo ).map( + { Object.entries( SORTING_DIRECTIONS ).map( ( [ direction, info ] ) => { - const isActive = + const isChecked = isSorted && view.sort.direction === direction; return ( - } - suffix={ - isActive && - } - onSelect={ ( event ) => { - event.preventDefault(); + name={ `view-table-sort-${ field.id }` } + value={ direction } + checked={ isChecked } + onChange={ ( e ) => { onChangeView( { ...view, sort: { field: field.id, - direction, + direction: e.target.value, }, } ); } } > - { info.label } - + + { info.label } + + ); } ) } @@ -132,11 +120,8 @@ function HeaderMenu( { field, view, onChangeView } ) { ) } { isHidable && ( } - onSelect={ ( event ) => { - event.preventDefault(); + onClick={ () => { onChangeView( { ...view, hiddenFields: view.hiddenFields.concat( @@ -145,34 +130,32 @@ function HeaderMenu( { field, view, onChangeView } ) { } ); } } > - { __( 'Hide' ) } + + { __( 'Hide' ) } + ) } { isFilterable && ( - } suffix={ - <> - { activeElement && - activeOperator === - OPERATOR_IN && - __( 'Is' ) } - { activeElement && - activeOperator === - OPERATOR_NOT_IN && - __( 'Is not' ) } - { activeElement && ' ' } - { activeElement?.label } - - + activeElement && ( + + ) } > - { __( 'Filter by' ) } - + + { __( 'Filter by' ) } + + } > @@ -182,16 +165,12 @@ function HeaderMenu( { field, view, onChangeView } ) { activeElement?.value === element.value; return ( - - ) - } - onSelect={ () => { + name={ `view-table-${ filter.field.id }` } + value={ element.value } + checked={ isActive } + onClick={ () => { onChangeView( { ...view, page: 1, @@ -209,100 +188,73 @@ function HeaderMenu( { field, view, onChangeView } ) { } ); } } > - { element.label } - + + { element.label } + + ); } ) } { filter.operators.length > 1 && ( - - { activeOperator === - OPERATOR_IN && - __( 'Is' ) } - { activeOperator === - OPERATOR_NOT_IN && - __( 'Is not' ) } - { ' ' } - + } > - { __( 'Conditions' ) } - + + { __( 'Conditions' ) } + + } > - - ) - } - onSelect={ () => - onChangeView( { - ...view, - page: 1, - filters: [ - ...otherFilters, - { - field: filter.field, - operator: - OPERATOR_IN, - value: filterInView?.value, - }, - ], - } ) - } - > - { __( 'Is' ) } - - - ) - } - onSelect={ () => - onChangeView( { - ...view, - page: 1, - filters: [ - ...otherFilters, - { - field: filter.field, - operator: - OPERATOR_NOT_IN, - value: filterInView?.value, - }, - ], - } ) - } - > - { __( 'Is not' ) } - - + { Object.entries( OPERATORS ).map( + ( [ + operator, + { label, key }, + ] ) => ( + + onChangeView( { + ...view, + page: 1, + filters: [ + ...otherFilters, + { + field: filter.field, + operator: + e.target + .value, + value: filterInView?.value, + }, + ], + } ) + } + > + + { label } + + + ) + ) } + ) } - + ) } diff --git a/test/e2e/specs/site-editor/new-templates-list.spec.js b/test/e2e/specs/site-editor/new-templates-list.spec.js index c95668d00b845..df7f88b600442 100644 --- a/test/e2e/specs/site-editor/new-templates-list.spec.js +++ b/test/e2e/specs/site-editor/new-templates-list.spec.js @@ -86,7 +86,7 @@ test.describe( 'Templates', () => { test( 'Field visibility', async ( { admin, page } ) => { await admin.visitSiteEditor( { path: '/wp_template/all' } ); await page.getByRole( 'button', { name: 'Description' } ).click(); - await page.getByRole( 'menuitemradio', { name: 'Hide' } ).click(); + await page.getByRole( 'menuitem', { name: 'Hide' } ).click(); await expect( page.getByRole( 'button', { name: 'Description' } ) ).toBeHidden(); From 1df4fd6b8cb5537f8a36ac60aab1e3ccdd9377c2 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Thu, 21 Dec 2023 19:20:07 +0000 Subject: [PATCH 03/63] Fix: Empty dropdown(s) on the grid view. (#57316) --- packages/dataviews/src/item-actions.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/dataviews/src/item-actions.js b/packages/dataviews/src/item-actions.js index 17690f0064112..a4fdcfc87d673 100644 --- a/packages/dataviews/src/item-actions.js +++ b/packages/dataviews/src/item-actions.js @@ -186,6 +186,9 @@ function CompactItemActions( { item, primaryActions, secondaryActions } ) { size="compact" icon={ moreVertical } label={ __( 'Actions' ) } + disabled={ + ! primaryActions.length && ! secondaryActions.length + } /> } placement="bottom-end" From 071082ce485b36f84ca654de5f1a4e1a57d7eb80 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Fri, 22 Dec 2023 11:26:29 +1100 Subject: [PATCH 04/63] Background image: Add backgroundSize and repeat features (#57005) * Background image block support: Add backgroundSize feature and controls * Rename control to Repeat * Allow setting a fixed size * Add default controls support * Allow empty Size to be treated as auto * Change helptext to refer to width instead of size * Add background-repeat option * Rename toggle to Fixed * Try combining the repeat image toggle with the size controls * Fix whitespace * Tidy up resets * Hide repeat toggle when using cover, ensure updating fixed size doesn't change repeat value --- docs/reference-guides/core-blocks.md | 2 +- lib/block-supports/background.php | 13 +- lib/class-wp-theme-json-gutenberg.php | 2 + lib/compat/wordpress-6.5/kses.php | 18 ++ lib/load.php | 1 + .../src/components/global-styles/hooks.js | 3 + packages/block-editor/src/hooks/background.js | 236 +++++++++++++++++- packages/block-editor/src/hooks/utils.js | 10 + packages/block-library/src/group/block.json | 6 +- packages/blocks/src/api/constants.js | 10 + .../style-engine/class-wp-style-engine.php | 16 +- .../src/styles/background/index.ts | 55 +++- packages/style-engine/src/types.ts | 2 + 13 files changed, 360 insertions(+), 14 deletions(-) create mode 100644 lib/compat/wordpress-6.5/kses.php diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 2ba78177e9ec6..9f25ad0a594b8 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -341,7 +341,7 @@ Gather blocks in a layout container. ([Source](https://github.com/WordPress/gute - **Name:** core/group - **Category:** design -- **Supports:** align (full, wide), anchor, ariaLabel, background (backgroundImage), color (background, button, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~ +- **Supports:** align (full, wide), anchor, ariaLabel, background (backgroundImage, backgroundSize), color (background, button, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** allowedBlocks, tagName, templateLock ## Heading diff --git a/lib/block-supports/background.php b/lib/block-supports/background.php index ab2fa84361fc2..c43603046c0c0 100644 --- a/lib/block-supports/background.php +++ b/lib/block-supports/background.php @@ -54,6 +54,8 @@ function gutenberg_render_background_support( $block_content, $block ) { $background_image_source = $block_attributes['style']['background']['backgroundImage']['source'] ?? null; $background_image_url = $block_attributes['style']['background']['backgroundImage']['url'] ?? null; $background_size = $block_attributes['style']['background']['backgroundSize'] ?? 'cover'; + $background_position = $block_attributes['style']['background']['backgroundPosition'] ?? null; + $background_repeat = $block_attributes['style']['background']['backgroundRepeat'] ?? null; $background_block_styles = array(); @@ -64,8 +66,15 @@ function gutenberg_render_background_support( $block_content, $block ) { // Set file based background URL. // TODO: In a follow-up, similar logic could be added to inject a featured image url. $background_block_styles['backgroundImage']['url'] = $background_image_url; - // Only output the background size when an image url is set. - $background_block_styles['backgroundSize'] = $background_size; + // Only output the background size and repeat when an image url is set. + $background_block_styles['backgroundSize'] = $background_size; + $background_block_styles['backgroundRepeat'] = $background_repeat; + $background_block_styles['backgroundPosition'] = $background_position; + + // If the background size is set to `contain` and no position is set, set the position to `center`. + if ( 'contain' === $background_size && ! isset( $background_position ) ) { + $background_block_styles['backgroundPosition'] = 'center'; + } } $styles = gutenberg_style_engine_get_styles( array( 'background' => $background_block_styles ) ); diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 43a3772a1c3af..4fe5a3a3bf6a8 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -354,6 +354,7 @@ class WP_Theme_JSON_Gutenberg { 'useRootPaddingAwareAlignments' => null, 'background' => array( 'backgroundImage' => null, + 'backgroundSize' => null, ), 'border' => array( 'color' => null, @@ -650,6 +651,7 @@ public static function get_element_class_name( $element ) { */ const APPEARANCE_TOOLS_OPT_INS = array( array( 'background', 'backgroundImage' ), + array( 'background', 'backgroundSize' ), array( 'border', 'color' ), array( 'border', 'radius' ), array( 'border', 'style' ), diff --git a/lib/compat/wordpress-6.5/kses.php b/lib/compat/wordpress-6.5/kses.php new file mode 100644 index 0000000000000..038d78645786f --- /dev/null +++ b/lib/compat/wordpress-6.5/kses.php @@ -0,0 +1,18 @@ + { const { getBlockAttributes, getSettings } = @@ -252,7 +296,7 @@ function BackgroundImagePanelItem( { clientId, setAttributes } ) { hasValue={ () => hasValue } label={ __( 'Background image' ) } onDeselect={ () => resetBackgroundImage( style, setAttributes ) } - isShownByDefault={ true } + isShownByDefault={ isShownByDefault } resetAllFilter={ resetAllFilter } panelId={ clientId } > @@ -302,8 +346,172 @@ function BackgroundImagePanelItem( { clientId, setAttributes } ) { ); } +function backgroundSizeHelpText( value ) { + if ( value === 'cover' || value === undefined ) { + return __( 'Stretch image to cover the block.' ); + } + if ( value === 'contain' ) { + return __( 'Resize image to fit without cropping.' ); + } + return __( 'Set a fixed width.' ); +} + +function BackgroundSizePanelItem( { + clientId, + isShownByDefault, + setAttributes, +} ) { + const style = useSelect( + ( select ) => + select( blockEditorStore ).getBlockAttributes( clientId )?.style, + [ clientId ] + ); + + const sizeValue = style?.background?.backgroundSize; + const repeatValue = style?.background?.backgroundRepeat; + + // An `undefined` value is treated as `cover` by the toggle group control. + // An empty string is treated as `auto` by the toggle group control. This + // allows a user to select "Size" and then enter a custom value, with an + // empty value being treated as `auto`. + const currentValueForToggle = + ( sizeValue !== undefined && + sizeValue !== 'cover' && + sizeValue !== 'contain' ) || + sizeValue === '' + ? 'auto' + : sizeValue || 'cover'; + + // If the current value is `cover` and the repeat value is `undefined`, then + // the toggle should be unchecked as the default state. Otherwise, the toggle + // should reflect the current repeat value. + const repeatCheckedValue = + repeatValue === 'no-repeat' || + ( currentValueForToggle === 'cover' && repeatValue === undefined ) + ? false + : true; + + const hasValue = hasBackgroundSizeValue( style ); + + const resetAllFilter = useCallback( ( previousValue ) => { + return { + ...previousValue, + style: { + ...previousValue.style, + background: { + ...previousValue.style?.background, + backgroundRepeat: undefined, + backgroundSize: undefined, + }, + }, + }; + }, [] ); + + const updateBackgroundSize = ( next ) => { + // When switching to 'contain' toggle the repeat off. + let nextRepeat = repeatValue; + + if ( next === 'contain' ) { + nextRepeat = 'no-repeat'; + } + + if ( + ( currentValueForToggle === 'cover' || + currentValueForToggle === 'contain' ) && + next === 'auto' + ) { + nextRepeat = undefined; + } + + setAttributes( { + style: cleanEmptyObject( { + ...style, + background: { + ...style?.background, + backgroundRepeat: nextRepeat, + backgroundSize: next, + }, + } ), + } ); + }; + + const toggleIsRepeated = () => { + setAttributes( { + style: cleanEmptyObject( { + ...style, + background: { + ...style?.background, + backgroundRepeat: + repeatCheckedValue === true ? 'no-repeat' : undefined, + }, + } ), + } ); + }; + + return ( + hasValue } + label={ __( 'Size' ) } + onDeselect={ () => resetBackgroundSize( style, setAttributes ) } + isShownByDefault={ isShownByDefault } + resetAllFilter={ resetAllFilter } + panelId={ clientId } + > + + + + + + { sizeValue !== undefined && + sizeValue !== 'cover' && + sizeValue !== 'contain' ? ( + + ) : null } + { currentValueForToggle !== 'cover' && ( + + ) } + + ); +} + export function BackgroundImagePanel( props ) { - const [ backgroundImage ] = useSettings( 'background.backgroundImage' ); + const [ backgroundImage, backgroundSize ] = useSettings( + 'background.backgroundImage', + 'background.backgroundSize' + ); + if ( ! backgroundImage || ! hasBackgroundSupport( props.name, 'backgroundImage' ) @@ -311,9 +519,27 @@ export function BackgroundImagePanel( props ) { return null; } + const showBackgroundSize = !! ( + backgroundSize && hasBackgroundSupport( props.name, 'backgroundSize' ) + ); + + const defaultControls = getBlockSupport( props.name, [ + BACKGROUND_SUPPORT_KEY, + '__experimentalDefaultControls', + ] ); + return ( - + + { showBackgroundSize && ( + + ) } ); } diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index 27c5c346458d2..1c597836e9ec5 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -174,6 +174,8 @@ export function useStyleOverride( { id, css, assets, __unstableType } = {} ) { */ export function useBlockSettings( name, parentLayout ) { const [ + backgroundImage, + backgroundSize, fontFamilies, fontSizes, customFontSize, @@ -217,6 +219,8 @@ export function useBlockSettings( name, parentLayout ) { isHeadingEnabled, isButtonEnabled, ] = useSettings( + 'background.backgroundImage', + 'background.backgroundSize', 'typography.fontFamilies', 'typography.fontSizes', 'typography.customFontSize', @@ -263,6 +267,10 @@ export function useBlockSettings( name, parentLayout ) { const rawSettings = useMemo( () => { return { + background: { + backgroundImage, + backgroundSize, + }, color: { palette: { custom: customColors, @@ -330,6 +338,8 @@ export function useBlockSettings( name, parentLayout ) { parentLayout, }; }, [ + backgroundImage, + backgroundSize, fontFamilies, fontSizes, customFontSize, diff --git a/packages/block-library/src/group/block.json b/packages/block-library/src/group/block.json index 92bbc1b0d1135..df59c25a7751f 100644 --- a/packages/block-library/src/group/block.json +++ b/packages/block-library/src/group/block.json @@ -29,7 +29,11 @@ "ariaLabel": true, "html": false, "background": { - "backgroundImage": true + "backgroundImage": true, + "backgroundSize": true, + "__experimentalDefaultControls": { + "backgroundImage": true + } }, "color": { "gradients": true, diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index 2afdee93278ce..f08717592cb4b 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -31,6 +31,16 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { requiresOptOut: true, useEngine: true, }, + backgroundRepeat: { + value: [ 'background', 'backgroundRepeat' ], + support: [ 'background', 'backgroundRepeat' ], + useEngine: true, + }, + backgroundSize: { + value: [ 'background', 'backgroundSize' ], + support: [ 'background', 'backgroundSize' ], + useEngine: true, + }, borderColor: { value: [ 'border', 'color' ], support: [ '__experimentalBorder', 'color' ], diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index fdb0083ad9eec..f0ecb9f903806 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -44,14 +44,26 @@ final class WP_Style_Engine { */ const BLOCK_STYLE_DEFINITIONS_METADATA = array( 'background' => array( - 'backgroundImage' => array( + 'backgroundImage' => array( 'property_keys' => array( 'default' => 'background-image', ), 'value_func' => array( self::class, 'get_url_or_value_css_declaration' ), 'path' => array( 'background', 'backgroundImage' ), ), - 'backgroundSize' => array( + 'backgroundPosition' => array( + 'property_keys' => array( + 'default' => 'background-position', + ), + 'path' => array( 'background', 'backgroundPosition' ), + ), + 'backgroundRepeat' => array( + 'property_keys' => array( + 'default' => 'background-repeat', + ), + 'path' => array( 'background', 'backgroundRepeat' ), + ), + 'backgroundSize' => array( 'property_keys' => array( 'default' => 'background-size', ), diff --git a/packages/style-engine/src/styles/background/index.ts b/packages/style-engine/src/styles/background/index.ts index 899a3b5b8f0f4..b9879ad2032a1 100644 --- a/packages/style-engine/src/styles/background/index.ts +++ b/packages/style-engine/src/styles/background/index.ts @@ -2,7 +2,7 @@ * Internal dependencies */ import type { GeneratedCSSRule, Style, StyleOptions } from '../../types'; -import { safeDecodeURI } from '../utils'; +import { generateRule, safeDecodeURI } from '../utils'; const backgroundImage = { name: 'backgroundImage', @@ -28,7 +28,7 @@ const backgroundImage = { } // If no background size is set, but an image is, default to cover. - if ( ! _backgroundSize ) { + if ( _backgroundSize === undefined ) { styleRules.push( { selector: options.selector, key: 'backgroundSize', @@ -40,4 +40,53 @@ const backgroundImage = { }, }; -export default [ backgroundImage ]; +const backgroundRepeat = { + name: 'backgroundRepeat', + generate: ( style: Style, options: StyleOptions ) => { + return generateRule( + style, + options, + [ 'background', 'backgroundRepeat' ], + 'backgroundRepeat' + ); + }, +}; + +const backgroundSize = { + name: 'backgroundSize', + generate: ( style: Style, options: StyleOptions ) => { + const _backgroundSize = style?.background?.backgroundSize; + const _backgroundPosition = style?.background?.backgroundPosition; + + const styleRules: GeneratedCSSRule[] = []; + + if ( _backgroundSize === undefined ) { + return styleRules; + } + + styleRules.push( + ...generateRule( + style, + options, + [ 'background', 'backgroundSize' ], + 'backgroundSize' + ) + ); + + // If background size is set to contain, but no position is set, default to center. + if ( + _backgroundSize === 'contain' && + _backgroundPosition === undefined + ) { + styleRules.push( { + selector: options.selector, + key: 'backgroundPosition', + value: 'center', + } ); + } + + return styleRules; + }, +}; + +export default [ backgroundImage, backgroundRepeat, backgroundSize ]; diff --git a/packages/style-engine/src/types.ts b/packages/style-engine/src/types.ts index f09f2c213eabb..91a73e158e1ce 100644 --- a/packages/style-engine/src/types.ts +++ b/packages/style-engine/src/types.ts @@ -26,6 +26,8 @@ export interface Style { url?: CSSProperties[ 'backgroundImage' ]; source?: string; }; + backgroundPosition?: CSSProperties[ 'backgroundPosition' ]; + backgroundRepeat?: CSSProperties[ 'backgroundRepeat' ]; backgroundSize?: CSSProperties[ 'backgroundSize' ]; }; border?: { From fc549c7d5f0b3d1dc169f3ef278b80e161d9ef47 Mon Sep 17 00:00:00 2001 From: Sahil Date: Fri, 22 Dec 2023 08:41:01 +0530 Subject: [PATCH 05/63] fix: updated the link for "Building a custom block editor" (#57319) --- docs/how-to-guides/platform/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/how-to-guides/platform/README.md b/docs/how-to-guides/platform/README.md index 2ed96d6c83d87..731ae26a1ead5 100644 --- a/docs/how-to-guides/platform/README.md +++ b/docs/how-to-guides/platform/README.md @@ -57,4 +57,5 @@ For more info, see the [Getting Started with JavaScript tutorial](/docs/how-to-g The [`@wordpress/block-editor` package](https://developer.wordpress.org/block-editor/packages/packages-block-editor/) allows you to create and use standalone block editors. -You can learn more by reading the [tutorial "Building a custom block editor"](/docs/how-to-guides/platform/custom-block-editor/README.md). +You can learn more by reading the [tutorial "Building a custom block editor"](/docs/how-to-guides/platform/custom-block-editor.md). + From 6318db01dc47cdb998e0151e1fbcc2c7c5757462 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:19:05 +0900 Subject: [PATCH 06/63] Palette Edit: Don't discards colors with default name and slug (#54332) * Palette Edit: Don't discards colors with default name and slug * Update comments and variables * Update changelog * Remove isTemporaryElement function and tests * Fix changelog typo Co-authored-by: Ramon --------- Co-authored-by: Ramon --- packages/components/CHANGELOG.md | 1 + .../components/src/palette-edit/index.tsx | 65 +++------------- .../src/palette-edit/test/index.tsx | 76 +------------------ 3 files changed, 13 insertions(+), 129 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index ecce61cb91509..093dea189bdf7 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -12,6 +12,7 @@ - `FormTokenField`: Fix a regression where the suggestion list would re-open after clicking away from the input ([#57002](https://github.com/WordPress/gutenberg/pull/57002)). - `Snackbar`: Remove erroneous `__unstableHTML` prop from TypeScript definitions ([#57218](https://github.com/WordPress/gutenberg/pull/57218)). - `Truncate`: improve handling of non-string `children` ([#57261](https://github.com/WordPress/gutenberg/pull/57261)). +- `PaletteEdit`: Don't discard colors with default name and slug ([#54332](https://github.com/WordPress/gutenberg/pull/54332)). ### Enhancements diff --git a/packages/components/src/palette-edit/index.tsx b/packages/components/src/palette-edit/index.tsx index ceadceca1a239..87df1894dc36f 100644 --- a/packages/components/src/palette-edit/index.tsx +++ b/packages/components/src/palette-edit/index.tsx @@ -60,7 +60,7 @@ import type { PaletteElement, } from './types'; -export const DEFAULT_COLOR = '#000'; +const DEFAULT_COLOR = '#000'; function NameInput( { value, onChange, label }: NameInputProps ) { return ( @@ -74,8 +74,8 @@ function NameInput( { value, onChange, label }: NameInputProps ) { } /** - * Returns a temporary name for a palette item in the format "Color + id". - * To ensure there are no duplicate ids, this function checks all slugs for temporary names. + * Returns a name for a palette item in the format "Color + id". + * To ensure there are no duplicate ids, this function checks all slugs. * It expects slugs to be in the format: slugPrefix + color- + number. * It then sets the id component of the new name based on the incremented id of the highest existing slug id. * @@ -88,10 +88,10 @@ export function getNameForPosition( elements: PaletteElement[], slugPrefix: string ) { - const temporaryNameRegex = new RegExp( `^${ slugPrefix }color-([\\d]+)$` ); + const nameRegex = new RegExp( `^${ slugPrefix }color-([\\d]+)$` ); const position = elements.reduce( ( previousValue, currentValue ) => { if ( typeof currentValue?.slug === 'string' ) { - const matches = currentValue?.slug.match( temporaryNameRegex ); + const matches = currentValue?.slug.match( nameRegex ); if ( matches ) { const id = parseInt( matches[ 1 ], 10 ); if ( id >= previousValue ) { @@ -103,7 +103,7 @@ export function getNameForPosition( }, 1 ); return sprintf( - /* translators: %s: is a temporary id for a custom color */ + /* translators: %s: is an id for a custom color */ __( 'Color %s' ), position ); @@ -261,32 +261,6 @@ function Option< T extends Color | Gradient >( { ); } -/** - * Checks if a color or gradient is a temporary element by testing against default values. - */ -export function isTemporaryElement( - slugPrefix: string, - { slug, color, gradient }: Color | Gradient -): Boolean { - const regex = new RegExp( `^${ slugPrefix }color-([\\d]+)$` ); - - // If the slug matches the temporary name regex, - // check if the color or gradient matches the default value. - if ( regex.test( slug ) ) { - // The order is important as gradient elements - // contain a color property. - if ( !! gradient ) { - return gradient === DEFAULT_GRADIENT; - } - - if ( !! color ) { - return color === DEFAULT_COLOR; - } - } - - return false; -} - function PaletteEditListView< T extends Color | Gradient >( { elements, onChange, @@ -302,23 +276,6 @@ function PaletteEditListView< T extends Color | Gradient >( { useEffect( () => { elementsReference.current = elements; }, [ elements ] ); - useEffect( () => { - return () => { - if ( - elementsReference.current?.some( ( element ) => - isTemporaryElement( slugPrefix, element ) - ) - ) { - const newElements = elementsReference.current.filter( - ( element ) => ! isTemporaryElement( slugPrefix, element ) - ); - onChange( newElements.length ? newElements : undefined ); - } - }; - // Disable reason: adding the missing dependency here would cause breaking changes that will require - // a heavier refactor to avoid. See https://github.com/WordPress/gutenberg/pull/43911 - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [] ); const debounceOnChange = useDebounce( onChange, 100 ); @@ -474,7 +431,7 @@ export function PaletteEdit( { : __( 'Add color' ) } onClick={ () => { - const tempOptionName = getNameForPosition( + const optionName = getNameForPosition( elements, slugPrefix ); @@ -484,10 +441,10 @@ export function PaletteEdit( { ...gradients, { gradient: DEFAULT_GRADIENT, - name: tempOptionName, + name: optionName, slug: slugPrefix + - kebabCase( tempOptionName ), + kebabCase( optionName ), }, ] ); } else { @@ -495,10 +452,10 @@ export function PaletteEdit( { ...colors, { color: DEFAULT_COLOR, - name: tempOptionName, + name: optionName, slug: slugPrefix + - kebabCase( tempOptionName ), + kebabCase( optionName ), }, ] ); } diff --git a/packages/components/src/palette-edit/test/index.tsx b/packages/components/src/palette-edit/test/index.tsx index 1a0b2fdaaab3f..1bf2802709de7 100644 --- a/packages/components/src/palette-edit/test/index.tsx +++ b/packages/components/src/palette-edit/test/index.tsx @@ -6,13 +6,8 @@ import { render, fireEvent, screen } from '@testing-library/react'; /** * Internal dependencies */ -import PaletteEdit, { - getNameForPosition, - isTemporaryElement, - DEFAULT_COLOR, -} from '..'; +import PaletteEdit, { getNameForPosition } from '..'; import type { PaletteElement } from '../types'; -import { DEFAULT_GRADIENT } from '../../custom-gradient-picker/constants'; describe( 'getNameForPosition', () => { test( 'should return 1 by default', () => { @@ -85,75 +80,6 @@ describe( 'getNameForPosition', () => { } ); } ); -describe( 'isTemporaryElement', () => { - [ - { - message: 'identifies temporary color', - slug: 'test-', - obj: { - name: '', - slug: 'test-color-1', - color: DEFAULT_COLOR, - }, - expected: true, - }, - { - message: 'identifies temporary gradient', - slug: 'test-', - obj: { - name: '', - slug: 'test-color-1', - gradient: DEFAULT_GRADIENT, - }, - expected: true, - }, - { - message: 'identifies custom color slug', - slug: 'test-', - obj: { - name: '', - slug: 'test-color-happy', - color: DEFAULT_COLOR, - }, - expected: false, - }, - { - message: 'identifies custom color value', - slug: 'test-', - obj: { - name: '', - slug: 'test-color-1', - color: '#ccc', - }, - expected: false, - }, - { - message: 'identifies custom gradient slug', - slug: 'test-', - obj: { - name: '', - slug: 'test-gradient-joy', - color: DEFAULT_GRADIENT, - }, - expected: false, - }, - { - message: 'identifies custom gradient value', - slug: 'test-', - obj: { - name: '', - slug: 'test-color-3', - color: 'linear-gradient(90deg, rgba(22, 22, 22, 1) 0%, rgb(155, 81, 224) 100%)', - }, - expected: false, - }, - ].forEach( ( { message, slug, obj, expected } ) => { - it( `should ${ message }`, () => { - expect( isTemporaryElement( slug, obj ) ).toBe( expected ); - } ); - } ); -} ); - describe( 'PaletteEdit', () => { const defaultProps = { colors: [ { color: '#ffffff', name: 'Base', slug: 'base' } ], From 51b81ef5aa09d54892db48da07ab2c439eb34e14 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 22 Dec 2023 08:14:52 +0100 Subject: [PATCH 07/63] TabPanel: refactor unit tests to @ariakit/test (#57302) --- .../components/src/tab-panel/test/index.tsx | 95 ++++++++----------- 1 file changed, 39 insertions(+), 56 deletions(-) diff --git a/packages/components/src/tab-panel/test/index.tsx b/packages/components/src/tab-panel/test/index.tsx index c07685935ce16..28dd1a81e9e84 100644 --- a/packages/components/src/tab-panel/test/index.tsx +++ b/packages/components/src/tab-panel/test/index.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import { press, hover, click } from '@ariakit/test'; /** * WordPress dependencies @@ -13,7 +13,6 @@ import { wordpress, category, media } from '@wordpress/icons'; * Internal dependencies */ import TabPanel from '..'; -import cleanupTooltip from '../../tooltip/test/utils'; const TABS = [ { @@ -95,8 +94,6 @@ describe.each( [ } ); it( 'should display a tooltip when hovering tabs provided with an icon', async () => { - const user = userEvent.setup(); - const panelRenderFunction = jest.fn(); const TABS_WITH_ICON = [ @@ -119,7 +116,7 @@ describe.each( [ screen.queryByText( TABS_WITH_ICON[ i ].title ) ).not.toBeInTheDocument(); - await user.hover( allTabs[ i ] ); + await hover( allTabs[ i ] ); await waitFor( () => expect( @@ -127,15 +124,12 @@ describe.each( [ ).toBeVisible() ); - await user.unhover( allTabs[ i ] ); + // Trigger closing the tooltip + await click( document.body ); } - - await cleanupTooltip( user ); } ); it( 'should display a tooltip when moving the selection via the keyboard on tabs provided with an icon', async () => { - const user = userEvent.setup(); - const mockOnSelect = jest.fn(); const panelRenderFunction = jest.fn(); @@ -161,7 +155,7 @@ describe.each( [ // Tab to focus the tablist. Make sure alpha is focused, and that the // corresponding tooltip is shown. expect( screen.queryByText( 'Alpha' ) ).not.toBeInTheDocument(); - await user.keyboard( '[Tab]' ); + await press.Tab(); expect( mockOnSelect ).toHaveBeenCalledTimes( 1 ); expect( screen.getByText( 'Alpha' ) ).toBeInTheDocument(); expect( await getSelectedTab() ).toHaveFocus(); @@ -169,7 +163,7 @@ describe.each( [ // Move selection with arrow keys. Make sure beta is focused, and that // the corresponding tooltip is shown. expect( screen.queryByText( 'Beta' ) ).not.toBeInTheDocument(); - await user.keyboard( '[ArrowRight]' ); + await press.ArrowRight(); expect( mockOnSelect ).toHaveBeenCalledTimes( 2 ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' ); expect( screen.getByText( 'Beta' ) ).toBeInTheDocument(); @@ -178,7 +172,7 @@ describe.each( [ // Move selection with arrow keys. Make sure gamma is focused, and that // the corresponding tooltip is shown. expect( screen.queryByText( 'Gamma' ) ).not.toBeInTheDocument(); - await user.keyboard( '[ArrowRight]' ); + await press.ArrowRight(); expect( mockOnSelect ).toHaveBeenCalledTimes( 3 ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' ); expect( screen.getByText( 'Gamma' ) ).toBeInTheDocument(); @@ -187,13 +181,11 @@ describe.each( [ // Move selection with arrow keys. Make sure beta is focused, and that // the corresponding tooltip is shown. expect( screen.queryByText( 'Beta' ) ).not.toBeInTheDocument(); - await user.keyboard( '[ArrowLeft]' ); + await press.ArrowLeft(); expect( mockOnSelect ).toHaveBeenCalledTimes( 4 ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' ); expect( screen.getByText( 'Beta' ) ).toBeInTheDocument(); expect( await getSelectedTab() ).toHaveFocus(); - - await cleanupTooltip( user ); } ); } ); @@ -283,7 +275,6 @@ describe.each( [ } ); it( 'should fall back to the tab associated to `initialTabName` if the currently active tab is removed', async () => { - const user = userEvent.setup(); const mockOnSelect = jest.fn(); const { rerender } = render( @@ -298,7 +289,7 @@ describe.each( [ expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' ); - await user.click( screen.getByRole( 'tab', { name: 'Alpha' } ) ); + await click( screen.getByRole( 'tab', { name: 'Alpha' } ) ); expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' ); @@ -386,7 +377,6 @@ describe.each( [ describe( 'Disabled Tab', () => { it( 'should disable the tab when `disabled` is `true`', async () => { - const user = userEvent.setup(); const mockOnSelect = jest.fn(); render( @@ -414,7 +404,7 @@ describe.each( [ // onSelect should not be called since the disabled tab is // highlighted, but not selected. - await user.keyboard( '[ArrowLeft]' ); + await press.ArrowLeft(); expect( mockOnSelect ).toHaveBeenCalledTimes( 1 ); } ); @@ -582,7 +572,6 @@ describe.each( [ describe( 'Tab Activation', () => { it( 'defaults to automatic tab activation (pointer clicks)', async () => { - const user = userEvent.setup(); const panelRenderFunction = jest.fn(); const mockOnSelect = jest.fn(); @@ -602,7 +591,7 @@ describe.each( [ expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' ); // Click on Beta, make sure beta is the selected tab - await user.click( screen.getByRole( 'tab', { name: 'Beta' } ) ); + await click( screen.getByRole( 'tab', { name: 'Beta' } ) ); expect( await getSelectedTab() ).toHaveTextContent( 'Beta' ); expect( @@ -611,7 +600,7 @@ describe.each( [ expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' ); // Click on Alpha, make sure beta is the selected tab - await user.click( screen.getByRole( 'tab', { name: 'Alpha' } ) ); + await click( screen.getByRole( 'tab', { name: 'Alpha' } ) ); expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( @@ -621,7 +610,6 @@ describe.each( [ } ); it( 'defaults to automatic tab activation (arrow keys)', async () => { - const user = userEvent.setup(); const mockOnSelect = jest.fn(); render( @@ -638,12 +626,12 @@ describe.each( [ // Tab to focus the tablist. Make sure alpha is focused. expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).not.toHaveFocus(); - await user.keyboard( '[Tab]' ); + await press.Tab(); expect( await getSelectedTab() ).toHaveFocus(); // Navigate forward with arrow keys and make sure the Beta tab is // selected automatically. - await user.keyboard( '[ArrowRight]' ); + await press.ArrowRight(); expect( await getSelectedTab() ).toHaveTextContent( 'Beta' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 2 ); @@ -651,7 +639,7 @@ describe.each( [ // Navigate backwards with arrow keys. Make sure alpha is // selected automatically. - await user.keyboard( '[ArrowLeft]' ); + await press.ArrowLeft(); expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 3 ); @@ -659,7 +647,6 @@ describe.each( [ } ); it( 'wraps around the last/first tab when using arrow keys', async () => { - const user = userEvent.setup(); const mockOnSelect = jest.fn(); render( @@ -676,12 +663,12 @@ describe.each( [ // Tab to focus the tablist. Make sure Alpha is focused. expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).not.toHaveFocus(); - await user.keyboard( '[Tab]' ); + await press.Tab(); expect( await getSelectedTab() ).toHaveFocus(); // Navigate backwards with arrow keys and make sure that the Gamma tab // (the last tab) is selected automatically. - await user.keyboard( '[ArrowLeft]' ); + await press.ArrowLeft(); expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 2 ); @@ -689,7 +676,7 @@ describe.each( [ // Navigate forward with arrow keys. Make sure alpha (the first tab) is // selected automatically. - await user.keyboard( '[ArrowRight]' ); + await press.ArrowRight(); expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 3 ); @@ -697,7 +684,6 @@ describe.each( [ } ); it( 'should not move tab selection when pressing the up/down arrow keys, unless the orientation is changed to `vertical`', async () => { - const user = userEvent.setup(); const mockOnSelect = jest.fn(); const { rerender } = render( @@ -714,18 +700,18 @@ describe.each( [ // Tab to focus the tablist. Make sure alpha is focused. expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).not.toHaveFocus(); - await user.keyboard( '[Tab]' ); + await press.Tab(); expect( await getSelectedTab() ).toHaveFocus(); // Press the arrow up key, nothing happens. - await user.keyboard( '[ArrowUp]' ); + await press.ArrowUp(); expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 1 ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' ); // Press the arrow down key, nothing happens - await user.keyboard( '[ArrowDown]' ); + await press.ArrowDown(); expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 1 ); @@ -753,7 +739,7 @@ describe.each( [ // Navigate forward with arrow keys and make sure the Beta tab is // selected automatically. - await user.keyboard( '[ArrowDown]' ); + await press.ArrowDown(); expect( await getSelectedTab() ).toHaveTextContent( 'Beta' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 2 ); @@ -761,7 +747,7 @@ describe.each( [ // Navigate backwards with arrow keys. Make sure alpha is // selected automatically. - await user.keyboard( '[ArrowUp]' ); + await press.ArrowUp(); expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 3 ); @@ -769,7 +755,7 @@ describe.each( [ // Navigate backwards with arrow keys. Make sure alpha is // selected automatically. - await user.keyboard( '[ArrowUp]' ); + await press.ArrowUp(); expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 4 ); @@ -777,7 +763,7 @@ describe.each( [ // Navigate backwards with arrow keys. Make sure alpha is // selected automatically. - await user.keyboard( '[ArrowDown]' ); + await press.ArrowDown(); expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 5 ); @@ -785,7 +771,6 @@ describe.each( [ } ); it( 'should move focus on a tab even if disabled with arrow key, but not with pointer clicks', async () => { - const user = userEvent.setup(); const mockOnSelect = jest.fn(); render( @@ -810,7 +795,7 @@ describe.each( [ // Tab to focus the tablist. Make sure Alpha is focused. expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' ); expect( await getSelectedTab() ).not.toHaveFocus(); - await user.keyboard( '[Tab]' ); + await press.Tab(); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 1 ); @@ -819,7 +804,9 @@ describe.each( [ // it was the tab that was last selected before delta. Therefore, the // `mockOnSelect` function gets called only twice (and not three times) // - it will receive focus, when using arrow keys - await user.keyboard( '[ArrowRight][ArrowRight][ArrowRight]' ); + await press.ArrowRight(); + await press.ArrowRight(); + await press.ArrowRight(); expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' ); expect( screen.getByRole( 'tab', { name: 'Delta' } ) @@ -830,7 +817,7 @@ describe.each( [ // Navigate backwards with arrow keys. The gamma tab receives focus. // The `mockOnSelect` callback doesn't fire, since the gamma tab was // already selected. - await user.keyboard( '[ArrowLeft]' ); + await press.ArrowLeft(); expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 3 ); @@ -838,34 +825,31 @@ describe.each( [ // Click on the disabled tab. Compared to using arrow keys to move the // focus, disabled tabs ignore pointer clicks — and therefore, they don't // receive focus, nor they cause the `mockOnSelect` function to fire. - await user.click( screen.getByRole( 'tab', { name: 'Delta' } ) ); + await click( screen.getByRole( 'tab', { name: 'Delta' } ) ); expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' ); expect( await getSelectedTab() ).toHaveFocus(); expect( mockOnSelect ).toHaveBeenCalledTimes( 3 ); } ); it( 'should not focus the next tab when the Tab key is pressed', async () => { - const user = userEvent.setup(); - render( undefined } /> ); // Tab should initially focus the first tab in the tablist, which // is Alpha. - await user.keyboard( '[Tab]' ); + await press.Tab(); expect( await screen.findByRole( 'tab', { name: 'Alpha' } ) ).toHaveFocus(); // Because all other tabs should have `tabindex=-1`, pressing Tab // should NOT move the focus to the next tab, which is Beta. - await user.keyboard( '[Tab]' ); + await press.Tab(); expect( await screen.findByRole( 'tab', { name: 'Beta' } ) ).not.toHaveFocus(); } ); it( 'switches to manual tab activation when the `selectOnMove` prop is set to `false`', async () => { - const user = userEvent.setup(); const mockOnSelect = jest.fn(); render( @@ -883,33 +867,33 @@ describe.each( [ // Click on Alpha and make sure it is selected. // onSelect shouldn't fire since the selected tab didn't change. - await user.click( screen.getByRole( 'tab', { name: 'Alpha' } ) ); + await click( screen.getByRole( 'tab', { name: 'Alpha' } ) ); expect( mockOnSelect ).toHaveBeenCalledTimes( 1 ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' ); // Navigate forward with arrow keys. Make sure Beta is focused, but // that the tab selection happens only when pressing the spacebar // or enter key. - await user.keyboard( '[ArrowRight]' ); + await press.ArrowRight(); expect( mockOnSelect ).toHaveBeenCalledTimes( 1 ); expect( await screen.findByRole( 'tab', { name: 'Beta' } ) ).toHaveFocus(); - await user.keyboard( '[Enter]' ); + await press.Enter(); expect( mockOnSelect ).toHaveBeenCalledTimes( 2 ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' ); // Navigate forward with arrow keys. Make sure Gamma (last tab) is // focused, but that tab selection happens only when pressing the // spacebar or enter key. - await user.keyboard( '[ArrowRight]' ); + await press.ArrowRight(); expect( mockOnSelect ).toHaveBeenCalledTimes( 2 ); expect( screen.getByRole( 'tab', { name: 'Gamma' } ) ).toHaveFocus(); - await user.keyboard( '[Space]' ); + await press.Space(); expect( mockOnSelect ).toHaveBeenCalledTimes( 3 ); expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' ); } ); @@ -931,7 +915,6 @@ describe.each( [ } ); it( 'should apply the `activeClass` to the selected tab', async () => { - const user = userEvent.setup(); const activeClass = 'my-active-tab'; render( @@ -952,7 +935,7 @@ describe.each( [ } ); // Click the 'Beta' tab - await user.click( screen.getByRole( 'tab', { name: 'Beta' } ) ); + await click( screen.getByRole( 'tab', { name: 'Beta' } ) ); // Make sure that only the selected tab has the active class expect( await getSelectedTab() ).toHaveTextContent( 'Beta' ); From 0080b752d8d980c8f06bb0ab27729f6a74f2e367 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Fri, 22 Dec 2023 17:07:13 +0800 Subject: [PATCH 08/63] Fix broken undo history stack for Pattern Overrides (#57088) * Fix broken undo history stack for Pattern Overrides * Fix unit test * Fix unit test * Revert uneeded changes --- .../provider/test/use-block-sync.js | 37 ++++++++------- .../src/components/provider/use-block-sync.js | 6 +++ .../block-editor/src/store/private-actions.js | 35 ++++++++++++-- packages/block-editor/src/store/reducer.js | 21 +++++++-- .../block-editor/src/store/undo-ignore.js | 4 ++ packages/block-library/src/block/edit.js | 47 ++++++++++--------- packages/core-data/src/entity-provider.js | 14 ++++-- 7 files changed, 111 insertions(+), 53 deletions(-) create mode 100644 packages/block-editor/src/store/undo-ignore.js diff --git a/packages/block-editor/src/components/provider/test/use-block-sync.js b/packages/block-editor/src/components/provider/test/use-block-sync.js index 09529c197516a..9b16e966249fa 100644 --- a/packages/block-editor/src/components/provider/test/use-block-sync.js +++ b/packages/block-editor/src/components/provider/test/use-block-sync.js @@ -263,13 +263,13 @@ describe( 'useBlockSync hook', () => { expect( onInput ).toHaveBeenCalledWith( [ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ], - { + expect.objectContaining( { selection: { selectionEnd: {}, selectionStart: {}, initialPosition: null, }, - } + } ) ); expect( onChange ).not.toHaveBeenCalled(); } ); @@ -303,13 +303,13 @@ describe( 'useBlockSync hook', () => { expect( onChange ).toHaveBeenCalledWith( [ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ], - { + expect.objectContaining( { selection: { selectionEnd: {}, selectionStart: {}, initialPosition: null, }, - } + } ) ); expect( onInput ).not.toHaveBeenCalled(); } ); @@ -406,13 +406,13 @@ describe( 'useBlockSync hook', () => { attributes: { foo: 2 }, }, ], - { + expect.objectContaining( { selection: { selectionEnd: {}, selectionStart: {}, initialPosition: null, }, - } + } ) ); expect( onInput ).not.toHaveBeenCalled(); } ); @@ -447,13 +447,16 @@ describe( 'useBlockSync hook', () => { { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } }, ]; - expect( onChange1 ).toHaveBeenCalledWith( updatedBlocks1, { - selection: { - initialPosition: null, - selectionEnd: {}, - selectionStart: {}, - }, - } ); + expect( onChange1 ).toHaveBeenCalledWith( + updatedBlocks1, + expect.objectContaining( { + selection: { + initialPosition: null, + selectionEnd: {}, + selectionStart: {}, + }, + } ) + ); const newBlocks = [ { clientId: 'b', innerBlocks: [], attributes: { foo: 1 } }, @@ -485,13 +488,13 @@ describe( 'useBlockSync hook', () => { // The second callback should be called with the new change. expect( onChange2 ).toHaveBeenCalledWith( [ { clientId: 'b', innerBlocks: [], attributes: { foo: 3 } } ], - { + expect.objectContaining( { selection: { selectionEnd: {}, selectionStart: {}, initialPosition: null, }, - } + } ) ); } ); @@ -544,13 +547,13 @@ describe( 'useBlockSync hook', () => { // Only the new callback should be called. expect( onChange2 ).toHaveBeenCalledWith( [ { clientId: 'b', innerBlocks: [], attributes: { foo: 3 } } ], - { + expect.objectContaining( { selection: { selectionEnd: {}, selectionStart: {}, initialPosition: null, }, - } + } ) ); } ); } ); diff --git a/packages/block-editor/src/components/provider/use-block-sync.js b/packages/block-editor/src/components/provider/use-block-sync.js index 4f2300f380892..969c0f1e4d1c5 100644 --- a/packages/block-editor/src/components/provider/use-block-sync.js +++ b/packages/block-editor/src/components/provider/use-block-sync.js @@ -9,6 +9,7 @@ import { cloneBlock } from '@wordpress/blocks'; * Internal dependencies */ import { store as blockEditorStore } from '../../store'; +import { undoIgnoreBlocks } from '../../store/undo-ignore'; const noop = () => {}; @@ -264,6 +265,10 @@ export default function useBlockSync( { const updateParent = isPersistent ? onChangeRef.current : onInputRef.current; + const undoIgnore = undoIgnoreBlocks.has( blocks ); + if ( undoIgnore ) { + undoIgnoreBlocks.delete( blocks ); + } updateParent( blocks, { selection: { selectionStart: getSelectionStart(), @@ -271,6 +276,7 @@ export default function useBlockSync( { initialPosition: getSelectedBlocksInitialCaretPosition(), }, + undoIgnore, } ); } previousAreBlocksDifferent = areBlocksDifferent; diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 1c29948d81416..48c5d15d469be 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -3,6 +3,11 @@ */ import { Platform } from '@wordpress/element'; +/** + * Internal dependencies + */ +import { undoIgnoreBlocks } from './undo-ignore'; + const castArray = ( maybeArray ) => Array.isArray( maybeArray ) ? maybeArray : [ maybeArray ]; @@ -291,10 +296,30 @@ export function deleteStyleOverride( id ) { }; } -export function syncDerivedBlockAttributes( clientId, attributes ) { - return { - type: 'SYNC_DERIVED_BLOCK_ATTRIBUTES', - clientIds: [ clientId ], - attributes, +/** + * A higher-order action that mark every change inside a callback as "non-persistent" + * and ignore pushing to the undo history stack. It's primarily used for synchronized + * derived updates from the block editor without affecting the undo history. + * + * @param {() => void} callback The synchronous callback to derive updates. + */ +export function syncDerivedUpdates( callback ) { + return ( { dispatch, select, registry } ) => { + registry.batch( () => { + // Mark every change in the `callback` as non-persistent. + dispatch( { + type: 'SET_EXPLICIT_PERSISTENT', + isPersistentChange: false, + } ); + callback(); + dispatch( { + type: 'SET_EXPLICIT_PERSISTENT', + isPersistentChange: undefined, + } ); + + // Ignore pushing undo stack for the updated blocks. + const updatedBlocks = select.getBlocks(); + undoIgnoreBlocks.add( updatedBlocks ); + } ); }; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 5319a3b255365..11811afd83f6f 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -453,14 +453,25 @@ const withBlockTree = function withPersistentBlockChange( reducer ) { let lastAction; let markNextChangeAsNotPersistent = false; + let explicitPersistent; return ( state, action ) => { let nextState = reducer( state, action ); - if ( action.type === 'SYNC_DERIVED_BLOCK_ATTRIBUTES' ) { - return nextState.isPersistentChange - ? { ...nextState, isPersistentChange: false } - : nextState; + let nextIsPersistentChange; + if ( action.type === 'SET_EXPLICIT_PERSISTENT' ) { + explicitPersistent = action.isPersistentChange; + nextIsPersistentChange = state.isPersistentChange ?? true; + } + + if ( explicitPersistent !== undefined ) { + nextIsPersistentChange = explicitPersistent; + return nextIsPersistentChange === nextState.isPersistentChange + ? nextState + : { + ...nextState, + isPersistentChange: nextIsPersistentChange, + }; } const isExplicitPersistentChange = @@ -473,7 +484,7 @@ function withPersistentBlockChange( reducer ) { markNextChangeAsNotPersistent = action.type === 'MARK_NEXT_CHANGE_AS_NOT_PERSISTENT'; - const nextIsPersistentChange = state?.isPersistentChange ?? true; + nextIsPersistentChange = state?.isPersistentChange ?? true; if ( state.isPersistentChange === nextIsPersistentChange ) { return state; } diff --git a/packages/block-editor/src/store/undo-ignore.js b/packages/block-editor/src/store/undo-ignore.js new file mode 100644 index 0000000000000..f0a64428ea7c2 --- /dev/null +++ b/packages/block-editor/src/store/undo-ignore.js @@ -0,0 +1,4 @@ +// Keep track of the blocks that should not be pushing an additional +// undo stack when editing the entity. +// See the implementation of `syncDerivedUpdates` and `useBlockSync`. +export const undoIgnoreBlocks = new WeakSet(); diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index fbfef0b4cf177..67b2680f6840e 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -137,6 +137,7 @@ export default function ReusableBlockEdit( { attributes: { ref, overrides }, __unstableParentLayout: parentLayout, clientId: patternClientId, + setAttributes, } ) { const registry = useRegistry(); const hasAlreadyRendered = useHasRecursion( ref ); @@ -154,7 +155,9 @@ export default function ReusableBlockEdit( { setBlockEditingMode, } = useDispatch( blockEditorStore ); const { getBlockEditingMode } = useSelect( blockEditorStore ); + const { syncDerivedUpdates } = unlock( useDispatch( blockEditorStore ) ); + // Apply the initial overrides from the pattern block to the inner blocks. useEffect( () => { const initialBlocks = editedRecord.blocks ?? @@ -164,17 +167,19 @@ export default function ReusableBlockEdit( { defaultValuesRef.current = {}; const editingMode = getBlockEditingMode( patternClientId ); + // Replace the contents of the blocks with the overrides. registry.batch( () => { setBlockEditingMode( patternClientId, 'default' ); - __unstableMarkNextChangeAsNotPersistent(); - replaceInnerBlocks( - patternClientId, - applyInitialOverrides( - initialBlocks, - initialOverrides.current, - defaultValuesRef.current - ) - ); + syncDerivedUpdates( () => { + replaceInnerBlocks( + patternClientId, + applyInitialOverrides( + initialBlocks, + initialOverrides.current, + defaultValuesRef.current + ) + ); + } ); setBlockEditingMode( patternClientId, editingMode ); } ); }, [ @@ -185,6 +190,7 @@ export default function ReusableBlockEdit( { registry, getBlockEditingMode, setBlockEditingMode, + syncDerivedUpdates, ] ); const innerBlocks = useSelect( @@ -220,29 +226,26 @@ export default function ReusableBlockEdit( { : InnerBlocks.ButtonBlockAppender, } ); - // Sync the `overrides` attribute from the updated blocks. - // `syncDerivedBlockAttributes` is an action that just like `updateBlockAttributes` - // but won't create an undo level. - // This can be abstracted into a `useSyncDerivedAttributes` hook if needed. + // Sync the `overrides` attribute from the updated blocks to the pattern block. + // `syncDerivedUpdates` is used here to avoid creating an additional undo level. useEffect( () => { const { getBlocks } = registry.select( blockEditorStore ); - const { syncDerivedBlockAttributes } = unlock( - registry.dispatch( blockEditorStore ) - ); let prevBlocks = getBlocks( patternClientId ); return registry.subscribe( () => { const blocks = getBlocks( patternClientId ); if ( blocks !== prevBlocks ) { prevBlocks = blocks; - syncDerivedBlockAttributes( patternClientId, { - overrides: getOverridesFromBlocks( - blocks, - defaultValuesRef.current - ), + syncDerivedUpdates( () => { + setAttributes( { + overrides: getOverridesFromBlocks( + blocks, + defaultValuesRef.current + ), + } ); } ); } }, blockEditorStore ); - }, [ patternClientId, registry ] ); + }, [ syncDerivedUpdates, patternClientId, registry, setAttributes ] ); let children = null; diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js index 4b82b62e318bc..5dc19f5225c76 100644 --- a/packages/core-data/src/entity-provider.js +++ b/packages/core-data/src/entity-provider.js @@ -196,7 +196,7 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { if ( noChange ) { return __unstableCreateUndoLevel( kind, name, id ); } - const { selection } = options; + const { selection, ...rest } = options; // We create a new function here on every persistent edit // to make sure the edit makes the post dirty and creates @@ -208,7 +208,10 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { ...updateFootnotes( newBlocks ), }; - editEntityRecord( kind, name, id, edits, { isCached: false } ); + editEntityRecord( kind, name, id, edits, { + isCached: false, + ...rest, + } ); }, [ kind, @@ -223,11 +226,14 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { const onInput = useCallback( ( newBlocks, options ) => { - const { selection } = options; + const { selection, ...rest } = options; const footnotesChanges = updateFootnotes( newBlocks ); const edits = { selection, ...footnotesChanges }; - editEntityRecord( kind, name, id, edits, { isCached: true } ); + editEntityRecord( kind, name, id, edits, { + isCached: true, + ...rest, + } ); }, [ kind, name, id, updateFootnotes, editEntityRecord ] ); From 22741661998834e69db74ad863705ee2ce97b446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Fri, 22 Dec 2023 10:54:05 +0100 Subject: [PATCH 09/63] DataViews: make `deferredRendering` prop optional (#57334) --- packages/dataviews/README.md | 2 +- packages/dataviews/src/dataviews.js | 2 +- packages/edit-site/src/components/page-pages/index.js | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index 1f4bdb91129f5..558c509139a66 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -235,7 +235,7 @@ Array of operations that can be performed upon each record. Each action is an ob - `getItemId`: function that receives an item and returns an unique identifier for it. By default, it uses the `id` of the item as unique identifier. If it's not, the consumer should provide their own. - `isLoading`: whether the data is loading. `false` by default. - `supportedLayouts`: array of layouts supported. By default, all are: `table`, `grid`, `list`. -- `deferredRendering`: whether the items should be rendered asynchronously. Required. +- `deferredRendering`: whether the items should be rendered asynchronously. Useful when there's a field that takes a lot of time (e.g.: previews). `false` by default. - `onSelectionChange`: callback that returns the selected items. So far, only the `list` view implements this. ## Contributing to this package diff --git a/packages/dataviews/src/dataviews.js b/packages/dataviews/src/dataviews.js index c506d872c64a6..216e482a8f447 100644 --- a/packages/dataviews/src/dataviews.js +++ b/packages/dataviews/src/dataviews.js @@ -31,7 +31,7 @@ export default function DataViews( { paginationInfo, supportedLayouts, onSelectionChange, - deferredRendering, + deferredRendering = false, } ) { const [ selection, setSelection ] = useState( [] ); diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index c9e36c1bd585d..56fda6e6154b2 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -342,7 +342,6 @@ export default function PagePages() { view={ view } onChangeView={ onChangeView } onSelectionChange={ onSelectionChange } - deferredRendering={ false } /> { view.type === LAYOUT_LIST && ( From ae7467d8bab6cbe538ac0de648b57a62480e2bdc Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 22 Dec 2023 11:33:39 +0100 Subject: [PATCH 10/63] Delete experimental radix-ui based DropdownMenu component (#55626) * Delete experimental radix-ui based DropdownMenu component * Remove "@radix-ui/react-dropdown-menu" dependency * Remove "Ariakit" from the export name * Rename folder (remove "ariakit") * CHANGELOG --- docs/manifest.json | 6 - package-lock.json | 768 ------------ packages/components/CHANGELOG.md | 1 + packages/components/package.json | 1 - .../src/dropdown-menu-v2-ariakit/README.md | 331 ----- .../src/dropdown-menu-v2-ariakit/index.tsx | 383 ------ .../stories/index.story.tsx | 617 --------- .../src/dropdown-menu-v2-ariakit/styles.ts | 346 ----- .../dropdown-menu-v2-ariakit/test/index.tsx | 1108 ----------------- .../src/dropdown-menu-v2-ariakit/types.ts | 179 --- .../components/src/dropdown-menu-v2/README.md | 211 ++-- .../components/src/dropdown-menu-v2/index.tsx | 552 ++++---- .../dropdown-menu-v2/stories/index.story.tsx | 648 ++++++++-- .../components/src/dropdown-menu-v2/styles.ts | 372 +++--- .../src/dropdown-menu-v2/test/index.tsx | 668 +++++++--- .../components/src/dropdown-menu-v2/types.ts | 282 ++--- packages/components/src/private-apis.ts | 40 +- packages/dataviews/src/add-filter.js | 12 +- .../dataviews/src/dropdown-menu-helper.js | 2 +- packages/dataviews/src/filter-summary.js | 10 +- packages/dataviews/src/item-actions.js | 8 +- packages/dataviews/src/view-actions.js | 10 +- packages/dataviews/src/view-table.js | 10 +- 23 files changed, 1752 insertions(+), 4813 deletions(-) delete mode 100644 packages/components/src/dropdown-menu-v2-ariakit/README.md delete mode 100644 packages/components/src/dropdown-menu-v2-ariakit/index.tsx delete mode 100644 packages/components/src/dropdown-menu-v2-ariakit/stories/index.story.tsx delete mode 100644 packages/components/src/dropdown-menu-v2-ariakit/styles.ts delete mode 100644 packages/components/src/dropdown-menu-v2-ariakit/test/index.tsx delete mode 100644 packages/components/src/dropdown-menu-v2-ariakit/types.ts diff --git a/docs/manifest.json b/docs/manifest.json index 7bb1e847ce03f..86a889406ce91 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -761,12 +761,6 @@ "markdown_source": "../packages/components/src/drop-zone/README.md", "parent": "components" }, - { - "title": "DropdownMenuV2Ariakit", - "slug": "dropdown-menu-v2-ariakit", - "markdown_source": "../packages/components/src/dropdown-menu-v2-ariakit/README.md", - "parent": "components" - }, { "title": "DropdownMenu", "slug": "dropdown-menu", diff --git a/package-lock.json b/package-lock.json index c52010a3347fc..5861648b6f55d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7541,85 +7541,6 @@ "@babel/runtime": "^7.13.10" } }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.2.tgz", - "integrity": "sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.2" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.2.tgz", - "integrity": "sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-slot": "1.0.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", @@ -7668,17 +7589,6 @@ "react-dom": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@radix-ui/react-direction": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.0.tgz", - "integrity": "sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz", @@ -7696,50 +7606,6 @@ "react-dom": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.4.tgz", - "integrity": "sha512-y6AT9+MydyXcByivdK1+QpjWoKaC7MLjkS/cH1Q3keEyMvDkiY85m8o2Bi6+Z1PPUlCsMULopxagQOSfN0wahg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-id": "1.0.0", - "@radix-ui/react-menu": "2.0.4", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.0.tgz", @@ -7778,215 +7644,6 @@ "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@radix-ui/react-menu": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.4.tgz", - "integrity": "sha512-mzKR47tZ1t193trEqlQoJvzY4u9vYfVH16ryBrVrCAGZzkgyWnMQYEZdUkM7y8ak9mrkKtJiqB47TlEnubeOFQ==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-collection": "1.0.2", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-direction": "1.0.0", - "@radix-ui/react-dismissable-layer": "1.0.3", - "@radix-ui/react-focus-guards": "1.0.0", - "@radix-ui/react-focus-scope": "1.0.2", - "@radix-ui/react-id": "1.0.0", - "@radix-ui/react-popper": "1.1.1", - "@radix-ui/react-portal": "1.0.2", - "@radix-ui/react-presence": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-roving-focus": "1.0.3", - "@radix-ui/react-slot": "1.0.1", - "@radix-ui/react-use-callback-ref": "1.0.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.3.tgz", - "integrity": "sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.0", - "@radix-ui/react-use-escape-keydown": "1.0.2" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.2.tgz", - "integrity": "sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-portal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz", - "integrity": "sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.2" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz", - "integrity": "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-menu/node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.1.tgz", - "integrity": "sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "0.7.2", - "@radix-ui/react-arrow": "1.0.2", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.0", - "@radix-ui/react-use-layout-effect": "1.0.0", - "@radix-ui/react-use-rect": "1.0.0", - "@radix-ui/react-use-size": "1.0.0", - "@radix-ui/rect": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-popper/node_modules/@floating-ui/core": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz", - "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==" - }, - "node_modules/@radix-ui/react-popper/node_modules/@floating-ui/dom": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz", - "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==", - "dependencies": { - "@floating-ui/core": "^0.7.3" - } - }, - "node_modules/@radix-ui/react-popper/node_modules/@floating-ui/react-dom": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz", - "integrity": "sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==", - "dependencies": { - "@floating-ui/dom": "^0.5.3", - "use-isomorphic-layout-effect": "^1.1.1" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, "node_modules/@radix-ui/react-portal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.0.tgz", @@ -8027,52 +7684,6 @@ "react-dom": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.3.tgz", - "integrity": "sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-collection": "1.0.2", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-direction": "1.0.0", - "@radix-ui/react-id": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.0", - "@radix-ui/react-use-controllable-state": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, "node_modules/@radix-ui/react-select": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz", @@ -8657,30 +8268,6 @@ } } }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.0.tgz", - "integrity": "sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/rect": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz", - "integrity": "sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, "node_modules/@radix-ui/react-visually-hidden": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", @@ -8766,14 +8353,6 @@ } } }, - "node_modules/@radix-ui/rect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz", - "integrity": "sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, "node_modules/@react-native-clipboard/clipboard": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.11.2.tgz", @@ -51620,19 +51199,6 @@ } } }, - "node_modules/use-isomorphic-layout-effect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-latest-callback": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz", @@ -54726,7 +54292,6 @@ "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "^2.0.1", - "@radix-ui/react-dropdown-menu": "2.0.4", "@types/gradient-parser": "0.1.3", "@types/highlight-words-core": "1.2.1", "@use-gesture/react": "^10.2.24", @@ -62037,67 +61602,6 @@ "@babel/runtime": "^7.13.10" } }, - "@radix-ui/react-arrow": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.2.tgz", - "integrity": "sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.2" - }, - "dependencies": { - "@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - } - }, - "@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - } - } - } - }, - "@radix-ui/react-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.2.tgz", - "integrity": "sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-slot": "1.0.1" - }, - "dependencies": { - "@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - } - }, - "@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - } - } - } - }, "@radix-ui/react-compose-refs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", @@ -62136,14 +61640,6 @@ "react-remove-scroll": "2.5.4" } }, - "@radix-ui/react-direction": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.0.tgz", - "integrity": "sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, "@radix-ui/react-dismissable-layer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz", @@ -62157,41 +61653,6 @@ "@radix-ui/react-use-escape-keydown": "1.0.0" } }, - "@radix-ui/react-dropdown-menu": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.4.tgz", - "integrity": "sha512-y6AT9+MydyXcByivdK1+QpjWoKaC7MLjkS/cH1Q3keEyMvDkiY85m8o2Bi6+Z1PPUlCsMULopxagQOSfN0wahg==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-id": "1.0.0", - "@radix-ui/react-menu": "2.0.4", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.0" - }, - "dependencies": { - "@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - } - }, - "@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - } - } - } - }, "@radix-ui/react-focus-guards": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.0.tgz", @@ -62220,166 +61681,6 @@ "@radix-ui/react-use-layout-effect": "1.0.0" } }, - "@radix-ui/react-menu": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.4.tgz", - "integrity": "sha512-mzKR47tZ1t193trEqlQoJvzY4u9vYfVH16ryBrVrCAGZzkgyWnMQYEZdUkM7y8ak9mrkKtJiqB47TlEnubeOFQ==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-collection": "1.0.2", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-direction": "1.0.0", - "@radix-ui/react-dismissable-layer": "1.0.3", - "@radix-ui/react-focus-guards": "1.0.0", - "@radix-ui/react-focus-scope": "1.0.2", - "@radix-ui/react-id": "1.0.0", - "@radix-ui/react-popper": "1.1.1", - "@radix-ui/react-portal": "1.0.2", - "@radix-ui/react-presence": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-roving-focus": "1.0.3", - "@radix-ui/react-slot": "1.0.1", - "@radix-ui/react-use-callback-ref": "1.0.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "dependencies": { - "@radix-ui/react-dismissable-layer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.3.tgz", - "integrity": "sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.0", - "@radix-ui/react-use-escape-keydown": "1.0.2" - } - }, - "@radix-ui/react-focus-scope": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.2.tgz", - "integrity": "sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.0" - } - }, - "@radix-ui/react-portal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz", - "integrity": "sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.2" - } - }, - "@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - } - }, - "@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - } - }, - "@radix-ui/react-use-escape-keydown": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz", - "integrity": "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.0" - } - }, - "react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "requires": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - } - } - } - }, - "@radix-ui/react-popper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.1.tgz", - "integrity": "sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==", - "requires": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "0.7.2", - "@radix-ui/react-arrow": "1.0.2", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.0", - "@radix-ui/react-use-layout-effect": "1.0.0", - "@radix-ui/react-use-rect": "1.0.0", - "@radix-ui/react-use-size": "1.0.0", - "@radix-ui/rect": "1.0.0" - }, - "dependencies": { - "@floating-ui/core": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz", - "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==" - }, - "@floating-ui/dom": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz", - "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==", - "requires": { - "@floating-ui/core": "^0.7.3" - } - }, - "@floating-ui/react-dom": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz", - "integrity": "sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==", - "requires": { - "@floating-ui/dom": "^0.5.3", - "use-isomorphic-layout-effect": "^1.1.1" - } - }, - "@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - } - }, - "@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - } - } - } - }, "@radix-ui/react-portal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.0.tgz", @@ -62408,43 +61709,6 @@ "@radix-ui/react-slot": "1.0.0" } }, - "@radix-ui/react-roving-focus": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.3.tgz", - "integrity": "sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-collection": "1.0.2", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-direction": "1.0.0", - "@radix-ui/react-id": "1.0.0", - "@radix-ui/react-primitive": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.0", - "@radix-ui/react-use-controllable-state": "1.0.0" - }, - "dependencies": { - "@radix-ui/react-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", - "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - } - }, - "@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - } - } - } - }, "@radix-ui/react-select": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz", @@ -62771,24 +62035,6 @@ "@babel/runtime": "^7.13.10" } }, - "@radix-ui/react-use-rect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.0.tgz", - "integrity": "sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/rect": "1.0.0" - } - }, - "@radix-ui/react-use-size": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz", - "integrity": "sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.0" - } - }, "@radix-ui/react-visually-hidden": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", @@ -62830,14 +62076,6 @@ } } }, - "@radix-ui/rect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz", - "integrity": "sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, "@react-native-clipboard/clipboard": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.11.2.tgz", @@ -70161,7 +69399,6 @@ "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "^2.0.1", - "@radix-ui/react-dropdown-menu": "2.0.4", "@types/gradient-parser": "0.1.3", "@types/highlight-words-core": "1.2.1", "@use-gesture/react": "^10.2.24", @@ -96891,11 +96128,6 @@ "tslib": "^2.0.0" } }, - "use-isomorphic-layout-effect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" - }, "use-latest-callback": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 093dea189bdf7..ff03a5ba483e9 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -22,6 +22,7 @@ ### Experimental +- `DropdownMenuV2`: remove temporary radix UI based implementation ([#55626](https://github.com/WordPress/gutenberg/pull/55626)). - `Tabs`: do not render hidden content ([#57046](https://github.com/WordPress/gutenberg/pull/57046)). - `Tabs`: improve hover and text alignment styles ([#57275](https://github.com/WordPress/gutenberg/pull/57275)). - `Tabs`: make sure `Tab`s are associated to the right `TabPanel`s, regardless of the order they're rendered in ([#57033](https://github.com/WordPress/gutenberg/pull/57033)). diff --git a/packages/components/package.json b/packages/components/package.json index b7581679d9094..79e961dc64da4 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -39,7 +39,6 @@ "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", "@floating-ui/react-dom": "^2.0.1", - "@radix-ui/react-dropdown-menu": "2.0.4", "@types/gradient-parser": "0.1.3", "@types/highlight-words-core": "1.2.1", "@use-gesture/react": "^10.2.24", diff --git a/packages/components/src/dropdown-menu-v2-ariakit/README.md b/packages/components/src/dropdown-menu-v2-ariakit/README.md deleted file mode 100644 index 2902b54116976..0000000000000 --- a/packages/components/src/dropdown-menu-v2-ariakit/README.md +++ /dev/null @@ -1,331 +0,0 @@ -# `DropdownMenu` (v2) - -
-This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. -
- -`DropdownMenu` displays a menu to the user (such as a set of actions or functions) triggered by a button. - - -## Design guidelines - -### Usage - -#### When to use a DropdownMenu - -Use a DropdownMenu when you want users to: - -- Choose an action or change a setting from a list, AND -- Only see the available choices contextually. - -`DropdownMenu` is a React component to render an expandable menu of buttons. It is similar in purpose to a `' . $content . ''; + }, + 'supports' => array( + 'interactivity' => true, + ), + ) + ); -function gutenberg_test_process_directives_helper_increment( $store ) { - return $store['state']['count'] + $store['context']['count']; -} + register_block_type( + 'test/context-level-2', + array( + 'render_callback' => function ( $attributes, $content ) { + return '
' . $content . '
'; + }, + 'supports' => array( + 'interactivity' => true, + ), + ) + ); -/** - * Tests for the gutenberg_interactivity_process_rendered_html function. - * - * @group interactivity-api - * @covers gutenberg_interactivity_process_rendered_html - */ -class Tests_Process_Directives extends WP_UnitTestCase { - public function test_correctly_call_attribute_directive_processor_on_closing_tag() { - - // PHPUnit cannot stub functions, only classes. - $test_helper = $this->createMock( Helper_Class::class ); - - $test_helper->expects( $this->exactly( 2 ) ) - ->method( 'process_foo_test' ) - ->with( - $this->callback( - function ( $p ) { - return 'DIV' === $p->get_tag() && ( - // Either this is a closing tag... - $p->is_tag_closer() || - // ...or it is an open tag, and has the directive attribute set. - ( ! $p->is_tag_closer() && 'abc' === $p->get_attribute( 'foo-test' ) ) - ); - } - ) - ); - - $directives = array( - 'foo-test' => array( $test_helper, 'process_foo_test' ), + register_block_type( + 'test/context-read-only', + array( + 'render_callback' => function () { + return '
'; + }, + 'supports' => array( + 'interactivity' => true, + ), + ) ); - $markup = '
Example:
This is a test>
Here is a nested div
'; - $tags = new WP_Directive_Processor( $markup ); - $tags->process_rendered_html( $tags, 'foo-', $directives ); - } + register_block_type( + 'test/non-interactive-with-directive', + array( + 'render_callback' => function () { + return ''; + }, + ) + ); - public function test_directives_with_double_hyphen_processed_correctly() { - $test_helper = $this->createMock( Helper_Class::class ); - $test_helper->expects( $this->atLeastOnce() ) - ->method( 'process_foo_test' ); + register_block_type( + 'test/context-level-with-manual-inner-block-rendering', + array( + 'render_callback' => function ( $attributes, $content, $block ) { + $inner_blocks_html = ''; + foreach ( $block->inner_blocks as $inner_block ) { + $inner_blocks_html .= $inner_block->render(); + } + return '
' . $inner_blocks_html . '
'; + }, + 'supports' => array( + 'interactivity' => true, + ), + ) + ); - $directives = array( - 'foo-test' => array( $test_helper, 'process_foo_test' ), + register_block_type( + 'test/directives-ordering', + array( + 'render_callback' => function () { + return ''; + }, + 'supports' => array( + 'interactivity' => true, + ), + ) ); + } - $markup = '
'; - $tags = new WP_Directive_Processor( $markup ); - $tags->process_rendered_html( $tags, 'foo-', $directives ); + public function tear_down() { + unregister_block_type( 'test/context-level-1' ); + unregister_block_type( 'test/context-level-2' ); + unregister_block_type( 'test/context-read-only' ); + unregister_block_type( 'test/non-interactive-with-directive' ); + unregister_block_type( 'test/context-level-with-manual-inner-block-rendering' ); + unregister_block_type( 'test/directives-ordering' ); + parent::tear_down(); } public function test_interactivity_process_directives_in_root_blocks() { - $block_content = '' . '

Welcome to WordPress. This is your first post. Edit or delete it, then start writing!

' . @@ -87,15 +103,11 @@ public function test_interactivity_process_directives_in_root_blocks() { '

Welcome to WordPress.

' . ''; - $parsed_block = parse_blocks( $block_content )[0]; - - $source_block = $parsed_block; - - $rendered_content = render_block( $parsed_block ); - + $parsed_block = parse_blocks( $block_content )[0]; + $source_block = $parsed_block; + $rendered_content = render_block( $parsed_block ); $parsed_block_second = parse_blocks( $block_content )[1]; - - $fake_parent_block = array(); + $fake_parent_block = array(); // Test that root block is intially emtpy. $this->assertEmpty( WP_Directive_Processor::$root_block ); @@ -117,16 +129,103 @@ public function test_interactivity_process_directives_in_root_blocks() { gutenberg_process_directives_in_root_blocks( $rendered_content, $parsed_block ); $this->assertEmpty( WP_Directive_Processor::$root_block ); } -} + public function test_directive_processing_of_interactive_block() { + $post_content = ''; + $rendered_blocks = do_blocks( $post_content ); + $p = new WP_HTML_Tag_Processor( $rendered_blocks ); + $p->next_tag( array( 'class_name' => 'level-1-input-1' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + $p->next_tag( array( 'class_name' => 'level-1-input-2' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + } + + public function test_directive_processing_two_interactive_blocks_at_same_level() { + $post_content = '
'; + $rendered_blocks = do_blocks( $post_content ); + $p = new WP_HTML_Tag_Processor( $rendered_blocks ); + $p->next_tag( array( 'class_name' => 'level-1-input-1' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + $p->next_tag( array( 'class_name' => 'level-1-input-2' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + $p->next_tag( array( 'class_name' => 'level-2-input-1' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-2', $value ); + } + + public function test_directives_are_processed_at_tag_end() { + $post_content = ''; + $rendered_blocks = do_blocks( $post_content ); + $p = new WP_HTML_Tag_Processor( $rendered_blocks ); + $p->next_tag( array( 'class_name' => 'level-1-input-1' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + $p->next_tag( array( 'class_name' => 'level-2-input-1' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-2', $value ); + $p->next_tag( array( 'class_name' => 'read-only-input-1' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + $p->next_tag( array( 'class_name' => 'level-1-input-2' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + } + + public function test_non_interactive_children_of_interactive_is_rendered() { + $post_content = '

Welcome

'; + $rendered_blocks = do_blocks( $post_content ); + $p = new WP_HTML_Tag_Processor( $rendered_blocks ); + $p->next_tag( array( 'class_name' => 'level-1-input-1' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + $p->next_tag( array( 'class_name' => 'read-only-input-1' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + $p->next_tag(); + $this->assertSame( 'P', $p->get_tag() ); + $p->next_tag( array( 'class_name' => 'level-1-input-2' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'level-1', $value ); + } + + public function test_non_interactive_blocks_are_not_processed() { + $post_content = ''; + $rendered_blocks = do_blocks( $post_content ); + $p = new WP_HTML_Tag_Processor( $rendered_blocks ); + $p->next_tag( array( 'class_name' => 'non-interactive-with-directive' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( null, $value ); + } + + public function test_non_interactive_blocks_with_manual_inner_block_rendering_are_not_processed() { + $post_content = ''; + $rendered_blocks = do_blocks( $post_content ); + $p = new WP_HTML_Tag_Processor( $rendered_blocks ); + $p->next_tag( array( 'class_name' => 'non-interactive-with-directive' ) ); + $value = $p->get_attribute( 'value' ); + $this->assertSame( null, $value ); + } + + public function test_directives_ordering() { + $post_content = ''; + $rendered_blocks = do_blocks( $post_content ); + $p = new WP_HTML_Tag_Processor( $rendered_blocks ); + $p->next_tag(); + + $value = $p->get_attribute( 'class' ); + $this->assertSame( 'other-class some-class', $value ); + + $value = $p->get_attribute( 'value' ); + $this->assertSame( 'some-value', $value ); + + $value = $p->get_attribute( 'style' ); + $this->assertSame( 'display: none;', $value ); + } -/** - * Tests for the gutenberg_interactivity_evaluate_reference function. - * - * @group interactivity-api - * @covers gutenberg_interactivity_evaluate_reference - */ -class Tests_Utils_Evaluate extends WP_UnitTestCase { public function test_evaluate_function_should_access_state() { // Init a simple store. wp_store( @@ -142,6 +241,7 @@ public function test_evaluate_function_should_access_state() { ), ) ); + $this->assertSame( 1, gutenberg_interactivity_evaluate_reference( 'state.core.number' ) ); $this->assertTrue( gutenberg_interactivity_evaluate_reference( 'state.core.bool' ) ); $this->assertSame( 'hi', gutenberg_interactivity_evaluate_reference( 'state.core.nested.string' ) ); @@ -158,10 +258,12 @@ public function test_evaluate_function_should_access_passed_context() { ), ), ); + $this->assertSame( 2, gutenberg_interactivity_evaluate_reference( 'context.local.number', $context ) ); $this->assertFalse( gutenberg_interactivity_evaluate_reference( 'context.local.bool', $context ) ); $this->assertTrue( gutenberg_interactivity_evaluate_reference( '!context.local.bool', $context ) ); $this->assertSame( 'bye', gutenberg_interactivity_evaluate_reference( 'context.local.nested.string', $context ) ); + // Previously defined state is also accessible. $this->assertSame( 1, gutenberg_interactivity_evaluate_reference( 'state.core.number' ) ); $this->assertTrue( gutenberg_interactivity_evaluate_reference( 'state.core.bool' ) ); @@ -174,7 +276,6 @@ public function test_evaluate_function_should_return_null_for_unresolved_paths() public function test_evaluate_function_should_execute_anonymous_functions() { $context = new WP_Directive_Context( array( 'count' => 2 ) ); - $helper = new Helper_Class(); wp_store( array( @@ -182,14 +283,13 @@ public function test_evaluate_function_should_execute_anonymous_functions() { 'count' => 3, ), 'selectors' => array( - 'anonymous_function' => function ( $store ) { + 'anonymous_function' => function ( $store ) { return $store['state']['count'] + $store['context']['count']; }, // Other types of callables should not be executed. - 'function_name' => 'gutenberg_test_process_directives_helper_increment', - 'class_method' => array( $helper, 'increment' ), - 'class_static_method' => 'Helper_Class::static_increment', - 'class_static_method_as_array' => array( 'Helper_Class', 'static_increment' ), + 'function_name' => 'gutenberg_test_process_directives_helper_increment', + 'class_method' => array( $this, 'increment' ), + 'class_static_method' => array( 'Tests_Process_Directives', 'static_increment' ), ), ) ); @@ -200,16 +300,12 @@ public function test_evaluate_function_should_execute_anonymous_functions() { gutenberg_interactivity_evaluate_reference( 'selectors.function_name', $context->get_context() ) ); $this->assertSame( - array( $helper, 'increment' ), + array( $this, 'increment' ), gutenberg_interactivity_evaluate_reference( 'selectors.class_method', $context->get_context() ) ); $this->assertSame( - 'Helper_Class::static_increment', + array( 'Tests_Process_Directives', 'static_increment' ), gutenberg_interactivity_evaluate_reference( 'selectors.class_static_method', $context->get_context() ) ); - $this->assertSame( - array( 'Helper_Class', 'static_increment' ), - gutenberg_interactivity_evaluate_reference( 'selectors.class_static_method_as_array', $context->get_context() ) - ); } } diff --git a/phpunit/experimental/interactivity-api/directives/wp-style-test.php b/phpunit/experimental/interactivity-api/directives/wp-style-test.php index 8942559b2fe89..51468bd8a2814 100644 --- a/phpunit/experimental/interactivity-api/directives/wp-style-test.php +++ b/phpunit/experimental/interactivity-api/directives/wp-style-test.php @@ -14,16 +14,16 @@ */ class Tests_Directives_WpStyle extends WP_UnitTestCase { public function test_directive_adds_style() { - $markup = '
Test
'; + $markup = '
Test
'; $tags = new WP_HTML_Tag_Processor( $markup ); $tags->next_tag(); - $context_before = new WP_Directive_Context( array( 'myblock' => array( 'color' => 'green' ) ) ); + $context_before = new WP_Directive_Context( array( 'color' => 'green' ) ); $context = $context_before; gutenberg_interactivity_process_wp_style( $tags, $context ); $this->assertSame( - '
Test
', + '
Test
', $tags->get_updated_html() ); $this->assertStringContainsString( 'color: green;', $tags->get_attribute( 'style' ) ); @@ -31,11 +31,11 @@ public function test_directive_adds_style() { } public function test_directive_ignores_empty_style() { - $markup = '
Test
'; + $markup = '
Test
'; $tags = new WP_HTML_Tag_Processor( $markup ); $tags->next_tag(); - $context_before = new WP_Directive_Context( array( 'myblock' => array( 'color' => 'green' ) ) ); + $context_before = new WP_Directive_Context( array( 'color' => 'green' ) ); $context = $context_before; gutenberg_interactivity_process_wp_style( $tags, $context ); @@ -43,4 +43,21 @@ public function test_directive_ignores_empty_style() { $this->assertStringNotContainsString( 'color: green;', $tags->get_attribute( 'style' ) ); $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-style directive changed context' ); } + + public function test_directive_works_without_style_attribute() { + $markup = '
Test
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'color' => 'green' ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_style( $tags, $context ); + + $this->assertSame( + '
Test
', + $tags->get_updated_html() + ); + $this->assertSame( 'color: green;', $tags->get_attribute( 'style' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-style directive changed context' ); + } } From 1c79fd8a53265e669dc2d24c87f00ad44f977a6b Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Thu, 28 Dec 2023 21:27:19 +0200 Subject: [PATCH 57/63] Performance: Avoid extra `useMarkPersistent` dispatch calls (#57435) --- .../src/components/rich-text/use-mark-persistent.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/rich-text/use-mark-persistent.js b/packages/block-editor/src/components/rich-text/use-mark-persistent.js index 9a564dfb7f97e..10e157452fbe2 100644 --- a/packages/block-editor/src/components/rich-text/use-mark-persistent.js +++ b/packages/block-editor/src/components/rich-text/use-mark-persistent.js @@ -11,8 +11,7 @@ import { store as blockEditorStore } from '../../store'; export function useMarkPersistent( { html, value } ) { const previousText = useRef(); - const hasActiveFormats = - value.activeFormats && !! value.activeFormats.length; + const hasActiveFormats = !! value.activeFormats?.length; const { __unstableMarkLastChangeAsPersistent } = useDispatch( blockEditorStore ); From 36396cc21c06e60a19a65259dfe5c53df84ebef1 Mon Sep 17 00:00:00 2001 From: Hrithik Dalal Date: Fri, 29 Dec 2023 01:40:10 +0530 Subject: [PATCH 58/63] Font Library: remove "has_font_mime_type" function (#57364) * Remove usage of has_font_mime_type function * Remove definition of has_font_mime_type function * Remove test cases for has_font_mime_type --- .../class-wp-font-family-utils.php | 15 ----- .../font-library/class-wp-font-family.php | 11 ---- .../wpFontFamilyUtils/hasFontMimeType.php | 61 ------------------- 3 files changed, 87 deletions(-) delete mode 100644 phpunit/tests/fonts/font-library/wpFontFamilyUtils/hasFontMimeType.php diff --git a/lib/experimental/fonts/font-library/class-wp-font-family-utils.php b/lib/experimental/fonts/font-library/class-wp-font-family-utils.php index 7d954e79e96a3..35e6856e50aad 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-family-utils.php +++ b/lib/experimental/fonts/font-library/class-wp-font-family-utils.php @@ -76,21 +76,6 @@ public static function merge_fonts_data( $font1, $font2 ) { return $merged_font; } - /** - * Returns whether the given file has a font MIME type. - * - * @since 6.5.0 - * - * @param string $filepath The file to check. - * @return bool True if the file has a font MIME type, false otherwise. - */ - public static function has_font_mime_type( $filepath ) { - $allowed_mime_types = WP_Font_Library::get_expected_font_mime_types_per_php_version(); - $filetype = wp_check_filetype( $filepath, $allowed_mime_types ); - - return in_array( $filetype['type'], $allowed_mime_types, true ); - } - /** * Format font family to make it valid CSS. * diff --git a/lib/experimental/fonts/font-library/class-wp-font-family.php b/lib/experimental/fonts/font-library/class-wp-font-family.php index 58d4f476e834d..a4204dfe1fa2c 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-family.php +++ b/lib/experimental/fonts/font-library/class-wp-font-family.php @@ -202,11 +202,6 @@ private function get_upload_overrides( $filename ) { * False if the download failed. */ private function download_asset( $url, $filename ) { - // Checks if the file to be downloaded has a font mime type. - if ( ! WP_Font_Family_Utils::has_font_mime_type( $filename ) ) { - return false; - } - // Include file with download_url() if function doesn't exist. if ( ! function_exists( 'download_url' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; @@ -263,12 +258,6 @@ private function move_font_face_asset( $font_face, $file ) { // because it is no longer needed. unset( $new_font_face['uploadedFile'] ); - // If the filename has no font mime type, don't move the file and - // return the font face definition without src to be ignored later. - if ( ! WP_Font_Family_Utils::has_font_mime_type( $filename ) ) { - return $new_font_face; - } - // Move the uploaded font asset from the temp folder to the fonts directory. if ( ! function_exists( 'wp_handle_upload' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; diff --git a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/hasFontMimeType.php b/phpunit/tests/fonts/font-library/wpFontFamilyUtils/hasFontMimeType.php deleted file mode 100644 index e30c199612b8a..0000000000000 --- a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/hasFontMimeType.php +++ /dev/null @@ -1,61 +0,0 @@ -assertTrue( WP_Font_Family_Utils::has_font_mime_type( $font_file ) ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_should_succeed_when_has_mime_type() { - return array( - 'ttf' => array( '/temp/piazzolla_400_italic.ttf' ), - 'otf' => array( '/temp/piazzolla_400_italic.otf' ), - 'woff' => array( '/temp/piazzolla_400_italic.woff' ), - 'woff2' => array( '/temp/piazzolla_400_italic.woff2' ), - ); - } - - /** - * @dataProvider data_should_fail_when_mime_not_supported - * - * @param string $font_file Font file path. - */ - public function test_should_fail_when_mime_not_supported( $font_file ) { - $this->assertFalse( WP_Font_Family_Utils::has_font_mime_type( $font_file ) ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_should_fail_when_mime_not_supported() { - return array( - 'exe' => array( '/temp/test.exe' ), - 'md' => array( '/temp/license.md' ), - 'php' => array( '/temp/test.php' ), - 'txt' => array( '/temp/test.txt' ), - 'zip' => array( '/temp/lato.zip' ), - ); - } -} From 10facf1cbd5234375b6a27dcf83aa26afa8905ea Mon Sep 17 00:00:00 2001 From: Hrithik Dalal Date: Fri, 29 Dec 2023 01:58:17 +0530 Subject: [PATCH 59/63] Update modal description and button label (#57368) --- .../font-library-modal/confirm-delete-dialog.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/confirm-delete-dialog.js b/packages/edit-site/src/components/global-styles/font-library-modal/confirm-delete-dialog.js index 259b6900dd16d..b87a921bd35e5 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/confirm-delete-dialog.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/confirm-delete-dialog.js @@ -13,8 +13,8 @@ function ConfirmDeleteDialog( { return ( @@ -22,7 +22,7 @@ function ConfirmDeleteDialog( { sprintf( /* translators: %s: Name of the font. */ __( - 'Would you like to remove %s and all its variants and assets?' + 'Are you sure you want to delete "%s" font and all its variants and assets?' ), font.name ) } From 663916f5bba185abc1a0ab0c1d00295496415bf9 Mon Sep 17 00:00:00 2001 From: Nick Diego Date: Thu, 28 Dec 2023 15:37:26 -0600 Subject: [PATCH 60/63] Update tutorial based on user feedback. (#57403) --- docs/getting-started/tutorial.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/tutorial.md b/docs/getting-started/tutorial.md index b6b3efcf4384a..392e905c44347 100644 --- a/docs/getting-started/tutorial.md +++ b/docs/getting-started/tutorial.md @@ -36,9 +36,9 @@ The first step in creating the Copyright Date Block is to scaffold the initial b Review the Get started with create-block documentation for an introduction to using this package. -You can use `create-block` from just about any directory on your computer and then use `wp-env` to create a local WordPress development environment with your new block plugin installed and activated. +You can use `create-block` from just about any directory (folder) on your computer and then use `wp-env` to create a local WordPress development environment with your new block plugin installed and activated. -Therefore, create a new directory (folder) on your computer called "Block Tutorial". Open your terminal and `cd` to this directory. Then run the following command. +Therefore, choose a directory to place the block plugin or optionally create a new folder called "Block Tutorial". Open your terminal and `cd` to this directory. Then run the following command.
If you are not using wp-env, instead, navigate to the plugins/ folder in your local WordPress installation using the terminal and run the following command. @@ -351,9 +351,10 @@ To enable this starting year functionality, you will need one attribute to store ### Updating block.json -Block attributes are generally specified in the [`block.json`](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-json/#data-storage-in-the-block-with-attributes) file. So open up the file and add the following section after the `example` in line 9. +Block attributes are generally specified in the [`block.json`](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-json/#data-storage-in-the-block-with-attributes) file. So open up the file and add the following section after the `example` property. ```json +"example": {}, "attributes": { "showStartingYear": { "type": "boolean" From 83e1a2c064c2233e8cd82dcccf4cd555c3497be1 Mon Sep 17 00:00:00 2001 From: Nick Diego Date: Thu, 28 Dec 2023 16:52:27 -0600 Subject: [PATCH 61/63] Change the slug for the theme.json doc to avoid conflicts (#57410) * Change doc slug to avoid conflicts. * Revert change to curation doc and modify URL of themes doc * Update docs/how-to-guides/themes/theme-support.md Co-authored-by: Aki Hamano <54422211+t-hamano@users.noreply.github.com> * Update docs/reference-guides/block-api/block-supports.md Co-authored-by: Aki Hamano <54422211+t-hamano@users.noreply.github.com> * Update docs/reference-guides/block-api/block-supports.md Co-authored-by: Aki Hamano <54422211+t-hamano@users.noreply.github.com> * Update packages/block-editor/src/components/height-control/README.md Co-authored-by: Aki Hamano <54422211+t-hamano@users.noreply.github.com> * Update packages/style-engine/docs/using-the-style-engine-with-block-supports.md Co-authored-by: Aki Hamano <54422211+t-hamano@users.noreply.github.com> * Fix link. --------- Co-authored-by: Aki Hamano <54422211+t-hamano@users.noreply.github.com> --- .../explanations/architecture/key-concepts.md | 2 +- docs/explanations/architecture/styles.md | 4 +-- docs/getting-started/glossary.md | 2 +- .../theme-json.md | 2 +- docs/how-to-guides/themes/README.md | 4 +-- ...-json.md => global-settings-and-styles.md} | 26 ------------------- docs/how-to-guides/themes/theme-support.md | 4 +-- docs/manifest.json | 4 +-- .../block-api/block-supports.md | 6 ++--- .../theme-json-reference/README.md | 2 +- .../theme-json-reference/theme-json-living.md | 2 +- docs/toc.json | 4 ++- .../src/components/height-control/README.md | 2 +- ...ng-the-style-engine-with-block-supports.md | 2 +- 14 files changed, 21 insertions(+), 45 deletions(-) rename docs/how-to-guides/themes/{theme-json.md => global-settings-and-styles.md} (98%) diff --git a/docs/explanations/architecture/key-concepts.md b/docs/explanations/architecture/key-concepts.md index 1ba009f782314..a041b86effdc3 100644 --- a/docs/explanations/architecture/key-concepts.md +++ b/docs/explanations/architecture/key-concepts.md @@ -65,6 +65,6 @@ More on [Site editing templates](/docs/explanations/architecture/full-site-editi ## Styles -Styles, formerly known as Global Styles and as such referenced in the code, is both an interface that users access through the editor and a configuration system done through [a `theme.json` file](/docs/how-to-guides/themes/theme-json.md). This file absorbs most of the configuration aspects usually scattered through various `add_theme_support` calls to simplify communicating with the editor. It thus aims to improve declaring what settings should be enabled, what specific tools a theme offers (like a custom color palette), the available design tools present, and an infrastructure that allows to coordinate the styles coming from WordPress, the active theme, and the user. +Styles, formerly known as Global Styles and as such referenced in the code, is both an interface that users access through the editor and a configuration system done through [a `theme.json` file](/docs/how-to-guides/themes/global-settings-and-styles.md). This file absorbs most of the configuration aspects usually scattered through various `add_theme_support` calls to simplify communicating with the editor. It thus aims to improve declaring what settings should be enabled, what specific tools a theme offers (like a custom color palette), the available design tools present, and an infrastructure that allows to coordinate the styles coming from WordPress, the active theme, and the user. Learn more about [Global Styles](/docs/explanations/architecture/styles.md#global-styles). diff --git a/docs/explanations/architecture/styles.md b/docs/explanations/architecture/styles.md index d62171a062205..94a8e91f94edb 100644 --- a/docs/explanations/architecture/styles.md +++ b/docs/explanations/architecture/styles.md @@ -15,7 +15,7 @@ The final HTML document is the result of a few things: The stylesheets loaded in the front end include: - **Blocks**. The stylesheets that come with the block. In the front end, you can find a single stylesheet with all block styles defined by WordPress (`wp-block-library-*` ) or separate stylesheets per block in use (as in `wp-block-group-*`, `wp-block-columns-*`, etc). See [this note](https://make.wordpress.org/core/2021/07/01/block-styles-loading-enhancements-in-wordpress-5-8/) for the full details. -- **Global styles**. These styles are generated on the fly by using data coming from a theme.json file: see [note](https://make.wordpress.org/core/2021/06/25/introducing-theme-json-in-wordpress-5-8/), [reference](https://developer.wordpress.org/block-editor/reference-guides/theme-json-reference/), and [how to guide](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-json/). Specifically, it merges the contents of the theme.json from WordPress, the theme.json from the theme (if it has one), and the user data provided via the global styles sidebar in the site editor. The result of processing this data is an embedded stylesheet whose id is `global-styles-inline-css`. +- **Global styles**. These styles are generated on the fly by using data coming from a theme.json file: see [note](https://make.wordpress.org/core/2021/06/25/introducing-theme-json-in-wordpress-5-8/), [reference](https://developer.wordpress.org/block-editor/reference-guides/theme-json-reference/), and [how to guide](https://developer.wordpress.org/block-editor/how-to-guides/themes/global-settings-and-styles/). Specifically, it merges the contents of the theme.json from WordPress, the theme.json from the theme (if it has one), and the user data provided via the global styles sidebar in the site editor. The result of processing this data is an embedded stylesheet whose id is `global-styles-inline-css`. - **Theme**. Historically, themes have enqueued their own stylesheets, where the id is based on the theme name, as in `twentytwentytwo-style-css`. In addition to having their own stylesheets they can now declare a theme.json file containing styles that will be part of the stylesheet generated by global styles. - **User**. Some of the user actions in the editor will generate style content. This is the case for features such as duotone, layout, or link color. - **Other**. WordPress and plugins can also enqueue stylesheets. @@ -515,7 +515,7 @@ There are currently four layout types in use: - Flex: Items are displayed using a Flexbox layout. Defaults to a horizontal orientation. Spacing between children is handled via the `gap` CSS property. - Grid: Items are displayed using a Grid layout. Defaults to an `auto-fill` approach to column generation but can also be set to a fixed number of columns. Spacing between children is handled via the `gap` CSS property. -For controlling spacing between blocks, and enabling block spacing controls see: [What is blockGap and how can I use it?](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-json/#what-is-blockgap-and-how-can-i-use-it). +For controlling spacing between blocks, and enabling block spacing controls see: [What is blockGap and how can I use it?](https://developer.wordpress.org/block-editor/how-to-guides/themes/global-settings-and-styles/#what-is-blockgap-and-how-can-i-use-it). ### Targeting layout or container blocks from themes diff --git a/docs/getting-started/glossary.md b/docs/getting-started/glossary.md index bff8925e4619e..ca509f6a321db 100644 --- a/docs/getting-started/glossary.md +++ b/docs/getting-started/glossary.md @@ -70,7 +70,7 @@ This refers to a collection of features that ultimately allows users to edit the The CSS styles generated by WordPress and enqueued as an embedded stylesheet in the front end of the site. The stylesheet ID is `global-styles-inline-css`. The contents of this stylesheet come from the default `theme.json` of WordPress, the theme's `theme.json`, and the styles provided by the user via the global styles sidebar in the site editor. -See [theme.json reference docs](/docs/reference-guides/theme-json-reference.md), the [how to guide](/docs/how-to-guides/themes/theme-json.md), and an introduction to [styles in the block editor](/docs/explanations/architecture/styles.md). +See [theme.json reference docs](/docs/reference-guides/theme-json-reference.md), the [how to guide](/docs/how-to-guides/themes/global-settings-and-styles.md), and an introduction to [styles in the block editor](/docs/explanations/architecture/styles.md). Compare to block styles. diff --git a/docs/how-to-guides/curating-the-editor-experience/theme-json.md b/docs/how-to-guides/curating-the-editor-experience/theme-json.md index c8f51ea3e9110..d373e0e81e345 100644 --- a/docs/how-to-guides/curating-the-editor-experience/theme-json.md +++ b/docs/how-to-guides/curating-the-editor-experience/theme-json.md @@ -159,7 +159,7 @@ Continuing the examples with duotone, this means you could allow full access to } ``` -You can read more about how best to [turn on/off options with theme.json here](/docs/how-to-guides/themes/theme-json.md). +You can read more about how best to [turn on/off options with theme.json here](/docs/how-to-guides/themes/global-settings-and-styles.md). ### Disable inherit default layout diff --git a/docs/how-to-guides/themes/README.md b/docs/how-to-guides/themes/README.md index 708ecba03d264..1510b20c30047 100644 --- a/docs/how-to-guides/themes/README.md +++ b/docs/how-to-guides/themes/README.md @@ -6,7 +6,7 @@ The block editor provides a number of options for theme designers and developers ### Classic theme -In terms of block editor terminology this is any theme that defines its templates in the traditional `.php` file format, and that doesn't have an `index.html` format template in the `/block-templates` or `/templates` folders. A `Classic` theme has the ability to provide configuration and styling options to the block editor, and block content, via [Theme Supports](/docs/how-to-guides/themes/theme-support.md), or by including a [theme.json](/docs/how-to-guides/themes/theme-json.md) file. A theme does not have to be a `Block` theme in order to take advantage of some of the flexibility provided by the use of a `theme.json` file. +In terms of block editor terminology this is any theme that defines its templates in the traditional `.php` file format, and that doesn't have an `index.html` format template in the `/block-templates` or `/templates` folders. A `Classic` theme has the ability to provide configuration and styling options to the block editor, and block content, via [Theme Supports](/docs/how-to-guides/themes/theme-support.md), or by including a [theme.json](/docs/how-to-guides/themes/global-settings-and-styles.md) file. A theme does not have to be a `Block` theme in order to take advantage of some of the flexibility provided by the use of a `theme.json` file. ### Block theme @@ -18,5 +18,5 @@ There isn't an FSE specific theme type. In WordPress > 5.9 FSE is enabled for an **Contents** -- [Global Settings (theme.json)](/docs/how-to-guides/themes/theme-json.md) +- [Global Settings (theme.json)](/docs/how-to-guides/themes/global-settings-and-styles.md) - [Theme Support](/docs/how-to-guides/themes/theme-support.md) diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/global-settings-and-styles.md similarity index 98% rename from docs/how-to-guides/themes/theme-json.md rename to docs/how-to-guides/themes/global-settings-and-styles.md index 1f7480649f6ab..130b6271d13bd 100644 --- a/docs/how-to-guides/themes/theme-json.md +++ b/docs/how-to-guides/themes/global-settings-and-styles.md @@ -2,32 +2,6 @@ WordPress 5.8 comes with [a new mechanism](https://make.wordpress.org/core/2021/06/25/introducing-theme-json-in-wordpress-5-8/) to configure the editor that enables a finer-grained control and introduces the first step in managing styles for future WordPress releases: the `theme.json` file. Then `theme.json` [evolved to a v2](https://make.wordpress.org/core/2022/01/08/updates-for-settings-styles-and-theme-json/) with WordPress 5.9 release. This page documents its format. -- Rationale - - Settings for the block editor - - Settings can be controlled per block - - Styles are managed - - CSS Custom Properties: presets & custom -- Specification - - version - - settings - - Backward compatibility with add_theme_support - - Presets - - Custom - - Setting examples - - styles - - Top-level - - Block-level - - Elements - - Variations - - customTemplates - - templateParts - - patterns -- FAQ - - The naming schema of CSS Custom Properties - - Why using -- as a separator? - - How settings under "custom" create new CSS Custom Properties - - Why does it take so long to update the styles in the browser? - ## Rationale The Block Editor API has evolved at different velocities and there are some growing pains, specially in areas that affect themes. Examples of this are: the ability to [control the editor programmatically](https://make.wordpress.org/core/2020/01/23/controlling-the-block-editor/), or [a block style system](https://github.com/WordPress/gutenberg/issues/9534) that facilitates user, theme, and core style preferences. diff --git a/docs/how-to-guides/themes/theme-support.md b/docs/how-to-guides/themes/theme-support.md index b978ede928b83..88e69938737b7 100644 --- a/docs/how-to-guides/themes/theme-support.md +++ b/docs/how-to-guides/themes/theme-support.md @@ -315,7 +315,7 @@ Themes can opt out of generated block layout styles that provide default structu add_theme_support( 'disable-layout-styles' ); ``` -For themes looking to customize `blockGap` styles or block spacing, see [the developer docs on Global Settings & Styles](/docs/how-to-guides/themes/theme-json/#what-is-blockgap-and-how-can-i-use-it). +For themes looking to customize `blockGap` styles or block spacing, see [the developer docs on Global Settings & Styles](/docs/how-to-guides/themes/global-settings-and-styles.md#what-is-blockgap-and-how-can-i-use-it). ### Supporting custom line heights @@ -434,7 +434,7 @@ add_theme_support( 'custom-spacing' ); ## Link color control -Link support has been made stable as part of WordPress 5.8. It's `false` by default and themes can enable it via the [theme.json file](./theme-json.md): +Link support has been made stable as part of WordPress 5.8. It's `false` by default and themes can enable it via the [theme.json file](/docs/how-to-guides/curating-the-editor-experience/theme-json.md): ```json { diff --git a/docs/manifest.json b/docs/manifest.json index db3ac4ff5ca57..5629675c0b57e 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -301,8 +301,8 @@ }, { "title": "Global Settings & Styles (theme.json)", - "slug": "theme-json", - "markdown_source": "../docs/how-to-guides/themes/theme-json.md", + "slug": "global-settings-and-styles", + "markdown_source": "../docs/how-to-guides/themes/global-settings-and-styles.md", "parent": "themes" }, { diff --git a/docs/reference-guides/block-api/block-supports.md b/docs/reference-guides/block-api/block-supports.md index 7fd0e68c9bd8c..4a59c34813448 100644 --- a/docs/reference-guides/block-api/block-supports.md +++ b/docs/reference-guides/block-api/block-supports.md @@ -437,7 +437,7 @@ _**Note:** Since WordPress 6.2._ - Subproperties: - `minHeight`: type `boolean`, default value `false` -This value signals that a block supports some of the CSS style properties related to dimensions. When it does, the block editor will show UI controls for the user to set their values if [the theme declares support](/docs/how-to-guides/themes/theme-json/#opt-in-into-ui-controls). +This value signals that a block supports some of the CSS style properties related to dimensions. When it does, the block editor will show UI controls for the user to set their values if [the theme declares support](/docs/how-to-guides/themes/global-settings-and-styles.md#opt-in-into-ui-controls). ```js supports: { @@ -491,7 +491,7 @@ selectors: { The filter can be applied to an element inside the block by setting the `selectors.filter.duotone` selector. -Duotone presets are sourced from `color.duotone` in [theme.json](/docs/how-to-guides/themes/theme-json.md). +Duotone presets are sourced from `color.duotone` in [theme.json](/docs/how-to-guides/themes/global-settings-and-styles.md). When the block declares support for `filter.duotone`, the attributes definition is extended to include the attribute `style`: @@ -675,7 +675,7 @@ _**Note:** Since WordPress 6.2._ - Subproperties: - `sticky`: type `boolean`, default value `false` -This value signals that a block supports some of the CSS style properties related to position. When it does, the block editor will show UI controls for the user to set their values if [the theme declares support](/docs/how-to-guides/themes/theme-json/#opt-in-into-ui-controls). +This value signals that a block supports some of the CSS style properties related to position. When it does, the block editor will show UI controls for the user to set their values if [the theme declares support](/docs/how-to-guides/themes/global-settings-and-styles.md#opt-in-into-ui-controls). Note that sticky position controls are currently only available for blocks set at the root level of the document. Setting a block to the `sticky` position will stick the block to its most immediate parent when the user scrolls the page. diff --git a/docs/reference-guides/theme-json-reference/README.md b/docs/reference-guides/theme-json-reference/README.md index 92f6f77e298c0..11605b21625ad 100644 --- a/docs/reference-guides/theme-json-reference/README.md +++ b/docs/reference-guides/theme-json-reference/README.md @@ -1,6 +1,6 @@ # Theme.json Reference -This reference guide lists the settings and style properties defined in the theme.json schema. See the [theme.json how to guide](/docs/how-to-guides/themes/theme-json.md) for examples and guide on how to use the theme.json file in your theme. +This reference guide lists the settings and style properties defined in the theme.json schema. See the [theme.json how to guide](/docs/how-to-guides/themes/global-settings-and-styles.md) for examples and guide on how to use the theme.json file in your theme. - [Version 2 (living reference)](/docs/reference-guides/theme-json-reference/theme-json-living.md) diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index 627fee6071816..4baa5a6009ded 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -6,7 +6,7 @@ > - the [theme.json v1](/docs/reference-guides/theme-json-reference/theme-json-v1.md) specification, and > - the [reference to migrate from theme.json v1 to v2](/docs/reference-guides/theme-json-reference/theme-json-migrations.md). -This reference guide lists the settings and style properties defined in the `theme.json` schema. See the [theme.json how to guide](/docs/how-to-guides/themes/theme-json.md) for examples and guidance on how to use the `theme.json` file in your theme. +This reference guide lists the settings and style properties defined in the `theme.json` schema. See the [theme.json how to guide](/docs/how-to-guides/themes/global-settings-and-styles.md) for examples and guidance on how to use the `theme.json` file in your theme. ## Schema diff --git a/docs/toc.json b/docs/toc.json index 2a0eb6470bd08..49110f8bed957 100644 --- a/docs/toc.json +++ b/docs/toc.json @@ -130,7 +130,9 @@ { "docs/how-to-guides/propagating-updates.md": [] }, { "docs/how-to-guides/themes/README.md": [ - { "docs/how-to-guides/themes/theme-json.md": [] }, + { + "docs/how-to-guides/themes/global-settings-and-styles.md": [] + }, { "docs/how-to-guides/themes/theme-support.md": [] } ] }, diff --git a/packages/block-editor/src/components/height-control/README.md b/packages/block-editor/src/components/height-control/README.md index 8853f9ef89321..67b52f1d56f9b 100644 --- a/packages/block-editor/src/components/height-control/README.md +++ b/packages/block-editor/src/components/height-control/README.md @@ -2,7 +2,7 @@ The `HeightControl` component adds a linked unit control and slider component for controlling the height of a block within the block editor. It supports passing a label, and is used for controlling the minimum height dimensions of Group blocks. -_Note:_ It is worth noting that the minimum height option is an opt-in feature. Themes need to declare support for it before it'll be available, and a convenient way to do that is via opting in to the [appearanceTools](/docs/how-to-guides/themes/theme-json/#opt-in-into-ui-controls) UI controls. +_Note:_ It is worth noting that the minimum height option is an opt-in feature. Themes need to declare support for it before it'll be available, and a convenient way to do that is via opting in to the [appearanceTools](/docs/how-to-guides/themes/global-settings-and-styles.md#opt-in-into-ui-controls) UI controls. ## Development guidelines diff --git a/packages/style-engine/docs/using-the-style-engine-with-block-supports.md b/packages/style-engine/docs/using-the-style-engine-with-block-supports.md index 42350a17ffcd6..27d80df189cf3 100644 --- a/packages/style-engine/docs/using-the-style-engine-with-block-supports.md +++ b/packages/style-engine/docs/using-the-style-engine-with-block-supports.md @@ -231,4 +231,4 @@ array( */ ``` -Read more about [global styles](https://developer.wordpress.org/block-editor/explanations/architecture/styles/#global-styles) and [preset CSS custom properties](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-json/#css-custom-properties-presets-custom) and [theme supports](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-support/). +Read more about [global styles](https://developer.wordpress.org/block-editor/explanations/architecture/styles/#global-styles) and [preset CSS custom properties](https://developer.wordpress.org/block-editor/how-to-guides/themes/global-settings-and-styles.md#css-custom-properties-presets-custom) and [theme supports](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-support/). From 6f6b083b22a28265025580255e0057ea93e5d478 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Fri, 29 Dec 2023 10:25:01 +0400 Subject: [PATCH 62/63] Migrate 'allowed patterns' e2e tests to Playwright (#57399) * Migrate 'allowed patterns' e2e tests to Playwright * Remove old test file * Update test setup --- .../editor/various/allowed-patterns.test.js | 74 ----------------- .../editor/various/allowed-patterns.spec.js | 79 +++++++++++++++++++ 2 files changed, 79 insertions(+), 74 deletions(-) delete mode 100644 packages/e2e-tests/specs/editor/various/allowed-patterns.test.js create mode 100644 test/e2e/specs/editor/various/allowed-patterns.spec.js diff --git a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js deleted file mode 100644 index 449306f06d18d..0000000000000 --- a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * WordPress dependencies - */ -import { - activatePlugin, - createNewPost, - deactivatePlugin, - searchForPattern, - toggleGlobalBlockInserter, -} from '@wordpress/e2e-test-utils'; - -const checkPatternExistence = async ( name, available = true ) => { - await searchForPattern( name ); - const patternElement = await page.waitForXPath( - `//div[@role = 'option']//div[contains(text(), '${ name }')]`, - { timeout: 5000, visible: available, hidden: ! available } - ); - const patternExists = !! patternElement; - await toggleGlobalBlockInserter(); - return patternExists; -}; - -const TEST_PATTERNS = [ - [ 'Test: Single heading', true ], - [ 'Test: Single paragraph', false ], - [ 'Test: Paragraph inside group', false ], -]; - -describe( 'Allowed Patterns', () => { - beforeAll( async () => { - await activatePlugin( 'gutenberg-test-allowed-patterns' ); - await createNewPost(); - } ); - afterAll( async () => { - await deactivatePlugin( 'gutenberg-test-allowed-patterns' ); - } ); - - describe( 'Disable blocks plugin disabled', () => { - for ( const [ patternName ] of TEST_PATTERNS ) { - it( `should show test pattern "${ patternName }"`, async () => { - expect( await checkPatternExistence( patternName, true ) ).toBe( - true - ); - } ); - } - } ); - - describe( 'Disable blocks plugin enabled', () => { - beforeAll( async () => { - await activatePlugin( - 'gutenberg-test-allowed-patterns-disable-blocks' - ); - await createNewPost(); - } ); - afterAll( async () => { - await deactivatePlugin( - 'gutenberg-test-allowed-patterns-disable-blocks' - ); - } ); - - for ( const [ patternName, shouldBeAvailable ] of TEST_PATTERNS ) { - it( `should${ - shouldBeAvailable ? '' : ' not' - } show test "pattern ${ patternName }"`, async () => { - expect( - await checkPatternExistence( - patternName, - shouldBeAvailable - ) - ).toBe( shouldBeAvailable ); - } ); - } - } ); -} ); diff --git a/test/e2e/specs/editor/various/allowed-patterns.spec.js b/test/e2e/specs/editor/various/allowed-patterns.spec.js new file mode 100644 index 0000000000000..78407217442de --- /dev/null +++ b/test/e2e/specs/editor/various/allowed-patterns.spec.js @@ -0,0 +1,79 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Allowed Patterns', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( 'gutenberg-test-allowed-patterns' ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await Promise.all( [ + requestUtils.deactivatePlugin( 'gutenberg-test-allowed-patterns' ), + requestUtils.deactivatePlugin( + 'gutenberg-test-allowed-patterns-disable-blocks' + ), + ] ); + } ); + + test( 'should show all patterns when blocks are not disabled', async ( { + admin, + page, + } ) => { + await admin.createNewPost(); + await page + .getByRole( 'toolbar', { name: 'Document tools' } ) + .getByRole( 'button', { name: 'Toggle block inserter' } ) + .click(); + + await page + .getByRole( 'region', { + name: 'Block Library', + } ) + .getByRole( 'searchbox', { + name: 'Search for blocks and patterns', + } ) + .fill( 'Test:' ); + + await expect( + page + .getByRole( 'listbox', { name: 'Block patterns' } ) + .getByRole( 'option' ) + ).toHaveText( [ + 'Test: Single heading', + 'Test: Single paragraph', + 'Test: Paragraph inside group', + ] ); + } ); + + test( 'should show only allowed patterns when blocks are disabled', async ( { + admin, + page, + requestUtils, + } ) => { + await requestUtils.activatePlugin( + 'gutenberg-test-allowed-patterns-disable-blocks' + ); + await admin.createNewPost(); + await page + .getByRole( 'toolbar', { name: 'Document tools' } ) + .getByRole( 'button', { name: 'Toggle block inserter' } ) + .click(); + + await page + .getByRole( 'region', { + name: 'Block Library', + } ) + .getByRole( 'searchbox', { + name: 'Search for blocks and patterns', + } ) + .fill( 'Test:' ); + + await expect( + page + .getByRole( 'listbox', { name: 'Block patterns' } ) + .getByRole( 'option' ) + ).toHaveText( [ 'Test: Single heading' ] ); + } ); +} ); From 3591f4d9da4c941ce50851673018e5841074d997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Fri, 29 Dec 2023 09:44:53 +0100 Subject: [PATCH 63/63] DataViews: fallback to `(no title)` if there's no rendered title (#57434) --- packages/edit-site/src/components/page-pages/index.js | 9 ++++----- .../edit-site/src/components/page-templates/index.js | 7 +++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index 33884d1c5659d..0c35ecf38051c 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -216,7 +216,7 @@ export default function PagePages() { { header: __( 'Title' ), id: 'title', - getValue: ( { item } ) => item.title?.rendered || item.slug, + getValue: ( { item } ) => item.title?.rendered, render: ( { item } ) => { return ( @@ -235,13 +235,12 @@ export default function PagePages() { } } > { decodeEntities( - item.title?.rendered || item.slug + item.title?.rendered ) || __( '(no title)' ) } ) : ( - decodeEntities( - item.title?.rendered || item.slug - ) || __( '(no title)' ) + decodeEntities( item.title?.rendered ) || + __( '(no title)' ) ) } diff --git a/packages/edit-site/src/components/page-templates/index.js b/packages/edit-site/src/components/page-templates/index.js index 9c52aaa7f12f6..864c4c3fccd02 100644 --- a/packages/edit-site/src/components/page-templates/index.js +++ b/packages/edit-site/src/components/page-templates/index.js @@ -92,8 +92,7 @@ function TemplateTitle( { item, view } ) { if ( view.type === LAYOUT_LIST ) { return ( <> - { decodeEntities( item.title?.rendered || item.slug ) || - __( '(no title)' ) } + { decodeEntities( item.title?.rendered ) || __( '(no title)' ) } ); } @@ -108,7 +107,7 @@ function TemplateTitle( { item, view } ) { canvas: 'edit', } } > - { decodeEntities( item.title?.rendered || item.slug ) || + { decodeEntities( item.title?.rendered ) || __( '(no title)' ) } @@ -210,7 +209,7 @@ export default function DataviewsTemplates() { { header: __( 'Template' ), id: 'title', - getValue: ( { item } ) => item.title?.rendered || item.slug, + getValue: ( { item } ) => item.title?.rendered, render: ( { item } ) => ( ),