diff --git a/packages/dataviews/src/stories/fixtures.js b/packages/dataviews/src/stories/fixtures.js
new file mode 100644
index 0000000000000..6b9073e2cc78d
--- /dev/null
+++ b/packages/dataviews/src/stories/fixtures.js
@@ -0,0 +1,126 @@
+/**
+ * WordPress dependencies
+ */
+import { trash } from '@wordpress/icons';
+import {
+ Button,
+ __experimentalText as Text,
+ __experimentalHStack as HStack,
+ __experimentalVStack as VStack,
+} from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import { LAYOUT_TABLE } from '../constants';
+
+export const data = [
+ {
+ id: 1,
+ title: 'Apollo',
+ description: 'Apollo description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+ {
+ id: 2,
+ title: 'Space',
+ description: 'Space description',
+ image: 'https://live.staticflickr.com/5678/21911065441_92e2d44708_b.jpg',
+ },
+ {
+ id: 3,
+ title: 'NASA',
+ description: 'NASA photo',
+ image: 'https://live.staticflickr.com/742/21712365770_8f70a2c91e_b.jpg',
+ },
+ {
+ id: 4,
+ title: 'Neptune',
+ description: 'Neptune description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+ {
+ id: 5,
+ title: 'Mercury',
+ description: 'Mercury description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+ {
+ id: 6,
+ title: 'Venus',
+ description: 'Venus description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+ {
+ id: 7,
+ title: 'Earth',
+ description: 'Earth description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+ {
+ id: 8,
+ title: 'Mars',
+ description: 'Mars description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+ {
+ id: 9,
+ title: 'Jupiter',
+ description: 'Jupiter description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+ {
+ id: 10,
+ title: 'Saturn',
+ description: 'Saturn description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+ {
+ id: 11,
+ title: 'Uranus',
+ description: 'Uranus description',
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
+ },
+];
+
+export const DEFAULT_VIEW = {
+ type: LAYOUT_TABLE,
+ search: '',
+ page: 1,
+ perPage: 10,
+ hiddenFields: [ 'image' ],
+ layout: {},
+ filters: [],
+};
+
+export const actions = [
+ {
+ id: 'delete',
+ label: 'Delete item',
+ isPrimary: true,
+ icon: trash,
+ hideModalHeader: true,
+ RenderModal: ( { item, closeModal } ) => {
+ return (
+
+
+ { `Are you sure you want to delete "${ item.title }"?` }
+
+
+
+
+
+
+ );
+ },
+ },
+ {
+ id: 'secondary',
+ label: 'Secondary action',
+ callback() {},
+ },
+];
diff --git a/packages/dataviews/src/stories/index.story.js b/packages/dataviews/src/stories/index.story.js
new file mode 100644
index 0000000000000..e0bea0c92c2b2
--- /dev/null
+++ b/packages/dataviews/src/stories/index.story.js
@@ -0,0 +1,137 @@
+/**
+ * WordPress dependencies
+ */
+import { useState, useMemo, useCallback } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { DataViews, LAYOUT_GRID, LAYOUT_TABLE } from '../index';
+
+import { DEFAULT_VIEW, actions, data } from './fixtures';
+
+const meta = {
+ title: 'DataViews (Experimental)/DataViews',
+ component: DataViews,
+};
+export default meta;
+
+const defaultConfigPerViewType = {
+ [ LAYOUT_TABLE ]: {},
+ [ LAYOUT_GRID ]: {
+ mediaField: 'image',
+ primaryField: 'title',
+ },
+};
+
+function normalizeSearchInput( input = '' ) {
+ return input.trim().toLowerCase();
+}
+
+const fields = [
+ {
+ header: 'Image',
+ id: 'image',
+ render: ( { item } ) => {
+ return (
+
+ );
+ },
+ width: 50,
+ enableSorting: false,
+ },
+ {
+ header: 'Title',
+ id: 'title',
+ getValue: ( { item } ) => item.title,
+ maxWidth: 400,
+ enableHiding: false,
+ },
+ {
+ header: 'Description',
+ id: 'description',
+ getValue: ( { item } ) => item.description,
+ maxWidth: 200,
+ enableSorting: false,
+ },
+];
+
+export const Default = ( props ) => {
+ const [ view, setView ] = useState( DEFAULT_VIEW );
+ const { shownData, paginationInfo } = useMemo( () => {
+ let filteredData = [ ...data ];
+ // Handle global search.
+ if ( view.search ) {
+ const normalizedSearch = normalizeSearchInput( view.search );
+ filteredData = filteredData.filter( ( item ) => {
+ return [
+ normalizeSearchInput( item.title ),
+ normalizeSearchInput( item.description ),
+ ].some( ( field ) => field.includes( normalizedSearch ) );
+ } );
+ }
+ // Handle sorting.
+ if ( view.sort ) {
+ const stringSortingFields = [ 'title' ];
+ const fieldId = view.sort.field;
+ if ( stringSortingFields.includes( fieldId ) ) {
+ const fieldToSort = fields.find( ( field ) => {
+ return field.id === fieldId;
+ } );
+ filteredData.sort( ( a, b ) => {
+ const valueA = fieldToSort.getValue( { item: a } ) ?? '';
+ const valueB = fieldToSort.getValue( { item: b } ) ?? '';
+ return view.sort.direction === 'asc'
+ ? valueA.localeCompare( valueB )
+ : valueB.localeCompare( valueA );
+ } );
+ }
+ }
+ // Handle pagination.
+ const start = ( view.page - 1 ) * view.perPage;
+ const totalItems = filteredData?.length || 0;
+ filteredData = filteredData?.slice( start, start + view.perPage );
+ return {
+ shownData: filteredData,
+ paginationInfo: {
+ totalItems,
+ totalPages: Math.ceil( totalItems / view.perPage ),
+ },
+ };
+ }, [ view ] );
+ const onChangeView = useCallback(
+ ( viewUpdater ) => {
+ let updatedView =
+ typeof viewUpdater === 'function'
+ ? viewUpdater( view )
+ : viewUpdater;
+ if ( updatedView.type !== view.type ) {
+ updatedView = {
+ ...updatedView,
+ layout: {
+ ...defaultConfigPerViewType[ updatedView.type ],
+ },
+ };
+ }
+
+ setView( updatedView );
+ },
+ [ view, setView ]
+ );
+ return (
+
+ );
+};
+Default.args = {
+ actions,
+ getItemId: ( item ) => item.id,
+ isLoading: false,
+ supportedLayouts: [ LAYOUT_TABLE, LAYOUT_GRID ],
+};
diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss
index b18ddb4075c72..5dd89f4c27970 100644
--- a/packages/dataviews/src/style.scss
+++ b/packages/dataviews/src/style.scss
@@ -3,6 +3,7 @@
height: calc(100% - #{$grid-unit-40} * 2);
overflow: auto;
padding: $grid-unit-40 $grid-unit-40 0;
+ box-sizing: border-box;
> div {
min-height: 100%;
@@ -100,7 +101,7 @@
}
}
- .dataviews-view-grid__title {
+ .dataviews-view-grid__primary-field {
min-height: $grid-unit-30;
a {
diff --git a/packages/dataviews/src/view-grid.js b/packages/dataviews/src/view-grid.js
index 8a39bdd8353d1..89dd7d393430d 100644
--- a/packages/dataviews/src/view-grid.js
+++ b/packages/dataviews/src/view-grid.js
@@ -2,6 +2,7 @@
* WordPress dependencies
*/
import {
+ FlexBlock,
__experimentalGrid as Grid,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
@@ -45,10 +46,12 @@ export default function ViewGrid( { data, fields, view, actions, getItemId } ) {
{ mediaField?.render( { item, view } ) }
- { primaryField?.render( { item, view } ) }
+
+ { primaryField?.render( { item, view } ) }
+