From 4efb352e8b3e84daf69d02209d45cc000c4a2efa Mon Sep 17 00:00:00 2001 From: Ryan Welcher Date: Tue, 6 Aug 2024 15:45:58 -0400 Subject: [PATCH 001/205] Update data-core.md (#64309) Fix heading level causing issues with sidebar menu --- docs/reference-guides/data/data-core.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index f4138c49dc8d6b..f08fbc960b8b28 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -2,7 +2,7 @@ Namespace: `core`. -### Dynamically generated selectors +## Dynamically generated selectors There are a number of user-friendly selectors that are wrappers of the more generic `getEntityRecord` and `getEntityRecords` that can be used to retrieve information for the various entities. From 0f81a4262f394139b411035b4db3a29c938b0947 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Wed, 7 Aug 2024 05:57:06 +0900 Subject: [PATCH 002/205] Add margin-bottom lint rules for ToggleControl (#64213) * Fix in ImageSettingsPanel * Fix in block hooks * Fix in Avatar block * Fix in Details block * Fix in Latest posts block * Fix in Query Loop block * Fix in Tag Cloud block * Fix in Add New Pattern modal * Fix in Create pattern modal * Fix in ReusableBlockConvertButton * Add eslint rule * Add prop to docs * Fix up Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: tyxla --- .eslintrc.js | 1 + .../src/components/global-styles/image-settings-panel.js | 1 + packages/block-editor/src/hooks/block-hooks.js | 1 + packages/block-library/src/avatar/edit.js | 1 + packages/block-library/src/details/edit.js | 1 + packages/block-library/src/latest-posts/edit.js | 1 + .../inspector-controls/enhanced-pagination-control.js | 1 + packages/block-library/src/tag-cloud/edit.js | 1 + packages/components/src/toggle-control/README.md | 9 +++++++++ packages/components/src/toggle-control/index.tsx | 1 + .../edit-post/src/components/init-pattern-modal/index.js | 1 + packages/patterns/src/components/create-pattern-modal.js | 1 + .../reusable-block-convert-button.js | 1 + 13 files changed, 21 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index 97c9f395194f35..cb132fbb3d7aba 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -297,6 +297,7 @@ module.exports = { 'SearchControl', 'TextControl', 'TextareaControl', + 'ToggleControl', 'ToggleGroupControl', 'TreeSelect', ].map( ( componentName ) => ( { diff --git a/packages/block-editor/src/components/global-styles/image-settings-panel.js b/packages/block-editor/src/components/global-styles/image-settings-panel.js index f668e7e5efc244..4ebc20ab201983 100644 --- a/packages/block-editor/src/components/global-styles/image-settings-panel.js +++ b/packages/block-editor/src/components/global-styles/image-settings-panel.js @@ -67,6 +67,7 @@ export default function ImageSettingsPanel( { panelId={ panelId } > { attributes.isLink && ( setAttributes( { diff --git a/packages/block-library/src/details/edit.js b/packages/block-library/src/details/edit.js index 3432c00d7ab0c5..314556ba6d5919 100644 --- a/packages/block-library/src/details/edit.js +++ b/packages/block-library/src/details/edit.js @@ -48,6 +48,7 @@ function DetailsEdit( { attributes, setAttributes, clientId } ) { diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index ce8f75c671c833..49a24b08f68d77 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -202,6 +202,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { diff --git a/packages/block-library/src/query/edit/inspector-controls/enhanced-pagination-control.js b/packages/block-library/src/query/edit/inspector-controls/enhanced-pagination-control.js index 293baead3f5c62..e1001cda4dab39 100644 --- a/packages/block-library/src/query/edit/inspector-controls/enhanced-pagination-control.js +++ b/packages/block-library/src/query/edit/inspector-controls/enhanced-pagination-control.js @@ -36,6 +36,7 @@ export default function EnhancedPaginationControl( { return ( <> diff --git a/packages/components/src/toggle-control/README.md b/packages/components/src/toggle-control/README.md index 3a77ef6942d2e5..cf480360265ed4 100644 --- a/packages/components/src/toggle-control/README.md +++ b/packages/components/src/toggle-control/README.md @@ -15,6 +15,7 @@ const MyToggleControl = () => { return ( setValue( ( state ) => ! state ) } diff --git a/packages/edit-post/src/components/init-pattern-modal/index.js b/packages/edit-post/src/components/init-pattern-modal/index.js index a40ffc5dc58562..bb448953364f05 100644 --- a/packages/edit-post/src/components/init-pattern-modal/index.js +++ b/packages/edit-post/src/components/init-pattern-modal/index.js @@ -74,6 +74,7 @@ export default function InitPatternModal() { __next40pxDefaultSize /> Date: Wed, 7 Aug 2024 07:08:17 +0900 Subject: [PATCH 003/205] SelectControl: Infer `value` type from `options` (#64069) * SelectControl: Infer `value` type from `options` * Don't infer onChange * Add static type tests Co-authored-by: Mikey Binns * Rename generic to `V` (for `value`) * Add changelog * Simplify return type --------- Co-authored-by: Mikey Binns Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: mikeybinns Co-authored-by: ciampo Co-authored-by: jsnajdr Co-authored-by: tyxla Co-authored-by: DaniGuardiola --- packages/components/CHANGELOG.md | 1 + .../components/src/date-time/time/index.tsx | 35 ++--- .../components/src/query-controls/index.tsx | 6 +- .../components/src/select-control/index.tsx | 20 ++- .../select-control/test/select-control.tsx | 125 ++++++++++++++++++ .../components/src/select-control/types.ts | 118 +++++++++-------- 6 files changed, 226 insertions(+), 79 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index a71b5a0ddef35f..8ecbf1528d6f08 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -13,6 +13,7 @@ - `TimeInput`: Expose as subcomponent of `TimePicker` ([#63145](https://github.com/WordPress/gutenberg/pull/63145)). - `RadioControl`: add support for option help text ([#63751](https://github.com/WordPress/gutenberg/pull/63751)). +- `SelectControl`: Infer `value` type from `options` ([#64069](https://github.com/WordPress/gutenberg/pull/64069)). - `Guide`: Add `__next40pxDefaultSize` to buttons ([#64181](https://github.com/WordPress/gutenberg/pull/64181)). - `SelectControl`: Pass through `options` props ([#64211](https://github.com/WordPress/gutenberg/pull/64211)). diff --git a/packages/components/src/date-time/time/index.tsx b/packages/components/src/date-time/time/index.tsx index 809376d99d3036..5f706d69190095 100644 --- a/packages/components/src/date-time/time/index.tsx +++ b/packages/components/src/date-time/time/index.tsx @@ -77,10 +77,28 @@ export function TimePicker( { ); }, [ currentTime ] ); + const monthOptions = [ + { value: '01', label: __( 'January' ) }, + { value: '02', label: __( 'February' ) }, + { value: '03', label: __( 'March' ) }, + { value: '04', label: __( 'April' ) }, + { value: '05', label: __( 'May' ) }, + { value: '06', label: __( 'June' ) }, + { value: '07', label: __( 'July' ) }, + { value: '08', label: __( 'August' ) }, + { value: '09', label: __( 'September' ) }, + { value: '10', label: __( 'October' ) }, + { value: '11', label: __( 'November' ) }, + { value: '12', label: __( 'December' ) }, + ] as const; + const { day, month, year, minutes, hours } = useMemo( () => ( { day: format( date, 'dd' ), - month: format( date, 'MM' ), + month: format( + date, + 'MM' + ) as ( typeof monthOptions )[ number ][ 'value' ], year: format( date, 'yyyy' ), minutes: format( date, 'mm' ), hours: format( date, 'HH' ), @@ -146,20 +164,7 @@ export function TimePicker( { __next40pxDefaultSize __nextHasNoMarginBottom value={ month } - options={ [ - { value: '01', label: __( 'January' ) }, - { value: '02', label: __( 'February' ) }, - { value: '03', label: __( 'March' ) }, - { value: '04', label: __( 'April' ) }, - { value: '05', label: __( 'May' ) }, - { value: '06', label: __( 'June' ) }, - { value: '07', label: __( 'July' ) }, - { value: '08', label: __( 'August' ) }, - { value: '09', label: __( 'September' ) }, - { value: '10', label: __( 'October' ) }, - { value: '11', label: __( 'November' ) }, - { value: '12', label: __( 'December' ) }, - ] } + options={ monthOptions } onChange={ ( value ) => { const newDate = setMonth( date, Number( value ) - 1 ); setDate( newDate ); diff --git a/packages/components/src/query-controls/index.tsx b/packages/components/src/query-controls/index.tsx index de53c63a9b8a82..3557335ebac5a0 100644 --- a/packages/components/src/query-controls/index.tsx +++ b/packages/components/src/query-controls/index.tsx @@ -85,7 +85,11 @@ export function QueryControls( { __next40pxDefaultSize={ __next40pxDefaultSize } key="query-controls-order-select" label={ __( 'Order by' ) } - value={ `${ orderBy }/${ order }` } + value={ + orderBy === undefined || order === undefined + ? undefined + : `${ orderBy }/${ order }` + } options={ [ { label: __( 'Newest to oldest' ), diff --git a/packages/components/src/select-control/index.tsx b/packages/components/src/select-control/index.tsx index 874b6ace1ea949..8a3b6bda68a160 100644 --- a/packages/components/src/select-control/index.tsx +++ b/packages/components/src/select-control/index.tsx @@ -42,8 +42,8 @@ function SelectOptions( { } ); } -function UnforwardedSelectControl( - props: WordPressComponentProps< SelectControlProps, 'select', false >, +function UnforwardedSelectControl< V extends string >( + props: WordPressComponentProps< SelectControlProps< V >, 'select', false >, ref: React.ForwardedRef< HTMLSelectElement > ) { const { @@ -82,12 +82,14 @@ function UnforwardedSelectControl( const selectedOptions = Array.from( event.target.options ).filter( ( { selected } ) => selected ); - const newValues = selectedOptions.map( ( { value } ) => value ); + const newValues = selectedOptions.map( + ( { value } ) => value as V + ); props.onChange?.( newValues, { event } ); return; } - props.onChange?.( event.target.value, { event } ); + props.onChange?.( event.target.value as V, { event } ); }; const classes = clsx( 'components-select-control', className ); @@ -164,6 +166,14 @@ function UnforwardedSelectControl( * }; * ``` */ -export const SelectControl = forwardRef( UnforwardedSelectControl ); +export const SelectControl = forwardRef( UnforwardedSelectControl ) as < + V extends string, +>( + props: WordPressComponentProps< + SelectControlProps< V >, + 'select', + false + > & { ref?: React.Ref< HTMLSelectElement > } +) => React.JSX.Element | null; export default SelectControl; diff --git a/packages/components/src/select-control/test/select-control.tsx b/packages/components/src/select-control/test/select-control.tsx index f2da74d9a6e911..0e8a6891087043 100644 --- a/packages/components/src/select-control/test/select-control.tsx +++ b/packages/components/src/select-control/test/select-control.tsx @@ -100,4 +100,129 @@ describe( 'SelectControl', () => { screen.getByRole( 'option', { name: 'Aria label' } ) ).toBeInTheDocument(); } ); + + /* eslint-disable jest/expect-expect */ + describe( 'static typing', () => { + describe( 'single', () => { + it( 'should infer the value type from available `options`, but not the `value` or `onChange` prop', () => { + const onChange: ( value: 'foo' | 'bar' ) => void = () => {}; + + ; + + value === 'string' } + />; + } ); + + it( 'should accept an explicit type argument', () => { + + // @ts-expect-error "string" is not "narrow" or "value" + value="string" + options={ [ + { + value: 'narrow', + label: 'Narrow', + }, + { + // @ts-expect-error "string" is not "narrow" or "value" + value: 'string', + label: 'String', + }, + ] } + />; + } ); + } ); + + describe( 'multiple', () => { + it( 'should infer the value type from available `options`, but not the `value` or `onChange` prop', () => { + const onChange: ( + value: ( 'foo' | 'bar' )[] + ) => void = () => {}; + + ; + + + // @ts-expect-error "string" is not "narrow" or "value" + value.forEach( ( v ) => v === 'string' ) + } + />; + } ); + + it( 'should accept an explicit type argument', () => { + + multiple + // @ts-expect-error "string" is not "narrow" or "value" + value={ [ 'string' ] } + options={ [ + { + value: 'narrow', + label: 'Narrow', + }, + { + // @ts-expect-error "string" is not "narrow" or "value" + value: 'string', + label: 'String', + }, + ] } + />; + } ); + } ); + } ); + /* eslint-enable jest/expect-expect */ } ); diff --git a/packages/components/src/select-control/types.ts b/packages/components/src/select-control/types.ts index a5d0d740c593cc..4e7211ab9abfb2 100644 --- a/packages/components/src/select-control/types.ts +++ b/packages/components/src/select-control/types.ts @@ -9,7 +9,7 @@ import type { ChangeEvent, ReactNode } from 'react'; import type { InputBaseProps } from '../input-control/types'; import type { BaseControlProps } from '../base-control/types'; -type SelectControlBaseProps = Pick< +type SelectControlBaseProps< V extends string > = Pick< InputBaseProps, | '__next36pxDefaultSize' | '__next40pxDefaultSize' @@ -27,7 +27,7 @@ type SelectControlBaseProps = Pick< * each with a `label` and `value` property, as well as any other * `