WordPress has some sweet, sweet new APIs. One of them I am super excited about is Data Views. Data Views is the API to introduce modern table views into WordPress and I was excited to try it out for my Personal OS plugin.
Example: Custom taxonomy
In my example, I wanted to create a “Bucketlist” page in my WP-Admin to render a custom taxonomy that would hold my Bucketlist. We will need 3 elements:
- We will need to hook into PHP to create a new WP-Admin page and render React there
- React code to display Data Views
- A build process to compile this React code.
React Gutenberg code
I will start with the actual Gutenberg React code. I put my code together using this fantastic tutorialand the code from WordPress Storybook
import { Icon, __experimentalHStack as HStack } from '@wordpress/components';
import domReady from '@wordpress/dom-ready';
import { useState, useMemo, createRoot } from '@wordpress/element';
// Per https://github.com/WordPress/gutenberg/tree/trunk/packages/dataviews :
// Important note If you're trying to use the DataViews component in a WordPress plugin or theme and you're building your scripts using the @wordpress/scripts package, you need to import the components from @wordpress/dataviews/wp instead of @wordpress/dataviews.
import { DataViews, filterSortAndPaginate } from '@wordpress/dataviews/wp';
import { __ } from '@wordpress/i18n';
import { useEntityRecords } from '@wordpress/core-data';
import './style.scss';
import { trash, flag } from '@wordpress/icons';
function NotebookAdmin() {
const [view, setView] = useState( {
type: 'table',
search: '',
page: 1,
perPage: 100,
fields: ['name', 'description', 'flags', 'count'],
layout: {},
filters: [],
sort: {
order: 'asc',
orderby: 'name',
},
} );
// Our setup in this custom taxonomy.
const fields = [
{
label: __( 'Name', 'your-textdomain' ),
id: 'name',
enableHiding: false,
enableGlobalSearch: true,
type: 'string',
},
{
label: __( 'Description', 'your-textdomain' ),
id: 'description',
enableSorting: false,
enableGlobalSearch: true,
type: 'string',
},
{
label: __( 'Flags', 'your-textdomain' ),
id: 'flags',
header: (
<HStack spacing={ 1 } justify="start">
<Icon icon={ flag } />
<span>{ __( 'Flags', 'your-textdomain' ) }</span>
</HStack>
),
type: 'array',
render: ( { item } ) => {
return item.meta?.flag?.join( ', ' ) || '';
},
enableSorting: false,
},
{
label: __( 'Count', 'your-textdomain' ),
id: 'count',
enableSorting: true,
enableGlobalSearch: false,
type: 'number',
},
];
// We will use the entity records hook to fetch all the items from the "notebook" custom taxonomy
const { records } = useEntityRecords( 'taxonomy', 'notebook', {
per_page: -1,
page: 1,
hide_empty: false,
} );
// filterSortAndPaginate works in memory. We theoretically could pass the parameters to backend to filter sort and paginate there.
const { data: shownData, paginationInfo } = useMemo( () => {
return filterSortAndPaginate( records, view, fields );
}, [view, records] );
return (
<DataViews
getItemId={ ( item ) => item.id.toString() }
paginationInfo={ paginationInfo }
data={ shownData }
view={ view }
fields={ fields }
onChangeView={ setView }
actions={ [
{
id: 'delete',
label: __( 'Delete', 'your-textdomain' ),
icon: trash,
callback: async ( items ) => {
// Implement delete functionality
console.log( 'Delete items:', items );
},
},
] }
defaultLayouts={ {
table: {
// Define default table layout settings
spacing: 'normal',
showHeader: true,
},
} }
/>
);
}
domReady( () => {
const root = createRoot( document.getElementById( 'bucketlist-root' ) );
root.render( <NotebookAdmin /> );
} );
In your plugin you have to import the components from @wordpress/dataviews/wp instead of @wordpress/dataviews. I expect this is a recent change and it will be further evolve. You also need to add
“@wordpress/dependency-extraction-webpack-plugin”: “^6.14.0”
To your devpependencies (check at the end of this post)
If you you import directly from @wordpress/dataviews you will get errors like these (click to expand)
column-header-menu.js:105 Uncaught TypeError: Cannot read properties of undefined (reading 'Group')
at HeaderMenu (column-header-menu.js:105:44)
at renderWithHooks (react-dom.js?ver=18.3.1:15496:20)
at updateForwardRef (react-dom.js?ver=18.3.1:19255:22)
at beginWork (react-dom.js?ver=18.3.1:21685:18)
at HTMLUnknownElement.callCallback (react-dom.js?ver=18.3.1:4151:16)
at Object.invokeGuardedCallbackDev (react-dom.js?ver=18.3.1:4200:18)
at invokeGuardedCallback (react-dom.js?ver=18.3.1:4264:33)
at beginWork$1 (react-dom.js?ver=18.3.1:27500:9)
at performUnitOfWork (react-dom.js?ver=18.3.1:26606:14)
at workLoopSync (react-dom.js?ver=18.3.1:26515:7)
react-dom.js?ver=18.3.1:18714 The above error occurred in the <AddFilterMenu> component:
at AddFilterMenu (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:352:3)
at div
at FiltersToggle (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:742:3)
at div
at http://localhost:8901/wp-includes/js/dist/components.js?ver=490baf0…:14024:47
at UnforwardedView (http://localhost:8901/wp-includes/js/dist/components.js?ver=490baf0…:14903:3)
at UnconnectedHStack (http://localhost:8901/wp-includes/js/dist/components.js?ver=490baf0…:32221:23)
at div
at http://localhost:8901/wp-includes/js/dist/components.js?ver=490baf0…:14024:47
at UnforwardedView (http://localhost:8901/wp-includes/js/dist/components.js?ver=490baf0…:14903:3)
at UnconnectedHStack (http://localhost:8901/wp-includes/js/dist/components.js?ver=490baf0…:32221:23)
at div
at DataViews (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:2415:3)
at BucketlistAdmin (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:14023:87)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
3
react-dom.js?ver=18.3.1:18714 The above error occurred in the <ForwardRef(HeaderMenu)> component:
at HeaderMenu (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:3894:3)
at th
at tr
at thead
at table
at ViewTable (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:4363:3)
at DataViewsLayout (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:1526:69)
at div
at DataViews (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:2415:3)
at BucketlistAdmin (http://localhost:8901/wp-content/plugins/personalos/modules/bucketlist/js/build/admin.js?ver=5a41136…:14023:87)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
react-dom.js?ver=18.3.1:26972 Uncaught TypeError: Cannot read properties of undefined (reading 'Item')
at add-filter.js:32:31
at Array.map (<anonymous>)
at AddFilterMenu (add-filter.js:31:1)
at renderWithHooks (react-dom.js?ver=18.3.1:15496:20)
at mountIndeterminateComponent (react-dom.js?ver=18.3.1:20113:15)
at beginWork (react-dom.js?ver=18.3.1:21636:18)
at beginWork$1 (react-dom.js?ver=18.3.1:27475:16)
at performUnitOfWork (react-dom.js?ver=18.3.1:26606:14)
at workLoopSync (react-dom.js?ver=18.3.1:26515:7)
at renderRootSync (react-dom.js?ver=18.3.1:26483:9)
PHP end
In PHP, you have to:
- set up a WP-Admin page
- enqueue your script
- enqueue your style
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
public function add_admin_menu(): void {
add_menu_page(
'Bucketlist',
'Bucketlist',
'read',
'bucketlist',
array( $this, 'render_admin_page' ),
'dashicons-list-view'
);
}
public function render_admin_page(): void {
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<div id="bucketlist-root"></div>
</div>
<?php
}
public function enqueue_admin_scripts( string $hook ): void {
if ( 'toplevel_page_bucketlist' !== $hook ) {
return;
}
$asset_file = plugin_dir_path( __FILE__ ) . 'js/build/admin.asset.php';
$asset = require $asset_file;
wp_enqueue_script(
'bucketlist-admin',
plugin_dir_url( __FILE__ ) . 'js/build/admin.js',
$asset['dependencies'],
$asset['version'],
false
);
wp_enqueue_style(
'bucketlist-admin',
plugin_dir_url( __FILE__ ) . 'js/build/style-admin.css',
array(),
$asset['version']
);
}
Build code
This is my package.json
. Remember to add ”@wordpress/dependency-extraction-webpack-plugin”: “^6.14.0″
in this specific version.
Otherwise, wp-scripts
will treat dataviews as WordPress dependency and it won’t render in your app!
{
"name": "bucketlist",
"version": "1.0.0",
"description": "Bucketlist module for PersonalOS",
"scripts": {
"build": "wp-scripts build js/src/admin.js --output-path=js/build",
"start": "wp-scripts start js/src/admin.js --output-path=js/build"
},
"dependencies": {
"@wordpress/components": "^25.0.0",
"@wordpress/data": "^9.0.0",
"@wordpress/dataviews": "^4.10.0",
"@wordpress/element": "^5.0.0"
},
"devDependencies": {
"@wordpress/scripts": "^30.7.0",
"@wordpress/dependency-extraction-webpack-plugin": "^6.14.0"
}
}
For style tweaks and the rest you can refer to this fantastic walkthrough
The post WordPress Data Views: Basic setup appeared first on Artur Piszek.
Top comments (0)