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

Add: Bulk actions API to dataviews and an initial bulk trash action. #56476

Closed
wants to merge 1 commit into from

Conversation

jorgefilipecosta
Copy link
Member

This PR adds a bulk actions mechanism to the dataviews and an initial bulk trash action.
It also adds an item selection mechanism to the list view.

Testing

Verify we can select all visible rows.
Verify we can deselect all visible rows.
Select some pages and verify it is possible to trash them all.
Verify the Deselect button works.

Screenshot

Screenshot 2023-11-23 at 12 22 07

@jorgefilipecosta jorgefilipecosta added the [Feature] DataViews Work surrounding upgrading and evolving views in the site editor and beyond label Nov 23, 2023
Copy link

github-actions bot commented Nov 23, 2023

Size Change: +1.02 kB (0%)

Total Size: 1.72 MB

Filename Size Change
build/edit-site/index.min.js 210 kB +874 B (0%)
build/edit-site/style-rtl.css 14.7 kB +75 B (+1%)
build/edit-site/style.css 14.7 kB +76 B (+1%)
ℹ️ 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 322 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.75 kB
build/block-library/blocks/gallery/style.css 1.75 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.02 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.04 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 647 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 475 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.49 kB
build/block-library/blocks/social-links/style.css 1.49 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 213 kB
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.7 kB
build/block-library/style.css 14.7 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.1 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.1 kB
build/components/style.css 12.1 kB
build/compose/index.min.js 12.8 kB
build/core-commands/index.min.js 2.73 kB
build/core-data/index.min.js 72.6 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 34.9 kB
build/edit-post/style-rtl.css 7.56 kB
build/edit-post/style.css 7.55 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.64 kB
build/editor/index.min.js 48.6 kB
build/editor/style-rtl.css 3.74 kB
build/editor/style.css 3.73 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/file.min.js 442 B
build/interactivity/image.min.js 2.15 kB
build/interactivity/index.min.js 12.5 kB
build/interactivity/navigation.min.js 1.16 kB
build/interactivity/query.min.js 791 B
build/interactivity/search.min.js 610 B
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/modules/importmap-polyfill.min.js 12.2 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 5.28 kB
build/patterns/style-rtl.css 564 B
build/patterns/style.css 564 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 994 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.74 kB
build/reusable-blocks/style-rtl.css 265 B
build/reusable-blocks/style.css 265 B
build/rich-text/index.min.js 9.98 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

@ntsekouras
Copy link
Contributor

Just noting IMO the best place to implement bulk actions would be the templates list, since not all items have the same available actions. This use case will probably inform us better of the API we need to introduce. What do you think?

@ntsekouras
Copy link
Contributor

Thank you for the PR Jorge! I'd expect the bulk actions container to position itself at the top when the list is bigger than the viewport.

Screen.Recording.2023-11-27.at.3.36.05.PM.mov

Another thing is that we could add the some selected state in the main toggle that selects all items. We do the same in other places.

Would it be hard to add a bulk action(delete) in templates too? As I mention above:

Just noting IMO the best place to implement bulk actions would be the templates list, since not all items have the same available actions. This use case will probably inform us better of the API we need to introduce. What do you think?

@@ -307,11 +330,14 @@ export default function PagePages() {
paginationInfo={ paginationInfo }
fields={ fields }
actions={ actions }
bulkActions={ bulkActions }
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you explored if we can have a single actions API that also support an action to be a bulk one?

Copy link
Member Author

Choose a reason for hiding this comment

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

Very good question 👍 I think the way a bulk action is done should be its own function and not a single action called multiple times ( in the future bulk actions may have special endpoints), but as part of the normal actions API we can have a bulkCallback that specifies an action supports being applied in bulk and how that is done. We may also be able to reuse the isEligible. Would you prefer this alternative implementation? If yes I can give it a try.

Copy link
Contributor

Choose a reason for hiding this comment

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

An exploration at this point where we introduce a new API would be useful I think. I'm not opposed to this approach, but we need to explore if we can combine them. It could lead to a more elegant API or a more complex one 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like we should only have one API personally. An action is just a bulk action that applies to a single item. I believe we can have flags about whether an action can apply to multiple items or not.

Copy link
Member Author

Choose a reason for hiding this comment

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

I feel like we should only have one API personally. An action is just a bulk action that applies to a single item. I believe we can have flags about whether an action can apply to multiple items or not.

This may be a good way to think about it, implement the action as a bulk action but have a special condition where if it is just one item we show more user-friendly messages, etc. I will give it a try.

@@ -157,6 +158,20 @@ export default function PagePages() {
totalPages,
} = useEntityRecords( 'postType', postType, queryArgs );

useEffect( () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this code doing?

Copy link
Member Author

@jorgefilipecosta jorgefilipecosta Nov 28, 2023

Choose a reason for hiding this comment

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

I added a comment, it removes any selected pages that are no longer in the list of visible pages. For example, remove a deleted page from the set of selected pages.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm.. so this is something every consumer should have, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes if we follow the current direction of being the consumer managing the selection state, the consumer needs this logic.

@jorgefilipecosta jorgefilipecosta force-pushed the add/bulk-actions branch 3 times, most recently from 7212676 to 25f4c2b Compare November 28, 2023 20:50
@jorgefilipecosta
Copy link
Member Author

Hello @ntsekouras, @youknowriad thank you for the reviews! This PR has been updated and there is no longer a separation between the API for bulk actions and actions.

Additionally, I have submitted a separate PR to implement bulk actions for templates, which is functional and can be merged after this one #56615. It did not require any API changes and the implementation was straightforward.

As per your suggestion, I have also added the "some selected" state.

Regarding the bulk actions no longer being visible if the viewport is small, the issue is a bit complex. I am currently using a popover to display the information above the pagination. In order to ensure that the popover is always visible, one possible solution would be to add a property to the popover that makes it visible at the edge of the viewport if the element it references is outside the viewport. However, I am unsure if such a property would make sense cc: @ciampo. Another alternative would be to not use a popover and try to position things with CSS. This would mean things would not be above the pagination, they would be positioned always at the edge of the viewport. I guess we can leave this discussion for a separate issue where we polish the UI.

)
);
}
}, [ pages, selection ] );
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be done automatically before calling the. onChangeSelection callback or when calling the onView callback with a page change.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hello @youknowriad, I am uncertain about how to approach the issue at hand. It seems that if a page is deleted, neither the onChangeSelection nor the onView functions are triggered. Essentially, we require a mechanism to automatically remove the deleted page (or page that stopped being visible because of a state change) from the selection set if it was previously included.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is another point that I don't get why the selection handling is at the consumer level and not handled internally, but 🤷

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't mind internal selection state but we need an onChangeSelection callback to adapt the UI in site-by-side view.

For the pages removed externally, what's wrong with keeping an outdated selection object with things that are not visible?? Is that bad.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not convinced the preview selection and bulk action selection are the same thing. This is how it looks like in practice when we do so:

Gravacao.do.ecra.2023-12-04.as.09.55.13.mov

My suggestion would be to separate concerns:

  • Leave the current selection/setSelection for the list view and iterate on the API in a separate PR. I've started exploring an alternative that uses a onClickPreview callback at DataViews: iterate on list view #56746
  • In this PR, make DataViews absorb the bulk edit selection.

For me, bulk action selection is such a fundamental operation that the consumer should not have to deal with it. It seems that the concerns shared (have a controlled&uncontrolled selection) would be solved if we do not conflate preview & bulk action selection.

Copy link
Contributor

Choose a reason for hiding this comment

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

Screenshot 2023-12-04 at 1 13 53 PM

Personally I have a feeling that in views where there's a preview, we want to show some kind of messaging (or stack in the preview) while the bulk actions should be visible as well. In the screenshot above, I'm sharing what "Mail" app does in Mac OS.

If you select a single email, the top bar shows the "actions" for that email and shows the preview on the right but if you select multiple emails, it shows a stack on the right and the "actions" are updated, the ones that apply to a single email are disabled so they in fact become bulk actions.

So I think there's value in treating the selections as similar. I do think the way selection is performed can be different from a view to another: for instance in table view, selection happens using the checkboxes while in the list view it happens using click or "ctrl + click to multi select"

Copy link
Member

Choose a reason for hiding this comment

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

I see your point. I agree selection state could be something shared across all views – even if they work differently UI wise. I'd still argue that we don't want the selection to be controlled in the list view. To me the question is: who should handle the selection affordance in the list view: a field or via the view itself?

The current implementation makes the selection affordance to be provided by a field (through the <Link /> component). Though the mockups for the list view that I've started to implement at #56746 make the selection affordance to be provided by the view (structural li element of the view, not a link rendered by a field).

By having the selection affordance in the view, we don't need a controlled selection, we just need to relay the selection to the consumer, which we can do by a callback prop:

<DataViews
  onItemsSelected={ ( { itemsSelected  } ) => { /* handle the preview or any other thing * / } }
/>

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the selection should definitely be updated down => top (dataviews component calling a callback onSelectionChange or onItemsSelected if you prefer). If a field is updating the outside "selection" object directly, that's a bug (regardless of whether we're maintaining the state internally or externally) for me.

That said, I do see some need of top => down selection change potentially in the future (thinking about things like undo/redo...) It's probably too early to think about that though. So whether it's controlled or uncontrolled today doesn't matter much.

Copy link
Member

Choose a reason for hiding this comment

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

If a field is updating the outside "selection" object directly, that's a bug (regardless of whether we're maintaining the state internally or externally) for me.

This is how it works today: the title field sets the selection. 🤔

So, if I understood correctly, does it sound like we agree to the following plan:

  1. selection work the same for preview & bulk actions
  2. absorb selection as part of dataview's responsibility, not the consumer's
  3. dataviews should expose an onSelectionChange callback for the consumer

Given the current implementation for the preview, I'm not sure that we'd be able to make dataviews absorb selection fully in this PR. If @jorgefilipecosta feels confident, go ahead. Otherwise, I guess we could ship this as it is (selection controlled by the consumer) and revisit when something like #56746 lands.

Does that sound sensible?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes,

I think the issue is that instead of calling the setSelection state, the field should receive a setSelection callback in the render function and call it. Fields could be defined in very different places than where the selection state is hold (either internally or externally)

@ntsekouras
Copy link
Contributor

ntsekouras commented Nov 29, 2023

Regarding the bulk actions no longer being visible if the viewport is small, the issue is a bit complex. I am currently using a popover to display the information above the pagination.

Maybe forwardRef the ViewComponent and pass the ref as anchor to the Popover? That's what I had done in the first POC.

Let's get some design feedback though --cc @jameskoster @SaxonF

import { unlock } from '../../lock-unlock';

const {
DropdownMenuV2Ariakit: DropdownMenu,
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't revert the recent changes here. Maybe a bad rebase? By seeing this it makes me wonder if more changes in other files could slip in that shouldn't? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

It was a rebase issue, it is fixed. I did a check in other files and did not notice other cases.

setSelection( data.map( ( { id } ) => id ) );
}
} }
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

This checkbox is unlabelled.

Copy link
Member Author

Choose a reason for hiding this comment

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

A label was added 👍

);
}
} }
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

This checkbox is unlabelled.

Copy link
Member Author

Choose a reason for hiding this comment

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

A label was added 👍

@afercia afercia added the Needs Accessibility Feedback Need input from accessibility label Nov 29, 2023
@afercia
Copy link
Contributor

afercia commented Nov 29, 2023

Chiming in here from #56615

The selection checkboxes are unlabelled. When omitting the label prop, CheckboxControl does reander a <label> element that is properlyy associated with the checkbox but the label element is empty...

@ciampo this is one more case that proves these input components including BaseControl are open to misues. As discussed in #51740 we need to make sure all iinteractive controls are always properly labelled.

That said, in this specific case we may need to handle the labelling in some special way.

  • First, I'm not sure these checkboxes should live in their own table column. While that's the pattern used in the core admin, the labeling of that column header has always been a problem. I'd like to explore alternative solutions.
  • The 'select all' checkbox should be labelled 'Select all' or something alont that line.
  • Ideally, the other select checkboxes should be labelled with the name of the template / template part but avoiding repetitions like in the core admin tables. Ideally, the name in the second column should become the label for these checkboxes.
  • I'm not sure the names in the second column should be H3 headings.
  • When bulk selecting, the Bulk actions UI appears in a sort of floating popover at the bottom of the table. I'm not sure this is ideal from an usability and accessibility perspective.

I'd like to thank everyone for the efforts on the Data Views implementation, I do realize it's a big task. I'd think this is a great opportunity to build something better than what we currently have in core. For the core tables, for a long time we could only try to implement small improvements but we couldn't rebuild everything from scratch because of backward compatibility issue. Now that we're starting fresh with the editor Data Views, this is the time to get it right since the begining.

I'd love to hear thoughts from other accessibility specialists Cc @joedolson @alexstine @andrewhayward

@Tropicalista
Copy link

Why aren't you using react table selection api?

@@ -249,6 +253,54 @@ function ViewList( {
}
return column;
} );
if ( selection !== undefined ) {
_columns.unshift( {

Choose a reason for hiding this comment

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

Is there any reason why you choose this approach instead of more simple one, using react table api?
I mean the same code here can be something like this:

	_columns.unshift( {
		header: ( props ) => {
			return (
				<CheckboxControl
					__nextHasNoMarginBottom
					checked={ props.table.getIsAllRowsSelected() }
					indeterminate={ props.table.getIsSomeRowsSelected() }
					onChange={ () =>
						props.table.toggleAllRowsSelected()
					}
				/>
			);
		},
		id: 'selection',
		cell: ( props ) => {
			return (
				<CheckboxControl
					__nextHasNoMarginBottom
					checked={ props.row.getIsSelected() }
					onChange={ props.row.getToggleSelectedHandler() }
				/>
			);
		},
		enableHiding: false,
		width: 40,
	} );

Copy link
Member Author

Choose a reason for hiding this comment

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

Hi @Tropicalista,

Thank you for sharing insights and the code that uses the react table selection API. However, we have decided not to use it for two reasons:

Firstly, we have other views that are not compatible with tanstack, such as the grid view, and we need the selection to work seamlessly across all views.

Secondly, we are not certain about our future plans regarding tanstack, so we prefer to avoid using many of their API's and keep our options open.

@jorgefilipecosta
Copy link
Member Author

Hi @afercia,
Thank you a lot for the review and important insights, it is great to have your feedback. And I agree this is an important opportunity to have accessible interface for managing the pages, posts etc...

I added labels that reference the page being selected/unselected.

First, I'm not sure these checkboxes should live in their own table column. While that's the pattern used in the core admin, the labeling of that column header has always been a problem. I'd like to explore alternative solutions.

I'm not sure what other UI we could use besides their own table column but I'm going to cc: @SaxonF and @jameskoster in case they have some insights. At least for now we have proper labels.

I'm not sure the names in the second column should be H3 headings.

This is an already existing issue and not part of this PR but I'm happy to address it. I guess the second column (name of the page) should not be a heading at all and just text/span, is that correct?

When bulk selecting, the Bulk actions UI appears in a sort of floating popover at the bottom of the table. I'm not sure this is ideal from an usability and accessibility perspective.

I'm going to cc:@SaxonF and @jameskoster again in case they have some insights in how to improve the usability and accessibility.

Any other feedback is welcome.

Copy link

github-actions bot commented Nov 29, 2023

Flaky tests detected in 8e0ab84.
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/7099836059
📝 Reported issues:

@joedolson
Copy link
Contributor

I agree that we shouldn't be using heading inside the table. There are relatively few cases where headings really make sense inside a table, since you're combining two different types of semantics. It probably wouldn't cause any major problems, but if a screen reader user is navigating inside a table, then they'll use table navigation tools to move around, and not jump from heading to heading.

I'm certainly game to explore alternate solutions to the checkbox being in its own column, but I also don't have a lot of great ideas. Since we can't use interactive content as a label - e.g., anything that's a link or button - there are going to be problems with using any of the existing visible content.

const [ isModalOpen, setIsModalOpen ] = useState( false );
const actionTriggerProps = {
action,
onClick: () => setIsModalOpen( true ),
};
const additionalProps = {};
if ( action.isBulk ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We could normalize the items and always pass an array of items in actions.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right now we pass an array for bulk actions and a single item without an array for non-bulk actions. Normalizing means that we would pass an array even for actions that don't support bulk. Is that ok? I don't have a preference but I can follow the approach of always passing an array if we think it is ok.

Copy link
Contributor

Choose a reason for hiding this comment

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

No strong opinions but I think it's better to normalize and always pass an array.

@oandregal
Copy link
Member

There has been some file & component renames in the last couple of days, which requires the current in-flight PRs to be updated. I've gone through those PRs and rebased them – hope I didn't miss anything!

@@ -274,7 +342,15 @@ function ViewTable( {
}

return _columns;
}, [ fields, actions, view ] );
}, [
areAllSelected,
Copy link
Member

@oandregal oandregal Dec 1, 2023

Choose a reason for hiding this comment

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

Is this necessary? It depends on selection and data changes, so do we have every case covered with them? Anyway, if we move the line where areAllSelected inside the useMemo we could remove it.

@@ -472,6 +548,10 @@ function ViewTable( {
header.column.columnDef
.maxWidth || undefined,
} }
className={
header.column.columnDef.className ||
Copy link
Member

Choose a reason for hiding this comment

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

What is this for? I don't see any className prop in the pages fields' definition.

Copy link
Member

Choose a reason for hiding this comment

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

I see now: it's for the selection field. I've commented above.

},
enableHiding: false,
width: 40,
className: 'dataviews-table-view__selection-column',
Copy link
Member

Choose a reason for hiding this comment

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

What do you think of scoping the CSS using the data-field-id that we already inject? We target the actions column using this mechanism. It'd be good to use the same mechanism for all.

Also, I don't like that we expand the Field API to allow className for fields (the change below to support this class opens it up for every field).

}
/>
),
id: 'selection',
Copy link
Member

Choose a reason for hiding this comment

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

This name, like the actions field below, is something that may conflict with consumers field IDs. What if we use a more scoped name such as dataviews-selection?

If the change for the actions becomes too convoluted, in can be done in a separate PR.

width: 100%;
}

.dataviews-table-view__selection-column label {
Copy link
Member

Choose a reason for hiding this comment

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

Note the related comment above, this could be th [data-field-id="dataviews-selection"].

<Toolbar label="Bulk actions">
<div className="dataviews-bulk-actions-toolbar-wrapper">
<ToolbarGroup>
<ToolbarButton onClick={ () => {} } disabled={ true }>
Copy link
Member

Choose a reason for hiding this comment

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

I'm not familiar with the toolbar component, so I may be missing context: I found it weird that we use an “inactive” button. Can we use a ToolbarItem instead?

Copy link
Member

@oandregal oandregal left a comment

Choose a reason for hiding this comment

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

This is great progress, Jorge! I've left some comments, though it's mostly ready code/API wise.

@alexstine
Copy link
Contributor

@jameskoster Do note that if something is visually hidden, it will likely look like a keyboard trap when users enter it. You cannot do this with interactive control elements. Also note that this is the same issue that a lot of Gutenberg suffers from, all the appearing and disappearing is great visual UX but is a royal pain for people without sight. UI shouldn't come and go as much as it does on the modern web today.

The suggestion is a WCAG violation though so sadly it's a non-starter anyway.

@afercia
Copy link
Contributor

afercia commented Dec 4, 2023

I will try to keep each point in a separate comment.

Bulk select mode reveals bulk action controls

I'd think that, typically, bulk actions are a flow users take once in a while, not that often. More often, users will work on a single image. As such, I'd would like to propose to have the Bulk select and bulk action controls not shown by default.

Why we would want to have the checkboxes always there even when they are not needed?

@joedolson @alexstine I'd greatly appreciate your thoughts.

  • When the checkboxes are always shown by default, they add a lot of visual and semantic noise. All these checkboxes are tabbable and announced by screen readers also when they are not needed. I'd tend to think selection checkboxes should only be shown when users want to nulk select and bulk edit.
  • Example of the above are, for example:
    • outllok.office.com where the checkboxes appear only after clicking the 'Select' button in the messages list, which switches the UI to a sort of 'selection' mode.
    • The WordPress Media Library > Grid view. There is a 'Bulk select' button that switches the UI to a 'selection' mode. Bulk actions are shown only in this mode.

Screenshots:

outlook office com

media library grid view Bulk select button

Showing the Bulk select and bulk action controls only when needed would have a few pros:

  • The standard mode has less tab stops and it's easier to navigate with keyboard.
  • The standard mode is cleaner to explore and navigate for screen reader users as well.
  • Switching to 'Bulk select' mode should be announced cleaearly, both visually and audibly.
  • Bulk select mode reveals additional controls for bulk selection and actions that are only needed in this mode.
  • When switching mode, focus should be managed properly.

This is not unprecedented and seems to me it would be a common pattern for users. If done well, it can be accessible I think.

@afercia
Copy link
Contributor

afercia commented Dec 4, 2023

Bulk actions toolbar

The principle of revealing actions on selection works well and is fairly standard across design systems. The main thing to sort seems to be convenient proximity in the DOM/tab order for the bulk actions toolbar.

Yes, and I think the current UI for the bulk actions toolbar is less than ideal.

First of all, it may be visually out of screen. That really depends on the screen size. Mobile is a concern too.
On my screen, a 16-inch (3072 × 1920) retina display, when I select some checkboxes nothing seems to happen. Absolutely nothing. The floating bulk actions toolbar is out of screen, at the bottom of the table ans it's not visible. Screenshot:

blk actions toolbar out of view

This isn't a great UX. There is a good reason why in the classic admin the bulk actions are placed both at the top and at the bottom of the table.

I'd really wish we kept bulk actions at the top.

I'd totally second what @alexstine suggested. It feels natural and expected to have the bulk actions at the top. It is convenient to make them available also at the bottom.

Of couser, basec on previous considerations about making the bulk actions show only when needed, the bulk actions toolbar may be shown only when in 'selection' mode.

Let's please avoid floating / sticky / fixed UIs please, as they come with inherent usability and accessibility problems especially for keyboard users.

Forgive the possibly naive idea, but could there be a (visually hidden) "Skip to bulk actions toolbar" link close to each checkbox, and a similar "Return to table" link in the toolbar?

That's something we could explore but it would add some noise, I think. If we make the UI have as less tab stops as possible I think skip links would not be necessary.

@afercia
Copy link
Contributor

afercia commented Dec 4, 2023

Icon-only buttons

Let's please avoid Icon-only buttons as much as possible. Icon-only buttons should only be used when there's no space to use visible text.

For example, the 'View options' button:

grid icon view options

Visually, the icon looks like a table or a grid. It reminds me of the 'Grid view' icon in the Media Library. As a user, I would expect htat it switches the view to a grid view. Instead, it opens a dropdown menu.

The icon glyph really doesn't suggest the idea of 'View options'. The 'View options' themselves contain a variety of options that are different in nature and very difficult to communicate ny the means of an icon.

Also, the icon is placed separately from other controls, it's 'in isolation' in a blank part of the UI with no other adjacent controls. For users with visual impairment, this icon is very difficult to see.

The View options menu

Screenshot:
view options menu

First of all, I woudl like to remind that the multi-level version of the dropdown menu still needs to be tested for accessibility and I would like to hear other a11y specialist thoughts about it.

Also: please do not place the selected value inside the menu item this way, as it becomes part of the accessible name of the menuitem. I reported this issue several times across the whole project. An interactive control is not the place where to put selected values or states. Instead, an interactive control must only contain its name, prefereably the name of the associated action.

For example, this is not OK:

<div role="menuitem" ... >Sort by<span ...>Author</span></div>

The accessible name becomes Sort by author, which is incorrect. Same applies to the other menuitems that show the selected value. Reminder that you can inspect the accessible name in the browser's dev tools. Screenshot:

Screenshot 2023-12-04 at 11 56 39

Accessible name and selected value or state must be communicated separately.

Visually, it may be ok but semantically this is incorrect. Please move the selected value / state out of the element with role="menuitem". A better experience for screen reader users would be a correct and more meaningful feedback by using a description of the control. Something like:

<div role="menuitem" aria-describedby="sort-description" ... >Sort by</div>
<span aria-hidden="true" ...>Author</span>
<span id="sort-description" hidden ...>Currently sorted by Author</span>

@afercia
Copy link
Contributor

afercia commented Dec 4, 2023

Dropdown menus with only one item

This was previously noted in other issues and PRs. A dropdown menu that contains only one item is arguably a good UI. When there is only one item a dropdown is unnecessary. It's also obtrusive as it hides the item and it forces users to a double click for a control that should be in first function instead.

Screenshot:

ellipsis actions dropdown with only one item

@jameskoster
Copy link
Contributor

@alexstine

Do note that if something is visually hidden, it will likely look like a keyboard trap when users enter it.

Could you expand on that? My thinking was that the toolbar would contain the bulk action tools, but each button would be disabled. Also, if you wouldn't mind linking to the reference that makes this approach a non-starter it would help my understanding of the problem(s).

@alexstine
Copy link
Contributor

@afercia I agree with this.

Bulk select mode reveals bulk action controls

This makes more sense then having to select a checkbox to eventually have bulk action controls appear. Selecting a button such as Enable bulk selection makes more sense.

@jameskoster I've never been very good at finding WCAG requirements that specifically fail but let me try to explain this a little more.

  1. Tab through a page.
  2. Eventually see the focus outline disappear.
  3. Focus outline went away because you tabbed into the bulk actions toolbar which is currently only visible to screen readers: class="screen-reader-text".
  4. This looks like a keyboard trap visually because focus has gone into a void.

Thanks.

@jameskoster
Copy link
Contributor

jameskoster commented Dec 4, 2023

One problem with a dedicated "bulk select mode" is that it overly de-emphasises bulk actions on views where they are more prevalent, e.g. comment management. We also need to consider the upcoming "List view" which might employ a different selection pattern.

I agree there should be a way for users (or views via config) to control the prominence of bulk actions, but wouldn't a view option for toggling the checkbox column be a more flexible solution?

@ciampo
Copy link
Contributor

ciampo commented Dec 4, 2023

@afercia, related to the DropdownMenu component:

First of all, I woudl like to remind that the multi-level version of the dropdown menu still needs to be tested for accessibility and I would like to hear other a11y specialist thoughts about it.

The menu received a first round of review (including a focus on a11y) in #54939. It has since received more feedback across other PRs. Its underlying implementation is also based on ariakit's Menu, which should in theory offer a great starting point already. If you have any specific feedback to share about the component, you may report it in #50459.

please do not place the selected value inside the menu item this way, as it becomes part of the accessible name of the menuitem

This is good feedback, and implementing the proposed solution (using the aria-hidden attribute) should be straightforward for consumers of DropdownMenu. While using the latest version of the component, the code would look similar to this:

  <DropdownMenu trigger={
    <DropdownMenuItem suffix={<span aria-hidden="true">Author</span>}>
      Sort by
    </DropdownMenuItem>
  }>
    {/* nested menu items */}
  </DropdownMenu>

Squashed commits:
[43c3d85] Fix rebase
[cb954b6] Fix rebase
[0a3723a] add selection label functionality
[3c48c68] Add labels
[2ae6b76] Lint fixes
[77dc74c] Feedback
[d0d1456] Add: Bulk actions API and trash action.
@jameskoster
Copy link
Contributor

Here's an alternative approach I'd like to get feedback on @alexstine.

bulk
  • Bulk actions live in a dedicated "Bulk edit" DropdownMenu positioned at the top of the table, possibly before filters, or before the view options button.
  • All actions (e.g. "Delete", "Change status", "Change author") are presented in a group in the menu, disabled until a selection has been made.
  • A second group in the menu includes selection options e.g. "Select current page", "Select all", "Select none".

Do you think something like this could work?

@alexstine
Copy link
Contributor

@jameskoster Seems fine to me. Then you can save the space in the table not showing checkboxes all the time.

@jameskoster
Copy link
Contributor

I'm not sure we can get around that one, the checkboxes probably need to remain to enable partial selection (e.g. select rows 4 and 8 of 20). I could potentially see a view option to toggle bulk editing though – when off the checkboxes and 'bulk edit' menu would be hidden.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] DataViews Work surrounding upgrading and evolving views in the site editor and beyond Needs Accessibility Feedback Need input from accessibility [Type] Experimental Experimental feature or API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants