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 @@ -5,6 +5,7 @@
### Bug Fixes

- DataForm: Fix focus loss when collapsing in Card view. [#75689](https://github.com/WordPress/gutenberg/pull/75689)
- DataViews: Avoid flickering while refreshing. [#74572](https://github.com/WordPress/gutenberg/pull/74572)
- DataViews: Fix spacing in first column. [#75693](https://github.com/WordPress/gutenberg/pull/75693)
- DataViews: Fix search input losing characters during debounce when externally synced. [#75810](https://github.com/WordPress/gutenberg/pull/75810)
- DataForm: Fix vertical alignment of summary fields in card layout. [#75864](https://github.com/WordPress/gutenberg/pull/75864)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type DataViewsContextType< Item > = {
setIsShowingFilter: ( value: boolean ) => void;
config: { perPageSizes: number[] };
empty?: ReactNode;
hasInitiallyLoaded?: boolean;
hasInfiniteScrollHandler: boolean;
itemListLabel?: string;
onReset?: ( () => void ) | false;
Expand Down Expand Up @@ -84,6 +85,7 @@ const DataViewsContext = createContext< DataViewsContextType< any > >( {
filters: [],
isShowingFilter: false,
setIsShowingFilter: () => {},
hasInitiallyLoaded: false,
hasInfiniteScrollHandler: false,
config: {
perPageSizes: [],
Expand Down
51 changes: 39 additions & 12 deletions packages/dataviews/src/components/dataviews-footer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import clsx from 'clsx';

/**
* WordPress dependencies
*/
Expand All @@ -14,6 +19,7 @@ import {
useSomeItemHasAPossibleBulkAction,
} from '../dataviews-bulk-actions';
import { LAYOUT_GRID, LAYOUT_TABLE } from '../../constants';
import { useDelayedLoading } from '../../hooks/use-delayed-loading';

const EMPTY_ARRAY: [] = [];

Expand All @@ -23,30 +29,51 @@ export default function DataViewsFooter() {
paginationInfo: { totalItems = 0, totalPages },
data,
actions = EMPTY_ARRAY,
isLoading,
hasInitiallyLoaded,
hasInfiniteScrollHandler,
} = useContext( DataViewsContext );

const isRefreshing =
!! isLoading &&
hasInitiallyLoaded &&
! hasInfiniteScrollHandler &&
!! data?.length;

const isDelayedRefreshing = useDelayedLoading( !! isRefreshing );

const hasBulkActions =
useSomeItemHasAPossibleBulkAction( actions, data ) &&
[ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type );

if (
! totalItems ||
! totalPages ||
( totalPages <= 1 && ! hasBulkActions )
! isRefreshing &&
( ! totalItems ||
! totalPages ||
( totalPages <= 1 && ! hasBulkActions ) )
) {
return null;
}
return (
!! totalItems && (
<Stack
direction="row"
justify="end"
align="center"
( !! totalItems || isRefreshing ) && (
<div
className="dataviews-footer"
gap="sm"
// @ts-ignore
inert={ isRefreshing ? 'true' : undefined }
>
{ hasBulkActions && <BulkActionsFooter /> }
<DataViewsPagination />
</Stack>
<Stack
direction="row"
justify="end"
align="center"
className={ clsx( 'dataviews-footer__content', {
'is-refreshing': isDelayedRefreshing,
} ) }
gap="sm"
>
{ hasBulkActions && <BulkActionsFooter /> }
<DataViewsPagination />
</Stack>
</div>
)
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@use "@wordpress/base-styles/colors" as *;
@use "@wordpress/base-styles/variables" as *;
@use "@wordpress/base-styles/z-index" as *;
@use "../../dataviews/style" as *;

.dataviews-footer {
position: sticky;
Expand All @@ -16,10 +17,14 @@
}

z-index: z-index(".dataviews-footer");

.is-refreshing {
@include dataviews-refreshing();
}
}

@container (max-width: 560px) {
.dataviews-footer {
.dataviews-footer__content {
flex-direction: column !important;

.dataviews-bulk-actions-footer__container {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) {
fields,
getItemId,
getItemLevel,
hasInitiallyLoaded,
isLoading,
view,
onChangeView,
Expand All @@ -40,6 +41,10 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) {
empty = <p>{ __( 'No results' ) }</p>,
} = useContext( DataViewsContext );

if ( ! hasInitiallyLoaded ) {
return null;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do this without involving the context (as part of dataviews/index.tsx, so no other components need to handle it, e.g., footer)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that, but there is a tradeoff here. In order to handle properly in one place for both DefaultUI and free composition which renders children, we should add the check there, meaning there would be no UI at all until the initial load has completed. Now, even though it's spread among more components, I think it's the better alternative.


const ViewComponent = VIEW_LAYOUTS.find(
( v ) => v.type === view.type && defaultLayouts[ v.type ]
)?.component as ComponentType< ViewBaseProps< any > >;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,40 @@ import type { ViewActivityProps } from '../../../types';
import getDataByGroup from '../utils/get-data-by-group';
import ActivityGroup from './activity-group';
import ActivityItems from './activity-items';
import { useDelayedLoading } from '../../../hooks/use-delayed-loading';

export default function ViewActivity< Item >(
props: ViewActivityProps< Item >
) {
const { empty, data, fields, isLoading, view, className } = props;

// Handle empty/loading states
const hasData = data?.length;
const isDelayedLoading = useDelayedLoading( !! isLoading );
const hasData = !! data?.length;

// Check if data should be grouped
const groupField = view.groupBy?.field
? fields.find( ( field ) => field.id === view.groupBy?.field )
: null;
const dataByGroup =
hasData && groupField ? getDataByGroup( data, groupField ) : null;

const isInfiniteScroll = view.infiniteScrollEnabled && ! dataByGroup;
if ( ! hasData ) {
return (
<div
className={ clsx( {
'dataviews-loading': isLoading,
'dataviews-no-results': ! hasData && ! isLoading,
className={ clsx( 'dataviews-no-results', {
'is-refreshing': isDelayedLoading,
} ) }
>
{ ! hasData &&
( isLoading ? (
<p>
<Spinner />
</p>
) : (
empty
) ) }
{ empty }
</div>
);
}

const wrapperClassName = clsx( 'dataviews-view-activity', className );

// Check if data should be grouped
const groupField = view.groupBy?.field
? fields.find( ( field ) => field.id === view.groupBy?.field )
: null;
const dataByGroup = groupField ? getDataByGroup( data, groupField ) : null;
const isInert = ! isInfiniteScroll && !! isLoading;
const wrapperClassName = clsx( 'dataviews-view-activity', className, {
'is-refreshing': ! isInfiniteScroll && isDelayedLoading,
} );

// Convert dataByGroup entries into array.
const groupedEntries = dataByGroup
Expand All @@ -60,7 +59,13 @@ export default function ViewActivity< Item >(
// Render grouped activity
if ( hasData && groupField && dataByGroup ) {
return (
<Stack direction="column" gap="sm" className={ wrapperClassName }>
<Stack
direction="column"
gap="sm"
className={ wrapperClassName }
// @ts-ignore
inert={ isInert ? 'true' : undefined }
>
{ groupedEntries.map(
( [ groupName, groupData ]: [ string, Item[] ] ) => (
<ActivityGroup< Item >
Expand All @@ -87,10 +92,12 @@ export default function ViewActivity< Item >(
<div
className={ wrapperClassName }
role={ view.infiniteScrollEnabled ? 'feed' : undefined }
// @ts-ignore
inert={ isInert ? 'true' : undefined }
>
<ActivityItems< Item > { ...props } />
</div>
{ hasData && isLoading && (
{ isInfiniteScroll && isLoading && (
<p className="dataviews-loading-more">
<Spinner />
</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@use "@wordpress/base-styles/colors" as *;
@use "@wordpress/base-styles/variables" as *;
@use "../../../dataviews/style" as *;

.dataviews-view-activity {
margin: 0 0 auto;
Expand Down Expand Up @@ -211,6 +212,10 @@
}
}

&.is-refreshing {
@include dataviews-refreshing();
}

& + .dataviews-pagination {
justify-content: space-between;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ interface CompositeGridProps< Item > {
data: Item[];
isInfiniteScroll: boolean;
className?: string;
inert?: string;
isLoading?: boolean;
view: ViewGridType;
fields: NormalizedField< Item >[];
Expand All @@ -313,6 +314,7 @@ export default function CompositeGrid< Item >( {
data,
isInfiniteScroll,
className,
inert,
isLoading,
view,
fields,
Expand Down Expand Up @@ -375,6 +377,8 @@ export default function CompositeGrid< Item >( {
aria-busy={ isLoading }
aria-rowcount={ isInfiniteScroll ? undefined : totalRows }
ref={ resizeObserverRef }
// @ts-ignore
inert={ inert }
>
{ chunk( data, gridColumns ).map( ( row, i ) => (
<Composite.Row
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Stack } from '@wordpress/ui';
import type { ViewGridProps } from '../../../types';
import getDataByGroup from '../utils/get-data-by-group';
import CompositeGrid from './composite-grid';
import { useDelayedLoading } from '../../../hooks/use-delayed-loading';

function ViewGrid< Item >( {
actions,
Expand All @@ -32,14 +33,29 @@ function ViewGrid< Item >( {
className,
empty,
}: ViewGridProps< Item > ) {
const isDelayedLoading = useDelayedLoading( !! isLoading );
const hasData = !! data?.length;
const groupField = view.groupBy?.field
? fields.find( ( f ) => f.id === view.groupBy?.field )
: null;
const dataByGroup = groupField ? getDataByGroup( data, groupField ) : null;
const isInfiniteScroll = view.infiniteScrollEnabled && ! dataByGroup;
if ( ! hasData ) {
return (
<div
className={ clsx( 'dataviews-no-results', {
'is-refreshing': isDelayedLoading,
} ) }
>
{ empty }
</div>
);
}
const gridProps = {
className,
className: clsx( className, {
'is-refreshing': ! isInfiniteScroll && isDelayedLoading,
} ),
inert: ! isInfiniteScroll && !! isLoading ? 'true' : undefined,
isLoading,
view,
fields,
Expand Down Expand Up @@ -87,34 +103,15 @@ function ViewGrid< Item >( {
}
{
// Render a single grid with all data.
hasData && ! dataByGroup && (
! dataByGroup && (
<CompositeGrid
{ ...gridProps }
data={ data }
isInfiniteScroll={ !! isInfiniteScroll }
/>
)
}
{
// Render empty state.
! hasData && (
<div
className={ clsx( {
'dataviews-loading': isLoading,
'dataviews-no-results': ! isLoading,
} ) }
>
{ isLoading ? (
<p>
<Spinner />
</p>
) : (
empty
) }
</div>
)
}
{ hasData && isLoading && (
{ isInfiniteScroll && isLoading && (
<p className="dataviews-loading-more">
<Spinner />
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@use "@wordpress/base-styles/variables" as *;
@use "@wordpress/base-styles/z-index" as *;
@use "../utils/grid-items.scss" as *;
@use "../../../dataviews/style" as *;

.dataviews-view-grid {
padding: 0 $grid-unit-30 $grid-unit-30;
Expand Down Expand Up @@ -164,6 +165,10 @@
padding-bottom: $grid-unit-15;
}
}

&.is-refreshing {
@include dataviews-refreshing();
}
}

.dataviews-view-grid__field-value:empty,
Expand Down
Loading
Loading