Skip to content

CustomSelectControl: Use key to determine the selected item instead of name#72189

Merged
arthur791004 merged 6 commits intotrunkfrom
fix/custom-select-active-item
Nov 7, 2025
Merged

CustomSelectControl: Use key to determine the selected item instead of name#72189
arthur791004 merged 6 commits intotrunkfrom
fix/custom-select-active-item

Conversation

@arthur791004
Copy link
Copy Markdown
Contributor

@arthur791004 arthur791004 commented Oct 9, 2025

What?

If the options have duplicate name, then all of them will be shown as the selected item when any of them is selected. For example, if you have a list of the pages as below, and you want to use the CustomSelectControl, then it makes the result becomes weird.

const options = [
  {
    key: 0, // page.id
    name: 'Home', // page.title
  },
  {
    key: 1, // page.id
    name: 'Home', // page.title
  },
];

Why?

The "key" property should be the unique value in the options. As a result, we should use "key" to determine whether the item is selected to ensure there is only one selected item. Otherwise, if a list has options with the same name, all of them will be shown as selected item.

How?

Replace any condition that checks the selected item using its name property so that it instead uses the key property. The onChange event now passes the entire selectedItem, ensuring backward compatibility with no breaking changes

Testing Instructions

Storybook

  • Navigate to CustomSelectControl
  • Select different item
  • Ensure it works as before

Editor

  • Open a post
  • Select any block
  • Change the font
  • Ensure it works as before

Testing Instructions for Keyboard

Screenshots or screencast

Before After
image image

@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 9, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: arthur791004 <arthur791004@git.wordpress.org>
Co-authored-by: aduth <aduth@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@arthur791004 arthur791004 added the [Type] Bug An existing feature does not function as intended label Oct 9, 2025
@t-hamano t-hamano added the [Package] Components /packages/components label Oct 9, 2025
@arthur791004 arthur791004 force-pushed the fix/custom-select-active-item branch from 131df7a to 416d16c Compare October 9, 2025 13:25
@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 9, 2025

Flaky tests detected in 0bec13d.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/19122253270
📝 Reported issues:

@mirka mirka requested a review from a team November 1, 2025 04:01
Copy link
Copy Markdown
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change makes sense to me 👍 It's also more consistent with documented examples. It might be nice to include a regression test for what we're trying to fix here.

Comment on lines -191 to -193
renderSelectedValue={
showSelectedHint ? renderSelectedValueHint : undefined
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we change the logic here? It seemed like this was a simplification to avoid rendering the <Styled.SelectedExperimentalHintWrapper> wrapper if we don't intend to show the selected hint?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now need the render function because the value becomes the key of the options. I pushed changes to avoid rendering the wrapper by a0f4a6e.

<VisuallyHidden>
<span id={ descriptionId }>
{ getDescribedBy( currentValue, describedBy ) }
{ getDescribedBy( selectedOption.name, describedBy ) }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: The function argument is still named currentValue even though we're passing the name here, which could be confusing with how we're now associating "value" with "key" elsewhere (and key not being the same as name).

function getDescribedBy( currentValue: string, describedBy?: string ) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to currentName by 516f9d1

@arthur791004 arthur791004 force-pushed the fix/custom-select-active-item branch from 416d16c to a0f4a6e Compare November 4, 2025 06:07
{ currentValue }
{ selectedOptionHint && (
{ selectedOption.name }
{ showSelectedHint && selectedOption.hint && (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the changes in 516f9d1, this code could never be reached without the condition being satisfied, right?

Suggested change
{ showSelectedHint && selectedOption.hint && (

Copy link
Copy Markdown
Contributor Author

@arthur791004 arthur791004 Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to remove it 🤦‍♂️ Done by 434b6c7.

@arthur791004 arthur791004 force-pushed the fix/custom-select-active-item branch from a04c490 to 434b6c7 Compare November 5, 2025 04:48

const renderSelectedValue = () => {
if ( ! showSelectedHint || ! selectedOption.hint ) {
return selectedOption.name;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised that TypeScript doesn't raise an issue here, since I think selectedOption could be undefined if the options array is empty (i.e. options[ 0 ] would be undefined if options is an empty array). It'd be pretty unconventional usage, but we're doing safe property access above, so maybe we should here as well:

defaultValue: options[ 0 ]?.key,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, you're right! Fixed by 0bec13d.

This is because:

  • For an array type T[], TypeScript defines the indexed access type T[]["0"] as simply T.
  • It doesn't try to analyze if the array could be empty at runtime.
  • So it trusts that "options is an array of T, so options[0] is of type T".

We have to enable below to force TS raises the value is probably undefined.

{
  "compilerOptions": {
    "noUncheckedIndexedAccess": true
  }
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice sleuthing, that makes sense.

I think that TypeScript option could make sense to enable, though I might fear it could introduce a whole bunch of new issues to work through.

Copy link
Copy Markdown
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@arthur791004 arthur791004 merged commit 400815b into trunk Nov 7, 2025
39 checks passed
@arthur791004 arthur791004 deleted the fix/custom-select-active-item branch November 7, 2025 02:55
@arthur791004
Copy link
Copy Markdown
Contributor Author

Thanks for the review!

@github-actions github-actions bot added this to the Gutenberg 22.1 milestone Nov 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Components /packages/components [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants