From 473a4729986475efbd68546e873c8966ca7c0e51 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 12 Mar 2026 16:05:42 +0100 Subject: [PATCH 1/9] DataViews: Wrap ViewComponent in a container --- .../src/components/dataviews-layout/index.tsx | 38 ++++++++++--------- .../components/dataviews-layout/style.scss | 5 +++ .../dataviews/stories/free-composition.tsx | 12 ++++-- .../src/dataviews/stories/index.story.tsx | 10 +++++ .../dataviews/src/dataviews/stories/style.css | 5 --- packages/dataviews/src/style.scss | 1 + 6 files changed, 44 insertions(+), 27 deletions(-) create mode 100644 packages/dataviews/src/components/dataviews-layout/style.scss diff --git a/packages/dataviews/src/components/dataviews-layout/index.tsx b/packages/dataviews/src/components/dataviews-layout/index.tsx index e7406bcbea4d6c..85fbc1689ee145 100644 --- a/packages/dataviews/src/components/dataviews-layout/index.tsx +++ b/packages/dataviews/src/components/dataviews-layout/index.tsx @@ -50,23 +50,25 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) { )?.component as ComponentType< ViewBaseProps< any > >; return ( - +
+ +
); } diff --git a/packages/dataviews/src/components/dataviews-layout/style.scss b/packages/dataviews/src/components/dataviews-layout/style.scss new file mode 100644 index 00000000000000..336541f39b9fac --- /dev/null +++ b/packages/dataviews/src/components/dataviews-layout/style.scss @@ -0,0 +1,5 @@ +.dataviews-layout__container { + flex: 1; + min-height: 0; + overflow: auto; +} diff --git a/packages/dataviews/src/dataviews/stories/free-composition.tsx b/packages/dataviews/src/dataviews/stories/free-composition.tsx index 5c7129c7ff1e6d..4c3cec7331cd4c 100644 --- a/packages/dataviews/src/dataviews/stories/free-composition.tsx +++ b/packages/dataviews/src/dataviews/stories/free-composition.tsx @@ -102,9 +102,9 @@ function PlanetOverview( { planets }: { planets: SpaceObject[] } ) { - + ); } @@ -127,12 +127,16 @@ function PlanetOverview( { planets }: { planets: SpaceObject[] } ) { * This pattern is useful when you need full control over the UI layout * while still leveraging DataViews' data management and state handling. */ -export const FreeCompositionComponent = () => { +export const FreeCompositionComponent = ( { + containerHeight = '600px', +}: { + containerHeight?: string; +} ) => { const [ view, setView ] = useState< View >( { type: LAYOUT_TABLE, search: '', page: 1, - perPage: 10, + perPage: 20, layout: { enableMoving: false, }, @@ -152,7 +156,7 @@ export const FreeCompositionComponent = () => { ); return ( -
+
item.id.toString() } paginationInfo={ paginationInfo } diff --git a/packages/dataviews/src/dataviews/stories/index.story.tsx b/packages/dataviews/src/dataviews/stories/index.story.tsx index e917cd959976e9..e0d2f191ee650a 100644 --- a/packages/dataviews/src/dataviews/stories/index.story.tsx +++ b/packages/dataviews/src/dataviews/stories/index.story.tsx @@ -245,6 +245,16 @@ export const MinimalUI = { export const FreeComposition = { render: FreeCompositionComponent, + args: { + containerHeight: '80vh', + }, + argTypes: { + containerHeight: { + control: 'select', + options: [ 'auto', '600px', '80vh' ], + description: 'Height of the container', + }, + }, }; export const WithCard = { diff --git a/packages/dataviews/src/dataviews/stories/style.css b/packages/dataviews/src/dataviews/stories/style.css index a8a8b0806c1c38..9eff940b5daa5d 100644 --- a/packages/dataviews/src/dataviews/stories/style.css +++ b/packages/dataviews/src/dataviews/stories/style.css @@ -3,11 +3,6 @@ text-wrap: pretty; } -.free-composition { - height: 600px; - overflow: auto; -} - .free-composition-heading, .free-composition-header { padding: 16px 48px; diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index eaa99e31403b41..9f4833c2046773 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -1,5 +1,6 @@ @use "./dataviews/style.scss" as *; @use "./components/dataviews-bulk-actions/style.scss" as *; +@use "./components/dataviews-layout/style.scss" as *; @use "./components/dataviews-filters/style.scss" as *; @use "./components/dataviews-footer/style.scss" as *; @use "./components/dataviews-pagination/style.scss" as *; From 2b7debe9b3d9c487da251180bfb475d5201e1679 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 12 Mar 2026 16:10:46 +0100 Subject: [PATCH 2/9] add changelog --- packages/dataviews/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md index 32db2664d30d8e..5a197567b7b856 100644 --- a/packages/dataviews/CHANGELOG.md +++ b/packages/dataviews/CHANGELOG.md @@ -7,6 +7,8 @@ - DataForm: Reduce panel's dialog min-width. [#76345](https://github.com/WordPress/gutenberg/pull/76345) - DataViews: Add border to sticky table headers. [#76396](https://github.com/WordPress/gutenberg/pull/76396) +- DataViews: Fix scrolling so the scrollbar appears on the active layout when DataViews is rendered in a constrained-height container. This may slightly change the UI depending on the container height. + ### Bug Fixes - DataViews: Fix last column classname in table layout. [#76133](https://github.com/WordPress/gutenberg/pull/76133) From fcb6f0124414e25aa4206580eb3cd83169ccd94f Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 12 Mar 2026 16:25:05 +0100 Subject: [PATCH 3/9] fix changelog --- packages/dataviews/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md index 5a197567b7b856..a3efe6e479a856 100644 --- a/packages/dataviews/CHANGELOG.md +++ b/packages/dataviews/CHANGELOG.md @@ -7,7 +7,7 @@ - DataForm: Reduce panel's dialog min-width. [#76345](https://github.com/WordPress/gutenberg/pull/76345) - DataViews: Add border to sticky table headers. [#76396](https://github.com/WordPress/gutenberg/pull/76396) -- DataViews: Fix scrolling so the scrollbar appears on the active layout when DataViews is rendered in a constrained-height container. This may slightly change the UI depending on the container height. +- DataViews: Fix scrolling so the scrollbar appears on the active layout when DataViews is rendered in a constrained-height container. This may slightly change the UI depending on the container height. [#76453](https://github.com/WordPress/gutenberg/pull/76453) ### Bug Fixes From f400645a3a5d3b7fc15515ef249b8da3f28289aa Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 12 Mar 2026 18:14:30 +0100 Subject: [PATCH 4/9] fix infinite scroll --- .../src/components/dataviews-layout/index.tsx | 3 +- .../dataviews/src/dataviews-picker/index.tsx | 2 +- packages/dataviews/src/dataviews/index.tsx | 26 ++++++++---- .../src/dataviews/test/dataviews.tsx | 41 +++++++++++++++++++ 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/packages/dataviews/src/components/dataviews-layout/index.tsx b/packages/dataviews/src/components/dataviews-layout/index.tsx index 85fbc1689ee145..12bace17cdcbd0 100644 --- a/packages/dataviews/src/components/dataviews-layout/index.tsx +++ b/packages/dataviews/src/components/dataviews-layout/index.tsx @@ -38,6 +38,7 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) { isItemClickable, renderItemLink, defaultLayouts, + containerRef, empty =

{ __( 'No results' ) }

, } = useContext( DataViewsContext ); @@ -50,7 +51,7 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) { )?.component as ComponentType< ViewBaseProps< any > >; return ( -
+
( { hasInfiniteScrollHandler: !! infiniteScrollHandler, } } > -
+
{ children ?? ( ) } diff --git a/packages/dataviews/src/dataviews/index.tsx b/packages/dataviews/src/dataviews/index.tsx index cf5363816eeaec..de97c78db5dc06 100644 --- a/packages/dataviews/src/dataviews/index.tsx +++ b/packages/dataviews/src/dataviews/index.tsx @@ -195,9 +195,19 @@ function DataViews< Item >( { } }, [ hasPrimaryOrLockedFilters, isShowingFilter ] ); + const { + data: displayData, + paginationInfo: displayPaginationInfo, + hasInitiallyLoaded, + } = useData( data, isLoading, paginationInfo ); + // Attach scroll event listener for infinite scroll useEffect( () => { - if ( ! view.infiniteScrollEnabled || ! containerRef.current ) { + if ( + ! hasInitiallyLoaded || + ! view.infiniteScrollEnabled || + ! containerRef.current + ) { return; } @@ -220,7 +230,11 @@ function DataViews< Item >( { container.removeEventListener( 'scroll', handleScroll ); handleScroll.cancel(); // Cancel any pending throttled calls }; - }, [ infiniteScrollHandler, view.infiniteScrollEnabled ] ); + }, [ + hasInitiallyLoaded, + infiniteScrollHandler, + view.infiniteScrollEnabled, + ] ); // Filter out DataViewsPicker layouts. const defaultLayouts = useMemo( @@ -237,12 +251,6 @@ function DataViews< Item >( { [ defaultLayoutsProperty ] ); - const { - data: displayData, - paginationInfo: displayPaginationInfo, - hasInitiallyLoaded, - } = useData( data, isLoading, paginationInfo ); - if ( ! defaultLayouts[ view.type ] ) { return null; } @@ -280,7 +288,7 @@ function DataViews< Item >( { onReset, } } > -
+
{ children ?? ( { expect( screen.getByText( 'Actions' ) ).toBeInTheDocument(); } ); + it( 'should trigger infinite scroll when the layout container scrolls', () => { + const infiniteScrollHandler = jest.fn(); + const { container } = render( + + ); + // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access + const layoutContainer = container.querySelector( + '.dataviews-layout__container' + ) as HTMLDivElement; + + Object.defineProperties( layoutContainer, { + scrollTop: { + configurable: true, + value: 500, + }, + scrollHeight: { + configurable: true, + value: 1000, + }, + clientHeight: { + configurable: true, + value: 500, + }, + } ); + + fireEvent.scroll( layoutContainer ); + + expect( infiniteScrollHandler ).toHaveBeenCalledTimes( 1 ); + } ); + it( 'should trigger the onClickItem callback if isItemClickable returns true and title field is clicked', async () => { const onClickItemCallback = jest.fn(); From 052db07b52387143a5340dd9bcbdd753faa59bf0 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 12 Mar 2026 18:27:20 +0100 Subject: [PATCH 5/9] address feedback --- packages/dataviews/CHANGELOG.md | 2 +- packages/dataviews/src/components/dataviews-layout/style.scss | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md index a3efe6e479a856..36572cc21edb19 100644 --- a/packages/dataviews/CHANGELOG.md +++ b/packages/dataviews/CHANGELOG.md @@ -7,7 +7,7 @@ - DataForm: Reduce panel's dialog min-width. [#76345](https://github.com/WordPress/gutenberg/pull/76345) - DataViews: Add border to sticky table headers. [#76396](https://github.com/WordPress/gutenberg/pull/76396) -- DataViews: Fix scrolling so the scrollbar appears on the active layout when DataViews is rendered in a constrained-height container. This may slightly change the UI depending on the container height. [#76453](https://github.com/WordPress/gutenberg/pull/76453) +- DataViews: Update scrolling so the scrollbar appears on the active layout when DataViews is rendered in a constrained-height container. This may slightly change the UI depending on the container height. [#76453](https://github.com/WordPress/gutenberg/pull/76453) ### Bug Fixes diff --git a/packages/dataviews/src/components/dataviews-layout/style.scss b/packages/dataviews/src/components/dataviews-layout/style.scss index 336541f39b9fac..7c196f322d80bc 100644 --- a/packages/dataviews/src/components/dataviews-layout/style.scss +++ b/packages/dataviews/src/components/dataviews-layout/style.scss @@ -2,4 +2,6 @@ flex: 1; min-height: 0; overflow: auto; + display: flex; + flex-direction: column; } From 7e1b6549af86bbd656efc7887d7e10c6ade913ad Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 12 Mar 2026 18:29:37 +0100 Subject: [PATCH 6/9] fix unit test --- .../src/dataviews/test/dataviews.tsx | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/packages/dataviews/src/dataviews/test/dataviews.tsx b/packages/dataviews/src/dataviews/test/dataviews.tsx index d65d11e7c6a5f2..5b10578efb4078 100644 --- a/packages/dataviews/src/dataviews/test/dataviews.tsx +++ b/packages/dataviews/src/dataviews/test/dataviews.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import { render, screen } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; /** @@ -240,6 +240,47 @@ describe( 'DataViews component', () => { expect( screen.getByText( 'TEST TITLE' ) ).toBeInTheDocument(); } ); + it( 'should trigger infinite scroll when the layout container scrolls', () => { + const infiniteScrollHandler = jest.fn(); + const { container } = render( + + ); + // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access + const layoutContainer = container.querySelector( + '.dataviews-layout__container' + ) as HTMLDivElement; + + Object.defineProperties( layoutContainer, { + scrollTop: { + configurable: true, + value: 500, + }, + scrollHeight: { + configurable: true, + value: 1000, + }, + clientHeight: { + configurable: true, + value: 500, + }, + } ); + + fireEvent.scroll( layoutContainer ); + + expect( infiniteScrollHandler ).toHaveBeenCalledTimes( 1 ); + } ); + describe( 'in table view', () => { it( 'should display columns for each field', () => { render( ); @@ -284,47 +325,6 @@ describe( 'DataViews component', () => { expect( screen.getByText( 'Actions' ) ).toBeInTheDocument(); } ); - it( 'should trigger infinite scroll when the layout container scrolls', () => { - const infiniteScrollHandler = jest.fn(); - const { container } = render( - - ); - // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access - const layoutContainer = container.querySelector( - '.dataviews-layout__container' - ) as HTMLDivElement; - - Object.defineProperties( layoutContainer, { - scrollTop: { - configurable: true, - value: 500, - }, - scrollHeight: { - configurable: true, - value: 1000, - }, - clientHeight: { - configurable: true, - value: 500, - }, - } ); - - fireEvent.scroll( layoutContainer ); - - expect( infiniteScrollHandler ).toHaveBeenCalledTimes( 1 ); - } ); - it( 'should trigger the onClickItem callback if isItemClickable returns true and title field is clicked', async () => { const onClickItemCallback = jest.fn(); From ceb4e49d0062ce92b1dfe449c426e47171c0b057 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 12 Mar 2026 18:35:07 +0100 Subject: [PATCH 7/9] fix style --- packages/dataviews/src/components/dataviews-layout/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dataviews/src/components/dataviews-layout/style.scss b/packages/dataviews/src/components/dataviews-layout/style.scss index 7c196f322d80bc..d5a2a70caac330 100644 --- a/packages/dataviews/src/components/dataviews-layout/style.scss +++ b/packages/dataviews/src/components/dataviews-layout/style.scss @@ -4,4 +4,5 @@ overflow: auto; display: flex; flex-direction: column; + background-color: inherit; } From cd813e74e1e540ee0513ae7747b1f809ead6aebc Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Mon, 16 Mar 2026 14:48:29 +0100 Subject: [PATCH 8/9] set containerHeight via decorator --- .../dataviews/src/dataviews/stories/empty.tsx | 4 +- .../dataviews/stories/free-composition.tsx | 68 +++++++++---------- .../src/dataviews/stories/index.story.tsx | 32 ++++----- .../src/dataviews/stories/infinite-scroll.tsx | 6 -- .../src/dataviews/stories/layout-activity.tsx | 1 + .../src/dataviews/stories/layout-custom.tsx | 10 ++- .../src/dataviews/stories/layout-grid.tsx | 1 + .../src/dataviews/stories/layout-list.tsx | 1 + .../src/dataviews/stories/layout-table.tsx | 1 + .../src/dataviews/stories/with-card.tsx | 8 ++- 10 files changed, 63 insertions(+), 69 deletions(-) diff --git a/packages/dataviews/src/dataviews/stories/empty.tsx b/packages/dataviews/src/dataviews/stories/empty.tsx index 5e19d4ad0c16c3..21e69809d7304f 100644 --- a/packages/dataviews/src/dataviews/stories/empty.tsx +++ b/packages/dataviews/src/dataviews/stories/empty.tsx @@ -47,11 +47,9 @@ const CustomEmptyComponent = () => ( const EmptyComponent = ( { customEmpty, - containerHeight, isLoading, }: { customEmpty?: boolean; - containerHeight?: 'auto' | '50vh' | '100vh'; isLoading?: boolean; } ) => { const [ view, setView ] = useState< View >( { @@ -69,7 +67,7 @@ const EmptyComponent = ( { style={ { display: 'flex', flexDirection: 'column', - height: containerHeight, + height: '100%', } } > { +export const FreeCompositionComponent = () => { const [ view, setView ] = useState< View >( { type: LAYOUT_TABLE, search: '', @@ -156,38 +152,36 @@ export const FreeCompositionComponent = ( { ); return ( -
- item.id.toString() } - paginationInfo={ paginationInfo } - data={ processedData } - view={ view } - fields={ fields } - actions={ actions } - onChangeView={ setView } - defaultLayouts={ { - table: {}, - grid: {}, - } } - empty={ - - - No planets - - { `Try a different search because “${ view.search }” returned no results.` } - - - } - > - - -
+ item.id.toString() } + paginationInfo={ paginationInfo } + data={ processedData } + view={ view } + fields={ fields } + actions={ actions } + onChangeView={ setView } + defaultLayouts={ { + table: {}, + grid: {}, + } } + empty={ + + + No planets + + { `Try a different search because “${ view.search }” returned no results.` } + + + } + > + + ); }; diff --git a/packages/dataviews/src/dataviews/stories/index.story.tsx b/packages/dataviews/src/dataviews/stories/index.story.tsx index e0d2f191ee650a..b93dc696f9e687 100644 --- a/packages/dataviews/src/dataviews/stories/index.story.tsx +++ b/packages/dataviews/src/dataviews/stories/index.story.tsx @@ -23,6 +23,16 @@ import './style.css'; const meta = { title: 'DataViews/DataViews', component: DataViews, + args: { + containerHeight: 'auto', + }, + argTypes: { + containerHeight: { + control: 'select', + options: [ 'auto', '600px', '80vh' ], + description: 'Height of the container', + }, + }, // Use fullscreen layout and a wrapper div with padding to resolve conflicts // between Ariakit's Dialog (usePreventBodyScroll) and Storybook's body padding // (sb-main-padding class). This ensures consistent layout in DataViews stories @@ -31,9 +41,11 @@ const meta = { layout: 'fullscreen', }, decorators: [ - ( Story ) => ( + ( Story, { args }: { args: any } ) => (
- +
+ +
), ], @@ -212,7 +224,6 @@ export const Empty = { render: EmptyComponent, args: { customEmpty: false, - containerHeight: '50vh', isLoading: false, }, argTypes: { @@ -220,11 +231,6 @@ export const Empty = { control: 'boolean', description: 'Use custom empty state with planet illustration', }, - containerHeight: { - control: 'select', - options: [ 'auto', '50vh', '100vh' ], - description: 'Height of the container', - }, isLoading: { control: 'boolean', description: 'Show loading state', @@ -245,16 +251,6 @@ export const MinimalUI = { export const FreeComposition = { render: FreeCompositionComponent, - args: { - containerHeight: '80vh', - }, - argTypes: { - containerHeight: { - control: 'select', - options: [ 'auto', '600px', '80vh' ], - description: 'Height of the container', - }, - }, }; export const WithCard = { diff --git a/packages/dataviews/src/dataviews/stories/infinite-scroll.tsx b/packages/dataviews/src/dataviews/stories/infinite-scroll.tsx index c6097afc0f97a1..34d455cae9910a 100644 --- a/packages/dataviews/src/dataviews/stories/infinite-scroll.tsx +++ b/packages/dataviews/src/dataviews/stories/infinite-scroll.tsx @@ -106,12 +106,6 @@ const InfiniteScroll = () => { return ( <> - ` * - Still leverage DataViews sub-components for search and pagination */ -export const LayoutCustomComponent = () => { +export const LayoutCustomComponent = ( { + containerHeight, +}: { + containerHeight: string; +} ) => { const [ view, setView ] = useState< View >( { type: LAYOUT_TABLE, search: '', @@ -128,11 +132,11 @@ export const LayoutCustomComponent = () => { onChangeView={ setView } defaultLayouts={ { table: {} } } > -
+
-
+ ); }; diff --git a/packages/dataviews/src/dataviews/stories/layout-grid.tsx b/packages/dataviews/src/dataviews/stories/layout-grid.tsx index c3da8ec276c2bc..9cd4f197ce8107 100644 --- a/packages/dataviews/src/dataviews/stories/layout-grid.tsx +++ b/packages/dataviews/src/dataviews/stories/layout-grid.tsx @@ -63,6 +63,7 @@ export const LayoutTableComponent = ( {
{ +const WithCardComponent = ( { + containerHeight, +}: { + containerHeight: string; +} ) => { const [ view, setView ] = useState< View >( { type: LAYOUT_TABLE, search: '', @@ -37,7 +41,7 @@ const WithCardComponent = () => { return ( Header - + item.id.toString() } paginationInfo={ paginationInfo } From 0c4bbb73b4db3969cdb0398d8d3a0efedc6d4231 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Mon, 16 Mar 2026 14:49:22 +0100 Subject: [PATCH 9/9] remove overflow: auto from the wrapper --- packages/dataviews/src/dataviews/style.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dataviews/src/dataviews/style.scss b/packages/dataviews/src/dataviews/style.scss index 928766ac95e3af..bf9c53173478a3 100644 --- a/packages/dataviews/src/dataviews/style.scss +++ b/packages/dataviews/src/dataviews/style.scss @@ -15,7 +15,6 @@ .dataviews-wrapper, .dataviews-picker-wrapper { height: 100%; - overflow: auto; box-sizing: border-box; scroll-padding-bottom: $grid-unit-80; /* stylelint-disable-next-line property-no-unknown -- '@container' not globally permitted */