Skip to content
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

DataViews: implement multiple selection in filters #56468

Closed
wants to merge 6 commits into from

Conversation

oandregal
Copy link
Member

@oandregal oandregal commented Nov 23, 2023

Part of #55083
Related #55100

What?

Implements multiselection in filters.

Gravacao.do.ecra.2023-11-23.as.09.25.29.mov

Why?

Many fields support being filtered by multiple values at once.

How?

Updates the filter components & the view to query adaptor to take arrays instead of a string for the values.

Testing Instructions

  • Enable the "admin views" experiment and visit "Appareance > Editor > Manage all pages".
  • Interact with the top-level and column filters and verify they work as expected.

TODO / Questions

  • Allow single selection:
    • Field API to disable multiselection in a filter.
    • UI: use radios instead of checks to show checked items in single-selection filters.
  • Should the AddFilter support multiselection as well?
  • Implement "Clear" option to clear all the filter values at once (but doesn't reset/remove the filter from the view).
  • How filters scale as more options are selected. May be done separately, but it becomes urgent.

@oandregal oandregal self-assigned this Nov 23, 2023
@oandregal oandregal added [Type] Experimental Experimental feature or API. [Feature] Data Views Work surrounding upgrading and evolving views in the site editor and beyond labels Nov 23, 2023
Copy link

Size Change: +81 B (0%)

Total Size: 1.7 MB

Filename Size Change
build/edit-site/index.min.js 208 kB +81 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 964 B
build/annotations/index.min.js 2.71 kB
build/api-fetch/index.min.js 2.29 kB
build/autop/index.min.js 2.11 kB
build/blob/index.min.js 590 B
build/block-directory/index.min.js 7.25 kB
build/block-directory/style-rtl.css 1.04 kB
build/block-directory/style.css 1.04 kB
build/block-editor/content-rtl.css 4.29 kB
build/block-editor/content.css 4.28 kB
build/block-editor/default-editor-styles-rtl.css 403 B
build/block-editor/default-editor-styles.css 403 B
build/block-editor/index.min.js 248 kB
build/block-editor/style-rtl.css 15.7 kB
build/block-editor/style.css 15.7 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 138 B
build/block-library/blocks/audio/theme.css 138 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/button/editor-rtl.css 587 B
build/block-library/blocks/button/editor.css 587 B
build/block-library/blocks/button/style-rtl.css 633 B
build/block-library/blocks/button/style.css 632 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 421 B
build/block-library/blocks/columns/style.css 421 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 647 B
build/block-library/blocks/cover/editor.css 650 B
build/block-library/blocks/cover/style-rtl.css 1.7 kB
build/block-library/blocks/cover/style.css 1.69 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 98 B
build/block-library/blocks/details/style.css 98 B
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 138 B
build/block-library/blocks/embed/theme.css 138 B
build/block-library/blocks/file/editor-rtl.css 316 B
build/block-library/blocks/file/editor.css 316 B
build/block-library/blocks/file/style-rtl.css 280 B
build/block-library/blocks/file/style.css 281 B
build/block-library/blocks/file/view.min.js 320 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/form-input/editor-rtl.css 229 B
build/block-library/blocks/form-input/editor.css 228 B
build/block-library/blocks/form-input/style-rtl.css 343 B
build/block-library/blocks/form-input/style.css 343 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 343 B
build/block-library/blocks/form-submission-notification/editor.css 342 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/view.min.js 452 B
build/block-library/blocks/freeform/editor-rtl.css 2.61 kB
build/block-library/blocks/freeform/editor.css 2.61 kB
build/block-library/blocks/gallery/editor-rtl.css 957 B
build/block-library/blocks/gallery/editor.css 962 B
build/block-library/blocks/gallery/style-rtl.css 1.55 kB
build/block-library/blocks/gallery/style.css 1.55 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 654 B
build/block-library/blocks/group/editor.css 654 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 189 B
build/block-library/blocks/heading/style.css 189 B
build/block-library/blocks/html/editor-rtl.css 340 B
build/block-library/blocks/html/editor.css 341 B
build/block-library/blocks/image/editor-rtl.css 834 B
build/block-library/blocks/image/editor.css 833 B
build/block-library/blocks/image/style-rtl.css 1.61 kB
build/block-library/blocks/image/style.css 1.6 kB
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/image/view.min.js 2.05 kB
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 505 B
build/block-library/blocks/media-text/style.css 503 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 671 B
build/block-library/blocks/navigation-link/editor.css 672 B
build/block-library/blocks/navigation-link/style-rtl.css 103 B
build/block-library/blocks/navigation-link/style.css 103 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation/editor-rtl.css 2.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.27 kB
build/block-library/blocks/navigation/style.css 2.26 kB
build/block-library/blocks/navigation/view.min.js 1.07 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 401 B
build/block-library/blocks/page-list/editor.css 401 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 235 B
build/block-library/blocks/paragraph/editor.css 235 B
build/block-library/blocks/paragraph/style-rtl.css 335 B
build/block-library/blocks/paragraph/style.css 335 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 508 B
build/block-library/blocks/post-comments-form/style.css 508 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 141 B
build/block-library/blocks/post-excerpt/style.css 141 B
build/block-library/blocks/post-featured-image/editor-rtl.css 666 B
build/block-library/blocks/post-featured-image/editor.css 662 B
build/block-library/blocks/post-featured-image/style-rtl.css 345 B
build/block-library/blocks/post-featured-image/style.css 345 B
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 409 B
build/block-library/blocks/post-template/style.css 408 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/style-rtl.css 69 B
build/block-library/blocks/post-time-to-read/style.css 69 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 335 B
build/block-library/blocks/pullquote/style.css 335 B
build/block-library/blocks/pullquote/theme-rtl.css 168 B
build/block-library/blocks/pullquote/theme.css 168 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 486 B
build/block-library/blocks/query/editor.css 486 B
build/block-library/blocks/query/style-rtl.css 312 B
build/block-library/blocks/query/style.css 308 B
build/block-library/blocks/query/view.min.js 637 B
build/block-library/blocks/quote/style-rtl.css 237 B
build/block-library/blocks/quote/style.css 237 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 140 B
build/block-library/blocks/read-more/style.css 140 B
build/block-library/blocks/rss/editor-rtl.css 149 B
build/block-library/blocks/rss/editor.css 149 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 184 B
build/block-library/blocks/search/editor.css 184 B
build/block-library/blocks/search/style-rtl.css 613 B
build/block-library/blocks/search/style.css 613 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/search/view.min.js 471 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 234 B
build/block-library/blocks/separator/style.css 234 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 329 B
build/block-library/blocks/shortcode/editor.css 329 B
build/block-library/blocks/site-logo/editor-rtl.css 760 B
build/block-library/blocks/site-logo/editor.css 760 B
build/block-library/blocks/site-logo/style-rtl.css 204 B
build/block-library/blocks/site-logo/style.css 204 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 682 B
build/block-library/blocks/social-links/editor.css 681 B
build/block-library/blocks/social-links/style-rtl.css 1.45 kB
build/block-library/blocks/social-links/style.css 1.45 kB
build/block-library/blocks/spacer/editor-rtl.css 359 B
build/block-library/blocks/spacer/editor.css 359 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 432 B
build/block-library/blocks/table/editor.css 432 B
build/block-library/blocks/table/style-rtl.css 646 B
build/block-library/blocks/table/style.css 645 B
build/block-library/blocks/table/theme-rtl.css 157 B
build/block-library/blocks/table/theme.css 157 B
build/block-library/blocks/tag-cloud/style-rtl.css 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 403 B
build/block-library/blocks/template-part/editor.css 403 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/term-description/style-rtl.css 111 B
build/block-library/blocks/term-description/style.css 111 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 191 B
build/block-library/blocks/video/style.css 191 B
build/block-library/blocks/video/theme-rtl.css 139 B
build/block-library/blocks/video/theme.css 139 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.11 kB
build/block-library/common.css 1.11 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 12.5 kB
build/block-library/editor.css 12.4 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 212 kB
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.5 kB
build/block-library/style.css 14.5 kB
build/block-library/theme-rtl.css 700 B
build/block-library/theme.css 705 B
build/block-serialization-default-parser/index.min.js 1.13 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/blocks/index.min.js 51.5 kB
build/commands/index.min.js 15.5 kB
build/commands/style-rtl.css 947 B
build/commands/style.css 942 B
build/components/index.min.js 256 kB
build/components/style-rtl.css 12 kB
build/components/style.css 12 kB
build/compose/index.min.js 12.7 kB
build/core-commands/index.min.js 2.72 kB
build/core-data/index.min.js 72.5 kB
build/customize-widgets/index.min.js 12.1 kB
build/customize-widgets/style-rtl.css 1.43 kB
build/customize-widgets/style.css 1.43 kB
build/data-controls/index.min.js 651 B
build/data/index.min.js 8.87 kB
build/date/index.min.js 17.9 kB
build/deprecated/index.min.js 462 B
build/dom-ready/index.min.js 336 B
build/dom/index.min.js 4.68 kB
build/edit-post/classic-rtl.css 571 B
build/edit-post/classic.css 571 B
build/edit-post/index.min.js 35.7 kB
build/edit-post/style-rtl.css 7.58 kB
build/edit-post/style.css 7.57 kB
build/edit-site/style-rtl.css 14.3 kB
build/edit-site/style.css 14.3 kB
build/edit-widgets/index.min.js 17.2 kB
build/edit-widgets/style-rtl.css 4.65 kB
build/edit-widgets/style.css 4.65 kB
build/editor/index.min.js 47.1 kB
build/editor/style-rtl.css 3.72 kB
build/editor/style.css 3.71 kB
build/element/index.min.js 4.87 kB
build/escape-html/index.min.js 548 B
build/format-library/index.min.js 7.76 kB
build/format-library/style-rtl.css 577 B
build/format-library/style.css 577 B
build/hooks/index.min.js 1.57 kB
build/html-entities/index.min.js 454 B
build/i18n/index.min.js 3.61 kB
build/interactivity/index.min.js 11.4 kB
build/is-shallow-equal/index.min.js 535 B
build/keyboard-shortcuts/index.min.js 1.76 kB
build/keycodes/index.min.js 1.9 kB
build/list-reusable-blocks/index.min.js 2.11 kB
build/list-reusable-blocks/style-rtl.css 865 B
build/list-reusable-blocks/style.css 865 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 964 B
build/nux/index.min.js 2.01 kB
build/nux/style-rtl.css 775 B
build/nux/style.css 771 B
build/patterns/index.min.js 4.82 kB
build/patterns/style-rtl.css 567 B
build/patterns/style.css 566 B
build/plugins/index.min.js 1.81 kB
build/preferences-persistence/index.min.js 1.85 kB
build/preferences/index.min.js 1.26 kB
build/primitives/index.min.js 994 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 988 B
build/react-i18n/index.min.js 631 B
build/react-refresh-entry/index.min.js 9.46 kB
build/react-refresh-runtime/index.min.js 6.78 kB
build/redux-routine/index.min.js 2.71 kB
build/reusable-blocks/index.min.js 2.75 kB
build/reusable-blocks/style-rtl.css 265 B
build/reusable-blocks/style.css 265 B
build/rich-text/index.min.js 9.96 kB
build/router/index.min.js 1.79 kB
build/server-side-render/index.min.js 1.96 kB
build/shortcode/index.min.js 1.4 kB
build/style-engine/index.min.js 1.98 kB
build/token-list/index.min.js 587 B
build/url/index.min.js 3.83 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/vendors/react-dom.min.js 41.8 kB
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 967 B
build/warning/index.min.js 259 B
build/widgets/index.min.js 7.18 kB
build/widgets/style-rtl.css 1.18 kB
build/widgets/style.css 1.18 kB
build/wordcount/index.min.js 1.03 kB

compressed-size-action

@oandregal
Copy link
Member Author

oandregal commented Nov 23, 2023

This PR puts more pressure on fixing how filters grow (Edit: #56467 may be a good enough starting point.)

Gravacao.do.ecra.2023-11-23.as.09.37.32.mov

@oandregal
Copy link
Member Author

UI: use radios instead of checkboxes to show checked items for single-selection filters.

@jameskoster @SaxonF I was thinking about filters that allow multi-selection vs the ones that don't (only a single item can be selected). I think I saw somewhere a mockup where these would work differently: checkboxes (selected items with checkbox) vs radios (selected item with circle), respectively. Is this correct?

If so, I'd welcome some clarification. In testing the new components and talking to @ciampo about their behavior (see conversation), he brought up that radios are meant to always have an item selected. So. I wonder how would you like the empty state to work for single selection filters if we use radios (e.g.: how a user clears a selection after having selected some item.). Do we bring back the "None/All" item for single-selection filters? Do we use checkboxes for both single and multi selection filters? etc.

@ciampo
Copy link
Contributor

ciampo commented Nov 23, 2023

I'm also going to add that, at least in terms of semantics, DropdownMenu implements the menu role and exposes checkbox and radio menu items:

  • the purpose of a menu is to access a set of commands, actions, or links. For example, a dropdown to access a set of commands to edit a document.
  • checkboxes are good when the user can select one or more options, independently
  • radios are good when only one option can be selected at a time

Looking at this use case, potentially an alternative to using checkboxes and/or radios inside a menu could be to use the listbox or combobox patterns (which are basically similar to how a <select /> HTML element behaves), since they allow for single or multiple selections. I'm exploring a similar pattern in #56400 (which also adds a search input field at the top of the list).

Related read: https://ariakit.org/components/menu#should-i-use-menu-or-select

Cc'ing @andrewhayward for further advice on this (and to make sure that my advice makes sense!)

@andrewhayward
Copy link
Contributor

Looking at this use case, potentially an alternative to using the menu pattern + checkboxes or radios could be to use the listbox or combobox patterns (which are basically similar to how a <select /> HTML element behaves), since they allow for single or multiple selections. I'm exploring a similar pattern in #56400 (which also adds a search input field at the top of the list).

To confirm @ciampo's suspicions, these should be comboboxes rather than menus.

@ciampo
Copy link
Contributor

ciampo commented Nov 23, 2023

In practical terms, currently using the DropdownMenu + radio/checkbox items is the closest thing that we have in the components library.

  • CustomSelectControl would potentially work for single selection, but it currently doesn't support multiple selection (we are in fact working on refactoring and improving the component)
  • In Try/dropdown menu combobox #56400 I've been exploring using a combobox + list of options inside a dropdown, which hopefully (when ready) would fulfill the needs of the filters UI.

@jameskoster
Copy link
Contributor

Would the use of a combobox affect the design at all? Ideally we:

  • Avoid the "None"/"All"/"Any" option.
  • Can include a "Remove filter" option when editing the filter.
  • Use the radio design (dots) when editing, IE:
Screenshot 2023-11-23 at 11 03 56

@jameskoster
Copy link
Contributor

More generally for this PR, could we try the following format on the filter button:

Status is: Draft, Published, or Scheduled

The "or" part will be important for when we add and operators, e.g. "Category is: Recipes and Reviews".

@oandregal
Copy link
Member Author

oandregal commented Nov 23, 2023

Status is: Draft, Published, or Scheduled

The "or" part will be important for when we add and operators, e.g. "Category is: Recipes and Reviews".

What would think of codifying that piece of data right after the filter name:

  • Author in: Admin, Editor: or operator.
  • Category is: Recipes, Reviews: and operator.

Would this be clear enough? I'm thinking that it provides the same info in a more compressed fashion, and it doesn't require users to scan both the beginning and end of the sentence to understand how the filter works.

We could use different wording: Author contains: Admin, Editor, for example. The important part to me is that we use FilterName OperatorDescription: values.

@ciampo
Copy link
Contributor

ciampo commented Nov 23, 2023

Avoid the "None"/"All"/"Any" option.

Normally, if semantically we're using radio items (ie. a list of options where only one option can be selected at the same time), we can't unselect the currently selected item. That's why a "None"/"All"/"Any" option may be needed to "disable" the selection.

In this specific case, since it's possible to delete the filter by clicking the "Remove filter" menu item (which would cause the dropdown to disappear) I guess there's no need for such an option.

Use the radio design (dots) when editing, IE:

Radios are great when we want the user to be able to select only one option at a time from all options available. Also, selected radios can't be unselected.

This means that there will be instances where the radio semantics won't work for a filter. In those cases, if we were to start using radio-like design also for items that are not actually radio items, that would have the potential to really confuse the end user.

Can include a "Remove filter" option when editing the filter.

Adding a "remove filter" action (ie. not an option that can be selected from a list) can potentially mean that this widget needs to be a menu, since "remove filter" would be a menuitem. The same reasoning would probably apply to if the same dropdown also had the necessity for nested "menus", since those are characteristics of menu widgets (and not comboboxes).

What needs to be understood, then, is what is ideally the best way (in terms of semantics and UX) to implement this widget:

  • it can have both standard "menu items" but also selectable options
  • where those selectable options can be:
    • multiple selection
    • single selection (with the "nice to have" of being able to unselect an item that is already selected)
    • with or without a search input, used to filter those options

@diegohaz and @andrewhayward :

  • first of all, please correct anything that I said before that is incorrect 🙏
  • in your opinion, what is the best way to implement the requirements around all the different variations for "selectable options"? My gut tells me that we may want to avoid using ``menucheckboxitemandmenuradioitem` entirely, and instead just use a combination of `combobox` (when the search input field is needed) and `listbox` (which would support natively single vs multiple selection, including de-selecting selected items). Although I'm not sure if those widgets can be rendered inside a `menu` and have sibling `menuitem`s (like the need for a "Remove filter" button above)

@andrewhayward
Copy link
Contributor

andrewhayward commented Nov 23, 2023

Warning

I'm brain-dumping here, so take this with a pinch of salt!

What needs to be understood, then, is what is ideally the best way (in terms of semantics and UX) to implement this widget

As a baseline, the trigger control should be a comboxbox. It's needs to represent both a filter name, and a filter value. Menus don't give us this option.

Comboboxs, while typically associated with listboxs, do not have to be...

Combobox: An input that controls another element, such as a listbox or grid, that can dynamically pop up to help the user set the value of the input.
...
Elements with the role combobox have an implicit aria-haspopup value of listbox. If the combobox popup element has a role other than listbox, authors MUST specify a value for aria-haspopup that corresponds to the role of its popup.
...
Authors MUST ensure the popup element associated with a combobox has a role of listbox, tree, grid, or dialog.

So I wonder if the most appropriate route here is to have a combobox trigger paired with a dialog, containing both a listbox and a menu (as needed).

<button role="combobox" aria-haspopup="dialog">Comments</button>
...
<div role="dialog">
  <div role="listbox">
    <button role="option" aria-selected="true">Enabled</button>
    <button role="option" aria-selected="false">Disabled</button>
  </div>
  <div role="menu">
    <button role="menuitem">Reset filter</button>
  </div>
</div>

When the popup is opened, focus is placed into the listbox, and up/down are constrained to that context. Tab would move focus to the menu, where again up and down could be constrained.

We'd need to experiment with this a little to see whether it actually works, but in principle it might suit our needs here.

For multi-selection, it would only be slightly different...

<button role="combobox" aria-haspopup="dialog">Author</button>
...
<div role="dialog">
  <div role="listbox" aria-multiselectable>
    <button role="option" aria-checked="true">Author McName</button>
    <button role="option" aria-checked="false">Writer O'Surname</button>
    <button role="option" aria-checked="true">Scribe Lastname</button>
  </div>
  <div role="menu">
    <button role="menuitem">Reset filter</button>
  </div>
</div>

@andrewhayward
Copy link
Contributor

Making sure that a button behaves reasonably as a combobox, with both a name and a value, is a different conversation, but something we should aim for.

Again, this is untested, but something like this could work:

<div role="combobox" aria-label="Author">
  <span aria-hidden="true">
    Author is Scribe Lastname
  </span>
  <span class="visually-hidden">
    Scribe Lastname
  </span>
</div>

It has its issues – the label isn't strictly visible, what's rendered doesn't match the semantics, etc – but might be as close as we're going to get.

@diegohaz
Copy link
Member

  • in your opinion, what is the best way to implement the requirements around all the different variations for "selectable options"? My gut tells me that we may want to avoid using ``menucheckboxitemandmenuradioitem` entirely, and instead just use a combination of `combobox` (when the search input field is needed) and `listbox` (which would support natively single vs multiple selection, including de-selecting selected items). Although I'm not sure if those widgets can be rendered inside a `menu` and have sibling `menuitem`s (like the need for a "Remove filter" button above)

In theory, a role=option element can be selected (using aria-selected), and it can act as a link or execute an action (in which case, the aria-selected attribute isn't used).

A select widget is essentially a select-only role=combobox element with aria-haspopup=listbox (or grid, dialog, and so on). Therefore, a select widget can contain both selectable and non-selectable options that activate actions related to the widget.

So, I'd say it's fine for the filters to use this pattern.

There's a similar example on Ariakit (unlisted), but the "Remove filter" button is rendered outside the filter select: https://ariakit.org/examples/select-menu-default-open

@diegohaz
Copy link
Member

Making sure that a button behaves reasonably as a combobox, with both a name and a value, is a different conversation, but something we should aim for.

Again, this is untested, but something like this could work:

<div role="combobox" aria-label="Author">
  <span aria-hidden="true">
    Author is Scribe Lastname
  </span>
  <span class="visually-hidden">
    Scribe Lastname
  </span>
</div>

It has its issues – the label isn't strictly visible, what's rendered doesn't match the semantics, etc – but might be as close as we're going to get.

Alternatively, it could use aria-labelledby:

<div role="combobox" aria-labelledby="label">
  <span id="label" aria-hidden="true">
    Author is 
  </span>
  Scribe Lastname, Other
</div>

This will result in "Author is " as the label, and "Scribe Lastname, Other" as the value.

@andrewhayward
Copy link
Contributor

Alternatively, it could use aria-labelledby:

<div role="combobox" aria-labelledby="label">
  <span id="label" aria-hidden="true">
    Author is 
  </span>
  Scribe Lastname, Other
</div>

This will result in "Author is " as the label, and "Scribe Lastname, Other" as the value.

That was my first thought, but we'd need to be mindful of translation, which becomes that much harder when sentences are broken up across DOM nodes.

@jameskoster
Copy link
Contributor

jameskoster commented Nov 23, 2023

We could use different wording: Author contains: Admin, Editor,

@oandregal the wording is tricky. I don't know that 'contains' translates to 'or' very well 🤔 It also makes the button quite long. Maybe we don't need to communicate the operator in the label. It can be made quite clear in the popover like:

Screenshot 2023-11-23 at 14 58 13

There we have more space to get the wording right (above is just a placeholder). What do you think?

@andrewhayward
Copy link
Contributor

A select widget is essentially a select-only role=combobox element with aria-haspopup=listbox (or grid, dialog, and so on). Therefore, a select widget can contain both selectable and non-selectable options that activate actions related to the widget.

The problem with that is if an option doesn't have an explicit aria-selected value, the user agent can provide an implicit value, and when the listbox is single-select that value is supposed to be true if the option has focus. So there's not really any guaranteed way for the user to know if an option is selected because it's selected, or because it's focused.

As a general rule, we should try to keep options for choices and menu items for actions.

@andrewhayward
Copy link
Contributor

andrewhayward commented Nov 23, 2023

That was my first thought, but we'd need to be mindful of translation, which becomes that much harder when sentences are broken up across DOM nodes.

Of course, if the final output is going to be more akin to {label}: {value} then we'd probably get away with it!

@diegohaz
Copy link
Member

diegohaz commented Nov 23, 2023

A select widget is essentially a select-only role=combobox element with aria-haspopup=listbox (or grid, dialog, and so on). Therefore, a select widget can contain both selectable and non-selectable options that activate actions related to the widget.

The problem with that is if an option doesn't have an explicit aria-selected value, the user agent can provide an implicit value, and when the listbox is single-select that value is supposed to be true if the option has focus. So there's not really any guaranteed way for the user to know if an option is selected because it's selected, or because it's focused.

As a general rule, we should try to keep options for choices and menu items for actions.

It's been a while since I last tested it. I'm not sure if browsers already implement this spec, but as far as I understand, they should only include an implicit aria-selected if there is no other option with an explicit aria-selected value:

In certain conditions, a user agent MAY provide an implicit value for aria-selected for each option in a listbox, and if it does, the user agent MUST ensure the following conditions are met before providing an implicit value:

https://w3c.github.io/aria/#option

MDN also makes a distinction between selectable, non-selectable, and disabled options:

All selectable options should have aria-selected match their state, true when selected and false when not. If an option is not selectable, aria-selected can be omitted. A disabled option can have aria-disabled="true" and aria-selected="false" to communicate to the user that the option is present, albeit disabled.

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/option_role

If browsers don't follow this and add an implicit aria-selected value regardless, or if the popup includes additional items such as the "Results Match all selected" item shared by @jameskoster above, I agree that using a dialog with a listbox inside is a better choice. However, perhaps the other items are merely tabbable elements within the dialog, allowing us to render the "Results" item as a combobox instead of a menuitem.

@andrewhayward
Copy link
Contributor

It's been a while since I last tested it. I'm not sure if browsers already implement this spec, but as far as I understand, they should only include an implicit aria-selected if there is no other option with an explicit aria-selected value:

I don't have anything else to hand right now, but with the following very minimal test case...

<div role="listbox" aria-label="Selected?">
  <button role="option" aria-selected="true">True</button>
  <button role="option" aria-selected="false">False</button>
  <button role="option">Undefined</button>
</div>

...VoiceOver reads the following in Safari:

  • True, selected (1 of 3)
  • False, text (2 of 3)
  • Undefined, text (3 of 3)

Chrome/Edge:

  • True, selected (1 of 3)
  • False, not selected (2 of 3)
  • Undefined, selected (3 of 3)

Firefox:

  • True, selected (1 of 3)
  • False (2 of 3)
  • Undefined, selected (3 of 3)

So it's clearly at least variable!

@andrewhayward
Copy link
Contributor

If browsers don't follow this and add an implicit aria-selected value regardless, or if the popup includes additional items such as the "Results Match all selected" item shared by @jameskoster above, I agree that using a dialog with a listbox inside is a better choice. However, perhaps the other items are merely tabbable elements within the dialog, allowing us to render the "Results" item as a combobox instead of a menuitem.

As with all these things, user experience is priority zero. So agreed, ultimately we go with whatever actually makes the most sense. If that's a collection of buttons following a listbox, then so be it. There's no concrete "right" answer with this one!

@diegohaz
Copy link
Member

Chrome/Edge:

  • True, selected (1 of 3)
  • False, not selected (2 of 3)
  • Undefined, selected (3 of 3)

Thanks for testing. I opened an issue on Chromium: https://bugs.chromium.org/p/chromium/issues/detail?id=1504835

@oandregal
Copy link
Member Author

I've been working on refactoring a bit the existing components to be able to add this feature all at once in all of them. I'd like to land #56514 before this PR.

@ciampo
Copy link
Contributor

ciampo commented Nov 24, 2023

What is not clear to me, is what we identified as the best way to implement these widgets — if I followed the conversation correctly, are we thinking of using a dialog with a listbox inside (basically what @andrewhayward suggested in this comment) ?

If so, shouldn't we first prototype a proof of concept before moving forward? And where should such a component live? Probably better to start by having it as a private component for data views, and later decided whether it makes sense to abstract it and move it to the @wordpress/components package?

@oandregal
Copy link
Member Author

oandregal commented Feb 7, 2024

#58569 redesigns the filter UX, multi-selection would need to be added to that work.

@oandregal oandregal closed this Feb 7, 2024
@oandregal oandregal deleted the update/filter-component-multiple-selection branch February 7, 2024 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Data Views Work surrounding upgrading and evolving views in the site editor and beyond [Type] Experimental Experimental feature or API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants