From d5ded5a0e9b803d189137b6704144107fa609488 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Mon, 2 Oct 2023 14:12:14 +0300 Subject: [PATCH 1/3] [Experimental]: First version of pages list in site editor --- lib/experimental/editor-settings.php | 3 + lib/experiments-page.php | 12 ++ package-lock.json | 46 +++++ packages/edit-site/package.json | 1 + .../src/components/dataviews/context.js | 8 + .../src/components/dataviews/dataviews.js | 60 ++++++ .../dataviews/global-search-input.js | 10 + .../src/components/dataviews/index.js | 2 + .../src/components/dataviews/list-view.js | 112 +++++++++++ .../src/components/dataviews/pagination.js | 119 +++++++++++ .../src/components/dataviews/style.scss | 36 ++++ .../src/components/dataviews/text-filter.js | 37 ++++ .../src/components/dataviews/view-actions.js | 162 +++++++++++++++ .../src/components/page-main/index.js | 3 + .../src/components/page-pages/index.js | 190 ++++++++++++++++++ .../index.js | 19 ++ .../sidebar-navigation-screen-pages/index.js | 15 +- .../edit-site/src/components/sidebar/index.js | 6 + packages/edit-site/src/style.scss | 1 + .../edit-site/src/utils/get-is-list-page.js | 5 +- 20 files changed, 841 insertions(+), 6 deletions(-) create mode 100644 packages/edit-site/src/components/dataviews/context.js create mode 100644 packages/edit-site/src/components/dataviews/dataviews.js create mode 100644 packages/edit-site/src/components/dataviews/global-search-input.js create mode 100644 packages/edit-site/src/components/dataviews/index.js create mode 100644 packages/edit-site/src/components/dataviews/list-view.js create mode 100644 packages/edit-site/src/components/dataviews/pagination.js create mode 100644 packages/edit-site/src/components/dataviews/style.scss create mode 100644 packages/edit-site/src/components/dataviews/text-filter.js create mode 100644 packages/edit-site/src/components/dataviews/view-actions.js create mode 100644 packages/edit-site/src/components/page-pages/index.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pages-list/index.js diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index c09b5cde0f16b..f5cc2ec969d33 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -16,6 +16,9 @@ function gutenberg_enable_experiments() { if ( $gutenberg_experiments && array_key_exists( 'gutenberg-zoomed-out-view', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableZoomedOutView = true', 'before' ); } + if ( $gutenberg_experiments && array_key_exists( 'gutenberg-dataviews', $gutenberg_experiments ) ) { + wp_add_inline_script( 'wp-block-editor', 'window.__experimentalAdminViews = true', 'before' ); + } if ( $gutenberg_experiments && array_key_exists( 'gutenberg-color-randomizer', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableColorRandomizer = true', 'before' ); } diff --git a/lib/experiments-page.php b/lib/experiments-page.php index 133d968ba2cb7..3bf53efd61622 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -67,6 +67,18 @@ function gutenberg_initialize_experiments_settings() { ) ); + add_settings_field( + 'gutenberg-dataviews', + __( 'New admin views', 'gutenberg' ), + 'gutenberg_display_experiment_field', + 'gutenberg-experiments', + 'gutenberg_experiments_section', + array( + 'label' => __( 'Test the new views for different entities like pages.', 'gutenberg' ), + 'id' => 'gutenberg-dataviews', + ) + ); + add_settings_field( 'gutenberg-color-randomizer', __( 'Color randomizer ', 'gutenberg' ), diff --git a/package-lock.json b/package-lock.json index f3e3d5930054a..cc0ac9a5d4bbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13854,6 +13854,37 @@ "resolved": "https://registry.npmjs.org/@tannin/postfix/-/postfix-1.1.0.tgz", "integrity": "sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==" }, + "node_modules/@tanstack/react-table": { + "version": "8.10.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.10.3.tgz", + "integrity": "sha512-Qya1cJ+91arAlW7IRDWksRDnYw28O446jJ/ljkRSc663EaftJoBCAU10M+VV1K6MpCBLrXq1BD5IQc1zj/ZEjA==", + "dependencies": { + "@tanstack/table-core": "8.10.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.10.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.10.3.tgz", + "integrity": "sha512-hJ55YfJlWbfzRROfcyA/kC1aZr/shsLA8XNAwN8jXylhYWGLnPmiJJISrUfj4dMMWRiFi0xBlnlC7MLH+zSrcw==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", @@ -56741,6 +56772,7 @@ "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", + "@tanstack/react-table": "^8.10.3", "@wordpress/a11y": "file:../a11y", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/block-editor": "file:../block-editor", @@ -67791,6 +67823,19 @@ "resolved": "https://registry.npmjs.org/@tannin/postfix/-/postfix-1.1.0.tgz", "integrity": "sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==" }, + "@tanstack/react-table": { + "version": "8.10.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.10.3.tgz", + "integrity": "sha512-Qya1cJ+91arAlW7IRDWksRDnYw28O446jJ/ljkRSc663EaftJoBCAU10M+VV1K6MpCBLrXq1BD5IQc1zj/ZEjA==", + "requires": { + "@tanstack/table-core": "8.10.3" + } + }, + "@tanstack/table-core": { + "version": "8.10.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.10.3.tgz", + "integrity": "sha512-hJ55YfJlWbfzRROfcyA/kC1aZr/shsLA8XNAwN8jXylhYWGLnPmiJJISrUfj4dMMWRiFi0xBlnlC7MLH+zSrcw==" + }, "@testing-library/dom": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", @@ -69910,6 +69955,7 @@ "version": "file:packages/edit-site", "requires": { "@babel/runtime": "^7.16.0", + "@tanstack/react-table": "^8.10.3", "@wordpress/a11y": "file:../a11y", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/block-editor": "file:../block-editor", diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 3441b79ff9dd4..21cff84cf564b 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -27,6 +27,7 @@ "react-native": "src/index", "dependencies": { "@babel/runtime": "^7.16.0", + "@tanstack/react-table": "^8.10.3", "@wordpress/a11y": "file:../a11y", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/block-editor": "file:../block-editor", diff --git a/packages/edit-site/src/components/dataviews/context.js b/packages/edit-site/src/components/dataviews/context.js new file mode 100644 index 0000000000000..4f1e16622a57e --- /dev/null +++ b/packages/edit-site/src/components/dataviews/context.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { createContext, useContext } from '@wordpress/element'; + +const DataViewsContext = createContext( {} ); +export const useDataViewsContext = () => useContext( DataViewsContext ); +export default DataViewsContext; diff --git a/packages/edit-site/src/components/dataviews/dataviews.js b/packages/edit-site/src/components/dataviews/dataviews.js new file mode 100644 index 0000000000000..d147a72e3e9b0 --- /dev/null +++ b/packages/edit-site/src/components/dataviews/dataviews.js @@ -0,0 +1,60 @@ +/** + * External dependencies + */ +import { + getCoreRowModel, + getFilteredRowModel, + getSortedRowModel, + getPaginationRowModel, + useReactTable, +} from '@tanstack/react-table'; + +/** + * WordPress dependencies + */ +import { + __experimentalVStack as VStack, + __experimentalHStack as HStack, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import ListView from './list-view'; +import { Pagination } from './pagination'; +import ViewActions from './view-actions'; +import GlobalSearchInput from './global-search-input'; +import DataViewsContext from './context'; + +export default function DataViews( { + data, + fields, + isLoading, + paginationInfo, + options, +} ) { + const dataView = useReactTable( { + data, + columns: fields, + ...options, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + } ); + return ( + +
+ + + + + + { /* This component will be selected based on viewConfigs. Now we only have the list view. */ } + + + +
+
+ ); +} diff --git a/packages/edit-site/src/components/dataviews/global-search-input.js b/packages/edit-site/src/components/dataviews/global-search-input.js new file mode 100644 index 0000000000000..8bd8022520a28 --- /dev/null +++ b/packages/edit-site/src/components/dataviews/global-search-input.js @@ -0,0 +1,10 @@ +/** + * Internal dependencies + */ +import TextFilter from './text-filter'; +import { useDataViewsContext } from './context'; + +export default function GlobalSearchInput( props ) { + const dataView = useDataViewsContext(); + return ; +} diff --git a/packages/edit-site/src/components/dataviews/index.js b/packages/edit-site/src/components/dataviews/index.js new file mode 100644 index 0000000000000..3fea6fd63714b --- /dev/null +++ b/packages/edit-site/src/components/dataviews/index.js @@ -0,0 +1,2 @@ +export { default as DataViews } from './dataviews'; +export { PAGE_SIZE_VALUES } from './view-actions'; diff --git a/packages/edit-site/src/components/dataviews/list-view.js b/packages/edit-site/src/components/dataviews/list-view.js new file mode 100644 index 0000000000000..e9e9a75cce482 --- /dev/null +++ b/packages/edit-site/src/components/dataviews/list-view.js @@ -0,0 +1,112 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { flexRender } from '@tanstack/react-table'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { chevronDown, chevronUp } from '@wordpress/icons'; +import { Button } from '@wordpress/components'; +import { forwardRef } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { useDataViewsContext } from './context'; + +const sortIcons = { asc: chevronUp, desc: chevronDown }; +function Header( { header } ) { + if ( header.isPlaceholder ) { + return null; + } + const text = flexRender( + header.column.columnDef.header, + header.getContext() + ); + if ( ! header.column.getCanSort() ) { + return text; + } + const sortDirection = header.column.getIsSorted(); + return ( + + + + { createInterpolateElement( + sprintf( + // translators: %1$s: Current page number, %2$s: Total number of pages. + _x( ' of %2$s', 'paging' ), + currentPage, + numPages + ), + { + CurrenPageControl: ( + { + if ( value > numPages ) return; + dataView.setPageIndex( value - 1 ); + } } + step="1" + value={ currentPage } + isDragEnabled={ false } + spinControls="none" + /> + ), + } + ) } + + + + + ) } + + + ); +} diff --git a/packages/edit-site/src/components/dataviews/style.scss b/packages/edit-site/src/components/dataviews/style.scss new file mode 100644 index 0000000000000..47e80782255a4 --- /dev/null +++ b/packages/edit-site/src/components/dataviews/style.scss @@ -0,0 +1,36 @@ +.dataviews-wrapper { + width: 100%; + padding: $grid-unit-40; +} + +.dataviews-list-view { + width: 100%; + text-indent: 0; + border-color: inherit; + border-collapse: collapse; + position: relative; + a { + text-decoration: none; + } + th { + text-align: left; + font-weight: normal; + padding: 0 $grid-unit-20 $grid-unit-20; + color: $gray-700; + } + td, + th { + padding: $grid-unit-15; + &:last-child { + text-align: right; + } + } + tr { + border-bottom: 1px solid $gray-100; + } +} + +.dataviews__per-page-control-prefix { + color: $gray-700; + text-wrap: nowrap; +} diff --git a/packages/edit-site/src/components/dataviews/text-filter.js b/packages/edit-site/src/components/dataviews/text-filter.js new file mode 100644 index 0000000000000..76a06c1448617 --- /dev/null +++ b/packages/edit-site/src/components/dataviews/text-filter.js @@ -0,0 +1,37 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useEffect } from '@wordpress/element'; +import { SearchControl } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import useDebouncedInput from '../../utils/use-debounced-input'; + +export default function TextFilter( { + className, + searchLabel = __( 'Filter list' ), + onChange, +} ) { + const [ search, setSearch, debouncedSearch ] = useDebouncedInput(); + useEffect( () => { + onChange( debouncedSearch ); + }, [ debouncedSearch, onChange ] ); + return ( + + ); +} diff --git a/packages/edit-site/src/components/dataviews/view-actions.js b/packages/edit-site/src/components/dataviews/view-actions.js new file mode 100644 index 0000000000000..2e35bfaf7caac --- /dev/null +++ b/packages/edit-site/src/components/dataviews/view-actions.js @@ -0,0 +1,162 @@ +/** + * WordPress dependencies + */ +import { + Button, + Icon, + SelectControl, + privateApis as componentsPrivateApis, + __experimentalInputControlPrefixWrapper as InputControlPrefixWrapper, +} from '@wordpress/components'; +import { + chevronRightSmall, + check, + blockTable, + chevronDown, +} from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import { useDataViewsContext } from './context'; + +const { + DropdownMenuV2, + DropdownMenuGroupV2, + DropdownMenuItemV2, + DropdownSubMenuV2, + DropdownSubMenuTriggerV2, +} = unlock( componentsPrivateApis ); + +export const PAGE_SIZE_VALUES = [ 5, 20, 50 ]; + +export function PageSizeControl() { + const dataView = useDataViewsContext(); + const label = __( 'Rows per page:' ); + return ( + + { label } + + } + value={ dataView.getState().pagination.pageSize } + options={ PAGE_SIZE_VALUES.map( ( pageSize ) => ( { + value: pageSize, + label: pageSize, + } ) ) } + onChange={ ( value ) => dataView.setPageSize( +value ) } + /> + ); +} + +function PageSizeMenu() { + const dataView = useDataViewsContext(); + const currenPageSize = dataView.getState().pagination.pageSize; + return ( + + { currenPageSize } + { ' ' } + + } + > + { /* TODO: probably label per view type. */ } + { __( 'Rows per page' ) } + + } + > + { PAGE_SIZE_VALUES.map( ( size ) => { + return ( + + } + onSelect={ ( event ) => { + // We need to handle this on DropDown component probably.. + event.preventDefault(); + dataView.setPageSize( size ); + } } + // TODO: check about role and a11y. + role="menuitemcheckbox" + > + { size } + + ); + } ) } + + ); +} + +function FieldsVisibilityMenu() { + const dataView = useDataViewsContext(); + const hideableFields = dataView + .getAllColumns() + .filter( ( columnn ) => columnn.getCanHide() ); + if ( ! hideableFields?.length ) { + return null; + } + return ( + } + > + { __( 'Fields' ) } + + } + > + { hideableFields?.map( ( column ) => { + return ( + + } + onSelect={ ( event ) => { + event.preventDefault(); + column.getToggleVisibilityHandler()( event ); + } } + role="menuitemcheckbox" + > + { column.columnDef.header } + + ); + } ) } + + ); +} + +export default function ViewActions( { className } ) { + return ( + + { __( 'View' ) } + + + } + > + + + + + + ); +} diff --git a/packages/edit-site/src/components/page-main/index.js b/packages/edit-site/src/components/page-main/index.js index af017a8db9700..10b5b99dc2fbf 100644 --- a/packages/edit-site/src/components/page-main/index.js +++ b/packages/edit-site/src/components/page-main/index.js @@ -9,6 +9,7 @@ import { privateApis as routerPrivateApis } from '@wordpress/router'; import PagePatterns from '../page-patterns'; import PageTemplateParts from '../page-template-parts'; import PageTemplates from '../page-templates'; +import PagePages from '../page-pages'; import { unlock } from '../../lock-unlock'; const { useLocation } = unlock( routerPrivateApis ); @@ -24,6 +25,8 @@ export default function PageMain() { return ; } else if ( path === '/patterns' ) { return ; + } else if ( window?.__experimentalAdminViews && path === '/pages' ) { + return ; } return null; diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js new file mode 100644 index 0000000000000..e7b8f913efe7c --- /dev/null +++ b/packages/edit-site/src/components/page-pages/index.js @@ -0,0 +1,190 @@ +/** + * WordPress dependencies + */ +import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; +import { + VisuallyHidden, + __experimentalHeading as Heading, + __experimentalVStack as VStack, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { useEntityRecords } from '@wordpress/core-data'; +import { decodeEntities } from '@wordpress/html-entities'; +import { useState, useEffect, useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Page from '../page'; +import Link from '../routes/link'; +import PageActions from '../page-actions'; +import { DataViews, PAGE_SIZE_VALUES } from '../dataviews'; + +const EMPTY_ARRAY = []; + +export default function PagePages() { + const [ reset, setResetQuery ] = useState( ( v ) => ! v ); + const [ globalFilter, setGlobalFilter ] = useState( '' ); + const [ paginationInfo, setPaginationInfo ] = useState(); + const [ { pageIndex, pageSize }, setPagination ] = useState( { + pageIndex: 0, + pageSize: PAGE_SIZE_VALUES[ 0 ], + } ); + // Request post statuses to get the proper labels. + const [ postStatuses, setPostStatuses ] = useState( EMPTY_ARRAY ); + useEffect( () => { + apiFetch( { + path: '/wp/v2/statuses', + } ).then( setPostStatuses ); + }, [] ); + + // TODO: probably memo other objects passed as state(ex:https://tanstack.com/table/v8/docs/examples/react/pagination-controlled). + const pagination = useMemo( + () => ( { pageIndex, pageSize } ), + [ pageIndex, pageSize ] + ); + const [ sorting, setSorting ] = useState( [ + { order: 'desc', orderby: 'date' }, + ] ); + const queryArgs = useMemo( + () => ( { + per_page: pageSize, + page: pageIndex + 1, // tanstack starts from zero. + _embed: 'author', + order: sorting[ 0 ]?.desc ? 'desc' : 'asc', + orderby: sorting[ 0 ]?.id, + search: globalFilter, + status: [ 'publish', 'draft' ], + } ), + [ + globalFilter, + sorting[ 0 ]?.id, + sorting[ 0 ]?.desc, + pageSize, + pageIndex, + reset, + ] + ); + const { records, isResolving: isLoading } = useEntityRecords( + 'postType', + 'page', + queryArgs + ); + useEffect( () => { + // Make extra request to handle controlled pagination. + apiFetch( { + path: addQueryArgs( '/wp/v2/pages', { + ...queryArgs, + _fields: 'id', + } ), + method: 'HEAD', + parse: false, + } ).then( ( res ) => { + const totalPages = parseInt( res.headers.get( 'X-WP-TotalPages' ) ); + const totalItems = parseInt( res.headers.get( 'X-WP-Total' ) ); + setPaginationInfo( { + totalPages, + totalItems, + } ); + } ); + // Status should not make extra request if already did.. + }, [ globalFilter, pageSize, reset ] ); + + const fields = useMemo( + () => [ + { + header: __( 'Title' ), + id: 'title', + accessorFn: ( page ) => page.title?.rendered || page.slug, + cell: ( props ) => { + const page = props.row.original; + return ( + + + + { decodeEntities( props.getValue() ) } + + + + ); + }, + maxWidth: 400, + sortingFn: 'alphanumeric', + enableHiding: false, + }, + { + header: __( 'Author' ), + id: 'author', + accessorFn: ( page ) => page._embedded?.author[ 0 ]?.name, + cell: ( props ) => { + const author = props.row.original._embedded?.author[ 0 ]; + return ( + + { author.name } + + ); + }, + }, + { + header: 'Status', + id: 'status', + cell: ( props ) => + postStatuses[ props.row.original.status ]?.name, + }, + { + header: { __( 'Actions' ) }, + id: 'actions', + cell: ( props ) => { + const page = props.row.original; + return ( + setResetQuery() } + /> + ); + }, + enableHiding: false, + }, + ], + [ postStatuses ] + ); + + // TODO: we need to handle properly `data={ data || EMPTY_ARRAY }` for when `isLoading`. + return ( + + { + setGlobalFilter( value ); + setPagination( { pageIndex: 0, pageSize } ); + }, + // TODO: check these callbacks and maybe reset the query when needed... + onPaginationChange: setPagination, + meta: { resetQuery: setResetQuery }, + } } + /> + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pages-list/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pages-list/index.js new file mode 100644 index 0000000000000..fca8fa1dbac30 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pages-list/index.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import SidebarNavigationScreen from '../sidebar-navigation-screen'; + +export default function SidebarNavigationScreenPagesList() { + return ( + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js index 4d143235e9597..e9a6163a0047e 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js @@ -136,6 +136,16 @@ export default function SidebarNavigationScreenPages() { }; }; + const pagesLink = useLink( { path: '/pages' } ); + const manageAllPagesProps = window?.__experimentalAdminViews + ? { ...pagesLink } + : { + href: 'edit.php?post_type=page', + onClick: () => { + document.location = 'edit.php?post_type=page'; + }, + }; + return ( <> { showAddPage && ( @@ -220,10 +230,7 @@ export default function SidebarNavigationScreenPages() { ) ) } { - document.location = 'edit.php?post_type=page'; - } } + { ...manageAllPagesProps } > { __( 'Manage all pages' ) } diff --git a/packages/edit-site/src/components/sidebar/index.js b/packages/edit-site/src/components/sidebar/index.js index 9e035759ea9ad..183046b87a426 100644 --- a/packages/edit-site/src/components/sidebar/index.js +++ b/packages/edit-site/src/components/sidebar/index.js @@ -27,6 +27,7 @@ import SaveHub from '../save-hub'; import { unlock } from '../../lock-unlock'; import SidebarNavigationScreenPages from '../sidebar-navigation-screen-pages'; import SidebarNavigationScreenPage from '../sidebar-navigation-screen-page'; +import SidebarNavigationScreenPagesList from '../sidebar-navigation-screen-pages-list'; const { useLocation } = unlock( routerPrivateApis ); @@ -53,6 +54,11 @@ function SidebarScreens() { + { window?.__experimentalAdminViews && ( + + + + ) } diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 111696241d0d6..e95cd3571c419 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -4,6 +4,7 @@ @import "./components/block-editor/style.scss"; @import "./components/canvas-loader/style.scss"; @import "./components/code-editor/style.scss"; +@import "./components/dataviews/style.scss"; @import "./components/global-styles/style.scss"; @import "./components/global-styles/screen-revisions/style.scss"; @import "./components/header-edit-mode/style.scss"; diff --git a/packages/edit-site/src/utils/get-is-list-page.js b/packages/edit-site/src/utils/get-is-list-page.js index 600e686618bf9..2ee661253cf06 100644 --- a/packages/edit-site/src/utils/get-is-list-page.js +++ b/packages/edit-site/src/utils/get-is-list-page.js @@ -14,8 +14,9 @@ export default function getIsListPage( isMobileViewport ) { return ( - path === '/wp_template/all' || - path === '/wp_template_part/all' || + [ '/wp_template/all', '/wp_template_part/all', '/pages' ].includes( + path + ) || ( path === '/patterns' && // Don't treat "/patterns" without categoryType and categoryId as a // list page in mobile because the sidebar covers the whole page. From 532d337fd770ce7a997704379900ba65b00a92d2 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Mon, 2 Oct 2023 15:35:46 +0300 Subject: [PATCH 2/3] remove DataViewsContext and SidebarNavigationScreenPagesList --- .../src/components/dataviews/context.js | 8 ----- .../src/components/dataviews/dataviews.js | 30 +++++++++---------- .../dataviews/global-search-input.js | 10 ------- .../src/components/dataviews/list-view.js | 8 +---- .../src/components/dataviews/pagination.js | 5 ++-- .../src/components/dataviews/view-actions.js | 16 ++++------ .../index.js | 19 ------------ .../edit-site/src/components/sidebar/index.js | 9 ++++-- 8 files changed, 31 insertions(+), 74 deletions(-) delete mode 100644 packages/edit-site/src/components/dataviews/context.js delete mode 100644 packages/edit-site/src/components/dataviews/global-search-input.js delete mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pages-list/index.js diff --git a/packages/edit-site/src/components/dataviews/context.js b/packages/edit-site/src/components/dataviews/context.js deleted file mode 100644 index 4f1e16622a57e..0000000000000 --- a/packages/edit-site/src/components/dataviews/context.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * WordPress dependencies - */ -import { createContext, useContext } from '@wordpress/element'; - -const DataViewsContext = createContext( {} ); -export const useDataViewsContext = () => useContext( DataViewsContext ); -export default DataViewsContext; diff --git a/packages/edit-site/src/components/dataviews/dataviews.js b/packages/edit-site/src/components/dataviews/dataviews.js index d147a72e3e9b0..bf492cb5691a3 100644 --- a/packages/edit-site/src/components/dataviews/dataviews.js +++ b/packages/edit-site/src/components/dataviews/dataviews.js @@ -23,8 +23,7 @@ import { import ListView from './list-view'; import { Pagination } from './pagination'; import ViewActions from './view-actions'; -import GlobalSearchInput from './global-search-input'; -import DataViewsContext from './context'; +import TextFilter from './text-filter'; export default function DataViews( { data, @@ -43,18 +42,19 @@ export default function DataViews( { getPaginationRowModel: getPaginationRowModel(), } ); return ( - -
- - - - - - { /* This component will be selected based on viewConfigs. Now we only have the list view. */ } - - - -
-
+
+ + + + + + { /* This component will be selected based on viewConfigs. Now we only have the list view. */ } + + + +
); } diff --git a/packages/edit-site/src/components/dataviews/global-search-input.js b/packages/edit-site/src/components/dataviews/global-search-input.js deleted file mode 100644 index 8bd8022520a28..0000000000000 --- a/packages/edit-site/src/components/dataviews/global-search-input.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Internal dependencies - */ -import TextFilter from './text-filter'; -import { useDataViewsContext } from './context'; - -export default function GlobalSearchInput( props ) { - const dataView = useDataViewsContext(); - return ; -} diff --git a/packages/edit-site/src/components/dataviews/list-view.js b/packages/edit-site/src/components/dataviews/list-view.js index e9e9a75cce482..55a871d3df8c9 100644 --- a/packages/edit-site/src/components/dataviews/list-view.js +++ b/packages/edit-site/src/components/dataviews/list-view.js @@ -12,11 +12,6 @@ import { chevronDown, chevronUp } from '@wordpress/icons'; import { Button } from '@wordpress/components'; import { forwardRef } from '@wordpress/element'; -/** - * Internal dependencies - */ -import { useDataViewsContext } from './context'; - const sortIcons = { asc: chevronUp, desc: chevronDown }; function Header( { header } ) { if ( header.isPlaceholder ) { @@ -41,8 +36,7 @@ function Header( { header } ) { ); } -function ListView( { className, isLoading = false }, ref ) { - const dataView = useDataViewsContext(); +function ListView( { dataView, className, isLoading = false }, ref ) { const { rows } = dataView.getRowModel(); const hasRows = !! rows?.length; if ( isLoading ) { diff --git a/packages/edit-site/src/components/dataviews/pagination.js b/packages/edit-site/src/components/dataviews/pagination.js index 2d1b0d1155b2e..3adecd2bb8085 100644 --- a/packages/edit-site/src/components/dataviews/pagination.js +++ b/packages/edit-site/src/components/dataviews/pagination.js @@ -13,17 +13,16 @@ import { sprintf, __, _x, _n } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useDataViewsContext } from './context'; import { PageSizeControl } from './view-actions'; // For now this is copied from the patterns list Pagination component, because // the datatable pagination starts from index zero(`0`). Eventually all lists will be // using this one. export function Pagination( { + dataView, // If passed, use it, as it's for controlled pagination. totalItems = 0, } ) { - const dataView = useDataViewsContext(); const currentPage = dataView.getState().pagination.pageIndex + 1; const numPages = dataView.getPageCount(); const _totalItems = totalItems || dataView.getCoreRowModel().rows.length; @@ -113,7 +112,7 @@ export function Pagination( { ) } - + ); } diff --git a/packages/edit-site/src/components/dataviews/view-actions.js b/packages/edit-site/src/components/dataviews/view-actions.js index 2e35bfaf7caac..a13e2cdf25865 100644 --- a/packages/edit-site/src/components/dataviews/view-actions.js +++ b/packages/edit-site/src/components/dataviews/view-actions.js @@ -20,7 +20,6 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import { unlock } from '../../lock-unlock'; -import { useDataViewsContext } from './context'; const { DropdownMenuV2, @@ -32,8 +31,7 @@ const { export const PAGE_SIZE_VALUES = [ 5, 20, 50 ]; -export function PageSizeControl() { - const dataView = useDataViewsContext(); +export function PageSizeControl( { dataView } ) { const label = __( 'Rows per page:' ); return ( columnn.getCanHide() ); @@ -141,7 +137,7 @@ function FieldsVisibilityMenu() { ); } -export default function ViewActions( { className } ) { +export default function ViewActions( { dataView, className } ) { return ( - - + + ); diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pages-list/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pages-list/index.js deleted file mode 100644 index fca8fa1dbac30..0000000000000 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pages-list/index.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import SidebarNavigationScreen from '../sidebar-navigation-screen'; - -export default function SidebarNavigationScreenPagesList() { - return ( - - ); -} diff --git a/packages/edit-site/src/components/sidebar/index.js b/packages/edit-site/src/components/sidebar/index.js index 183046b87a426..a233cf1eca198 100644 --- a/packages/edit-site/src/components/sidebar/index.js +++ b/packages/edit-site/src/components/sidebar/index.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { memo, useRef } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; import { __experimentalNavigatorProvider as NavigatorProvider, __experimentalNavigatorScreen as NavigatorScreen, @@ -27,7 +28,7 @@ import SaveHub from '../save-hub'; import { unlock } from '../../lock-unlock'; import SidebarNavigationScreenPages from '../sidebar-navigation-screen-pages'; import SidebarNavigationScreenPage from '../sidebar-navigation-screen-page'; -import SidebarNavigationScreenPagesList from '../sidebar-navigation-screen-pages-list'; +import SidebarNavigationScreen from '../sidebar-navigation-screen'; const { useLocation } = unlock( routerPrivateApis ); @@ -56,7 +57,11 @@ function SidebarScreens() { { window?.__experimentalAdminViews && ( - + ) } From 625810f3df5c9989a5c8f47e00ca758b667147cc Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Tue, 3 Oct 2023 09:18:19 +0300 Subject: [PATCH 3/3] add sorting controls in view actions --- .../src/components/dataviews/view-actions.js | 100 ++++++++++++++++-- 1 file changed, 94 insertions(+), 6 deletions(-) diff --git a/packages/edit-site/src/components/dataviews/view-actions.js b/packages/edit-site/src/components/dataviews/view-actions.js index a13e2cdf25865..1ede6ebcd8b75 100644 --- a/packages/edit-site/src/components/dataviews/view-actions.js +++ b/packages/edit-site/src/components/dataviews/view-actions.js @@ -13,6 +13,8 @@ import { check, blockTable, chevronDown, + arrowUp, + arrowDown, } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; @@ -67,7 +69,7 @@ function PageSizeMenu( { dataView } ) { suffix={ <> { currenPageSize } - { ' ' } + } > @@ -116,20 +118,20 @@ function FieldsVisibilityMenu( { dataView } ) { } > - { hideableFields?.map( ( column ) => { + { hideableFields?.map( ( field ) => { return ( + field.getIsVisible() && } onSelect={ ( event ) => { event.preventDefault(); - column.getToggleVisibilityHandler()( event ); + field.getToggleVisibilityHandler()( event ); } } role="menuitemcheckbox" > - { column.columnDef.header } + { field.columnDef.header } ); } ) } @@ -137,6 +139,91 @@ function FieldsVisibilityMenu( { dataView } ) { ); } +// This object is used to construct the sorting options per sortable field. +const sortingItemsInfo = { + asc: { icon: arrowUp, label: __( 'Sort ascending' ) }, + desc: { icon: arrowDown, label: __( 'Sort descending' ) }, +}; +function SortMenu( { dataView } ) { + const sortableFields = dataView + .getAllColumns() + .filter( ( columnn ) => columnn.getCanSort() ); + if ( ! sortableFields?.length ) { + return null; + } + const currentSortedField = sortableFields.find( ( field ) => + field.getIsSorted() + ); + return ( + + { currentSortedField?.columnDef.header } + + + } + > + { __( 'Sort by' ) } + + } + > + { sortableFields?.map( ( field ) => { + const sortedDirection = field.getIsSorted(); + return ( + } + > + { field.columnDef.header } + + } + side="left" + > + { Object.entries( sortingItemsInfo ).map( + ( [ direction, info ] ) => { + return ( + } + suffix={ + sortedDirection === direction && ( + + ) + } + onSelect={ ( event ) => { + event.preventDefault(); + if ( + sortedDirection === direction + ) { + dataView.resetSorting(); + } else { + dataView.setSorting( [ + { + id: field.id, + desc: + direction === + 'desc', + }, + ] ); + } + } } + > + { info.label } + + ); + } + ) } + + ); + } ) } + + ); +} + export default function ViewActions( { dataView, className } ) { return ( +