Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/dataviews/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- Dataform: Add new `url` field type and field control. [#71518](https://github.com/WordPress/gutenberg/pull/71518)
- Dataform: Add new `password` field type and field control. [#71545](https://github.com/WordPress/gutenberg/pull/71545)
- DataForm: Add a textarea control for use with the `text` field type ([#71495](https://github.com/WordPress/gutenberg/pull/71495))
- DataViews: support groupBy in the list layout. [#71548](https://github.com/WordPress/gutenberg/pull/71548)

### Bug Fixes

Expand Down
15 changes: 2 additions & 13 deletions packages/dataviews/src/dataviews-layouts/grid/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import type { SetSelection } from '../../private-types';
import { ItemClickWrapper } from '../utils/item-click-wrapper';
import { GridItems } from '../utils/grid-items';
const { Badge } = unlock( componentsPrivateApis );
import getDataByGroup from '../utils/get-data-by-group';

interface GridItemProps< Item > {
view: ViewGridType;
Expand Down Expand Up @@ -338,19 +339,7 @@ function ViewGrid< Item >( {
const groupField = view.groupByField
? fields.find( ( f ) => f.id === view.groupByField )
: null;

// Group data by groupByField if specified
const dataByGroup = groupField
? data.reduce( ( groups: Map< string, typeof data >, item ) => {
const groupName = groupField.getValue( { item } );
if ( ! groups.has( groupName ) ) {
groups.set( groupName, [] );
}
groups.get( groupName )?.push( item );
return groups;
}, new Map< string, typeof data >() )
: null;

const dataByGroup = groupField ? getDataByGroup( data, groupField ) : null;
const isInfiniteScroll = view.infiniteScrollEnabled && ! dataByGroup;

return (
Expand Down
67 changes: 66 additions & 1 deletion packages/dataviews/src/dataviews-layouts/list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
useState,
useContext,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { __, sprintf } from '@wordpress/i18n';
import { moreVertical } from '@wordpress/icons';
import { useRegistry } from '@wordpress/data';

Expand All @@ -44,6 +44,7 @@ import type {
ViewListProps,
ActionModal as ActionModalType,
} from '../../types';
import getDataByGroup from '../utils/get-data-by-group';

interface ListViewItemProps< Item > {
view: ViewListType;
Expand Down Expand Up @@ -517,6 +518,70 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) {
);
}

const groupField = view.groupByField
? fields.find( ( field ) => field.id === view.groupByField )
: null;
const dataByGroup = groupField ? getDataByGroup( data, groupField ) : null;

// Render data grouped by field
if ( hasData && groupField && dataByGroup ) {
return (
<Composite
id={ `${ baseId }` }
render={ <div /> }
className="dataviews-view-list__group"
role="grid"
activeId={ activeCompositeId }
setActiveId={ setActiveCompositeId }
>
<VStack
spacing={ 4 }
className={ clsx( 'dataviews-view-list', className ) }
>
{ Array.from( dataByGroup.entries() ).map(
( [ groupName, groupItems ] ) => (
<VStack key={ groupName } spacing={ 2 }>
<h3 className="dataviews-view-list__group-header">
{ sprintf(
// translators: 1: The label of the field e.g. "Date". 2: The value of the field, e.g.: "May 2022".
__( '%1$s: %2$s' ),
groupField.label,
groupName
) }
</h3>
{ groupItems.map( ( item ) => {
const id =
generateCompositeItemIdPrefix( item );
return (
<ListItem
key={ id }
view={ view }
idPrefix={ id }
actions={ actions }
item={ item }
isSelected={ item === selectedItem }
onSelect={ onSelect }
mediaField={ mediaField }
titleField={ titleField }
descriptionField={
descriptionField
}
otherFields={ otherFields }
onDropdownTriggerKeyDown={
onDropdownTriggerKeyDown
}
/>
);
} ) }
</VStack>
)
) }
</VStack>
</Composite>
);
}

// Render ungrouped data
return (
<>
<Composite
Expand Down
8 changes: 8 additions & 0 deletions packages/dataviews/src/dataviews-layouts/list/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,11 @@ div.dataviews-view-list {
justify-content: space-between;
}
}

.dataviews-view-list__group-header {
font-size: $font-size-large;
font-weight: $font-weight-medium;
color: $gray-900;
margin: 0 0 $grid-unit-10 0;
padding: 0 $grid-unit-30;
}
14 changes: 2 additions & 12 deletions packages/dataviews/src/dataviews-layouts/picker-grid/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import type {
import type { SetSelection } from '../../private-types';
import { GridItems } from '../utils/grid-items';
const { Badge } = unlock( componentsPrivateApis );
import getDataByGroup from '../utils/get-data-by-group';

interface GridItemProps< Item > {
view: ViewPickerGridType;
Expand Down Expand Up @@ -301,18 +302,7 @@ function ViewPickerGrid< Item >( {
const groupField = view.groupByField
? fields.find( ( f ) => f.id === view.groupByField )
: null;

// Group data by groupByField if specified
const dataByGroup = groupField
? data.reduce( ( groups: Map< string, typeof data >, item ) => {
const groupName = groupField.getValue( { item } );
if ( ! groups.has( groupName ) ) {
groups.set( groupName, [] );
}
groups.get( groupName )?.push( item );
return groups;
}, new Map< string, typeof data >() )
: null;
const dataByGroup = groupField ? getDataByGroup( data, groupField ) : null;

const isInfiniteScroll = view.infiniteScrollEnabled && ! dataByGroup;

Expand Down
15 changes: 2 additions & 13 deletions packages/dataviews/src/dataviews-layouts/table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import type { SetSelection } from '../../private-types';
import ColumnHeaderMenu from './column-header-menu';
import ColumnPrimary from './column-primary';
import { useIsHorizontalScrollEnd } from './use-is-horizontal-scroll-end';
import getDataByGroup from '../utils/get-data-by-group';

interface TableColumnFieldProps< Item > {
fields: NormalizedField< Item >[];
Expand Down Expand Up @@ -336,22 +337,10 @@ function ViewTable< Item >( {
( field ) => field.id === view.descriptionField
);

// Get group field if groupByField is specified
const groupField = view.groupByField
? fields.find( ( f ) => f.id === view.groupByField )
: null;

// Group data by groupByField if specified
const dataByGroup = groupField
? data.reduce( ( groups: Map< string, typeof data >, item ) => {
const groupName = groupField.getValue( { item } );
if ( ! groups.has( groupName ) ) {
groups.set( groupName, [] );
}
groups.get( groupName )?.push( item );
return groups;
}, new Map< string, typeof data >() )
: null;
const dataByGroup = groupField ? getDataByGroup( data, groupField ) : null;
const { showTitle = true, showMedia = true, showDescription = true } = view;
const hasPrimaryColumn =
( titleField && showTitle ) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Internal dependencies
*/
import type { NormalizedField } from '../../types';

export default function getDataByGroup< Item >(
data: any[],
groupByField: NormalizedField< Item >
): Map< string, any[] > {
return data.reduce( ( groups: Map< string, typeof data >, item ) => {
const groupName = groupByField.getValue( { item } );
if ( ! groups.has( groupName ) ) {
groups.set( groupName, [] );
}
groups.get( groupName )?.push( item );
return groups;
}, new Map< string, typeof data >() );
}
Loading