Skip to content

[Controls] Bulk select for options list control #221010

Merged
cqliu1 merged 36 commits intoelastic:mainfrom
cqliu1:controls/options-list/select-deselect-all
Jun 26, 2025
Merged

[Controls] Bulk select for options list control #221010
cqliu1 merged 36 commits intoelastic:mainfrom
cqliu1:controls/options-list/select-deselect-all

Conversation

@cqliu1
Copy link
Contributor

@cqliu1 cqliu1 commented May 20, 2025

Summary

Closes #181694.
Closes #222121.

This adds Select all and Deselect all buttons to the options list popover to allow users to make bulk selections. Bulk selections will only be available when there are 100 or fewer available options.

Screenshot 2025-05-29 at 10 18 28 AM

Disabled bulk select

Bulk selection is disabled when there are no available options.
Screenshot 2025-05-23 at 2 38 05 PM

Bulk selection is also disabled when total cardinality is over 100.

Screenshot 2025-05-29 at 10 29 33 AM

When showOnlySelected is enabled

Even if the total cardinality is greater than the bulk selection limit, bulk selection is enabled when showOnlySelected is enabled, and the number of selection options is fewer than the bulk selection limit (e.g. fewer than 100 selections). Per feedback, we opted to disabled bulk selection when this is enabled.

Checklist

Check the PR satisfies following conditions.

Reviewers should verify this PR satisfies this list as well.

  • Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support
  • Documentation was added for features that require explanation or tutorials
  • Unit or functional tests were updated or added to match the most common scenarios
  • If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the docker list
  • This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The release_note:breaking label should be applied in these situations.
  • Flaky Test Runner was used on any tests changed
  • The PR description includes the appropriate Release Notes section, and the correct release_note:* label is applied per the guidelines

Identify risks

Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging.

@cqliu1 cqliu1 force-pushed the controls/options-list/select-deselect-all branch from d036377 to 812c7ee Compare May 21, 2025 17:30
@kibanamachine
Copy link
Contributor

Cloud deployments require a Github label, please add ci:cloud-deploy or ci:cloud-redeploy and trigger the job through the checkbox again.

@cqliu1 cqliu1 force-pushed the controls/options-list/select-deselect-all branch from d0646ff to 263c283 Compare May 23, 2025 19:01
@cqliu1 cqliu1 changed the title [Controls] Select/deselect all for options list control May 23, 2025
@cqliu1 cqliu1 changed the title [Controls] Bulk select all for options list control May 23, 2025
@cqliu1 cqliu1 marked this pull request as ready for review May 23, 2025 22:19
@cqliu1 cqliu1 requested a review from a team as a code owner May 23, 2025 22:19
@cqliu1 cqliu1 added Feature:Dashboard Dashboard related features Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas t// loe:medium Medium Level of Effort impact:high Addressing this issue will have a high level of impact on the quality/strength of our product. Project:Controls v9.1.0 v8.19.0 labels May 23, 2025
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-presentation (Team:Presentation)

@cqliu1 cqliu1 added release_note:feature Makes this part of the condensed release notes release_note:enhancement backport:version Backport to applied version labels and removed release_note:feature Makes this part of the condensed release notes labels May 23, 2025
cqliu1 added 2 commits May 27, 2025 07:34
Fix select and deselect logic

Fix types

Fetch more options on select/deselect all

Add loadMoreOptions callback

Add const for bulk selection limit

Add i18n

Remove unused import

Handle showOnlySelected with bulk selection

Add options list action bar tests

Remove console log

Clean up code

Added test
@cqliu1 cqliu1 force-pushed the controls/options-list/select-deselect-all branch from bd338d9 to 080d7d7 Compare May 27, 2025 14:42
@Heenawter Heenawter self-requested a review May 27, 2025 16:27
@cqliu1 cqliu1 added the ci:cloud-deploy Create or update a Cloud deployment label May 27, 2025
Comment on lines 142 to 173
<EuiToolTip
content={
hasTooManyOptions
? OptionsListStrings.popover.getMaximumBulkSelectionTooltip()
: undefined
}
>
<EuiButtonEmpty
size="xs"
disabled={isBulkSelectDisabled}
data-test-subj="optionsList-control-selectAll"
onClick={() => handleBulkAction(componentApi.selectAll)}
>
{OptionsListStrings.popover.getSelectAllButtonLabel()}
</EuiButtonEmpty>
</EuiToolTip>
<EuiToolTip
content={
hasTooManyOptions
? OptionsListStrings.popover.getMaximumBulkSelectionTooltip()
: undefined
}
>
<EuiButtonEmpty
size="xs"
disabled={isBulkSelectDisabled}
data-test-subj="optionsList-control-deselectAll"
onClick={() => handleBulkAction(componentApi.deselectAll)}
>
{OptionsListStrings.popover.getDeselectAllButtonLabel()}
</EuiButtonEmpty>
</EuiToolTip>
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we reduce the space here so that there is equal space on both sides of the |?

Screenshot 2025-05-27 at 11 37 31 AM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, by switching to EuiLink component instead, it renders more like EuiText. I opted to add | between select and deselect bc they looked almost like a single link.
Screenshot 2025-05-29 at 10 18 28 AM

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we wait on @elastic/kibana-design for this one 😆 The double | feels a bit busy to me and I would prefer just adding some padding between "select all" and "deselect all" (about the same size as the gap with the |, but without actually rendering the | if that makes sense)... But I'm not sure

Comment on lines 142 to 173
<EuiToolTip
content={
hasTooManyOptions
? OptionsListStrings.popover.getMaximumBulkSelectionTooltip()
: undefined
}
>
<EuiButtonEmpty
size="xs"
disabled={isBulkSelectDisabled}
data-test-subj="optionsList-control-selectAll"
onClick={() => handleBulkAction(componentApi.selectAll)}
>
{OptionsListStrings.popover.getSelectAllButtonLabel()}
</EuiButtonEmpty>
</EuiToolTip>
<EuiToolTip
content={
hasTooManyOptions
? OptionsListStrings.popover.getMaximumBulkSelectionTooltip()
: undefined
}
>
<EuiButtonEmpty
size="xs"
disabled={isBulkSelectDisabled}
data-test-subj="optionsList-control-deselectAll"
onClick={() => handleBulkAction(componentApi.deselectAll)}
>
{OptionsListStrings.popover.getDeselectAllButtonLabel()}
</EuiButtonEmpty>
</EuiToolTip>
Copy link
Contributor

@Heenawter Heenawter May 27, 2025

Choose a reason for hiding this comment

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

I wonder if we could make this work a little better for the min popover size (300px)? Ideally, I would want it constrained to a single line for this size, at least in English... But if we can't (since I know this will change depending on the length of the cardinality string), I would hope that we could at least make the two line layout look a bit more... intentional 😆 i.e. keeping "select all" + "deselect all" grouped, or using icons, or... something.

Min size Larger
image image
Copy link
Contributor Author

@cqliu1 cqliu1 May 29, 2025

Choose a reason for hiding this comment

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

Screenshot 2025-05-29 at 12 00 36 PM

Here I removed the | and I use a euiSizeS space between Select all and Deselect All.

Comment on lines 99 to 104

if (totalCardinality > availableOptions.length) {
const newAvailableOptions = (await loadMoreOptions()) ?? [];
bulkAction(newAvailableOptions.map(({ value }) => value as string));
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we need a more obvious loading state for this?

May-27-2025 15-26-12

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it be sufficient to just disable select/deselect buttons while the control is loading? I don't think we need any additional loading indicators since we have the spinner in the base control and the loading bar at the bottom of the popover.

Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it would be possible to add the following "loading icon" indicator beside each option (i.e. where the "checked" status usually is) as soon as "select all" is clicked in? This gives a better indication for "we are waiting to make the selections" to me...

May-29-2025 12-17-45

If not then yeah, maybe just disabling the "select all / deselect all" buttons would be sufficient

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm pretty sure that loading indicator is just the Eui icon being async imported - but maybe we could checkmark the visible ones right away - though that might make the code uglier.

Copy link
Contributor

Choose a reason for hiding this comment

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

m pretty sure that loading indicator is just the Eui icon being async imported

I know! I just don't love how we have two consecutive loading states happening, so if we could simulate the same "loading" state, I think this would help with the feeling of "I clicked this button but nothing is happening!"

)}
options={selectableOptions}
listProps={{ onFocusBadge: false, isVirtualized: false }}
listProps={{ onFocusBadge: false }}
Copy link
Contributor Author

@cqliu1 cqliu1 Jun 20, 2025

Choose a reason for hiding this comment

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

By enable virtualization in the EuiSelectable component in the invalid selections component, we get a list with a fixed height with scrolling enabled similar to the list of available options above it. This fixes the issue described in #222121.

Jun-20-2025.16-34-52.mp4
Copy link
Contributor

@andreadelrio andreadelrio left a comment

Choose a reason for hiding this comment

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

Design changes LGTM, nice job!

{ value: 'toot', docCount: 11 },
];

const renderComponent = ({
Copy link
Contributor

@mbondyra mbondyra Jun 26, 2025

Choose a reason for hiding this comment

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

I added some edits to these tests to make it more user-focused, like using selectors like getByRole, not getByTestId. or using get selectors and not query selectors (query should only be used when checking for non-existance, since they have worse messaging)

@elasticmachine
Copy link
Contributor

elasticmachine commented Jun 26, 2025

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
controls 470.3KB 472.5KB +2.2KB

History

@cqliu1 cqliu1 merged commit 749aeb7 into elastic:main Jun 26, 2025
10 checks passed
@kibanamachine
Copy link
Contributor

Starting backport for target branches: 8.19

https://github.com/elastic/kibana/actions/runs/15907258379

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting backport:version Backport to applied version labels ci:cloud-deploy Create or update a Cloud deployment Feature:Dashboard Dashboard related features impact:high Addressing this issue will have a high level of impact on the quality/strength of our product. loe:medium Medium Level of Effort needs_docs Project:Controls release_note:enhancement Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas t// v9.2.0

8 participants