From c2a7ecaa48a720b184243310de46ef75216ea84f Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 2 Sep 2024 18:05:25 +0200 Subject: [PATCH 1/6] Dataviews Filter search widget: do not use Composite store --- .../dataviews-filters/search-widget.tsx | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx index 1b369222b8f28c..48812dcdf28104 100644 --- a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx +++ b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx @@ -8,6 +8,7 @@ import removeAccents from 'remove-accents'; /** * WordPress dependencies */ +import { useInstanceId } from '@wordpress/compose'; import { __, sprintf } from '@wordpress/i18n'; import { useState, useMemo, useDeferredValue } from '@wordpress/element'; import { @@ -27,7 +28,6 @@ import type { Filter, NormalizedFilter, View } from '../../types'; const { CompositeV2: Composite, CompositeItemV2: CompositeItem, - useCompositeStoreV2: useCompositeStore, } = unlock( componentsPrivateApis ); interface SearchWidgetProps { @@ -84,22 +84,35 @@ const getNewValue = ( return [ value ]; }; +function generateCompositeItemId( + prefix: string, + filterElement: NormalizedFilter[ 'elements' ][ number ] +) { + return `${ prefix }-${ filterElement.value }`; +} + function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) { - const compositeStore = useCompositeStore( { - virtualFocus: true, - focusLoop: true, + const baseId = useInstanceId( ListBox, 'dataviews-filter-list-box' ); + + const [ activeCompositeId, setActiveCompositeId ] = useState< + string | null | undefined + >( // When we have no or just one operator, we can set the first item as active. - // We do that by passing `undefined` to `defaultActiveId`. Otherwise, we set it to `null`, - // so the first item is not selected, since the focus is on the operators control. - defaultActiveId: filter.operators?.length === 1 ? undefined : null, - } ); + // We do that by setting the initial `activeId` to `undefined`.Otherwise, + // we set it to `null`, so the first item is not selected, + // since the focus is on the operators control. + filter.operators?.length === 1 ? undefined : null + ); const currentFilter = view.filters?.find( ( f ) => f.field === filter.field ); const currentValue = getCurrentValue( filter, currentFilter ); return ( { - if ( ! compositeStore.getState().activeId ) { - compositeStore.move( compositeStore.first() ); + // `onFocusVisible` needs the `Composite` component to be focusable, + // which is implicitly achieved via the `virtualFocus: true` option + // in the `useCompositeStore` hook. + if ( ! activeCompositeId && filter.elements.length ) { + setActiveCompositeId( + generateCompositeItemId( baseId, filter.elements[ 0 ] ) + ); } } } render={ } @@ -120,6 +138,7 @@ function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) { key={ element.value } render={ Date: Mon, 2 Sep 2024 18:05:58 +0200 Subject: [PATCH 2/6] Use internal CompositeHover and CompositeTypeahead version --- .../src/components/dataviews-filters/search-widget.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx index 48812dcdf28104..48b3105465a429 100644 --- a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx +++ b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx @@ -28,6 +28,8 @@ import type { Filter, NormalizedFilter, View } from '../../types'; const { CompositeV2: Composite, CompositeItemV2: CompositeItem, + CompositeHoverV2: CompositeHover, + CompositeTypeaheadV2: CompositeTypeahead, } = unlock( componentsPrivateApis ); interface SearchWidgetProps { @@ -130,11 +132,10 @@ function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) { ); } } } - render={ } + render={ } > { filter.elements.map( ( element ) => ( - { element.label } - + ) ) } ); From 7547cdb4faf7f1cb75ea6ed5313c87ee9eae9f71 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 2 Sep 2024 18:18:13 +0200 Subject: [PATCH 3/6] Better comment --- .../src/components/dataviews-filters/search-widget.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx index 48b3105465a429..545dc36748a689 100644 --- a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx +++ b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx @@ -99,10 +99,12 @@ function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) { const [ activeCompositeId, setActiveCompositeId ] = useState< string | null | undefined >( - // When we have no or just one operator, we can set the first item as active. - // We do that by setting the initial `activeId` to `undefined`.Otherwise, - // we set it to `null`, so the first item is not selected, - // since the focus is on the operators control. + // When there are one or less operators, the first item is set as active + // (by setting the initial `activeId` to `undefined`). + // With 2 or more operators, the focus is moved on the operators control + // (by setting the initial `activeId` to `null`), meaning that there won't + // be an active item initially. Focus is then managed via the + // `onFocusVisible` callback. filter.operators?.length === 1 ? undefined : null ); const currentFilter = view.filters?.find( From c15399a0d46f88c899c9b04020e391d851e31e71 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 3 Sep 2024 13:23:08 +0200 Subject: [PATCH 4/6] Refactor generateCompositeItemId arguments --- .../dataviews-filters/search-widget.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx index 545dc36748a689..24ef3b5594b413 100644 --- a/packages/dataviews/src/components/dataviews-filters/search-widget.tsx +++ b/packages/dataviews/src/components/dataviews-filters/search-widget.tsx @@ -86,11 +86,11 @@ const getNewValue = ( return [ value ]; }; -function generateCompositeItemId( +function generateFilterElementCompositeItemId( prefix: string, - filterElement: NormalizedFilter[ 'elements' ][ number ] + filterElementValue: string ) { - return `${ prefix }-${ filterElement.value }`; + return `${ prefix }-${ filterElementValue }`; } function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) { @@ -130,7 +130,10 @@ function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) { // in the `useCompositeStore` hook. if ( ! activeCompositeId && filter.elements.length ) { setActiveCompositeId( - generateCompositeItemId( baseId, filter.elements[ 0 ] ) + generateFilterElementCompositeItemId( + baseId, + filter.elements[ 0 ].value + ) ); } } } @@ -141,7 +144,10 @@ function ListBox( { view, filter, onChangeView }: SearchWidgetProps ) { key={ element.value } render={ Date: Tue, 3 Sep 2024 13:33:05 +0200 Subject: [PATCH 5/6] Export Composite.Typeahead and Composite.Hover as private APIs --- packages/components/src/private-apis.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts index fa9fece048617e..7bcab0e052e022 100644 --- a/packages/components/src/private-apis.ts +++ b/packages/components/src/private-apis.ts @@ -18,6 +18,8 @@ lock( privateApis, { CompositeGroupV2: Composite.Group, CompositeItemV2: Composite.Item, CompositeRowV2: Composite.Row, + CompositeTypeaheadV2: Composite.Typeahead, + CompositeHoverV2: Composite.Hover, useCompositeStoreV2: useCompositeStore, __experimentalPopoverLegacyPositionToPlacement, createPrivateSlotFill, From 4b9d7c81c2e9b19005d4fed35bffe314590c2e32 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 3 Sep 2024 15:19:09 +0200 Subject: [PATCH 6/6] CHANGELOG --- packages/components/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 3b8d45be5861bc..5a58ddfda38b99 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -66,6 +66,7 @@ ### Internal +- `DropdownMenu` v2: expose CompositeTypeaheadV2 and CompositeHoverV2 via private APIs ([#64985](https://github.com/WordPress/gutenberg/pull/64985)). - `DropdownMenu` v2: fix flashing menu item styles when using keyboard ([#64873](https://github.com/WordPress/gutenberg/pull/64873), [#64942](https://github.com/WordPress/gutenberg/pull/64942)). - `DropdownMenu` v2: refactor to overloaded naming convention ([#64654](https://github.com/WordPress/gutenberg/pull/64654)). - `DropdownMenu` v2: add `GroupLabel` subcomponent ([#64854](https://github.com/WordPress/gutenberg/pull/64854)).