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

[Experiment POC] DataTable component(pages + templates list) #53906

Closed
wants to merge 17 commits into from

Conversation

ntsekouras
Copy link
Contributor

@ntsekouras ntsekouras commented Aug 24, 2023

What?

This is just an explorative PR about the new WP admin lists I had started some time ago, before the related PR that explores the new Media Library and I'm creating it now, as I was AFK. In general there is overlapping work and we should coordinate how we move forwards. That's also the reason I didn't focus much to add more filter controls.

Related issues: #53233, #50430

What I had in mind mostly is a DataTable component that could be used in core interfaces(pages/templates lists, etc..), but also be able to used by extenders for their lists, that would result in a more coherent UX. After discussions in the PR and the issue, the new components will live in edit site package and will be extracted to components package when are more stable.

I mostly experimented with the pages lists as it requires a controlled data table, that means we feed the table with the final data, manually handling filtering, sorting, pagination etc... In general I've implemented this by using a DataTableProvider, so we can compose components an have access to the table's instance.

An example of uncontrolled data table would be something like this:

<DataTable
	data={ data }
	columns={ columns }
	options={ {
		initialState: {
			pagination: { pageSize: 5 },
		},
	} }
>
	<VStack>
		<DataTableGlobalSearchInput />
		<DataTableRows className="edit-site-table" />
		<DataTablePagination />
	</VStack>
</DataTable>;

Extensibility

Whatever solution we end up with should have extensibility in mind. That means having a way to add columns and custom filters. For columns we could just have a filter and you can test that in dev tools with something like this:

wp.hooks.addFilter(
	'siteEditor.pageListColumns',
	'my/unique-filter',
	( columns ) => {
		return [
			{
				header: 'Extra status',
				id: 'extra-status',
				cell: ( props ) => props.row.original.status,
			},
			...columns,
		];
	}
);

Noting though, that if you do it in the dev tools, just trigger manually a rerender of the table by searching something for example.

What about custom filters? Probably we would need something similar to update the query for fetching the pages and is not implemented right now.

Notes

  1. Internally I used TanStack table
  2. There are REST API challenges that will need to be resolved. An example is how to efficiently get the number of total posts/pages in a request, without making a new one(response headers) and another one how to handle the nested entities.

Testing Instructions

  1. In site editor go to Manage all templates and Manage all pages and play around a bit.
  2. Don't focus on styling or bugs, but mostly on how/whether we should abstract functionality.

Any feedback would be appreciated 😄

@ntsekouras ntsekouras added the [Type] Experimental Experimental feature or API. label Aug 24, 2023
@ntsekouras ntsekouras self-assigned this Aug 24, 2023
@github-actions
Copy link

github-actions bot commented Aug 24, 2023

Size Change: +16.7 kB (+1%)

Total Size: 1.64 MB

Filename Size Change
build/edit-site/index.min.js 202 kB +16.6 kB (+9%) 🔍
build/edit-site/style-rtl.css 14 kB +32 B (0%)
build/edit-site/style.css 14 kB +29 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 964 B
build/annotations/index.min.js 2.7 kB
build/api-fetch/index.min.js 2.29 kB
build/autop/index.min.js 2.11 kB
build/blob/index.min.js 461 B
build/block-directory/index.min.js 7.05 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.28 kB
build/block-editor/content.css 4.27 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 218 kB
build/block-editor/style-rtl.css 15.6 kB
build/block-editor/style.css 15.6 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 311 B
build/block-library/blocks/file/style.css 312 B
build/block-library/blocks/file/view.min.js 321 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 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.42 kB
build/block-library/blocks/image/style.css 1.41 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 1.83 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 115 B
build/block-library/blocks/navigation-link/style.css 115 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.25 kB
build/block-library/blocks/navigation/style.css 2.23 kB
build/block-library/blocks/navigation/view.min.js 1.01 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 588 B
build/block-library/blocks/post-featured-image/editor.css 586 B
build/block-library/blocks/post-featured-image/style-rtl.css 322 B
build/block-library/blocks/post-featured-image/style.css 322 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 314 B
build/block-library/blocks/post-template/style.css 314 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 375 B
build/block-library/blocks/query/style.css 372 B
build/block-library/blocks/query/view.min.js 559 B
build/block-library/blocks/quote/style-rtl.css 222 B
build/block-library/blocks/quote/style.css 222 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 132 B
build/block-library/blocks/read-more/style.css 132 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 178 B
build/block-library/blocks/search/editor.css 178 B
build/block-library/blocks/search/style-rtl.css 594 B
build/block-library/blocks/search/style.css 594 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.44 kB
build/block-library/blocks/social-links/style.css 1.43 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.2 kB
build/block-library/editor.css 12.2 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 206 kB
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14 kB
build/block-library/style.css 14 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 246 kB
build/components/style-rtl.css 11.8 kB
build/components/style.css 11.8 kB
build/compose/index.min.js 12.7 kB
build/core-commands/index.min.js 2.62 kB
build/core-data/index.min.js 17.1 kB
build/customize-widgets/index.min.js 12 kB
build/customize-widgets/style-rtl.css 1.51 kB
build/customize-widgets/style.css 1.5 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.6 kB
build/edit-post/style-rtl.css 7.92 kB
build/edit-post/style.css 7.91 kB
build/edit-widgets/index.min.js 17 kB
build/edit-widgets/style-rtl.css 4.84 kB
build/edit-widgets/style.css 4.84 kB
build/editor/index.min.js 45.9 kB
build/editor/style-rtl.css 3.58 kB
build/editor/style.css 3.58 kB
build/element/index.min.js 4.87 kB
build/escape-html/index.min.js 548 B
build/format-library/index.min.js 7.75 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.74 kB
build/keycodes/index.min.js 1.9 kB
build/list-reusable-blocks/index.min.js 2.2 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 kB
build/nux/style-rtl.css 775 B
build/nux/style.css 771 B
build/patterns/index.min.js 3.54 kB
build/patterns/style-rtl.css 325 B
build/patterns/style.css 325 B
build/plugins/index.min.js 1.8 kB
build/preferences-persistence/index.min.js 1.85 kB
build/preferences/index.min.js 1.25 kB
build/primitives/index.min.js 994 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 972 B
build/react-i18n/index.min.js 624 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.72 kB
build/reusable-blocks/style-rtl.css 265 B
build/reusable-blocks/style.css 265 B
build/rich-text/index.min.js 10.2 kB
build/router/index.min.js 1.78 kB
build/server-side-render/index.min.js 1.95 kB
build/shortcode/index.min.js 1.4 kB
build/style-engine/index.min.js 1.98 kB
build/sync/index.min.js 53.8 kB
build/token-list/index.min.js 587 B
build/url/index.min.js 3.84 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 968 B
build/warning/index.min.js 259 B
build/widgets/index.min.js 7.17 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

@nerrad
Copy link
Contributor

nerrad commented Aug 24, 2023

A starter list of extensibility needs for consideration with any list table abstract (including what you've already mentioned):

  • custom filters (REST response, and controls)
  • search query modification
  • filtering quick actions for a row
  • filtering bulk actions
  • filtering core filter behaviour (eg. sortable) for any injected custom column data
  • injected row information (eg. comment count bubbles for comments)
  • filtering column names
  • mobile behaviour on collapse (which columns collapse? which is primary column?)

Feature set considerations (although I imagine this would evolve over time):

  • compact vs extended views
  • bulk edit
  • quick edit (although I imagine there is opportunity to significantly improve the flows depending on the user context)

@kevin940726
Copy link
Member

Thanks for putting up a draft PR for the discussion! @SaxonF has also done something similar in #54006, which I saw you already have a discussion in #53233 as well. I'm not sure where to put my thoughts. I hope this is the right place but we can also move some of the discussions elsewhere or even open a new discussion topic.

The API design here is actually very similar to my first immediate approach when I integrated the library. It accepts some required props like data and columns but also accepts other advanced tableOptions which will be passed to the useReactTable hook internally. However, I quickly abandoned the idea and went for a different approach in #53788.

One issue arose when I wanted to access the internal state of @tanstack/react-table, I couldn't if I didn't have the table instance returned by useReactTable. For instance, for filtering we would have to access the individual state via table.getColumn( 'COLUMN_ID' ).getFilterValue() and table.getColumn( 'COLUMN_ID' ).setFilterValue(). If we don't have the table instance available, then AFAIK we can't leverage the internal filtering feature of react-table. This is my main problem when hiding the useReactTable hook inside another <Table> or <DataTable> component.

We can, however, make everything controlled and use our own state, which seems to be the approach in this PR. It works for more complex cases where we need more controls, but also makes it significantly more difficult to implement a client-only simple table.

The solution I took is to just hoist the useReactTable hook up to the consumer component and let it handle all the options and states. This also feels better because we're not creating another abstraction on top of an already fully-featured library. Furthermore, a hook seems like a better abstraction than a component for this feature. The <Table> component can then just become a purely presentational UI component with some basic preconfigured options if needed.

One drawback of this approach is that we're locked into the @tanstack/react-table dependency, but I don't think it would be a problem. As seen in #53233, and described in shadcn/ui, every table is different, the abstraction for them should be flexible and powerful. If we abstract the package behind a WP <DataTable> component then we'd inevitably have to create a component that is as flexible and powerful as the original, plus all the maintenance costs. A simpler approach IMO is just to guide the users on how to integrate the package with our WP styling-only UI components to create their tables. If the package ever becomes obsolete, we can always update our docs and recommend a different package to use. It's easier for the users to find the resources and docs, easier for us to maintain and develop.

This is not to say that there should be only one abstraction though. If we ever find ourselves building the same table over and over again, for instance, the tables of templates and template parts are mostly the same, we can always extract it into another component too. The abstraction doesn't have to be all top-level either. We can build helpers or smaller hooks and components to implement different features like pagination and filtering.

WDYT? Does any of these make sense 😅? Any concerns or details I'm missing? Happy to discuss more! 🙇

@ntsekouras
Copy link
Contributor Author

ntsekouras commented Aug 31, 2023

Thank you for sharing your insights Kai!

I'm not sure where to put my thoughts.

Some of them I think it makes sense to be in the respective PRs, but probably it's better to try to keep decision related discussions in the main issue. Your comment is about the technical approach, so it would be nice to share a link/cross comment there, or share afterwards a summary of some discussions we're making, in order to be more concise. 🤷

The API design here is actually very similar to my first immediate approach when I integrated the library

API is just a first iteration and we would need to explore more to see if/how we can simplify. That goes for whatever solution we go for.

One issue arose when I wanted to access the internal state of @tanstack/react-table, I couldn't if I didn't have the table instance returned by useReactTable.

That's what this PR is mostly about. The Table component(can be renamed) is actually a context provider, so any other components like filters(even by third party devs) can have access to the instance. useDataTableContext is not currently exported, but we'll need to. Then we work with extensibility and composability in our minds.

We can, however, make everything controlled and use our own state, which seems to be the approach in this PR.

No, that's not something good and it's not what this PR does. It does that for pages list, because it's needed. Media list should be similar too, as the REST API has a cap of 100 entities and besides that, we shouldn't fetch all the entities in one go. There could be cases with hundreds or even thousands of entities.. For templates though(where pagination is not supported) all the state is controlled by the library.

every table is different, the abstraction for them should be flexible and powerful

I think that's the main reason for these explorations. I'd say though that not every table is the same in it's foundation and is different in representation, styles, etc.. By exploring different lists(pages, media, templates, etc..) we can see what is missing and what can be reused, and what will work best eventually.

If we abstract the package behind a WP component then we'd inevitably have to create a component that is as flexible and powerful as the original, plus all the maintenance costs.

This could be simply achieved by passing through all the TanStack options(which we do now), although we don't need to support everything, if we don't want to or can't. By suggesting/guiding using the external library we also add similar maintenance costs IMO. By having a wrapper of that, we don't need to worry about versions of TanStack consumers are using etc.. I think we aim for a table component that can be also used by plugins, to have a coherent user experience.

The drawback of my approach though is that I suggest exposing new APIs(components) early on. These will be locked for start though or just moved to edit site package and not exported.

One drawback of this approach is that we're locked into the @tanstack/react-table dependency

I think this is the same for both approaches. I don't see us replacing TanStack any time soon, after introducing it in our codebase.

I mean, I love we're discussing this and would appreciate more feedback!

@kevin940726
Copy link
Member

The Table component(can be renamed) is actually a context provider, so any other components like filters(even by third party devs) can have access to the instance...

This approach has a hidden gotcha: The table instance returned by useReactTable is a stable instance, so it by default doesn't work with React context. I see that you create a new table instance every time it's rendered, which could lead to over-rendering (not that it should be a concern necessarily though), and also that we can no longer pass the instance into a dependency array of useEffect without unwanted side-effects. This could however be solved by wrapping the instance with a new object though, which seems more performant too.

<DataTableContext.Provider value={ { table } }>

const useDataTableContext = () => useContext( DataTableContext ).table;

This could be simply achieved by passing through all the TanStack options(which we do now)...

True, then we just have to decide whether we want to make it a hook or a component (or both?). I guess the reason we want to use a component here is to also pass the context to the plugin's code underneath? If that's the case then I'm starting to see the point!

By suggesting/guiding using the external library we also add similar maintenance costs IMO. By having a wrapper of that, we don't need to worry about versions of TanStack consumers are using etc.

I was thinking more along the lines of reexporting useReactTable like what we do with React or other similar dependencies. So that we can control the version of the package if needed. I can see that this may introduce other problems if not handled carefully from the start though.

I can now understand your approach more! I was confused by the <DataTable> naming as I thought it was rendering <table> under the hood 😅 . I still don't know which part of the API design in this draft PR is ready for discussion though. What kind of feedback are you seeking at this stage? I think I like this approach now (if I'm not mistaken by anything 😅) and I'd love to help! ❤️

@ntsekouras ntsekouras changed the title [Experiment POC] DataTable component(pages list) [Experiment POC] DataTable component(pages + templates list) Sep 1, 2023
@ciampo
Copy link
Contributor

ciampo commented Sep 3, 2023

I have very little context about this whole project (been AFK for 2 months), but I'm going to leave some considerations:

Where should the table component live

Especially at a point where the component is very experimental, I'd avoid adding it to the @wordpress/components package. The edit-site package could be a better fit, at least initially.

I would also recommend having this component locked until we reach API stability. When/if we are happy with the component and we think it has reach stability and is enough of general purpose component, then we should move it to the @wordpress/components package

About re-exporting useReactTable

This is a risky approach, especially in the light of future-proofing around backwards-compat. What happens if the library stops being maintained, or we wanted to switch to a different third-party library?

Controlled vs uncontrolled

Letting tanstack handle all of the data filtering/sorting etc is definitely the easier choice, although I see that we could allow for controlled mode by exposing/forwarding a few props

Especially considering the requirements for this table to be extensible, we'd need to be sure that all extra data / filters / etc added at loading time would be taken into account by the component correctly even when in uncontrolled mode, and that all of those updates can be batched to avoid re-computing the data multiple times.

In the components package we did something similar with Emotion, which is not exported from the package and thus is only an internal implementation detail.

Monolithic vs subcomponents, and passing data around

Using React context to share data and callbacks amongst subcomponent is a technique used by many libraries (Radix and Ariakit use variations of this technique too, for example), although, as Kai mentions, it gets tricky to deal with object stability while avoiding too many re-renders.

We have the choice of exporting one large monolithic component, or to provide a suite of composable sub-components. The second option has the potential of ensuring more flexibility, but I guess we'd need to understand the requirements correctly so that we can identify which subcomponents to export, and which props should each component expose.

Which APIs to export

I know that this is still highly experimental, but in my experience, given the strict backwards-compat policy, we tend to add the least amount of APIs possible — we can always add more APIs as necessary, but changing or removing APIs already added is always tricky (if not impossible).

My suggestion is to design a complete set of APIs to cover as many use-cases as possible, but to actually add in the code only the ones that are necessary.


I hope that these considerations make sense, and excuses if some of them don't (I may have missed some of the context in that case)

@ntsekouras
Copy link
Contributor Author

ntsekouras commented Sep 4, 2023

Thanks for the feedback all!

I see that you create a new table instance every time it's rendered, which could lead to over-rendering (not that it should be a concern necessarily though)

Exactly! This should be part of the evaluation of the suggested implementation. Personally I don't believe it will be a problem, but we'll have to see how impactful it can be.

I guess the reason we want to use a component here is to also pass the context to the plugin's code underneath?

Yes. Although as you said we could even expose both if we need/want to.

I still don't know which part of the API design in this draft PR is ready for discussion though. What kind of feedback are you seeking at this stage? I think I like this approach now (if I'm not mistaken by anything 😅) and I'd love to help! ❤️

I think discussions are already a bit fruitful, by:

  1. Deciding to make the TanStack an implementation detail and using it inside a DataTable component
  2. Moving these components to edit-site
  3. Probably start by exploring the context provider implementation(like in this PR) and evaluate performance or enhancements

I think the next steps could be:

  1. Move the components in edit site(I'll do that now)
  2. Evaluate and add the filter control with the checkboxes from media library PR. Can we make this reusable? In general I think each of the filters controls should have a wrapper, to ensure extentions from third party consumers are cohesive.
  3. IMO exploring the extensibility API is really important in this stage, although we probably don't have to solve everything in the first iterations. A really important part of it is the add filter functionality based on the current designs from the main issue.
  4. Keep exploring all related lists(pages, media, templates, template parts) but focus on template and template parts lists, even if it's a much simpler use case(being uncontrolled).
  5. Start refining styles and basic components(like the Pagination ones).

So any help for all these would be really helpful. Feel free to explore and push directly here.

In the end, we can extract code and create smaller PRs to land what we need for now.

@ntsekouras
Copy link
Contributor Author

I implemented the logic for the Pagination Numbers component, so we could start refining the styles there and the tags(ex a, Button, etc..) we want. The functionality is similar to WP core's paginate_links function and the comment about the design is here. --cc @WordPress/gutenberg-design

@jameskoster
Copy link
Contributor

Thanks Nik. There are still some ongoing design explorations around pagination. Perhaps for now it makes sense to copy the format on the Pattern management view? This one:

Screenshot 2023-09-05 at 10 30 23

@ntsekouras ntsekouras force-pushed the try/datatable branch 2 times, most recently from 88bc334 to 61d3483 Compare September 14, 2023 12:48
@github-actions
Copy link

github-actions bot commented Sep 14, 2023

Flaky tests detected in c5ecb01a66835f6d9b65c9de029057eb3071c7c9.
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/6272197022
📝 Reported issues:

@ntsekouras ntsekouras force-pushed the try/datatable branch 2 times, most recently from 7e9060b to dc91a20 Compare September 20, 2023 13:13
@ntsekouras ntsekouras force-pushed the try/datatable branch 2 times, most recently from e435f44 to c5ecb01 Compare September 22, 2023 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Experimental Experimental feature or API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants