From a20e5eac89515978fdc7cdeb55916c0240eb9adc Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 13:46:40 -0600 Subject: [PATCH 01/54] Begin conversion of QueryControls to TypeScript --- .../{author-select.js => author-select.tsx} | 3 +- ...category-select.js => category-select.tsx} | 4 +- .../src/query-controls/index.native.js | 1 - .../query-controls/{index.js => index.tsx} | 44 +++++++- .../src/query-controls/{terms.js => terms.ts} | 15 ++- .../test/{terms.js => terms.ts} | 58 ++++++---- .../components/src/query-controls/types.ts | 104 ++++++++++++++++++ packages/components/tsconfig.json | 1 - 8 files changed, 197 insertions(+), 33 deletions(-) rename packages/components/src/query-controls/{author-select.js => author-select.tsx} (85%) rename packages/components/src/query-controls/{category-select.js => category-select.tsx} (87%) rename packages/components/src/query-controls/{index.js => index.tsx} (71%) rename packages/components/src/query-controls/{terms.js => terms.ts} (68%) rename packages/components/src/query-controls/test/{terms.js => terms.ts} (51%) create mode 100644 packages/components/src/query-controls/types.ts diff --git a/packages/components/src/query-controls/author-select.js b/packages/components/src/query-controls/author-select.tsx similarity index 85% rename from packages/components/src/query-controls/author-select.js rename to packages/components/src/query-controls/author-select.tsx index 77901ce8703d19..8eca13d6327617 100644 --- a/packages/components/src/query-controls/author-select.js +++ b/packages/components/src/query-controls/author-select.tsx @@ -3,6 +3,7 @@ */ import { buildTermsTree } from './terms'; import TreeSelect from '../tree-select'; +import type { AuthorSelectProps } from './types'; export default function AuthorSelect( { label, @@ -10,7 +11,7 @@ export default function AuthorSelect( { authorList, selectedAuthorId, onChange, -} ) { +}: AuthorSelectProps ) { if ( ! authorList ) return null; const termsTree = buildTermsTree( authorList ); return ( diff --git a/packages/components/src/query-controls/category-select.js b/packages/components/src/query-controls/category-select.tsx similarity index 87% rename from packages/components/src/query-controls/category-select.js rename to packages/components/src/query-controls/category-select.tsx index 5a401014676d3d..7009c7f72ca3cd 100644 --- a/packages/components/src/query-controls/category-select.js +++ b/packages/components/src/query-controls/category-select.tsx @@ -3,10 +3,12 @@ */ import { buildTermsTree } from './terms'; import TreeSelect from '../tree-select'; + /** * WordPress dependencies */ import { useMemo } from '@wordpress/element'; +import type { CategorySelectProps } from './types'; export default function CategorySelect( { label, @@ -15,7 +17,7 @@ export default function CategorySelect( { selectedCategoryId, onChange, ...props -} ) { +}: CategorySelectProps ) { const termsTree = useMemo( () => { return buildTermsTree( categoriesList ); }, [ categoriesList ] ); diff --git a/packages/components/src/query-controls/index.native.js b/packages/components/src/query-controls/index.native.js index 6ba18f646146e6..e7cb486038081a 100644 --- a/packages/components/src/query-controls/index.native.js +++ b/packages/components/src/query-controls/index.native.js @@ -79,7 +79,6 @@ const QueryControls = memo( noOptionLabel={ __( 'All' ) } selectedCategoryId={ selectedCategoryId } onChange={ onCategoryChange } - hideCancelButton={ true } /> ) } { onNumberOfItemsChange && ( diff --git a/packages/components/src/query-controls/index.js b/packages/components/src/query-controls/index.tsx similarity index 71% rename from packages/components/src/query-controls/index.js rename to packages/components/src/query-controls/index.tsx index e62d71eb2d8eb1..7bce9848e844e1 100644 --- a/packages/components/src/query-controls/index.js +++ b/packages/components/src/query-controls/index.tsx @@ -6,15 +6,43 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import CategorySelect from './category-select'; -import { RangeControl, SelectControl, FormTokenField } from '../'; import AuthorSelect from './author-select'; +import CategorySelect from './category-select'; +import FormTokenField from '../form-token-field'; +import RangeControl from '../range-control'; +import SelectControl from '../select-control'; +import type { QueryControlsProps } from './types'; const DEFAULT_MIN_ITEMS = 1; const DEFAULT_MAX_ITEMS = 100; const MAX_CATEGORIES_SUGGESTIONS = 20; -export default function QueryControls( { +/** + * A select control that queries for entities. + * + * ```jsx + * const MyQueryControls = () => ( + * { + * updateQuery( { orderBy: newOrderBy } ) + * } + * onOrderChange={ ( newOrder ) => { + * updateQuery( { order: newOrder } ) + * } + * categoriesList={ categories } + * selectedCategoryId={ category } + * onCategoryChange={ ( newCategory ) => { + * updateQuery( { category: newCategory } ) + * } + * onNumberOfItemsChange={ ( newNumberOfItems ) => { + * updateQuery( { numberOfItems: newNumberOfItems } ) + * } } + * /> + * ); + * ``` + */ +export function QueryControls( { authorList, selectedAuthorId, categoriesList, @@ -31,7 +59,7 @@ export default function QueryControls( { onNumberOfItemsChange, onOrderChange, onOrderByChange, -} ) { +}: QueryControlsProps ) { return [ onOrderChange && onOrderByChange && ( { + if ( Array.isArray( value ) ) { + return; + } + const [ newOrderBy, newOrder ] = value.split( '/' ); if ( newOrder !== order ) { onOrderChange( newOrder ); @@ -87,7 +119,7 @@ export default function QueryControls( { selectedCategories && selectedCategories.map( ( item ) => ( { id: item.id, - value: item.name || item.value, + value: item.name || item.value || '', } ) ) } suggestions={ Object.keys( categorySuggestions ) } @@ -119,3 +151,5 @@ export default function QueryControls( { ), ]; } + +export default QueryControls; diff --git a/packages/components/src/query-controls/terms.js b/packages/components/src/query-controls/terms.ts similarity index 68% rename from packages/components/src/query-controls/terms.js rename to packages/components/src/query-controls/terms.ts index 6405cf5d1ba5b0..bf96be6c079347 100644 --- a/packages/components/src/query-controls/terms.js +++ b/packages/components/src/query-controls/terms.ts @@ -3,14 +3,19 @@ */ import { groupBy } from 'lodash'; +/** + * Internal dependencies + */ +import type { Term } from './types'; + /** * Returns terms in a tree form. * - * @param {Array} flatTerms Array of terms in flat format. + * @param flatTerms Array of terms in flat format. * - * @return {Array} Array of terms in tree format. + * @return Array of terms in tree format. */ -export function buildTermsTree( flatTerms ) { +export function buildTermsTree( flatTerms: readonly Term[] ): Term[] { const flatTermsWithParentAndChildren = flatTerms.map( ( term ) => { return { children: [], @@ -23,7 +28,9 @@ export function buildTermsTree( flatTerms ) { if ( termsByParent.null && termsByParent.null.length ) { return flatTermsWithParentAndChildren; } - const fillWithChildren = ( terms ) => { + const fillWithChildren = ( + terms: Term[] + ): Array< Term & { children: Term[] } > => { return terms.map( ( term ) => { const children = termsByParent[ term.id ]; return { diff --git a/packages/components/src/query-controls/test/terms.js b/packages/components/src/query-controls/test/terms.ts similarity index 51% rename from packages/components/src/query-controls/test/terms.js rename to packages/components/src/query-controls/test/terms.ts index 68a1a7f10fb190..4e9120850c6441 100644 --- a/packages/components/src/query-controls/test/terms.js +++ b/packages/components/src/query-controls/test/terms.ts @@ -6,38 +6,54 @@ import { buildTermsTree } from '../terms'; describe( 'buildTermsTree()', () => { it( 'Should return same array as input with null parent and empty children added if parent is never specified.', () => { const input = Object.freeze( [ - { id: 2232, dummy: true }, - { id: 2245, dummy: true }, + { id: '2232', name: 'foo', dummy: true }, + { id: '2245', name: 'baz', dummy: true }, ] ); const output = Object.freeze( [ - { id: 2232, parent: null, children: [], dummy: true }, - { id: 2245, parent: null, children: [], dummy: true }, + { + id: '2232', + name: 'foo', + parent: null, + children: [], + dummy: true, + }, + { + id: '2245', + name: 'baz', + parent: null, + children: [], + dummy: true, + }, ] ); const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); } ); it( 'Should return same array as input with empty children added if all the elements are top level', () => { const input = Object.freeze( [ - { id: 2232, parent: 0, dummy: true }, - { id: 2245, parent: 0, dummy: false }, + { id: '2232', name: 'foo', parent: 0, dummy: true }, + { id: '2245', name: 'baz', parent: 0, dummy: false }, ] ); const output = [ - { id: 2232, parent: 0, children: [], dummy: true }, - { id: 2245, parent: 0, children: [], dummy: false }, + { id: '2232', name: 'foo', parent: 0, children: [], dummy: true }, + { id: '2245', name: 'baz', parent: 0, children: [], dummy: false }, ]; const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); } ); it( 'Should return element with its child if a child exists', () => { const input = Object.freeze( [ - { id: 2232, parent: 0 }, - { id: 2245, parent: 2232 }, + { id: '2232', name: 'foo', parent: 0 }, + { id: '2245', name: 'baz', parent: 2232 }, ] ); const output = [ { - id: 2232, + id: '2232', + name: 'foo', + value: '', parent: 0, - children: [ { id: 2245, parent: 2232, children: [] } ], + children: [ + { id: '2245', name: 'foo', parent: 2232, children: [] }, + ], }, ]; const termsTreem = buildTermsTree( input ); @@ -45,21 +61,23 @@ describe( 'buildTermsTree()', () => { } ); it( 'Should return elements with multiple children and elements with no children', () => { const input = Object.freeze( [ - { id: 2232, parent: 0 }, - { id: 2245, parent: 2232 }, - { id: 2249, parent: 0 }, - { id: 2246, parent: 2232 }, + { id: '2232', name: 'a', parent: 0 }, + { id: '2245', name: 'b', parent: 2232 }, + { id: '2249', name: 'c', parent: 0 }, + { id: '2246', name: 'd', parent: 2232 }, ] ); const output = [ { - id: 2232, + id: '2232', + name: 'a', + value: '', parent: 0, children: [ - { id: 2245, parent: 2232, children: [] }, - { id: 2246, parent: 2232, children: [] }, + { id: '2245', name: 'b', parent: 2232, children: [] }, + { id: '2246', name: 'd', parent: 2232, children: [] }, ], }, - { id: 2249, parent: 0, children: [] }, + { id: 2249, name: 'c', parent: 0, children: [] }, ]; const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts new file mode 100644 index 00000000000000..16b05eb3b5de10 --- /dev/null +++ b/packages/components/src/query-controls/types.ts @@ -0,0 +1,104 @@ +/** + * External dependencies + */ +import type { Key } from 'React'; + +/** + * Internal dependencies + */ +import type { Tree, TreeSelectProps } from '../tree-select/types'; + +export type Term = { + id: Tree[ 'id' ]; + name: Tree[ 'name' ]; + parent?: number | null; + value?: string; +}; + +export type CategorySelectProps = { + label: TreeSelectProps[ 'label' ]; + noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; + onChange: TreeSelectProps[ 'onChange' ]; + categoriesList: Term[]; + selectedCategoryId: TreeSelectProps[ 'selectedId' ]; + key: Key; +}; + +export type AuthorSelectProps = { + label: TreeSelectProps[ 'label' ]; + noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; + onChange: TreeSelectProps[ 'onChange' ]; + authorList?: Term[]; + selectedAuthorId?: string; +}; + +export type QueryControlsProps = { + /** + * An array of terms with author IDs that is passed into an `AuthorSelect` sub-component. + */ + authorList?: AuthorSelectProps[ 'authorList' ]; + /** + * The selected author ID. + */ + selectedAuthorId?: AuthorSelectProps[ 'selectedAuthorId' ]; + /** + * An array of categoris with their IDs; renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. + */ + categoriesList?: CategorySelectProps[ 'categoriesList' ]; + /** + * An array of category names; renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. + */ + categorySuggestions?: Term[ 'name' ][]; + /** + * The maximum items. + * + * @default 100 + */ + maxItems?: number; + /** + * The minimum of items. + * + * @default 1 + */ + minItems?: number; + /** + * The selected number of items to retrieve via the query. + */ + numberOfItems?: number; + /** + * A function that receives the new author value. If this is not specified, the author controls are not included. + */ + onAuthorChange?: () => void; + /** + * A function that receives the new category value. If this is not specified, the category controls are not included. + */ + onCategoryChange?: () => void; // TODO + /** + * A function that receives the new number of items value. If this is not specified, then the number of items range control is not included. + */ + onNumberOfItemsChange?: () => void; // TODO + /** + * A function that receives the new order value. If this or onOrderByChange are not specified, then the order controls are not included. + */ + onOrderChange?: ( newOrder: string ) => void; + /** + * A function that receives the new orderby value. If this or onOrderChange are not specified, then the order controls are not included. + */ + onOrderByChange?: ( newOrderBy: string ) => void; + /** + * The order in which to retrieve posts. Can be 'asc' or 'desc'. + */ + order?: 'asc' | 'desc'; + /** + * The meta key by which to order posts. Can be 'date' or 'title'. + */ + orderBy?: 'data' | 'title'; + /** + * The selected categories for the `categorySuggestions`. + */ + selectedCategories?: Term[]; + /** + * The selected category for the `categoriesList`. + */ + selectedCategoryId?: CategorySelectProps[ 'selectedCategoryId' ]; +}; diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index 6d7cfacb6a6abb..a56bfa512a402c 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -61,7 +61,6 @@ "src/notice", "src/palette-edit", "src/panel", - "src/query-controls", "src/responsive-wrapper", "src/sandbox", "src/toolbar", From 26991a72c22a078f2b3b0c82bd9ab3f9f577bf29 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 14:20:54 -0600 Subject: [PATCH 02/54] Remove the extra value properties --- packages/components/src/query-controls/test/terms.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/components/src/query-controls/test/terms.ts b/packages/components/src/query-controls/test/terms.ts index 4e9120850c6441..e57c13c72080bd 100644 --- a/packages/components/src/query-controls/test/terms.ts +++ b/packages/components/src/query-controls/test/terms.ts @@ -49,7 +49,6 @@ describe( 'buildTermsTree()', () => { { id: '2232', name: 'foo', - value: '', parent: 0, children: [ { id: '2245', name: 'foo', parent: 2232, children: [] }, @@ -70,14 +69,13 @@ describe( 'buildTermsTree()', () => { { id: '2232', name: 'a', - value: '', parent: 0, children: [ { id: '2245', name: 'b', parent: 2232, children: [] }, { id: '2246', name: 'd', parent: 2232, children: [] }, ], }, - { id: 2249, name: 'c', parent: 0, children: [] }, + { id: '2249', name: 'c', parent: 0, children: [] }, ]; const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); From df0b00670ada2d4a0f8265535f5fd978288704dc Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 14:26:37 -0600 Subject: [PATCH 03/54] Fix failing unit test --- packages/components/src/query-controls/test/terms.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/query-controls/test/terms.ts b/packages/components/src/query-controls/test/terms.ts index e57c13c72080bd..b604c74caed413 100644 --- a/packages/components/src/query-controls/test/terms.ts +++ b/packages/components/src/query-controls/test/terms.ts @@ -51,7 +51,7 @@ describe( 'buildTermsTree()', () => { name: 'foo', parent: 0, children: [ - { id: '2245', name: 'foo', parent: 2232, children: [] }, + { id: '2245', name: 'baz', parent: 2232, children: [] }, ], }, ]; From 3e88bbb9875758ea7a0b598d0139e0d9e02c5e5c Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 14:32:38 -0600 Subject: [PATCH 04/54] Create a TermsWithChildren type --- packages/components/src/query-controls/terms.ts | 10 +++++----- packages/components/src/query-controls/types.ts | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index bf96be6c079347..13ad63c736e16b 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -6,7 +6,7 @@ import { groupBy } from 'lodash'; /** * Internal dependencies */ -import type { Term } from './types'; +import type { Term, TermsWithChildren } from './types'; /** * Returns terms in a tree form. @@ -15,7 +15,9 @@ import type { Term } from './types'; * * @return Array of terms in tree format. */ -export function buildTermsTree( flatTerms: readonly Term[] ): Term[] { +export function buildTermsTree( + flatTerms: readonly Term[] +): TermsWithChildren { const flatTermsWithParentAndChildren = flatTerms.map( ( term ) => { return { children: [], @@ -28,9 +30,7 @@ export function buildTermsTree( flatTerms: readonly Term[] ): Term[] { if ( termsByParent.null && termsByParent.null.length ) { return flatTermsWithParentAndChildren; } - const fillWithChildren = ( - terms: Term[] - ): Array< Term & { children: Term[] } > => { + const fillWithChildren = ( terms: Term[] ): TermsWithChildren => { return terms.map( ( term ) => { const children = termsByParent[ term.id ]; return { diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 16b05eb3b5de10..b97315f538be04 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -15,6 +15,8 @@ export type Term = { value?: string; }; +export type TermsWithChildren = Array< Term & { children: Term[] } >; + export type CategorySelectProps = { label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; From d3ceb5e6b879cc7103529e1ed72cc57b4a1a7e74 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 14:41:45 -0600 Subject: [PATCH 05/54] Change the id to a string --- .../components/src/query-controls/README.md | 18 +++++++++--------- .../components/src/query-controls/types.ts | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 9a0f5b08e0cef5..377f75c918077e 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -9,29 +9,29 @@ import { QueryControls } from '@wordpress/components'; import { useState } from '@wordpress/element'; const QUERY_DEFAULTS = { - category: 1, + category: '1', categories: [ { - id: 1, + id: '1', name: 'Category 1', parent: 0, }, { - id: 2, + id: '2', name: 'Category 1b', parent: 1, }, { - id: 3, + id: '3', name: 'Category 2', parent: 0, }, ], maxItems: 20, - minItems: 1, + minItems: 1, numberOfItems: 10, order: 'asc', - orderBy: 'title', + orderBy: 'title', }; const MyQueryControls = () => { @@ -69,17 +69,17 @@ const QUERY_DEFAULTS = { selectedCategories: [ 1 ], categories: [ { - id: 1, + id: '1', name: 'Category 1', parent: 0, }, { - id: 2, + id: '2', name: 'Category 1b', parent: 1, }, { - id: 3, + id: '3', name: 'Category 2', parent: 0, }, diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index b97315f538be04..f5f47d8e070dc6 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -18,12 +18,12 @@ export type Term = { export type TermsWithChildren = Array< Term & { children: Term[] } >; export type CategorySelectProps = { + categoriesList: Term[]; + key: Key; label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; onChange: TreeSelectProps[ 'onChange' ]; - categoriesList: Term[]; - selectedCategoryId: TreeSelectProps[ 'selectedId' ]; - key: Key; + selectedCategoryId?: Term[ 'id' ]; }; export type AuthorSelectProps = { From 50179695bd52c3a3e906f499f13b73c6cc6e5aab Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 15:28:05 -0600 Subject: [PATCH 06/54] Make onCategoryChange an intersection type --- .../components/src/query-controls/types.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index f5f47d8e070dc6..70821299825e61 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -6,6 +6,7 @@ import type { Key } from 'React'; /** * Internal dependencies */ +import type { FormTokenFieldProps } from '../form-token-field/types'; import type { Tree, TreeSelectProps } from '../tree-select/types'; export type Term = { @@ -39,18 +40,18 @@ export type QueryControlsProps = { * An array of terms with author IDs that is passed into an `AuthorSelect` sub-component. */ authorList?: AuthorSelectProps[ 'authorList' ]; - /** - * The selected author ID. - */ - selectedAuthorId?: AuthorSelectProps[ 'selectedAuthorId' ]; /** * An array of categoris with their IDs; renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. */ - categoriesList?: CategorySelectProps[ 'categoriesList' ]; + categoriesList: CategorySelectProps[ 'categoriesList' ]; /** * An array of category names; renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. */ - categorySuggestions?: Term[ 'name' ][]; + categorySuggestions: Term[ 'name' ][]; + /** + * The selected author ID. + */ + selectedAuthorId?: AuthorSelectProps[ 'selectedAuthorId' ]; /** * The maximum items. * @@ -74,11 +75,12 @@ export type QueryControlsProps = { /** * A function that receives the new category value. If this is not specified, the category controls are not included. */ - onCategoryChange?: () => void; // TODO + onCategoryChange: CategorySelectProps[ 'onChange' ] & + FormTokenFieldProps[ 'onChange' ]; /** * A function that receives the new number of items value. If this is not specified, then the number of items range control is not included. */ - onNumberOfItemsChange?: () => void; // TODO + onNumberOfItemsChange?: ( newNumber?: number ) => void; /** * A function that receives the new order value. If this or onOrderByChange are not specified, then the order controls are not included. */ From 17382e9a167e3ba4a320426a06e1efd165c4eceb Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 15:33:34 -0600 Subject: [PATCH 07/54] Shorten the JSDoc lines --- .../components/src/query-controls/types.ts | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 70821299825e61..87f3d98c2517a9 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -28,24 +28,27 @@ export type CategorySelectProps = { }; export type AuthorSelectProps = { + authorList?: Term[]; label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; onChange: TreeSelectProps[ 'onChange' ]; - authorList?: Term[]; selectedAuthorId?: string; }; export type QueryControlsProps = { /** - * An array of terms with author IDs that is passed into an `AuthorSelect` sub-component. + * An array of terms with author IDs that is passed into + * an `AuthorSelect` sub-component. */ authorList?: AuthorSelectProps[ 'authorList' ]; /** - * An array of categoris with their IDs; renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. + * An array of categoris with their IDs; renders a `CategorySelect` + * sub-component when passed in conjunction with `onCategoryChange`. */ categoriesList: CategorySelectProps[ 'categoriesList' ]; /** - * An array of category names; renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. + * An array of category names; renders a `FormTokenField` component + * when passed in conjunction with `onCategoryChange`. */ categorySuggestions: Term[ 'name' ][]; /** @@ -69,24 +72,32 @@ export type QueryControlsProps = { */ numberOfItems?: number; /** - * A function that receives the new author value. If this is not specified, the author controls are not included. + * A function that receives the new author value. + * If this is not specified, the author controls are not included. */ onAuthorChange?: () => void; /** - * A function that receives the new category value. If this is not specified, the category controls are not included. + * A function that receives the new category value. + * If this is not specified, the category controls are not included. */ onCategoryChange: CategorySelectProps[ 'onChange' ] & FormTokenFieldProps[ 'onChange' ]; /** - * A function that receives the new number of items value. If this is not specified, then the number of items range control is not included. + * A function that receives the new number of items value. + * If this is not specified, then the number of items + * range control is not included. */ onNumberOfItemsChange?: ( newNumber?: number ) => void; /** - * A function that receives the new order value. If this or onOrderByChange are not specified, then the order controls are not included. + * A function that receives the new order value. + * If this or onOrderByChange are not specified, + * then the order controls are not included. */ onOrderChange?: ( newOrder: string ) => void; /** - * A function that receives the new orderby value. If this or onOrderChange are not specified, then the order controls are not included. + * A function that receives the new orderby value. + * If this or onOrderChange are not specified, + * then the order controls are not included. */ onOrderByChange?: ( newOrderBy: string ) => void; /** From 56e177e8dc879459d560ff8c5dff649707dee8c7 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 15:36:04 -0600 Subject: [PATCH 08/54] Correct the React import --- packages/components/src/query-controls/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 87f3d98c2517a9..70da48e20744d8 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -1,7 +1,7 @@ /** * External dependencies */ -import type { Key } from 'React'; +import type { Key } from 'react'; /** * Internal dependencies From 8c6dab7aa4ae6c82cd6e31ce5c5d6975b25adb92 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 15:38:30 -0600 Subject: [PATCH 09/54] Make the properties of QueryControlsProps optional --- packages/components/src/query-controls/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 70da48e20744d8..a15dc5f6e907fa 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -45,12 +45,12 @@ export type QueryControlsProps = { * An array of categoris with their IDs; renders a `CategorySelect` * sub-component when passed in conjunction with `onCategoryChange`. */ - categoriesList: CategorySelectProps[ 'categoriesList' ]; + categoriesList?: CategorySelectProps[ 'categoriesList' ]; /** * An array of category names; renders a `FormTokenField` component * when passed in conjunction with `onCategoryChange`. */ - categorySuggestions: Term[ 'name' ][]; + categorySuggestions?: Term[ 'name' ][]; /** * The selected author ID. */ @@ -80,7 +80,7 @@ export type QueryControlsProps = { * A function that receives the new category value. * If this is not specified, the category controls are not included. */ - onCategoryChange: CategorySelectProps[ 'onChange' ] & + onCategoryChange?: CategorySelectProps[ 'onChange' ] & FormTokenFieldProps[ 'onChange' ]; /** * A function that receives the new number of items value. From b6854a1194576a5b01aa7caf2b0f2402e7ad863e Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 15:47:21 -0600 Subject: [PATCH 10/54] Alphabetize types.ts --- packages/components/src/query-controls/README.md | 3 +-- packages/components/src/query-controls/types.ts | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 377f75c918077e..7c5a9e9d8766d1 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -123,11 +123,10 @@ An array of author IDs that is passed into an `AuthorSelect` sub-component. - Required: No - Platform: Web -#### selectedAuthorId +#### `selectedAuthorId`: `string` The selected author ID. -- Type: `Number` - Required: No - Platform: Web diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index a15dc5f6e907fa..72917ebecedc0e 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -51,10 +51,6 @@ export type QueryControlsProps = { * when passed in conjunction with `onCategoryChange`. */ categorySuggestions?: Term[ 'name' ][]; - /** - * The selected author ID. - */ - selectedAuthorId?: AuthorSelectProps[ 'selectedAuthorId' ]; /** * The maximum items. * @@ -75,7 +71,7 @@ export type QueryControlsProps = { * A function that receives the new author value. * If this is not specified, the author controls are not included. */ - onAuthorChange?: () => void; + onAuthorChange?: AuthorSelectProps[ 'onChange' ]; /** * A function that receives the new category value. * If this is not specified, the category controls are not included. @@ -108,6 +104,10 @@ export type QueryControlsProps = { * The meta key by which to order posts. Can be 'date' or 'title'. */ orderBy?: 'data' | 'title'; + /** + * The selected author ID. + */ + selectedAuthorId?: AuthorSelectProps[ 'selectedAuthorId' ]; /** * The selected categories for the `categorySuggestions`. */ From 992ce116e6242c8d97bf30609c93b550288a2531 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 15:54:05 -0600 Subject: [PATCH 11/54] Fix the suggestions --- packages/components/src/query-controls/README.md | 2 +- packages/components/src/query-controls/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 7c5a9e9d8766d1..03fb108a3d2cac 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -132,7 +132,7 @@ The selected author ID. #### categoriesList -An array of category IDs; renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. +An array of categories; renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. - Type: `Array` - Required: No diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index 7bce9848e844e1..21ec71f0dc705e 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -122,7 +122,7 @@ export function QueryControls( { value: item.name || item.value || '', } ) ) } - suggestions={ Object.keys( categorySuggestions ) } + suggestions={ categorySuggestions } onChange={ onCategoryChange } maxSuggestions={ MAX_CATEGORIES_SUGGESTIONS } /> From f68e197e0968e09ddfca2de92e7d014d321bce7a Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 15:59:57 -0600 Subject: [PATCH 12/54] Rename Term to Entity It can also be an author, so Term isn't right. --- .../components/src/query-controls/terms.ts | 6 ++--- .../components/src/query-controls/types.ts | 22 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index 13ad63c736e16b..93e4cd436663bc 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -6,7 +6,7 @@ import { groupBy } from 'lodash'; /** * Internal dependencies */ -import type { Term, TermsWithChildren } from './types'; +import type { Entity, TermsWithChildren } from './types'; /** * Returns terms in a tree form. @@ -16,7 +16,7 @@ import type { Term, TermsWithChildren } from './types'; * @return Array of terms in tree format. */ export function buildTermsTree( - flatTerms: readonly Term[] + flatTerms: readonly Entity[] ): TermsWithChildren { const flatTermsWithParentAndChildren = flatTerms.map( ( term ) => { return { @@ -30,7 +30,7 @@ export function buildTermsTree( if ( termsByParent.null && termsByParent.null.length ) { return flatTermsWithParentAndChildren; } - const fillWithChildren = ( terms: Term[] ): TermsWithChildren => { + const fillWithChildren = ( terms: Entity[] ): TermsWithChildren => { return terms.map( ( term ) => { const children = termsByParent[ term.id ]; return { diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 72917ebecedc0e..574adf4022e1e6 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -9,26 +9,26 @@ import type { Key } from 'react'; import type { FormTokenFieldProps } from '../form-token-field/types'; import type { Tree, TreeSelectProps } from '../tree-select/types'; -export type Term = { +export type Entity = { id: Tree[ 'id' ]; name: Tree[ 'name' ]; parent?: number | null; value?: string; }; -export type TermsWithChildren = Array< Term & { children: Term[] } >; +export type TermsWithChildren = Array< Entity & { children: Entity[] } >; export type CategorySelectProps = { - categoriesList: Term[]; + categoriesList: Entity[]; key: Key; label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; onChange: TreeSelectProps[ 'onChange' ]; - selectedCategoryId?: Term[ 'id' ]; + selectedCategoryId?: Entity[ 'id' ]; }; export type AuthorSelectProps = { - authorList?: Term[]; + authorList?: Entity[]; label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; onChange: TreeSelectProps[ 'onChange' ]; @@ -42,15 +42,15 @@ export type QueryControlsProps = { */ authorList?: AuthorSelectProps[ 'authorList' ]; /** - * An array of categoris with their IDs; renders a `CategorySelect` + * An array of categories, renders a `CategorySelect` * sub-component when passed in conjunction with `onCategoryChange`. */ categoriesList?: CategorySelectProps[ 'categoriesList' ]; /** - * An array of category names; renders a `FormTokenField` component + * An array of category names, renders a `FormTokenField` component * when passed in conjunction with `onCategoryChange`. */ - categorySuggestions?: Term[ 'name' ][]; + categorySuggestions?: Entity[ 'name' ][]; /** * The maximum items. * @@ -97,11 +97,11 @@ export type QueryControlsProps = { */ onOrderByChange?: ( newOrderBy: string ) => void; /** - * The order in which to retrieve posts. Can be 'asc' or 'desc'. + * The order in which to retrieve posts. */ order?: 'asc' | 'desc'; /** - * The meta key by which to order posts. Can be 'date' or 'title'. + * The meta key by which to order posts. */ orderBy?: 'data' | 'title'; /** @@ -111,7 +111,7 @@ export type QueryControlsProps = { /** * The selected categories for the `categorySuggestions`. */ - selectedCategories?: Term[]; + selectedCategories?: Entity[]; /** * The selected category for the `categoriesList`. */ From f4ab051fcad622d16b2c1697a56fd0a6c39acb81 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 16:10:18 -0600 Subject: [PATCH 13/54] Fix the type of categorySuggestions --- packages/components/src/query-controls/index.tsx | 2 +- packages/components/src/query-controls/types.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index 21ec71f0dc705e..7bce9848e844e1 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -122,7 +122,7 @@ export function QueryControls( { value: item.name || item.value || '', } ) ) } - suggestions={ categorySuggestions } + suggestions={ Object.keys( categorySuggestions ) } onChange={ onCategoryChange } maxSuggestions={ MAX_CATEGORIES_SUGGESTIONS } /> diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 574adf4022e1e6..8e925d22247a44 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -37,7 +37,7 @@ export type AuthorSelectProps = { export type QueryControlsProps = { /** - * An array of terms with author IDs that is passed into + * An array of authors that is passed into * an `AuthorSelect` sub-component. */ authorList?: AuthorSelectProps[ 'authorList' ]; @@ -50,7 +50,7 @@ export type QueryControlsProps = { * An array of category names, renders a `FormTokenField` component * when passed in conjunction with `onCategoryChange`. */ - categorySuggestions?: Entity[ 'name' ][]; + categorySuggestions?: { [ categoryName: Entity[ 'name' ] ]: Entity }; /** * The maximum items. * From 51e43fae88747f87dc92de623efbbe435ea7c25c Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 16:12:09 -0600 Subject: [PATCH 14/54] Correct the JSDoc summary for QueryControls --- packages/components/src/query-controls/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index 7bce9848e844e1..f378178004a204 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -18,7 +18,7 @@ const DEFAULT_MAX_ITEMS = 100; const MAX_CATEGORIES_SUGGESTIONS = 20; /** - * A select control that queries for entities. + * Select controls that query for entities. * * ```jsx * const MyQueryControls = () => ( From b87ddbc08099e6a01e974aaf41e163eb560a8f73 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 17:01:03 -0600 Subject: [PATCH 15/54] Make SelectControl accept a number --- .../components/src/query-controls/index.tsx | 2 +- .../src/query-controls/test/terms.ts | 40 +++++++++---------- .../components/src/query-controls/types.ts | 8 ++-- .../components/src/select-control/types.ts | 4 +- packages/components/src/tree-select/types.ts | 2 +- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index f378178004a204..e8ce58a75504f4 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -87,7 +87,7 @@ export function QueryControls( { }, ] } onChange={ ( value ) => { - if ( Array.isArray( value ) ) { + if ( typeof value !== 'string' ) { return; } diff --git a/packages/components/src/query-controls/test/terms.ts b/packages/components/src/query-controls/test/terms.ts index b604c74caed413..2b87ce98f13ae6 100644 --- a/packages/components/src/query-controls/test/terms.ts +++ b/packages/components/src/query-controls/test/terms.ts @@ -6,19 +6,19 @@ import { buildTermsTree } from '../terms'; describe( 'buildTermsTree()', () => { it( 'Should return same array as input with null parent and empty children added if parent is never specified.', () => { const input = Object.freeze( [ - { id: '2232', name: 'foo', dummy: true }, - { id: '2245', name: 'baz', dummy: true }, + { id: 2232, name: 'foo', dummy: true }, + { id: 2245, name: 'baz', dummy: true }, ] ); const output = Object.freeze( [ { - id: '2232', + id: 2232, name: 'foo', parent: null, children: [], dummy: true, }, { - id: '2245', + id: 2245, name: 'baz', parent: null, children: [], @@ -30,28 +30,28 @@ describe( 'buildTermsTree()', () => { } ); it( 'Should return same array as input with empty children added if all the elements are top level', () => { const input = Object.freeze( [ - { id: '2232', name: 'foo', parent: 0, dummy: true }, - { id: '2245', name: 'baz', parent: 0, dummy: false }, + { id: 2232, name: 'foo', parent: 0, dummy: true }, + { id: 2245, name: 'baz', parent: 0, dummy: false }, ] ); const output = [ - { id: '2232', name: 'foo', parent: 0, children: [], dummy: true }, - { id: '2245', name: 'baz', parent: 0, children: [], dummy: false }, + { id: 2232, name: 'foo', parent: 0, children: [], dummy: true }, + { id: 2245, name: 'baz', parent: 0, children: [], dummy: false }, ]; const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); } ); it( 'Should return element with its child if a child exists', () => { const input = Object.freeze( [ - { id: '2232', name: 'foo', parent: 0 }, - { id: '2245', name: 'baz', parent: 2232 }, + { id: 2232, name: 'foo', parent: 0 }, + { id: 2245, name: 'baz', parent: 2232 }, ] ); const output = [ { - id: '2232', + id: 2232, name: 'foo', parent: 0, children: [ - { id: '2245', name: 'baz', parent: 2232, children: [] }, + { id: 2245, name: 'baz', parent: 2232, children: [] }, ], }, ]; @@ -60,22 +60,22 @@ describe( 'buildTermsTree()', () => { } ); it( 'Should return elements with multiple children and elements with no children', () => { const input = Object.freeze( [ - { id: '2232', name: 'a', parent: 0 }, - { id: '2245', name: 'b', parent: 2232 }, - { id: '2249', name: 'c', parent: 0 }, - { id: '2246', name: 'd', parent: 2232 }, + { id: 2232, name: 'a', parent: 0 }, + { id: 2245, name: 'b', parent: 2232 }, + { id: 2249, name: 'c', parent: 0 }, + { id: 2246, name: 'd', parent: 2232 }, ] ); const output = [ { - id: '2232', + id: 2232, name: 'a', parent: 0, children: [ - { id: '2245', name: 'b', parent: 2232, children: [] }, - { id: '2246', name: 'd', parent: 2232, children: [] }, + { id: 2245, name: 'b', parent: 2232, children: [] }, + { id: 2246, name: 'd', parent: 2232, children: [] }, ], }, - { id: '2249', name: 'c', parent: 0, children: [] }, + { id: 2249, name: 'c', parent: 0, children: [] }, ]; const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 8e925d22247a44..a810a2b74742f9 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -7,11 +7,11 @@ import type { Key } from 'react'; * Internal dependencies */ import type { FormTokenFieldProps } from '../form-token-field/types'; -import type { Tree, TreeSelectProps } from '../tree-select/types'; +import type { TreeSelectProps } from '../tree-select/types'; export type Entity = { - id: Tree[ 'id' ]; - name: Tree[ 'name' ]; + id: number; + name: string; parent?: number | null; value?: string; }; @@ -32,7 +32,7 @@ export type AuthorSelectProps = { label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; onChange: TreeSelectProps[ 'onChange' ]; - selectedAuthorId?: string; + selectedAuthorId?: number; }; export type QueryControlsProps = { diff --git a/packages/components/src/select-control/types.ts b/packages/components/src/select-control/types.ts index 6126ba5043d6c4..d8831996a29c97 100644 --- a/packages/components/src/select-control/types.ts +++ b/packages/components/src/select-control/types.ts @@ -49,7 +49,7 @@ export interface SelectControlProps * The internal value used to choose the selected value. * This is also the value passed to `onChange` when the option is selected. */ - value: string; + value: string | number; id?: string; /** * Whether or not the option should have the disabled attribute. @@ -58,7 +58,7 @@ export interface SelectControlProps */ disabled?: boolean; }[]; - value?: string | string[]; + value?: number | string | string[]; /** * As an alternative to the `options` prop, `optgroup`s and `options` can be * passed in as `children` for more customizability. diff --git a/packages/components/src/tree-select/types.ts b/packages/components/src/tree-select/types.ts index ba58244e13bd27..a1c24db6d8f577 100644 --- a/packages/components/src/tree-select/types.ts +++ b/packages/components/src/tree-select/types.ts @@ -18,7 +18,7 @@ export type SelectOptions = Required< >[ 'options' ]; export interface Tree { - id: string; + id: number | string; name: string; children?: Tree[]; } From c8874282ce60e9f32353d5e6533c6457c98d9fd9 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 17:13:09 -0600 Subject: [PATCH 16/54] Change the order of number and string --- packages/components/src/select-control/README.md | 2 +- packages/components/src/select-control/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/select-control/README.md b/packages/components/src/select-control/README.md index 1d96f2ba92b76e..7f4f4e09864c3f 100644 --- a/packages/components/src/select-control/README.md +++ b/packages/components/src/select-control/README.md @@ -197,7 +197,7 @@ If this property is added, multiple values can be selected. The value passed sho An array of objects containing the following properties: - `label`: (string) The label to be shown to the user. -- `value`: (string) The internal value used to choose the selected value. This is also the value passed to onChange when the option is selected. +- `value`: (string | number) The internal value used to choose the selected value. This is also the value passed to onChange when the option is selected. - `disabled`: (boolean) Whether or not the option should have the disabled attribute. - Type: `Array` - Required: No diff --git a/packages/components/src/select-control/types.ts b/packages/components/src/select-control/types.ts index d8831996a29c97..09669716c52992 100644 --- a/packages/components/src/select-control/types.ts +++ b/packages/components/src/select-control/types.ts @@ -58,7 +58,7 @@ export interface SelectControlProps */ disabled?: boolean; }[]; - value?: number | string | string[]; + value?: string | string[] | number; /** * As an alternative to the `options` prop, `optgroup`s and `options` can be * passed in as `children` for more customizability. From 7957dd8efbc89e19101a1ae76fd2f32773687cfe Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 17:33:49 -0600 Subject: [PATCH 17/54] Empty commit to trigger CI/CD From cda599c2740f662a8af643706226f4b8660c0cfd Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 17:36:30 -0600 Subject: [PATCH 18/54] Remove needless change of id type --- packages/components/src/query-controls/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 03fb108a3d2cac..548e4c6f9322dc 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -9,20 +9,20 @@ import { QueryControls } from '@wordpress/components'; import { useState } from '@wordpress/element'; const QUERY_DEFAULTS = { - category: '1', + category: 1, categories: [ { - id: '1', + id: 1, name: 'Category 1', parent: 0, }, { - id: '2', + id: 2, name: 'Category 1b', parent: 1, }, { - id: '3', + id: 3, name: 'Category 2', parent: 0, }, @@ -69,17 +69,17 @@ const QUERY_DEFAULTS = { selectedCategories: [ 1 ], categories: [ { - id: '1', + id: 1, name: 'Category 1', parent: 0, }, { - id: '2', + id: 2, name: 'Category 1b', parent: 1, }, { - id: '3', + id: 3, name: 'Category 2', parent: 0, }, From 6b34f8421bc56ea7f3359e31644b8c5492a5319c Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 18:52:39 -0600 Subject: [PATCH 19/54] Update README.md based on types.ts --- .../components/src/query-controls/README.md | 71 ++++++++----------- .../components/src/query-controls/index.tsx | 2 +- .../components/src/query-controls/terms.ts | 6 +- .../components/src/query-controls/types.ts | 22 +++--- 4 files changed, 45 insertions(+), 56 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 548e4c6f9322dc..875b8faffbf354 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -115,127 +115,116 @@ The format of the categories list also needs to be updated to match what `FormTo ### Props -#### authorList +#### `authorList`: `Entity[]` -An array of author IDs that is passed into an `AuthorSelect` sub-component. +An array of authors that is passed into an `AuthorSelect` sub-component. -- Type: `Array` - Required: No - Platform: Web -#### `selectedAuthorId`: `string` +#### `categoriesList`: `Entity[]` -The selected author ID. +An array of categories, renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. - Required: No - Platform: Web -#### categoriesList +#### `categorySuggestions`: `{ [ categoryName: Entity[ 'name' ] ]: Entity }` -An array of categories; renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. +An array of categories, renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. -- Type: `Array` - Required: No - Platform: Web -#### categorySuggestions - -An array of category names; renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. - -- Type: `Array` -- Required: No -- Platform: Web +#### `maxItems`: `number` -#### maxItems +The maximum of items. -- Type: `Number` - Required: No - Default: 100 - Platform: Web -#### minItems +#### `minItems`: `number` + +The minimum of items. -- Type: `Number` - Required: No - Default: 1 - Platform: Web -#### numberOfItems +#### `numberOfItems`: `number` The selected number of items to retrieve via the query. -- Type: `Number` - Required: No - Platform: Web -#### onAuthorChange +#### `onAuthorChange`: `TreeSelectProps[ 'onChange' ]` A function that receives the new author value. If this is not specified, the author controls are not included. -- Type: `Function` - Required: No - Platform: Web -#### onCategoryChange +#### `onCategoryChange`: `TreeSelectProps[ 'onChange' ] & FormTokenFieldProps[ 'onChange' ]` A function that receives the new category value. If this is not specified, the category controls are not included. -- Type: `Function` - Required: No - Platform: Web -#### onNumberOfItemsChange +#### `onNumberOfItemsChange`: `( newNumber?: number ) => void` A function that receives the new number of items value. If this is not specified, then the number of items range control is not included. -- Type: `Function` - Required: No - Platform: Web -#### onOrderChange +#### `onOrderChange`: `( newOrder: string ) => void` A function that receives the new order value. If this or onOrderByChange are not specified, then the order controls are not included. -- Type: `Function` - Required: No - Platform: Web -#### onOrderByChange +#### `onOrderByChange`: `( newOrderBy: string ) => void` A function that receives the new orderby value. If this or onOrderChange are not specified, then the order controls are not included. -- Type: `Function` - Required: No - Platform: Web -#### order +#### `order`: `'asc' | 'desc'` -The order in which to retrieve posts. Can be 'asc' or 'desc'. +The order in which to retrieve posts. -- Type: `String` - Required: No - Platform: Web -#### orderBy +#### `orderBy`: `'data' | 'title'` -The meta key by which to order posts. Can be 'date' or 'title'. +The meta key by which to order posts. + +- Required: No +- Platform: Web + +#### `selectedAuthorId`: `Entity[ 'id' ]` + +The selected author ID. -- Type: `String` - Required: No - Platform: Web -#### selectedCategories +#### `selectedCategories`: `Entity[]` The selected categories for the `categorySuggestions`. -- Type: `Array` - Required: No - Platform: Web -#### selectedCategoryId +#### `selectedCategoryId`: `Entity[ 'id' ]` The selected category for the `categoriesList`. -- Type: `Number` - Required: No - Platform: Web diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index e8ce58a75504f4..4b4157805022a3 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -18,7 +18,7 @@ const DEFAULT_MAX_ITEMS = 100; const MAX_CATEGORIES_SUGGESTIONS = 20; /** - * Select controls that query for entities. + * Controls to query for posts. * * ```jsx * const MyQueryControls = () => ( diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index 93e4cd436663bc..bc955cad80a13b 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -16,9 +16,9 @@ import type { Entity, TermsWithChildren } from './types'; * @return Array of terms in tree format. */ export function buildTermsTree( - flatTerms: readonly Entity[] -): TermsWithChildren { - const flatTermsWithParentAndChildren = flatTerms.map( ( term ) => { + flatTerms?: readonly Entity[] +): TermsWithChildren | undefined { + const flatTermsWithParentAndChildren = flatTerms?.map( ( term ) => { return { children: [], parent: null, diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index a810a2b74742f9..43d7c08dba1538 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -19,20 +19,20 @@ export type Entity = { export type TermsWithChildren = Array< Entity & { children: Entity[] } >; export type CategorySelectProps = { - categoriesList: Entity[]; + categoriesList: QueryControlsProps[ 'categoriesList' ]; key: Key; label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; onChange: TreeSelectProps[ 'onChange' ]; - selectedCategoryId?: Entity[ 'id' ]; + selectedCategoryId: QueryControlsProps[ 'selectedCategoryId' ]; }; export type AuthorSelectProps = { - authorList?: Entity[]; + authorList: QueryControlsProps[ 'authorList' ]; label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; - onChange: TreeSelectProps[ 'onChange' ]; - selectedAuthorId?: number; + onChange: QueryControlsProps[ 'onAuthorChange' ]; + selectedAuthorId: QueryControlsProps[ 'selectedAuthorId' ]; }; export type QueryControlsProps = { @@ -40,12 +40,12 @@ export type QueryControlsProps = { * An array of authors that is passed into * an `AuthorSelect` sub-component. */ - authorList?: AuthorSelectProps[ 'authorList' ]; + authorList?: Entity[]; /** * An array of categories, renders a `CategorySelect` * sub-component when passed in conjunction with `onCategoryChange`. */ - categoriesList?: CategorySelectProps[ 'categoriesList' ]; + categoriesList?: Entity[]; /** * An array of category names, renders a `FormTokenField` component * when passed in conjunction with `onCategoryChange`. @@ -71,12 +71,12 @@ export type QueryControlsProps = { * A function that receives the new author value. * If this is not specified, the author controls are not included. */ - onAuthorChange?: AuthorSelectProps[ 'onChange' ]; + onAuthorChange?: TreeSelectProps[ 'onChange' ]; /** * A function that receives the new category value. * If this is not specified, the category controls are not included. */ - onCategoryChange?: CategorySelectProps[ 'onChange' ] & + onCategoryChange?: TreeSelectProps[ 'onChange' ] & FormTokenFieldProps[ 'onChange' ]; /** * A function that receives the new number of items value. @@ -107,7 +107,7 @@ export type QueryControlsProps = { /** * The selected author ID. */ - selectedAuthorId?: AuthorSelectProps[ 'selectedAuthorId' ]; + selectedAuthorId?: Entity[ 'id' ]; /** * The selected categories for the `categorySuggestions`. */ @@ -115,5 +115,5 @@ export type QueryControlsProps = { /** * The selected category for the `categoriesList`. */ - selectedCategoryId?: CategorySelectProps[ 'selectedCategoryId' ]; + selectedCategoryId?: Entity[ 'id' ]; }; From c52a456f49050b43485a93ad141764e35c61569a Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 18:55:17 -0600 Subject: [PATCH 20/54] Let TS infer the return type of buildTermsTree() --- packages/components/src/query-controls/terms.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index bc955cad80a13b..2c4ce7b46159d2 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -13,11 +13,9 @@ import type { Entity, TermsWithChildren } from './types'; * * @param flatTerms Array of terms in flat format. * - * @return Array of terms in tree format. + * @return Terms in tree format. */ -export function buildTermsTree( - flatTerms?: readonly Entity[] -): TermsWithChildren | undefined { +export function buildTermsTree( flatTerms?: readonly Entity[] ) { const flatTermsWithParentAndChildren = flatTerms?.map( ( term ) => { return { children: [], From be24601688c0541dcb1a787d356da063b12ee538 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 21 Dec 2022 19:04:28 -0600 Subject: [PATCH 21/54] Change array to object, as that's what categorySuggestions is --- packages/components/src/query-controls/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 875b8faffbf354..fe2fc238da1a76 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -131,7 +131,7 @@ An array of categories, renders a `CategorySelect` sub-component when passed in #### `categorySuggestions`: `{ [ categoryName: Entity[ 'name' ] ]: Entity }` -An array of categories, renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. +An object of categories, renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. - Required: No - Platform: Web From a27485e1379b97d7b3a1ad4770bf951161b9cb85 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 22 Dec 2022 02:03:22 -0600 Subject: [PATCH 22/54] Add a CHANGELOG entry --- packages/components/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 2c17553b489bcb..854393c24567eb 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -51,6 +51,7 @@ - `BottomSheetPickerCell`: Refactor away from `_.find()` for mobile ([#46537](https://github.com/WordPress/gutenberg/pull/46537)). - Refactor global styles context away from `_.find()` for mobile ([#46537](https://github.com/WordPress/gutenberg/pull/46537)). - `Dropdown`: Convert to TypeScript ([#45787](https://github.com/WordPress/gutenberg/pull/45787)). +- `QueryControls`: Convert to TypeScript ([#46721](https://github.com/WordPress/gutenberg/pull/46721)). ### Documentation From b9994d40ec96c6c8d853cd390178602f881809ab Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Fri, 23 Dec 2022 17:47:45 -0600 Subject: [PATCH 23/54] Add basic stories for QueryControls --- .../components/src/query-controls/README.md | 11 +- .../components/src/query-controls/index.tsx | 182 +++++++++--------- .../src/query-controls/stories/index.tsx | 87 +++++++++ .../components/src/query-controls/types.ts | 2 +- 4 files changed, 191 insertions(+), 91 deletions(-) create mode 100644 packages/components/src/query-controls/stories/index.tsx diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index fe2fc238da1a76..1a056fa6d9beb2 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -66,7 +66,16 @@ The `QueryControls` component now supports multiple category selection, to repla const QUERY_DEFAULTS = { orderBy: 'title', order: 'asc', - selectedCategories: [ 1 ], + selectedCategories: [ + { + id: 1, + name: 'Category 1', + }, + { + id: 2, + name: 'Category 1b', + }, + ], categories: [ { id: 1, diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index 4b4157805022a3..fdced8bd64970a 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -60,96 +60,100 @@ export function QueryControls( { onOrderChange, onOrderByChange, }: QueryControlsProps ) { - return [ - onOrderChange && onOrderByChange && ( - { - if ( typeof value !== 'string' ) { - return; - } + return ( + <> + { [ + onOrderChange && onOrderByChange && ( + { + if ( typeof value !== 'string' ) { + return; + } - const [ newOrderBy, newOrder ] = value.split( '/' ); - if ( newOrder !== order ) { - onOrderChange( newOrder ); - } - if ( newOrderBy !== orderBy ) { - onOrderByChange( newOrderBy ); - } - } } - /> - ), - categoriesList && onCategoryChange && ( - - ), - categorySuggestions && onCategoryChange && ( - ( { - id: item.id, - value: item.name || item.value || '', - } ) ) - } - suggestions={ Object.keys( categorySuggestions ) } - onChange={ onCategoryChange } - maxSuggestions={ MAX_CATEGORIES_SUGGESTIONS } - /> - ), - onAuthorChange && ( - - ), - onNumberOfItemsChange && ( - - ), - ]; + const [ newOrderBy, newOrder ] = value.split( '/' ); + if ( newOrder !== order ) { + onOrderChange( newOrder ); + } + if ( newOrderBy !== orderBy ) { + onOrderByChange( newOrderBy ); + } + } } + /> + ), + categoriesList && onCategoryChange && ( + + ), + categorySuggestions && onCategoryChange && ( + ( { + id: item.id, + value: item.name || item.value || '', + } ) ) + } + suggestions={ Object.keys( categorySuggestions ) } + onChange={ onCategoryChange } + maxSuggestions={ MAX_CATEGORIES_SUGGESTIONS } + /> + ), + onAuthorChange && ( + + ), + onNumberOfItemsChange && ( + + ), + ] } + + ); } export default QueryControls; diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx new file mode 100644 index 00000000000000..2c081656f66b14 --- /dev/null +++ b/packages/components/src/query-controls/stories/index.tsx @@ -0,0 +1,87 @@ +/** + * External dependencies + */ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; + +/** + * Internal dependencies + */ +import QueryControls from '..'; + +const meta: ComponentMeta< typeof QueryControls > = { + title: 'Components/QueryControls', + component: QueryControls, + parameters: { + controls: { + expanded: true, + }, + }, +}; +export default meta; + +const Template: ComponentStory< typeof QueryControls > = ( props ) => { + return ; +}; + +const noop = () => {}; +export const Default: ComponentStory< typeof QueryControls > = Template.bind( + {} +); +Default.args = { + authorList: [ + { + id: 1, + name: 'admin', + }, + { + id: 2, + name: 'editor', + }, + ], + categorySuggestions: { + TypeScript: { + id: 11, + name: 'TypeScript', + }, + JavaScript: { + id: 12, + name: 'JavaScript', + }, + }, + selectedCategories: [ + { + id: 11, + name: 'TypeScript', + }, + { + id: 12, + name: 'JavaScript', + }, + ], + numberOfItems: 5, + onAuthorChange: noop, + onCategoryChange: noop, + onNumberOfItemsChange: noop, + onOrderByChange: noop, + onOrderChange: noop, + order: 'desc', + orderBy: 'date', + selectedAuthorId: 1, +}; + +export const SelectSingleCategory: ComponentStory< typeof QueryControls > = + Template.bind( {} ); +SelectSingleCategory.args = { + categoriesList: [ + { + id: 11, + name: 'TypeScript', + }, + { + id: 12, + name: 'JavaScript', + }, + ], + onCategoryChange: noop, + selectedCategoryId: 11, +}; diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 43d7c08dba1538..f9647fb9cf23ae 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -103,7 +103,7 @@ export type QueryControlsProps = { /** * The meta key by which to order posts. */ - orderBy?: 'data' | 'title'; + orderBy?: 'date' | 'title'; /** * The selected author ID. */ From fe344b3402055cfc0e7049be41445fd1ed74f1b4 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Sat, 24 Dec 2022 00:17:33 -0600 Subject: [PATCH 24/54] Correct the type for selectedCategories --- .../components/src/query-controls/stories/index.tsx | 4 ++-- packages/components/src/query-controls/types.ts | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 2c081656f66b14..ad7adb917e0e9d 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -51,11 +51,11 @@ Default.args = { selectedCategories: [ { id: 11, - name: 'TypeScript', + value: 'TypeScript', }, { id: 12, - name: 'JavaScript', + value: 'JavaScript', }, ], numberOfItems: 5, diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index f9647fb9cf23ae..3136c759bddad9 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -16,6 +16,12 @@ export type Entity = { value?: string; }; +export type Category = { + id: number; + value: string; + name?: string; +}; + export type TermsWithChildren = Array< Entity & { children: Entity[] } >; export type CategorySelectProps = { @@ -111,9 +117,9 @@ export type QueryControlsProps = { /** * The selected categories for the `categorySuggestions`. */ - selectedCategories?: Entity[]; + selectedCategories?: Category[]; /** * The selected category for the `categoriesList`. */ - selectedCategoryId?: Entity[ 'id' ]; + selectedCategoryId?: Category[ 'id' ]; }; From c2574a7bedc2c02e657ea76e78d121b38caebaa7 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Sat, 24 Dec 2022 00:28:27 -0600 Subject: [PATCH 25/54] Correct the type of categorySuggestions --- packages/components/src/query-controls/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 1a056fa6d9beb2..22e4a36e1468c1 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -69,30 +69,30 @@ const QUERY_DEFAULTS = { selectedCategories: [ { id: 1, - name: 'Category 1', + value: 'Category 1', }, { id: 2, - name: 'Category 1b', + value: 'Category 1b', }, ], - categories: [ - { + categories: { + 'Category 1': { id: 1, name: 'Category 1', parent: 0, }, - { + 'Category 1b': { id: 2, name: 'Category 1b', parent: 1, }, - { + 'Category 2': { id: 3, name: 'Category 2', parent: 0, }, - ], + }, numberOfItems: 10, }; From d7ed654115e7df6ab579dd66918739c1bd7c11e4 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Sat, 24 Dec 2022 00:51:18 -0600 Subject: [PATCH 26/54] Update the types in README.md --- packages/components/src/query-controls/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 22e4a36e1468c1..a0e3e10b6bafdb 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -224,14 +224,14 @@ The selected author ID. - Required: No - Platform: Web -#### `selectedCategories`: `Entity[]` +#### `selectedCategories`: `Category[]` The selected categories for the `categorySuggestions`. - Required: No - Platform: Web -#### `selectedCategoryId`: `Entity[ 'id' ]` +#### `selectedCategoryId`: `Category[ 'id' ]` The selected category for the `categoriesList`. From 87c3588a616c5b94530a628fde5a7dae51c4bd43 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 11:23:04 -0600 Subject: [PATCH 27/54] Commit Marco's suggestion: Update packages/components/src/query-controls/terms.ts Co-authored-by: Marco Ciampini --- packages/components/src/query-controls/terms.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index 2c4ce7b46159d2..24f68d2652bb9c 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -11,7 +11,7 @@ import type { Entity, TermsWithChildren } from './types'; /** * Returns terms in a tree form. * - * @param flatTerms Array of terms in flat format. + * @param flatTerms Array of terms in flat format. * * @return Terms in tree format. */ From 7290d54471aed80abe3ba9c66ae7c00593c9396d Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 15:39:27 -0600 Subject: [PATCH 28/54] Add hideCancelButton back in --- packages/components/src/query-controls/index.native.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/query-controls/index.native.js b/packages/components/src/query-controls/index.native.js index e7cb486038081a..6ba18f646146e6 100644 --- a/packages/components/src/query-controls/index.native.js +++ b/packages/components/src/query-controls/index.native.js @@ -79,6 +79,7 @@ const QueryControls = memo( noOptionLabel={ __( 'All' ) } selectedCategoryId={ selectedCategoryId } onChange={ onCategoryChange } + hideCancelButton={ true } /> ) } { onNumberOfItemsChange && ( From 2442dd543021b311896131673b755043ff9c0456 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 16:02:17 -0600 Subject: [PATCH 29/54] Revert change to SelectControl and TreeSelect, instead change what's passed to TreeSelect --- .../components/src/query-controls/author-select.tsx | 2 +- .../components/src/query-controls/category-select.tsx | 2 +- packages/components/src/query-controls/terms.ts | 5 +++-- packages/components/src/query-controls/types.ts | 10 ++++++++-- packages/components/src/select-control/types.ts | 4 ++-- packages/components/src/tree-select/types.ts | 2 +- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/components/src/query-controls/author-select.tsx b/packages/components/src/query-controls/author-select.tsx index 8eca13d6327617..03e6b07d29cc0a 100644 --- a/packages/components/src/query-controls/author-select.tsx +++ b/packages/components/src/query-controls/author-select.tsx @@ -18,7 +18,7 @@ export default function AuthorSelect( { ); } diff --git a/packages/components/src/query-controls/category-select.tsx b/packages/components/src/query-controls/category-select.tsx index 7009c7f72ca3cd..d96763b7b4434c 100644 --- a/packages/components/src/query-controls/category-select.tsx +++ b/packages/components/src/query-controls/category-select.tsx @@ -26,7 +26,7 @@ export default function CategorySelect( { ); diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index 24f68d2652bb9c..e78aca3008ffe7 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -6,7 +6,7 @@ import { groupBy } from 'lodash'; /** * Internal dependencies */ -import type { Entity, TermsWithChildren } from './types'; +import type { Entity, EntityForTree, TermsWithChildren } from './types'; /** * Returns terms in a tree form. @@ -21,6 +21,7 @@ export function buildTermsTree( flatTerms?: readonly Entity[] ) { children: [], parent: null, ...term, + id: String( term.id ), }; } ); @@ -28,7 +29,7 @@ export function buildTermsTree( flatTerms?: readonly Entity[] ) { if ( termsByParent.null && termsByParent.null.length ) { return flatTermsWithParentAndChildren; } - const fillWithChildren = ( terms: Entity[] ): TermsWithChildren => { + const fillWithChildren = ( terms: EntityForTree[] ): TermsWithChildren => { return terms.map( ( term ) => { const children = termsByParent[ term.id ]; return { diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 3136c759bddad9..b0d676e8297b1b 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -16,14 +16,20 @@ export type Entity = { value?: string; }; +export type EntityForTree = Omit< Entity, 'id' > & { + id: string; +}; + +export type TermsWithChildren = Array< + EntityForTree & { children: EntityForTree[] } +>; + export type Category = { id: number; value: string; name?: string; }; -export type TermsWithChildren = Array< Entity & { children: Entity[] } >; - export type CategorySelectProps = { categoriesList: QueryControlsProps[ 'categoriesList' ]; key: Key; diff --git a/packages/components/src/select-control/types.ts b/packages/components/src/select-control/types.ts index 09669716c52992..6126ba5043d6c4 100644 --- a/packages/components/src/select-control/types.ts +++ b/packages/components/src/select-control/types.ts @@ -49,7 +49,7 @@ export interface SelectControlProps * The internal value used to choose the selected value. * This is also the value passed to `onChange` when the option is selected. */ - value: string | number; + value: string; id?: string; /** * Whether or not the option should have the disabled attribute. @@ -58,7 +58,7 @@ export interface SelectControlProps */ disabled?: boolean; }[]; - value?: string | string[] | number; + value?: string | string[]; /** * As an alternative to the `options` prop, `optgroup`s and `options` can be * passed in as `children` for more customizability. diff --git a/packages/components/src/tree-select/types.ts b/packages/components/src/tree-select/types.ts index a1c24db6d8f577..ba58244e13bd27 100644 --- a/packages/components/src/tree-select/types.ts +++ b/packages/components/src/tree-select/types.ts @@ -18,7 +18,7 @@ export type SelectOptions = Required< >[ 'options' ]; export interface Tree { - id: number | string; + id: string; name: string; children?: Tree[]; } From ecec1df19ee3eb32ff7d975bc53676d4a55e1c18 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 16:08:59 -0600 Subject: [PATCH 30/54] Revert README.md change to select-control --- packages/components/src/select-control/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/select-control/README.md b/packages/components/src/select-control/README.md index 7f4f4e09864c3f..1d96f2ba92b76e 100644 --- a/packages/components/src/select-control/README.md +++ b/packages/components/src/select-control/README.md @@ -197,7 +197,7 @@ If this property is added, multiple values can be selected. The value passed sho An array of objects containing the following properties: - `label`: (string) The label to be shown to the user. -- `value`: (string | number) The internal value used to choose the selected value. This is also the value passed to onChange when the option is selected. +- `value`: (string) The internal value used to choose the selected value. This is also the value passed to onChange when the option is selected. - `disabled`: (boolean) Whether or not the option should have the disabled attribute. - Type: `Array` - Required: No From 25aaac378fc6a38694893f960db793b0ba2cfee7 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 16:18:50 -0600 Subject: [PATCH 31/54] Commit Marco's suggestions for flatTerms verbatim --- packages/components/src/query-controls/terms.ts | 4 ++-- packages/components/src/query-controls/types.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index e78aca3008ffe7..aed7c699cc9460 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -15,8 +15,8 @@ import type { Entity, EntityForTree, TermsWithChildren } from './types'; * * @return Terms in tree format. */ -export function buildTermsTree( flatTerms?: readonly Entity[] ) { - const flatTermsWithParentAndChildren = flatTerms?.map( ( term ) => { +export function buildTermsTree( flatTerms: readonly Entity[] ) { + const flatTermsWithParentAndChildren = flatTerms.map( ( term ) => { return { children: [], parent: null, diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index b0d676e8297b1b..7d7d65a041ce12 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -31,11 +31,11 @@ export type Category = { }; export type CategorySelectProps = { - categoriesList: QueryControlsProps[ 'categoriesList' ]; + categoriesList: NonNullable< QueryControlsProps[ 'categoriesList' ] >; key: Key; label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; - onChange: TreeSelectProps[ 'onChange' ]; + onChange: NonNullable< TreeSelectProps[ 'onChange' ] >; selectedCategoryId: QueryControlsProps[ 'selectedCategoryId' ]; }; @@ -43,7 +43,7 @@ export type AuthorSelectProps = { authorList: QueryControlsProps[ 'authorList' ]; label: TreeSelectProps[ 'label' ]; noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; - onChange: QueryControlsProps[ 'onAuthorChange' ]; + onChange: NonNullable< QueryControlsProps[ 'onAuthorChange' ] >; selectedAuthorId: QueryControlsProps[ 'selectedAuthorId' ]; }; From d714edeeb38cc9e11fbe0ef213dee6e9e2622000 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 16:32:36 -0600 Subject: [PATCH 32/54] Commit Marco's suggestion for parameters --- packages/components/src/query-controls/stories/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index ad7adb917e0e9d..5ca671a3a36be7 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -12,9 +12,9 @@ const meta: ComponentMeta< typeof QueryControls > = { title: 'Components/QueryControls', component: QueryControls, parameters: { - controls: { - expanded: true, - }, + actions: { argTypesRegex: '^on.*' }, + controls: { expanded: true }, + docs: { source: { state: 'open' } }, }, }; export default meta; From 4e19a9ff13737b992d3b98f47fa55ee11cb85d82 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 16:46:33 -0600 Subject: [PATCH 33/54] Remove fallback to '' --- packages/components/src/query-controls/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index d14fb91c933951..41198ef8016353 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -122,7 +122,7 @@ export function QueryControls( { selectedCategories && selectedCategories.map( ( item ) => ( { id: item.id, - value: item.name || item.value || '', + value: item.name || item.value, } ) ) } suggestions={ Object.keys( categorySuggestions ) } From 0220eb96af7e7e1a48b33d14c2ff95c747936fcf Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 21:26:38 -0600 Subject: [PATCH 34/54] Save the author as a Number --- .../src/query-controls/stories/index.tsx | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 5ca671a3a36be7..237366210fd82b 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -3,10 +3,16 @@ */ import type { ComponentMeta, ComponentStory } from '@storybook/react'; +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + /** * Internal dependencies */ import QueryControls from '..'; +import type { Category } from '../types'; const meta: ComponentMeta< typeof QueryControls > = { title: 'Components/QueryControls', @@ -19,12 +25,41 @@ const meta: ComponentMeta< typeof QueryControls > = { }; export default meta; -const Template: ComponentStory< typeof QueryControls > = ( props ) => { - return ; +const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { + const [ ownNumberOfItems, setOwnNumberOfItems ] = useState( props.numberOfItems ); + const [ ownOrder, setOwnOrder ] = useState( props.order ); + const [ ownOrderBy, setOwnOrderBy ] = useState( props.orderBy ); + const [ ownSelectedAuthorId, setOwnSelectedAuthorId ] = useState( props.selectedAuthorId ); + const [ ownSelectedCategories, setOwnSelectedCategories ] = useState( props.selectedCategories ); + + const onCategoryChange = ( tokens: Array< Category | string > ) => { + const allCategories = tokens?.map( ( token ) => { + return typeof token === 'string' + ? ( props.categorySuggestions?.[ token ] as Category ) + : token; + } ) + + setOwnSelectedCategories( allCategories ); + }; + + return { + setOwnSelectedAuthorId( Number( newAuthor ) ); + }, + } } />; }; -const noop = () => {}; -export const Default: ComponentStory< typeof QueryControls > = Template.bind( +export const Default: ComponentStory< typeof QueryControls > = DefaultTemplate.bind( {} ); Default.args = { @@ -59,18 +94,28 @@ Default.args = { }, ], numberOfItems: 5, - onAuthorChange: noop, - onCategoryChange: noop, - onNumberOfItemsChange: noop, - onOrderByChange: noop, - onOrderChange: noop, order: 'desc', orderBy: 'date', selectedAuthorId: 1, }; +const SingleCategoryTemplate: ComponentStory< typeof QueryControls > = ( props ) => { + const [ ownOrder, setOwnOrder ] = useState( props.order ); + const [ ownOrderBy, setOwnOrderBy ] = useState( props.orderBy ); + const [ ownSelectedCategoryId, setSelectedCategoryId ] = useState( props.selectedCategoryId ); + + return ; +}; export const SelectSingleCategory: ComponentStory< typeof QueryControls > = - Template.bind( {} ); + SingleCategoryTemplate.bind( {} ); SelectSingleCategory.args = { categoriesList: [ { @@ -82,6 +127,5 @@ SelectSingleCategory.args = { name: 'JavaScript', }, ], - onCategoryChange: noop, selectedCategoryId: 11, }; From bb7331cf0723c12ba05ee81cb0483e2ac3063f4b Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 21:57:55 -0600 Subject: [PATCH 35/54] Fix the order and orderBy types --- .../src/query-controls/stories/index.tsx | 109 +++++++++++------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 237366210fd82b..3581fa993783e6 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -12,7 +12,7 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import QueryControls from '..'; -import type { Category } from '../types'; +import type { Category, QueryControlsProps } from '../types'; const meta: ComponentMeta< typeof QueryControls > = { title: 'Components/QueryControls', @@ -26,42 +26,59 @@ const meta: ComponentMeta< typeof QueryControls > = { export default meta; const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { - const [ ownNumberOfItems, setOwnNumberOfItems ] = useState( props.numberOfItems ); + const [ ownNumberOfItems, setOwnNumberOfItems ] = useState( + props.numberOfItems + ); const [ ownOrder, setOwnOrder ] = useState( props.order ); const [ ownOrderBy, setOwnOrderBy ] = useState( props.orderBy ); - const [ ownSelectedAuthorId, setOwnSelectedAuthorId ] = useState( props.selectedAuthorId ); - const [ ownSelectedCategories, setOwnSelectedCategories ] = useState( props.selectedCategories ); + const [ ownSelectedAuthorId, setOwnSelectedAuthorId ] = useState( + props.selectedAuthorId + ); + const [ ownSelectedCategories, setOwnSelectedCategories ] = useState( + props.selectedCategories + ); - const onCategoryChange = ( tokens: Array< Category | string > ) => { - const allCategories = tokens?.map( ( token ) => { - return typeof token === 'string' - ? ( props.categorySuggestions?.[ token ] as Category ) - : token; - } ) + const onCategoryChange: QueryControlsProps[ 'onCategoryChange' ] = ( + tokens + ) => { + if ( 'string' === typeof tokens ) { + return; + } - setOwnSelectedCategories( allCategories ); + const allCategories = tokens?.map( ( token ) => { + return typeof token === 'string' + ? props.categorySuggestions?.[ token ] + : token; + } ) as Category[]; + + setOwnSelectedCategories( allCategories ); }; - return { - setOwnSelectedAuthorId( Number( newAuthor ) ); - }, - } } />; + return ( + { + setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); + } } + onOrderByChange={ ( newOrderBy ) => { + setOwnOrderBy( newOrderBy as QueryControlsProps[ 'orderBy' ] ); + } } + order={ ownOrder } + orderBy={ ownOrderBy } + selectedAuthorId={ ownSelectedAuthorId } + numberOfItems={ ownNumberOfItems } + onNumberOfItemsChange={ setOwnNumberOfItems } + onAuthorChange={ ( newAuthor ) => { + setOwnSelectedAuthorId( Number( newAuthor ) ); + } } + /> + ); }; -export const Default: ComponentStory< typeof QueryControls > = DefaultTemplate.bind( - {} -); +export const Default: ComponentStory< typeof QueryControls > = + DefaultTemplate.bind( {} ); Default.args = { authorList: [ { @@ -99,20 +116,32 @@ Default.args = { selectedAuthorId: 1, }; -const SingleCategoryTemplate: ComponentStory< typeof QueryControls > = ( props ) => { +const SingleCategoryTemplate: ComponentStory< typeof QueryControls > = ( + props +) => { const [ ownOrder, setOwnOrder ] = useState( props.order ); const [ ownOrderBy, setOwnOrderBy ] = useState( props.orderBy ); - const [ ownSelectedCategoryId, setSelectedCategoryId ] = useState( props.selectedCategoryId ); + const [ ownSelectedCategoryId, setSelectedCategoryId ] = useState( + props.selectedCategoryId + ); - return ; + return ( + { + setSelectedCategoryId( Number( newCategory ) ); + } } + onOrderChange={ ( newOrder ) => { + setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); + } } + onOrderByChange={ ( newOrderBy ) => { + setOwnOrderBy( newOrderBy as QueryControlsProps[ 'orderBy' ] ); + } } + order={ ownOrder } + orderBy={ ownOrderBy } + selectedCategoryId={ ownSelectedCategoryId } + /> + ); }; export const SelectSingleCategory: ComponentStory< typeof QueryControls > = SingleCategoryTemplate.bind( {} ); From 30b3e9cccfb9858e99f25de63a2762230e8e6be8 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 22:09:49 -0600 Subject: [PATCH 36/54] Alphabetize the QueryControls props --- .../components/src/query-controls/stories/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 3581fa993783e6..5e9a550a6e8d82 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -59,12 +59,12 @@ const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { { ...props } selectedCategories={ ownSelectedCategories } onCategoryChange={ onCategoryChange } - onOrderChange={ ( newOrder ) => { - setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); - } } onOrderByChange={ ( newOrderBy ) => { setOwnOrderBy( newOrderBy as QueryControlsProps[ 'orderBy' ] ); } } + onOrderChange={ ( newOrder ) => { + setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); + } } order={ ownOrder } orderBy={ ownOrderBy } selectedAuthorId={ ownSelectedAuthorId } @@ -131,12 +131,12 @@ const SingleCategoryTemplate: ComponentStory< typeof QueryControls > = ( onCategoryChange={ ( newCategory ) => { setSelectedCategoryId( Number( newCategory ) ); } } - onOrderChange={ ( newOrder ) => { - setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); - } } onOrderByChange={ ( newOrderBy ) => { setOwnOrderBy( newOrderBy as QueryControlsProps[ 'orderBy' ] ); } } + onOrderChange={ ( newOrder ) => { + setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); + } } order={ ownOrder } orderBy={ ownOrderBy } selectedCategoryId={ ownSelectedCategoryId } From b03a5fbfdd9b6eee543eec7860199f6e0255ed3c Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 22:15:28 -0600 Subject: [PATCH 37/54] Fix unit tests for the ID being a string --- .../src/query-controls/test/terms.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/components/src/query-controls/test/terms.ts b/packages/components/src/query-controls/test/terms.ts index 2b87ce98f13ae6..0dcaa62f1440a8 100644 --- a/packages/components/src/query-controls/test/terms.ts +++ b/packages/components/src/query-controls/test/terms.ts @@ -11,14 +11,14 @@ describe( 'buildTermsTree()', () => { ] ); const output = Object.freeze( [ { - id: 2232, + id: '2232', name: 'foo', parent: null, children: [], dummy: true, }, { - id: 2245, + id: '2245', name: 'baz', parent: null, children: [], @@ -34,8 +34,8 @@ describe( 'buildTermsTree()', () => { { id: 2245, name: 'baz', parent: 0, dummy: false }, ] ); const output = [ - { id: 2232, name: 'foo', parent: 0, children: [], dummy: true }, - { id: 2245, name: 'baz', parent: 0, children: [], dummy: false }, + { id: '2232', name: 'foo', parent: 0, children: [], dummy: true }, + { id: '2245', name: 'baz', parent: 0, children: [], dummy: false }, ]; const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); @@ -47,11 +47,11 @@ describe( 'buildTermsTree()', () => { ] ); const output = [ { - id: 2232, + id: '2232', name: 'foo', parent: 0, children: [ - { id: 2245, name: 'baz', parent: 2232, children: [] }, + { id: '2245', name: 'baz', parent: 2232, children: [] }, ], }, ]; @@ -67,15 +67,15 @@ describe( 'buildTermsTree()', () => { ] ); const output = [ { - id: 2232, + id: '2232', name: 'a', parent: 0, children: [ - { id: 2245, name: 'b', parent: 2232, children: [] }, - { id: 2246, name: 'd', parent: 2232, children: [] }, + { id: '2245', name: 'b', parent: 2232, children: [] }, + { id: '2246', name: 'd', parent: 2232, children: [] }, ], }, - { id: 2249, name: 'c', parent: 0, children: [] }, + { id: '2249', name: 'c', parent: 0, children: [] }, ]; const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); From 65e69deaf2e1b263512ccaf36a2d7c6b6a0e3bc9 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 12 Jan 2023 22:30:58 -0600 Subject: [PATCH 38/54] Disable controls for values that you can change via the UI For other components that you can control via the UI like RangeControl, the value control is disabled. https://github.com/WordPress/gutenberg/blob/a35b3f245a9672e2a276d8c4c1785f42b3342f84/packages/components/src/range-control/stories/index.tsx#L46 --- .../src/query-controls/stories/index.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 5e9a550a6e8d82..e87943fc3ffb24 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -17,6 +17,14 @@ import type { Category, QueryControlsProps } from '../types'; const meta: ComponentMeta< typeof QueryControls > = { title: 'Components/QueryControls', component: QueryControls, + argTypes: { + numberOfItems: { control: { type: null } }, + order: { control: { type: null } }, + orderBy: { control: { type: null } }, + selectedAuthorId: { control: { type: null } }, + selectedCategories: { control: { type: null } }, + selectedCategoryId: { control: { type: null } }, + }, parameters: { actions: { argTypesRegex: '^on.*' }, controls: { expanded: true }, @@ -57,7 +65,7 @@ const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { return ( { setOwnOrderBy( newOrderBy as QueryControlsProps[ 'orderBy' ] ); @@ -67,12 +75,12 @@ const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { } } order={ ownOrder } orderBy={ ownOrderBy } - selectedAuthorId={ ownSelectedAuthorId } - numberOfItems={ ownNumberOfItems } onNumberOfItemsChange={ setOwnNumberOfItems } onAuthorChange={ ( newAuthor ) => { setOwnSelectedAuthorId( Number( newAuthor ) ); } } + selectedAuthorId={ ownSelectedAuthorId } + selectedCategories={ ownSelectedCategories } /> ); }; From 284051750cde774c2db36f60cc6b2e7bcd5ec953 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 18 Jan 2023 11:55:10 -0600 Subject: [PATCH 39/54] Apply Marco's suggestion for selected*Id --- packages/components/src/query-controls/author-select.tsx | 6 +++++- packages/components/src/query-controls/category-select.tsx | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/components/src/query-controls/author-select.tsx b/packages/components/src/query-controls/author-select.tsx index 03e6b07d29cc0a..37163aba4d3082 100644 --- a/packages/components/src/query-controls/author-select.tsx +++ b/packages/components/src/query-controls/author-select.tsx @@ -18,7 +18,11 @@ export default function AuthorSelect( { ); } diff --git a/packages/components/src/query-controls/category-select.tsx b/packages/components/src/query-controls/category-select.tsx index d96763b7b4434c..c082e3d9776de3 100644 --- a/packages/components/src/query-controls/category-select.tsx +++ b/packages/components/src/query-controls/category-select.tsx @@ -26,7 +26,11 @@ export default function CategorySelect( { ); From 6a8920643ec641c27270b9d6c1fb6171131864b1 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 19 Jan 2023 12:09:52 -0600 Subject: [PATCH 40/54] Commit Marco's suggestion to prevent type casting --- packages/components/src/query-controls/index.tsx | 12 ++++++++++-- .../components/src/query-controls/stories/index.tsx | 4 ++-- packages/components/src/query-controls/types.ts | 11 +++++++---- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index 41198ef8016353..4cacc06d76dbe5 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -96,10 +96,18 @@ export function QueryControls( { const [ newOrderBy, newOrder ] = value.split( '/' ); if ( newOrder !== order ) { - onOrderChange( newOrder ); + onOrderChange( + newOrder as NonNullable< + QueryControlsProps[ 'order' ] + > + ); } if ( newOrderBy !== orderBy ) { - onOrderByChange( newOrderBy ); + onOrderByChange( + newOrderBy as NonNullable< + QueryControlsProps[ 'orderBy' ] + > + ); } } } /> diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index e87943fc3ffb24..88832c0576b94b 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -68,10 +68,10 @@ const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { numberOfItems={ ownNumberOfItems } onCategoryChange={ onCategoryChange } onOrderByChange={ ( newOrderBy ) => { - setOwnOrderBy( newOrderBy as QueryControlsProps[ 'orderBy' ] ); + setOwnOrderBy( newOrderBy ); } } onOrderChange={ ( newOrder ) => { - setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); + setOwnOrder( newOrder ); } } order={ ownOrder } orderBy={ ownOrderBy } diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 7d7d65a041ce12..923f7c6a4b7ace 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -47,6 +47,9 @@ export type AuthorSelectProps = { selectedAuthorId: QueryControlsProps[ 'selectedAuthorId' ]; }; +type Order = 'asc' | 'desc'; +type OrderBy = 'date' | 'title'; + export type QueryControlsProps = { /** * An array of authors that is passed into @@ -101,21 +104,21 @@ export type QueryControlsProps = { * If this or onOrderByChange are not specified, * then the order controls are not included. */ - onOrderChange?: ( newOrder: string ) => void; + onOrderChange?: ( newOrder: Order ) => void; /** * A function that receives the new orderby value. * If this or onOrderChange are not specified, * then the order controls are not included. */ - onOrderByChange?: ( newOrderBy: string ) => void; + onOrderByChange?: ( newOrderBy: OrderBy ) => void; /** * The order in which to retrieve posts. */ - order?: 'asc' | 'desc'; + order?: Order; /** * The meta key by which to order posts. */ - orderBy?: 'date' | 'title'; + orderBy?: OrderBy; /** * The selected author ID. */ From 09e96fdae36d380e1b61451da198c2caa653be4c Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 19 Jan 2023 12:57:17 -0600 Subject: [PATCH 41/54] Part 1: Commit Marco's changes that handle multiple types --- .../src/query-controls/author-select.tsx | 13 ++- .../src/query-controls/category-select.tsx | 13 ++- .../components/src/query-controls/index.tsx | 82 ++++++++++++------- .../src/query-controls/stories/index.tsx | 81 +++++++++++++----- 4 files changed, 132 insertions(+), 57 deletions(-) diff --git a/packages/components/src/query-controls/author-select.tsx b/packages/components/src/query-controls/author-select.tsx index 37163aba4d3082..fb09dcc59007f9 100644 --- a/packages/components/src/query-controls/author-select.tsx +++ b/packages/components/src/query-controls/author-select.tsx @@ -3,6 +3,7 @@ */ import { buildTermsTree } from './terms'; import TreeSelect from '../tree-select'; +import type { TreeSelectProps } from '../tree-select/types'; import type { AuthorSelectProps } from './types'; export default function AuthorSelect( { @@ -10,13 +11,21 @@ export default function AuthorSelect( { noOptionLabel, authorList, selectedAuthorId, - onChange, + onChange: onChangeProp, }: AuthorSelectProps ) { if ( ! authorList ) return null; const termsTree = buildTermsTree( authorList ); return ( { @@ -24,7 +25,15 @@ export default function CategorySelect( { return ( @@ -112,32 +126,38 @@ export function QueryControls( { } } /> ), - categoriesList && onCategoryChange && ( - - ), - categorySuggestions && onCategoryChange && ( - + ), + isMultipleCategorySelection( props ) && + props.categorySuggestions && + props.onCategoryChange && ( + ( { - id: item.id, - value: item.name || item.value, - } ) ) - } - suggestions={ Object.keys( categorySuggestions ) } - onChange={ onCategoryChange } - maxSuggestions={ MAX_CATEGORIES_SUGGESTIONS } - /> - ), + label={ __( 'Categories' ) } + value={ + props.selectedCategories && + props.selectedCategories.map( ( item ) => ( { + id: item.id, + value: item.name || item.value, + } ) ) + } + suggestions={ Object.keys( + props.categorySuggestions + ) } + onChange={ props.onCategoryChange } + maxSuggestions={ MAX_CATEGORIES_SUGGESTIONS } + /> + ), onAuthorChange && ( = { title: 'Components/QueryControls', @@ -33,7 +38,15 @@ const meta: ComponentMeta< typeof QueryControls > = { }; export default meta; -const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { +export const Default: ComponentStory< typeof QueryControls > = ( args ) => { + const { + onAuthorChange, + onCategoryChange, + onNumberOfItemsChange, + onOrderByChange, + onOrderChange, + ...props + } = args as QueryControlsWithMultipleCategorySelectionProps; const [ ownNumberOfItems, setOwnNumberOfItems ] = useState( props.numberOfItems ); @@ -46,37 +59,50 @@ const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { props.selectedCategories ); - const onCategoryChange: QueryControlsProps[ 'onCategoryChange' ] = ( - tokens - ) => { - if ( 'string' === typeof tokens ) { - return; - } + const handleCategoryChange: QueryControlsWithMultipleCategorySelectionProps[ 'onCategoryChange' ] = + ( tokens ) => { + onCategoryChange?.( tokens ); - const allCategories = tokens?.map( ( token ) => { - return typeof token === 'string' - ? props.categorySuggestions?.[ token ] - : token; - } ) as Category[]; + const hasNoSuggestion = tokens.some( + ( token ) => + typeof token === 'string' && + ! props.categorySuggestions?.[ token ] + ); + if ( hasNoSuggestion ) { + return; + } + const allCategories = tokens + .map( ( token ) => { + return typeof token === 'string' + ? props.categorySuggestions?.[ token ] + : token; + } ) + .filter( Boolean ) as Category[]; - setOwnSelectedCategories( allCategories ); + setOwnSelectedCategories( allCategories ); }; return ( { + onOrderByChange?.( newOrderBy ); setOwnOrderBy( newOrderBy ); } } onOrderChange={ ( newOrder ) => { + onOrderChange?.( newOrder ); setOwnOrder( newOrder ); } } order={ ownOrder } orderBy={ ownOrderBy } - onNumberOfItemsChange={ setOwnNumberOfItems } + onNumberOfItemsChange={ ( newNumber ) => { + onNumberOfItemsChange?.( newNumber ); ++ setOwnNumberOfItems( newNumber ); + } } onAuthorChange={ ( newAuthor ) => { + onAuthorChange?.( newAuthor ); setOwnSelectedAuthorId( Number( newAuthor ) ); } } selectedAuthorId={ ownSelectedAuthorId } @@ -85,8 +111,6 @@ const DefaultTemplate: ComponentStory< typeof QueryControls > = ( props ) => { ); }; -export const Default: ComponentStory< typeof QueryControls > = - DefaultTemplate.bind( {} ); Default.args = { authorList: [ { @@ -125,24 +149,37 @@ Default.args = { }; const SingleCategoryTemplate: ComponentStory< typeof QueryControls > = ( - props + args ) => { + const { + onAuthorChange, + onCategoryChange, + onNumberOfItemsChange, + onOrderByChange, + onOrderChange, + ...props + } = args as QueryControlsWithSingleCategorySelectionProps; const [ ownOrder, setOwnOrder ] = useState( props.order ); const [ ownOrderBy, setOwnOrderBy ] = useState( props.orderBy ); const [ ownSelectedCategoryId, setSelectedCategoryId ] = useState( props.selectedCategoryId ); + const handleCategoryChange: QueryControlsWithSingleCategorySelectionProps[ 'onCategoryChange' ] = + ( newCategory ) => { + onCategoryChange?.( newCategory ); + setSelectedCategoryId( Number( newCategory ) ); + }; + return ( { - setSelectedCategoryId( Number( newCategory ) ); - } } + onCategoryChange={ handleCategoryChange } onOrderByChange={ ( newOrderBy ) => { setOwnOrderBy( newOrderBy as QueryControlsProps[ 'orderBy' ] ); } } onOrderChange={ ( newOrder ) => { + onOrderChange?.( newOrder ); setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); } } order={ ownOrder } From a90ced1eecce0590c5a644a7879786011ff3f59f Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 19 Jan 2023 14:38:19 -0600 Subject: [PATCH 42/54] Part 2: Commit Marco's change that handle multiple types https://github.com/WordPress/gutenberg/pull/46721#discussion_r1081406091 --- .../components/src/query-controls/types.ts | 104 ++++++++++-------- 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 923f7c6a4b7ace..ef914e30d0450e 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import type { Key } from 'react'; - /** * Internal dependencies */ @@ -30,42 +25,33 @@ export type Category = { name?: string; }; -export type CategorySelectProps = { - categoriesList: NonNullable< QueryControlsProps[ 'categoriesList' ] >; - key: Key; - label: TreeSelectProps[ 'label' ]; - noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; - onChange: NonNullable< TreeSelectProps[ 'onChange' ] >; - selectedCategoryId: QueryControlsProps[ 'selectedCategoryId' ]; +export type CategorySelectProps = Pick< + TreeSelectProps, + 'label' | 'noOptionLabel' +> & { + categoriesList: Entity[]; + onChange: ( newCategory: string ) => void; + selectedCategoryId?: Category[ 'id' ]; }; -export type AuthorSelectProps = { - authorList: QueryControlsProps[ 'authorList' ]; - label: TreeSelectProps[ 'label' ]; - noOptionLabel: TreeSelectProps[ 'noOptionLabel' ]; - onChange: NonNullable< QueryControlsProps[ 'onAuthorChange' ] >; - selectedAuthorId: QueryControlsProps[ 'selectedAuthorId' ]; +export type AuthorSelectProps = Pick< + TreeSelectProps, + 'label' | 'noOptionLabel' +> & { + authorList?: Entity[]; + onChange: ( newAuthor: string ) => void; + selectedAuthorId?: Entity[ 'id' ]; }; type Order = 'asc' | 'desc'; type OrderBy = 'date' | 'title'; -export type QueryControlsProps = { +type BaseQueryControlsProps = { /** * An array of authors that is passed into * an `AuthorSelect` sub-component. */ - authorList?: Entity[]; - /** - * An array of categories, renders a `CategorySelect` - * sub-component when passed in conjunction with `onCategoryChange`. - */ - categoriesList?: Entity[]; - /** - * An array of category names, renders a `FormTokenField` component - * when passed in conjunction with `onCategoryChange`. - */ - categorySuggestions?: { [ categoryName: Entity[ 'name' ] ]: Entity }; + authorList?: AuthorSelectProps[ 'authorList' ]; /** * The maximum items. * @@ -86,13 +72,7 @@ export type QueryControlsProps = { * A function that receives the new author value. * If this is not specified, the author controls are not included. */ - onAuthorChange?: TreeSelectProps[ 'onChange' ]; - /** - * A function that receives the new category value. - * If this is not specified, the category controls are not included. - */ - onCategoryChange?: TreeSelectProps[ 'onChange' ] & - FormTokenFieldProps[ 'onChange' ]; + onAuthorChange?: AuthorSelectProps[ 'onChange' ]; /** * A function that receives the new number of items value. * If this is not specified, then the number of items @@ -122,13 +102,45 @@ export type QueryControlsProps = { /** * The selected author ID. */ - selectedAuthorId?: Entity[ 'id' ]; - /** - * The selected categories for the `categorySuggestions`. - */ - selectedCategories?: Category[]; - /** - * The selected category for the `categoriesList`. - */ - selectedCategoryId?: Category[ 'id' ]; + selectedAuthorId?: AuthorSelectProps[ 'selectedAuthorId' ]; }; + +export type QueryControlsWithSingleCategorySelectionProps = + BaseQueryControlsProps & { + /** + * An array of categories, renders a `CategorySelect` + * sub-component when passed in conjunction with `onCategoryChange`. + */ + categoriesList?: CategorySelectProps[ 'categoriesList' ]; + /** + * The selected category for the `categoriesList`. + */ + selectedCategoryId?: CategorySelectProps[ 'selectedCategoryId' ]; + /** + * A function that receives the new category value. + * If this is not specified, the category controls are not included. + */ + onCategoryChange?: CategorySelectProps[ 'onChange' ]; + }; + +export type QueryControlsWithMultipleCategorySelectionProps = + BaseQueryControlsProps & { + /** + * An array of category names, renders a `FormTokenField` component + * when passed in conjunction with `onCategoryChange`. + */ + categorySuggestions?: { [ categoryName: Entity[ 'name' ] ]: Entity }; + /** + * The selected categories for the `categorySuggestions`. + */ + selectedCategories?: Category[]; + /** + * A function that receives the new category value. + * If this is not specified, the category controls are not included. + */ + onCategoryChange?: FormTokenFieldProps[ 'onChange' ]; + }; + +export type QueryControlsProps = + | QueryControlsWithSingleCategorySelectionProps + | QueryControlsWithMultipleCategorySelectionProps; From 07d72918ab4741902bfc43bc8de0b315b1fe68f8 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 19 Jan 2023 14:49:45 -0600 Subject: [PATCH 43/54] Update README.md for Marco's changes --- .../components/src/query-controls/README.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index a0e3e10b6bafdb..d5070309c891d2 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -124,14 +124,14 @@ The format of the categories list also needs to be updated to match what `FormTo ### Props -#### `authorList`: `Entity[]` +#### `authorList`: `AuthorSelectProps[ 'authorList' ]` An array of authors that is passed into an `AuthorSelect` sub-component. - Required: No - Platform: Web -#### `categoriesList`: `Entity[]` +#### `categoriesList`: `CategorySelectProps[ 'categoriesList' ]` An array of categories, renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. @@ -168,14 +168,14 @@ The selected number of items to retrieve via the query. - Required: No - Platform: Web -#### `onAuthorChange`: `TreeSelectProps[ 'onChange' ]` +#### `onAuthorChange`: `AuthorSelectProps[ 'onChange' ]` A function that receives the new author value. If this is not specified, the author controls are not included. - Required: No - Platform: Web -#### `onCategoryChange`: `TreeSelectProps[ 'onChange' ] & FormTokenFieldProps[ 'onChange' ]` +#### `onCategoryChange`: `CategorySelectProps[ 'onChange' ] | FormTokenFieldProps[ 'onChange' ]` A function that receives the new category value. If this is not specified, the category controls are not included. @@ -189,35 +189,35 @@ A function that receives the new number of items value. If this is not specified - Required: No - Platform: Web -#### `onOrderChange`: `( newOrder: string ) => void` +#### `onOrderChange`: `( newOrder: Order ) => void` A function that receives the new order value. If this or onOrderByChange are not specified, then the order controls are not included. - Required: No - Platform: Web -#### `onOrderByChange`: `( newOrderBy: string ) => void` +#### `onOrderByChange`: `( newOrderBy: OrderBy ) => void` A function that receives the new orderby value. If this or onOrderChange are not specified, then the order controls are not included. - Required: No - Platform: Web -#### `order`: `'asc' | 'desc'` +#### `order`: `Order` The order in which to retrieve posts. - Required: No - Platform: Web -#### `orderBy`: `'data' | 'title'` +#### `orderBy`: `OrderBy` The meta key by which to order posts. - Required: No - Platform: Web -#### `selectedAuthorId`: `Entity[ 'id' ]` +#### `selectedAuthorId`: `AuthorSelectProps[ 'selectedAuthorId' ]` The selected author ID. @@ -231,7 +231,7 @@ The selected categories for the `categorySuggestions`. - Required: No - Platform: Web -#### `selectedCategoryId`: `Category[ 'id' ]` +#### `selectedCategoryId`: `CategorySelectProps[ 'selectedCategoryId' ]` The selected category for the `categoriesList`. From 30748b9efdc9c8d47c12b68ac035d75454e4fa54 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 19 Jan 2023 16:25:51 -0600 Subject: [PATCH 44/54] Remove Category type entirely, replace with Entity intersection --- .../src/query-controls/stories/index.tsx | 10 ++++++---- packages/components/src/query-controls/types.ts | 14 ++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 642b3ae972a183..42751ecaabb330 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -13,7 +13,7 @@ import { useState } from '@wordpress/element'; */ import QueryControls from '..'; import type { - Category, + Entity, QueryControlsProps, QueryControlsWithSingleCategorySelectionProps, QueryControlsWithMultipleCategorySelectionProps, @@ -77,10 +77,10 @@ export const Default: ComponentStory< typeof QueryControls > = ( args ) => { ? props.categorySuggestions?.[ token ] : token; } ) - .filter( Boolean ) as Category[]; + .filter( Boolean ) as Array< Required< Entity > >; setOwnSelectedCategories( allCategories ); - }; + }; return ( = ( args ) => { orderBy={ ownOrderBy } onNumberOfItemsChange={ ( newNumber ) => { onNumberOfItemsChange?.( newNumber ); -+ setOwnNumberOfItems( newNumber ); + setOwnNumberOfItems( newNumber ); } } onAuthorChange={ ( newAuthor ) => { onAuthorChange?.( newAuthor ); @@ -135,10 +135,12 @@ Default.args = { selectedCategories: [ { id: 11, + name: 'TypeScript', value: 'TypeScript', }, { id: 12, + name: 'JavaScript', value: 'JavaScript', }, ], diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index ef914e30d0450e..9419de79450853 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -19,19 +19,13 @@ export type TermsWithChildren = Array< EntityForTree & { children: EntityForTree[] } >; -export type Category = { - id: number; - value: string; - name?: string; -}; - export type CategorySelectProps = Pick< TreeSelectProps, 'label' | 'noOptionLabel' > & { categoriesList: Entity[]; onChange: ( newCategory: string ) => void; - selectedCategoryId?: Category[ 'id' ]; + selectedCategoryId?: Entity[ 'id' ]; }; export type AuthorSelectProps = Pick< @@ -133,7 +127,11 @@ export type QueryControlsWithMultipleCategorySelectionProps = /** * The selected categories for the `categorySuggestions`. */ - selectedCategories?: Category[]; + selectedCategories?: Array< + Entity & { + value: string; + } + >; /** * A function that receives the new category value. * If this is not specified, the category controls are not included. From c6410a55f255090b961e1b47bdaa60edbfeb4222 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 19 Jan 2023 16:32:01 -0600 Subject: [PATCH 45/54] Make the category name optional --- packages/components/src/query-controls/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 9419de79450853..fe5d1a70afac40 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -128,7 +128,8 @@ export type QueryControlsWithMultipleCategorySelectionProps = * The selected categories for the `categorySuggestions`. */ selectedCategories?: Array< - Entity & { + Omit< Entity, 'name' > & { + name?: string; value: string; } >; From 9a7ea7054e8afeda47258278c36e20511d9e454a Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 19 Jan 2023 16:34:03 -0600 Subject: [PATCH 46/54] Remove the name from selectedCategories in the unit test --- packages/components/src/query-controls/stories/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 42751ecaabb330..554ba9c70ce5a4 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -135,12 +135,10 @@ Default.args = { selectedCategories: [ { id: 11, - name: 'TypeScript', value: 'TypeScript', }, { id: 12, - name: 'JavaScript', value: 'JavaScript', }, ], From b37a4a28301c1552d5c5fad3f7f748d0bb54354e Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Thu, 19 Jan 2023 16:41:08 -0600 Subject: [PATCH 47/54] Remove needless casting --- packages/components/src/query-controls/stories/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 554ba9c70ce5a4..54354ed5374841 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -14,7 +14,6 @@ import { useState } from '@wordpress/element'; import QueryControls from '..'; import type { Entity, - QueryControlsProps, QueryControlsWithSingleCategorySelectionProps, QueryControlsWithMultipleCategorySelectionProps, } from '../types'; @@ -176,11 +175,11 @@ const SingleCategoryTemplate: ComponentStory< typeof QueryControls > = ( { ...props } onCategoryChange={ handleCategoryChange } onOrderByChange={ ( newOrderBy ) => { - setOwnOrderBy( newOrderBy as QueryControlsProps[ 'orderBy' ] ); + setOwnOrderBy( newOrderBy ); } } onOrderChange={ ( newOrder ) => { onOrderChange?.( newOrder ); - setOwnOrder( newOrder as QueryControlsProps[ 'order' ] ); + setOwnOrder( newOrder ); } } order={ ownOrder } orderBy={ ownOrderBy } From 50b2a78d7d3b1e07fc5d445a25a10bb83df0a39e Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Mon, 23 Jan 2023 21:56:32 -0600 Subject: [PATCH 48/54] Commit Marco's formatting diff https://github.com/WordPress/gutenberg/pull/46721#discussion_r1084459158 --- packages/components/src/query-controls/author-select.tsx | 2 +- packages/components/src/query-controls/category-select.tsx | 2 +- packages/components/src/query-controls/index.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/components/src/query-controls/author-select.tsx b/packages/components/src/query-controls/author-select.tsx index fb09dcc59007f9..ebda05ffb9a365 100644 --- a/packages/components/src/query-controls/author-select.tsx +++ b/packages/components/src/query-controls/author-select.tsx @@ -17,7 +17,7 @@ export default function AuthorSelect( { const termsTree = buildTermsTree( authorList ); return ( Date: Mon, 23 Jan 2023 22:10:32 -0600 Subject: [PATCH 49/54] Commit Marco's diff verbatim to fix the types https://github.com/WordPress/gutenberg/pull/46721#discussion_r1084650059 --- .../components/src/query-controls/index.tsx | 4 ++ .../src/query-controls/stories/index.tsx | 15 ++++---- .../components/src/query-controls/terms.ts | 35 +++++++++++------ .../components/src/query-controls/types.ts | 38 ++++++++++--------- 4 files changed, 55 insertions(+), 37 deletions(-) diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index f94054096376bd..91769410b4fa92 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -148,6 +148,10 @@ export function QueryControls( { props.selectedCategories && props.selectedCategories.map( ( item ) => ( { id: item.id, + // Keeping the fallback to `item.value` for legacy reasons, + // even if items of `selectedCategories` should not have a + // `value` property. + // @ts-expect-error value: item.name || item.value, } ) ) } diff --git a/packages/components/src/query-controls/stories/index.tsx b/packages/components/src/query-controls/stories/index.tsx index 54354ed5374841..d7fa5e50a4531a 100644 --- a/packages/components/src/query-controls/stories/index.tsx +++ b/packages/components/src/query-controls/stories/index.tsx @@ -13,7 +13,7 @@ import { useState } from '@wordpress/element'; */ import QueryControls from '..'; import type { - Entity, + Category, QueryControlsWithSingleCategorySelectionProps, QueryControlsWithMultipleCategorySelectionProps, } from '../types'; @@ -76,7 +76,7 @@ export const Default: ComponentStory< typeof QueryControls > = ( args ) => { ? props.categorySuggestions?.[ token ] : token; } ) - .filter( Boolean ) as Array< Required< Entity > >; + .filter( Boolean ) as Array< Required< Category > >; setOwnSelectedCategories( allCategories ); }; @@ -125,20 +125,19 @@ Default.args = { TypeScript: { id: 11, name: 'TypeScript', + parent: 0, }, JavaScript: { id: 12, name: 'JavaScript', + parent: 0, }, }, selectedCategories: [ { id: 11, - value: 'TypeScript', - }, - { - id: 12, - value: 'JavaScript', + name: 'JavaScript', + parent: 0, }, ], numberOfItems: 5, @@ -194,10 +193,12 @@ SelectSingleCategory.args = { { id: 11, name: 'TypeScript', + parent: 0, }, { id: 12, name: 'JavaScript', + parent: 0, }, ], selectedCategoryId: 11, diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index aed7c699cc9460..3802e05d84c7d6 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -6,7 +6,12 @@ import { groupBy } from 'lodash'; /** * Internal dependencies */ -import type { Entity, EntityForTree, TermsWithChildren } from './types'; +import type { + Author, + Category, + TermWithParentAndChildren, + TermsByParent, +} from './types'; /** * Returns terms in a tree form. @@ -15,21 +20,27 @@ import type { Entity, EntityForTree, TermsWithChildren } from './types'; * * @return Terms in tree format. */ -export function buildTermsTree( flatTerms: readonly Entity[] ) { - const flatTermsWithParentAndChildren = flatTerms.map( ( term ) => { - return { - children: [], - parent: null, - ...term, - id: String( term.id ), - }; - } ); +export function buildTermsTree( flatTerms: readonly ( Author | Category )[] ) { + const flatTermsWithParentAndChildren: TermWithParentAndChildren[] = + flatTerms.map( ( term ) => { + return { + children: [], + parent: null, + ...term, + id: String( term.id ), + }; + } ); - const termsByParent = groupBy( flatTermsWithParentAndChildren, 'parent' ); + const termsByParent: TermsByParent = groupBy( + flatTermsWithParentAndChildren, + 'parent' + ); if ( termsByParent.null && termsByParent.null.length ) { return flatTermsWithParentAndChildren; } - const fillWithChildren = ( terms: EntityForTree[] ): TermsWithChildren => { + const fillWithChildren = ( + terms: TermWithParentAndChildren[] + ): TermWithParentAndChildren[] => { return terms.map( ( term ) => { const children = termsByParent[ term.id ]; return { diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index fe5d1a70afac40..6086d35e2a2b20 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -4,37 +4,42 @@ import type { FormTokenFieldProps } from '../form-token-field/types'; import type { TreeSelectProps } from '../tree-select/types'; -export type Entity = { +export type Author = { id: number; name: string; - parent?: number | null; - value?: string; }; -export type EntityForTree = Omit< Entity, 'id' > & { +export type Category = { + id: number; + name: string; + parent: number; +}; + +export type TermWithParentAndChildren = { id: string; + name: string; + parent: number | null; + children: TermWithParentAndChildren[]; }; -export type TermsWithChildren = Array< - EntityForTree & { children: EntityForTree[] } ->; +export type TermsByParent = Record< string, TermWithParentAndChildren[] >; export type CategorySelectProps = Pick< TreeSelectProps, 'label' | 'noOptionLabel' > & { - categoriesList: Entity[]; + categoriesList: Category[]; onChange: ( newCategory: string ) => void; - selectedCategoryId?: Entity[ 'id' ]; + selectedCategoryId?: Category[ 'id' ]; }; export type AuthorSelectProps = Pick< TreeSelectProps, 'label' | 'noOptionLabel' > & { - authorList?: Entity[]; + authorList?: Author[]; onChange: ( newAuthor: string ) => void; - selectedAuthorId?: Entity[ 'id' ]; + selectedAuthorId?: Author[ 'id' ]; }; type Order = 'asc' | 'desc'; @@ -123,16 +128,13 @@ export type QueryControlsWithMultipleCategorySelectionProps = * An array of category names, renders a `FormTokenField` component * when passed in conjunction with `onCategoryChange`. */ - categorySuggestions?: { [ categoryName: Entity[ 'name' ] ]: Entity }; + categorySuggestions?: { + [ categoryName: Category[ 'name' ] ]: Category; + }; /** * The selected categories for the `categorySuggestions`. */ - selectedCategories?: Array< - Omit< Entity, 'name' > & { - name?: string; - value: string; - } - >; + selectedCategories?: Category[]; /** * A function that receives the new category value. * If this is not specified, the category controls are not included. From 9e512a3a4f7c19d80175c038945f762217fdf949 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Mon, 23 Jan 2023 22:27:59 -0600 Subject: [PATCH 50/54] Update README.md for categorySuggestions --- packages/components/src/query-controls/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index d5070309c891d2..706b5c4de7b0c0 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -138,7 +138,7 @@ An array of categories, renders a `CategorySelect` sub-component when passed in - Required: No - Platform: Web -#### `categorySuggestions`: `{ [ categoryName: Entity[ 'name' ] ]: Entity }` +#### `categorySuggestions`: `{ [ categoryName: Category[ 'name' ] ]: Category }` An object of categories, renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. From 474ff5ec6e3e4edb66f7e5b6f12c416f936836e6 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Mon, 23 Jan 2023 22:44:42 -0600 Subject: [PATCH 51/54] Move CHANGELOG entry to Unreleased --- packages/components/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 44622939f351ce..c36b05b60690fc 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -14,6 +14,7 @@ - `DropdownMenu`: migrate Storybook to controls ([47149](https://github.com/WordPress/gutenberg/pull/47149)). - Removed deprecated `@storybook/addon-knobs` dependency from the package ([47152](https://github.com/WordPress/gutenberg/pull/47152)). - `ColorListPicker`: Convert to TypeScript ([#46358](https://github.com/WordPress/gutenberg/pull/46358)). +- `QueryControls`: Convert to TypeScript ([#46721](https://github.com/WordPress/gutenberg/pull/46721)). ### Bug Fix @@ -30,7 +31,6 @@ - `SandBox`: Convert to TypeScript ([#46478](https://github.com/WordPress/gutenberg/pull/46478)). - `ResponsiveWrapper`: Convert to TypeScript ([#46480](https://github.com/WordPress/gutenberg/pull/46480)). - `ItemGroup`: migrate Storybook to controls, refactor to TypeScript ([46945](https://github.com/WordPress/gutenberg/pull/46945)). -- `QueryControls`: Convert to TypeScript ([#46721](https://github.com/WordPress/gutenberg/pull/46721)). ### Bug Fix From 531d337aa5f4b0f191e35eb087310cb0534c7048 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Tue, 24 Jan 2023 13:19:23 -0600 Subject: [PATCH 52/54] Marco's diff: Update packages/components/src/query-controls/README.md Co-authored-by: Marco Ciampini --- packages/components/src/query-controls/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 706b5c4de7b0c0..f4ab2478fb0cfd 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -70,10 +70,12 @@ const QUERY_DEFAULTS = { { id: 1, value: 'Category 1', + parent: 0, }, { id: 2, value: 'Category 1b', + parent: 1, }, ], categories: { From 46f3c607d26bddedac19c84a9b65399a1cc73d99 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Tue, 24 Jan 2023 13:31:18 -0600 Subject: [PATCH 53/54] Marco's diff for types.ts DocBlocks https://github.com/WordPress/gutenberg/pull/46721#discussion_r1085415407 --- .../components/src/query-controls/README.md | 2 +- .../components/src/query-controls/types.ts | 33 +++++++++---------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index f4ab2478fb0cfd..313244f12ff4cc 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -140,7 +140,7 @@ An array of categories, renders a `CategorySelect` sub-component when passed in - Required: No - Platform: Web -#### `categorySuggestions`: `{ [ categoryName: Category[ 'name' ] ]: Category }` +#### `categorySuggestions`: `Record< Category[ 'name' ], Category >` An object of categories, renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index 6086d35e2a2b20..a17f33cdf51009 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -47,18 +47,17 @@ type OrderBy = 'date' | 'title'; type BaseQueryControlsProps = { /** - * An array of authors that is passed into - * an `AuthorSelect` sub-component. + * An array of the authors to select from. */ authorList?: AuthorSelectProps[ 'authorList' ]; /** - * The maximum items. + * The maximum number of items. * * @default 100 */ maxItems?: number; /** - * The minimum of items. + * The minimum number of items. * * @default 1 */ @@ -69,24 +68,24 @@ type BaseQueryControlsProps = { numberOfItems?: number; /** * A function that receives the new author value. - * If this is not specified, the author controls are not included. + * If this prop is not specified, the author controls are not included. */ onAuthorChange?: AuthorSelectProps[ 'onChange' ]; /** * A function that receives the new number of items value. - * If this is not specified, then the number of items + * If this prop is not specified, then the number of items * range control is not included. */ onNumberOfItemsChange?: ( newNumber?: number ) => void; /** * A function that receives the new order value. - * If this or onOrderByChange are not specified, + * If this prop or the `onOrderByChange` prop are not specified, * then the order controls are not included. */ onOrderChange?: ( newOrder: Order ) => void; /** * A function that receives the new orderby value. - * If this or onOrderChange are not specified, + * If this prop or the `onOrderChange` prop are not specified, * then the order controls are not included. */ onOrderByChange?: ( newOrderBy: OrderBy ) => void; @@ -107,17 +106,17 @@ type BaseQueryControlsProps = { export type QueryControlsWithSingleCategorySelectionProps = BaseQueryControlsProps & { /** - * An array of categories, renders a `CategorySelect` - * sub-component when passed in conjunction with `onCategoryChange`. + * An array of categories, renders UI that allows selecting one category at + * a time when passed in conjunction with the `onCategoryChange` prop. */ categoriesList?: CategorySelectProps[ 'categoriesList' ]; /** - * The selected category for the `categoriesList`. + * The selected category for the `categoriesList` prop. */ selectedCategoryId?: CategorySelectProps[ 'selectedCategoryId' ]; /** * A function that receives the new category value. - * If this is not specified, the category controls are not included. + * If this prop is not specified, the category controls are not included. */ onCategoryChange?: CategorySelectProps[ 'onChange' ]; }; @@ -125,14 +124,12 @@ export type QueryControlsWithSingleCategorySelectionProps = export type QueryControlsWithMultipleCategorySelectionProps = BaseQueryControlsProps & { /** - * An array of category names, renders a `FormTokenField` component - * when passed in conjunction with `onCategoryChange`. + * An array of category names, renders UI that enables multiple selection + * when passed in conjunction with the `onCategoryChange` prop. */ - categorySuggestions?: { - [ categoryName: Category[ 'name' ] ]: Category; - }; + categorySuggestions?: Record< Category[ 'name' ], Category >; /** - * The selected categories for the `categorySuggestions`. + * The selected categories for the `categorySuggestions` prop. */ selectedCategories?: Category[]; /** From 4aa13756a54888575f9b3e8ab74133485176b052 Mon Sep 17 00:00:00 2001 From: Ryan Kienstra Date: Wed, 25 Jan 2023 12:52:04 -0600 Subject: [PATCH 54/54] Marco's update to types.ts and README.md https://github.com/WordPress/gutenberg/pull/46721#discussion_r1086459574 --- .../components/src/query-controls/README.md | 44 +++++++++---------- .../components/src/query-controls/types.ts | 26 ++++++----- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/packages/components/src/query-controls/README.md b/packages/components/src/query-controls/README.md index 313244f12ff4cc..c34aae6f4217cd 100644 --- a/packages/components/src/query-controls/README.md +++ b/packages/components/src/query-controls/README.md @@ -122,34 +122,34 @@ const MyQueryControls = () => { }; ``` -The format of the categories list also needs to be updated to match what `FormTokenField` expects for making suggestions. +The format of the categories list also needs to be updated to match the expected type for the category suggestions. ### Props -#### `authorList`: `AuthorSelectProps[ 'authorList' ]` +#### `authorList`: `Author[]` -An array of authors that is passed into an `AuthorSelect` sub-component. +An array of the authors to select from. - Required: No - Platform: Web -#### `categoriesList`: `CategorySelectProps[ 'categoriesList' ]` +#### `categoriesList`: `Category[]` -An array of categories, renders a `CategorySelect` sub-component when passed in conjunction with `onCategoryChange`. +An array of categories. When passed in conjunction with the `onCategoryChange` prop, it causes the component to render UI that allows selecting one category at a time. - Required: No - Platform: Web #### `categorySuggestions`: `Record< Category[ 'name' ], Category >` -An object of categories, renders a `FormTokenField` component when passed in conjunction with `onCategoryChange`. +An object of categories with the category name as the key. When passed in conjunction with the `onCategoryChange` prop, it causes the component to render UI that enables multiple selection. - Required: No - Platform: Web #### `maxItems`: `number` -The maximum of items. +The maximum number of items. - Required: No - Default: 100 @@ -157,7 +157,7 @@ The maximum of items. #### `minItems`: `number` -The minimum of items. +The minimum number of items. - Required: No - Default: 1 @@ -170,56 +170,56 @@ The selected number of items to retrieve via the query. - Required: No - Platform: Web -#### `onAuthorChange`: `AuthorSelectProps[ 'onChange' ]` +#### `onAuthorChange`: `( newAuthor: string ) => void` -A function that receives the new author value. If this is not specified, the author controls are not included. +A function that receives the new author value. If not specified, the author controls are not rendered. - Required: No - Platform: Web #### `onCategoryChange`: `CategorySelectProps[ 'onChange' ] | FormTokenFieldProps[ 'onChange' ]` -A function that receives the new category value. If this is not specified, the category controls are not included. +A function that receives the new category value. If not specified, the category controls are not rendered. - Required: No - Platform: Web #### `onNumberOfItemsChange`: `( newNumber?: number ) => void` -A function that receives the new number of items value. If this is not specified, then the number of items range control is not included. +A function that receives the new number of items. If not specified, then the number of items range control is not rendered. - Required: No - Platform: Web -#### `onOrderChange`: `( newOrder: Order ) => void` +#### `onOrderChange`: `( newOrder: 'asc' | 'desc' ) => void` -A function that receives the new order value. If this or onOrderByChange are not specified, then the order controls are not included. +A function that receives the new order value. If this prop or the `onOrderByChange` prop are not specified, then the order controls are not rendered. - Required: No - Platform: Web -#### `onOrderByChange`: `( newOrderBy: OrderBy ) => void` +#### `onOrderByChange`: `( newOrderBy: 'date' | 'title' ) => void` -A function that receives the new orderby value. If this or onOrderChange are not specified, then the order controls are not included. +A function that receives the new orderby value. If this prop or the `onOrderChange` prop are not specified, then the order controls are not rendered. - Required: No - Platform: Web -#### `order`: `Order` +#### `order`: `'asc' | 'desc'` The order in which to retrieve posts. - Required: No - Platform: Web -#### `orderBy`: `OrderBy` +#### `orderBy`: `'date' | 'title'` The meta key by which to order posts. - Required: No - Platform: Web -#### `selectedAuthorId`: `AuthorSelectProps[ 'selectedAuthorId' ]` +#### `selectedAuthorId`: `number` The selected author ID. @@ -228,14 +228,14 @@ The selected author ID. #### `selectedCategories`: `Category[]` -The selected categories for the `categorySuggestions`. +The selected categories for the `categorySuggestions` prop. - Required: No - Platform: Web -#### `selectedCategoryId`: `CategorySelectProps[ 'selectedCategoryId' ]` +#### `selectedCategoryId`: `number` -The selected category for the `categoriesList`. +The selected category for the `categoriesList` prop. - Required: No - Platform: Web diff --git a/packages/components/src/query-controls/types.ts b/packages/components/src/query-controls/types.ts index a17f33cdf51009..63bbfeaa5a0aac 100644 --- a/packages/components/src/query-controls/types.ts +++ b/packages/components/src/query-controls/types.ts @@ -68,25 +68,25 @@ type BaseQueryControlsProps = { numberOfItems?: number; /** * A function that receives the new author value. - * If this prop is not specified, the author controls are not included. + * If not specified, the author controls are not rendered. */ onAuthorChange?: AuthorSelectProps[ 'onChange' ]; /** - * A function that receives the new number of items value. - * If this prop is not specified, then the number of items - * range control is not included. + * A function that receives the new number of items. + * If not specified, then the number of items + * range control is not rendered. */ onNumberOfItemsChange?: ( newNumber?: number ) => void; /** * A function that receives the new order value. * If this prop or the `onOrderByChange` prop are not specified, - * then the order controls are not included. + * then the order controls are not rendered. */ onOrderChange?: ( newOrder: Order ) => void; /** * A function that receives the new orderby value. * If this prop or the `onOrderChange` prop are not specified, - * then the order controls are not included. + * then the order controls are not rendered. */ onOrderByChange?: ( newOrderBy: OrderBy ) => void; /** @@ -106,8 +106,9 @@ type BaseQueryControlsProps = { export type QueryControlsWithSingleCategorySelectionProps = BaseQueryControlsProps & { /** - * An array of categories, renders UI that allows selecting one category at - * a time when passed in conjunction with the `onCategoryChange` prop. + * An array of categories. When passed in conjunction with the + * `onCategoryChange` prop, it causes the component to render UI that allows + * selecting one category at a time. */ categoriesList?: CategorySelectProps[ 'categoriesList' ]; /** @@ -116,7 +117,7 @@ export type QueryControlsWithSingleCategorySelectionProps = selectedCategoryId?: CategorySelectProps[ 'selectedCategoryId' ]; /** * A function that receives the new category value. - * If this prop is not specified, the category controls are not included. + * If not specified, the category controls are not rendered. */ onCategoryChange?: CategorySelectProps[ 'onChange' ]; }; @@ -124,8 +125,9 @@ export type QueryControlsWithSingleCategorySelectionProps = export type QueryControlsWithMultipleCategorySelectionProps = BaseQueryControlsProps & { /** - * An array of category names, renders UI that enables multiple selection - * when passed in conjunction with the `onCategoryChange` prop. + * An object of categories with the category name as the key. When passed in + * conjunction with the `onCategoryChange` prop, it causes the component to + * render UI that enables multiple selection. */ categorySuggestions?: Record< Category[ 'name' ], Category >; /** @@ -134,7 +136,7 @@ export type QueryControlsWithMultipleCategorySelectionProps = selectedCategories?: Category[]; /** * A function that receives the new category value. - * If this is not specified, the category controls are not included. + * If not specified, the category controls are not rendered. */ onCategoryChange?: FormTokenFieldProps[ 'onChange' ]; };