Skip to content

Dropdown improvements #932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Dropdown improvements #932

wants to merge 1 commit into from

Conversation

clintcs
Copy link
Collaborator

@clintcs clintcs commented Jun 6, 2025

🚀 Description

Another round of Dropdown bugfixes before I move on to generalizing Menu:

Minor

Dropdown Option's selected attribute is no longer reflected to match native and to resolve a bug when reset() is called on its form. This may be a breaking change for you if you have a mutation observer or CSS selector that targets selected.

Patch

Dropdown

  • The tag overflow button is now updated when a selected Dropdown Option is enabled or disabled.

  • Dropdown now reverts its value and Dropdown Options revert back to their initial selected state when reset() is called on the form.

  • Selected Dropdown Options whose value is an empty string now appear as selected.

  • Only the last selected option is presented to screenreaders as selected when single-select and multiple options are initially selected.

  • Tags now appear in the order that Dropdown Options are selected by the user.

  • Removing a tag via Enter no longer submits the form.

  • Disabled Dropdown Options are now enabled when selected programmatically.

  • When a Dropdown Option is disabled programmatically and more than one selected Dropdown Option has the same value, Dropdown now only removes the value corresponding to the disabled Dropdown Option from value.

  • When value is set programmatically, only the first Dropdown Option with a matching value is now selected instead of every Dropdown Option with a matching value.

  • Dropdown's input field is now populated with the label of the selected Dropdown Option when filterable and multiple is set to false programmatically.

Tab Group

  • The trailing overflow button is no longer incorrectly enabled in a rare case.

Additionally:

  • Filled in test and assertion gaps.
  • Added and improved code comments.

📋 Checklist

  • I have followed the Contributing Guidelines.
  • I have added tests to cover new or updated functionality.
  • I have added or updated Storybook stories.
  • I have localized new strings.
  • I have followed the ARIA Authoring Practices Guide or met with the Accessibility Team.
  • I have included a changeset.
  • I have scheduled a design review.
  • I have reviewed the Storybook and Visual Test Report links below.

🔬 Testing

Dropdown Option's selected attribute is no longer reflected to match native and to resolve a bug when reset() is called on its form

Copy link

changeset-bot bot commented Jun 6, 2025

🦋 Changeset detected

Latest commit: f6f7fd4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@crowdstrike/glide-core Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

github-actions bot commented Jun 6, 2025

Copy link
Contributor

github-actions bot commented Jun 6, 2025

@clintcs clintcs force-pushed the dropdown-tag-order branch 2 times, most recently from 10f0242 to 120c62d Compare June 6, 2025 19:20
@clintcs clintcs changed the title Show Dropdown tags in the order they are added Show Dropdown tags in the order that options are selected Jun 6, 2025
@clintcs clintcs force-pushed the dropdown-tag-order branch 12 times, most recently from 53fc9e6 to b49492f Compare June 9, 2025 15:58
@clintcs clintcs changed the title Show Dropdown tags in the order that options are selected More Dropdown bugfixes Jun 9, 2025
@clintcs clintcs force-pushed the dropdown-tag-order branch 8 times, most recently from f976acf to 9c220e1 Compare June 9, 2025 21:24

it('can be reset', async () => {
it('can be reset after options are selected via click', async () => {
Copy link
Collaborator Author

@clintcs clintcs Jun 10, 2025

Choose a reason for hiding this comment

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

Renamed to accommodate "can be reset after options are selected programmatically" below.

This test was falsely passing before because Dropdown wasn't opened before its options were clicked.

expect(host.value).to.deep.equal(['one', 'two']);
expect(tags?.length).to.equal(2);
expect(tags?.[0]?.label).to.equal('One');
expect(tags?.[1]?.label).to.equal('Two');
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added tag assertions throughout.

@@ -170,9 +170,6 @@ export default class DropdownOption extends LitElement {
this.#checkboxElementRef.value.checked = isSelected;
}

// Prefixed with "private" because Dropdown uses it internally, to track the set of
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This isn't actually how Dropdown uses this event. I started rewriting the comment then realized that it didn't add much given it's only used by one method: #onOptionsSelectedChange().

@@ -35,12 +35,13 @@ it('is accessible', async () => {
await expect(host).to.be.accessible();
});

it('sets `value` to that of its selected options', async () => {
it('can have selected options', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Renamed this test to accommodate more assertions.

@clintcs clintcs force-pushed the dropdown-tag-order branch 2 times, most recently from f66e20c to 27cfdc5 Compare June 10, 2025 11:56
@@ -367,3 +342,56 @@ it('does not include in its `value` disabled options that are selected', async (

expect(host.value).to.deep.equal([]);
});

it('only selects the first option with a matching value when multiple options have the same value and `value` is set', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When value is set programmatically, only the first Dropdown Option with a matching value is now selected instead of every Dropdown Option with a matching value.

@clintcs clintcs changed the title More Dropdown bugfixes Dropdown improvements Jun 10, 2025
@@ -12,22 +12,40 @@ it('is accessible ', async () => {
await expect(host).to.be.accessible();
});

it('has a selected option label when an option is initially selected', async () => {
it('can have a selected option', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Renamed this test to accommodate more assertions.

@@ -171,3 +264,33 @@ it('has no `formData` value when an option is selected that has no `value`', asy
const formData = new FormData(form);
expect(formData.get('name')).to.be.null;
});

it('does not submit its form when a tag is removed via Enter', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removing a tag via Enter no longer submits the form


it('can be reset', async () => {
it('can be reset after an option is selected via click', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Renamed to accommodate "can be reset after an option is selected programmatically" below.

expect(host.value).to.deep.equal(['one']);
});

it('can be reset after an option is deselected via click', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Gaps.

@@ -560,7 +560,6 @@ it('does not clear its filter when a tag is removed via Backspace', async () =>
</glide-core-dropdown>`,
);

await aTimeout(0); // Wait for Floating UI
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No need to wait for Floating UI. Awaiting sendKeys() is enough.

@@ -782,6 +779,46 @@ it('clears its input field when made multiselect programmatically', async () =>
expect(input?.value).to.be.empty.string;
});

it('updates itself made single-select programmatically and an option is selected', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Dropdown's input field is now populated with the label of the selected Dropdown Option when filterable and multiple is set to false programmatically.

@@ -1960,7 +1997,7 @@ it('shows an ellipsis when the label of its selected option is set programmatica
option.label = 'x'.repeat(500);
await host.updateComplete;

// Wait for the resize observer to do its thing.
// Wait for the Resize Observer to do its thing.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Doesn't make a different to me. Just made it the same as it is everywhere else.

@@ -77,21 +78,25 @@ it('selects options on click via mouse', async () => {

await aTimeout(0); // Wait for Floating UI
await click(options[0]);
await click(options[1]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Selecting multiple options doesn't test anything additional.

});

it('selects no options when `value` is set programmatically to an empty string and some options have no `value`', async () => {
it('only selects the first option when `value` is set programmatically and multiple options have the same `value`', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When value is set programmatically, only the first Dropdown Option with a matching value is now selected instead of every Dropdown Option with a matching value

@@ -848,39 +946,6 @@ it('deselects an option when its tag is removed', async () => {
expect(host.value).to.deep.equal(['two']);
});

it('deselects all but the last selected option when `multiple` is changed to `false` programmatically', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved to dropdown.test.interactions.single.ts next to the other tests that test changing from multiselect to single-select.

@clintcs clintcs force-pushed the dropdown-tag-order branch 2 times, most recently from 98cfa84 to be05e07 Compare June 10, 2025 12:10
expect(tagContainers?.length).to.equal(2);
});

it('has overflow text when its tags are overflowing', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Combined this test and the one above.

@@ -1436,77 +1460,42 @@ it('clicks its primary button when `click()` is called', async () => {
expect(event instanceof PointerEvent).to.be.true;
});

it('adds the `value` of a programmatically enabled option to its `value`', async () => {
it('updates itself when a selected option is disabled programmatically', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Renamed to accommodate more assertions.

@clintcs clintcs force-pushed the dropdown-tag-order branch from be05e07 to b9aa414 Compare June 10, 2025 12:20
});

it('sets `aria-selected` when an option is deselected programmatically', async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hard to tell from the diff. I removed this test and added its assertion to "deselects an option when it is deselected programmatically". Similar for the tests below.

@clintcs clintcs force-pushed the dropdown-tag-order branch from b9aa414 to f6f7fd4 Compare June 10, 2025 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant