Skip to content

Commit 990a4de

Browse files
dhasilvaclaude
andcommitted
Backup: fix DataViews list row separators, zebra, selection, pagination
Three layout fidelity fixes after wiring DataViews: 1. Pagination was rendered but inert — DataViews treats `data` as the already-paginated page when `paginationInfo` is passed. Slice `data` ourselves by `view.page` / `view.perPage` and forward the totals; row count now matches the per-page setting and the next/prev arrows actually advance the list. 2. Row separators, zebra striping, and the selected-row highlight all need to target the per-item `<div>` DataViews wraps each list entry in (not the `.dataviews-view-list__item-wrapper`, which is nested one level deeper and is therefore always `:nth-child(1)` of its parent). The selected state lands on that wrapping div as `.is-selected`. 3. The earlier `:global(...)` SCSS escape leaked literally into the compiled CSS (these files aren't CSS Modules), so none of the DataViews-class selectors were matching. Drop `:global` and write the descendant selectors directly under `.jpb-activity-list`. Also adds in-memory search filtering so the DataViews search box narrows the visible list before pagination kicks in. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8c362b7 commit 990a4de

2 files changed

Lines changed: 59 additions & 8 deletions

File tree

projects/packages/backup/src/dashboard/components/activity-list/index.tsx

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ const DEFAULT_PER_PAGE = 10;
9797
* @return The rendered list.
9898
*/
9999
export default function ActivityList( { selectedId, onSelect }: Props ) {
100+
// In DataViews' list layout, the `titleField`, `mediaField` and
101+
// `descriptionField` fields are rendered implicitly — anything else
102+
// in `fields` would render a second time as a generic row. Leave
103+
// `fields` empty so the title + media + description are the only
104+
// things shown per row.
100105
const [ view, setView ] = useState< View >( {
101106
type: 'list',
102107
page: 1,
@@ -106,7 +111,7 @@ export default function ActivityList( { selectedId, onSelect }: Props ) {
106111
titleField: 'title',
107112
mediaField: 'icon',
108113
descriptionField: 'description',
109-
fields: [ 'description' ],
114+
fields: [],
110115
} );
111116

112117
// Always pull the full fixture; DataViews handles pagination, search,
@@ -165,16 +170,37 @@ export default function ActivityList( { selectedId, onSelect }: Props ) {
165170
[ selectedId ]
166171
);
167172

173+
// DataViews treats `data` as the already-paginated/filtered page when
174+
// `paginationInfo` is passed. We want client-side everything for the
175+
// in-memory fixture, so we slice ourselves and forward the totals.
176+
const filtered = useMemo( () => {
177+
const q = ( view.search ?? '' ).toLowerCase();
178+
if ( ! q ) {
179+
return allItems;
180+
}
181+
return allItems.filter( item => {
182+
const haystack = `${ item.title } ${ item.summary ?? '' } ${ item.publishedAt }`;
183+
return haystack.toLowerCase().includes( q );
184+
} );
185+
}, [ allItems, view.search ] );
186+
187+
const perPage = view.perPage ?? DEFAULT_PER_PAGE;
188+
const page = view.page ?? 1;
189+
const paged = useMemo(
190+
() => filtered.slice( ( page - 1 ) * perPage, page * perPage ),
191+
[ filtered, page, perPage ]
192+
);
193+
168194
return (
169195
<div className="jpb-activity-list" aria-busy={ isLoading }>
170196
<DataViews< ActivityItem >
171-
data={ allItems }
197+
data={ paged }
172198
fields={ fields }
173199
view={ view }
174200
onChangeView={ setView }
175201
paginationInfo={ {
176-
totalItems: allItems.length,
177-
totalPages: Math.max( 1, Math.ceil( allItems.length / DEFAULT_PER_PAGE ) ),
202+
totalItems: filtered.length,
203+
totalPages: Math.max( 1, Math.ceil( filtered.length / perPage ) ),
178204
} }
179205
defaultLayouts={ { list: {} } }
180206
getItemId={ getRowId }

projects/packages/backup/src/dashboard/components/activity-list/style.scss

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,35 @@
44
min-height: 0;
55
overflow: hidden;
66

7-
// DataViews' list layout doesn't ship a column dedicated to the
8-
// "media" field — it renders the field's value inline at the start
9-
// of each row. We give that tile a fixed 32x32 white square with
10-
// a light border to match the legacy admin's icon affordance.
7+
// DataViews wraps each list item in its own per-item `<div>` (which
8+
// flips to `.is-selected` when chosen). The `__item-wrapper` is
9+
// nested one level deeper, so `:nth-child(even)` on the wrapper never
10+
// matches more than itself. Target the per-item `<div>` directly —
11+
// it's the only child of `.dataviews-view-list`.
12+
.dataviews-view-list > div {
13+
border-bottom: 1px solid var(--wp-components-color-gray-100, #f0f0f0);
14+
15+
&:last-child {
16+
border-bottom: 0;
17+
}
18+
19+
// Subtle alternating background — same treatment as Calypso's
20+
// activity-log list.
21+
&:nth-child(even) {
22+
background-color: var(--wp-components-color-gray-100, #fafafa);
23+
}
24+
25+
// DataViews puts `is-selected` on the per-item `<div>` when the
26+
// row is chosen via `selection` + `onChangeSelection`.
27+
&.is-selected,
28+
&.is-selected:nth-child(even) {
29+
background-color: var(--wp-admin-theme-color-background, #e0ecf7);
30+
}
31+
}
32+
33+
// Tile the media slot in a 32x32 white square with a thin border so
34+
// each row reads as "icon | title | description", matching the
35+
// legacy admin.
1136
&__icon {
1237
display: inline-flex;
1338
align-items: center;

0 commit comments

Comments
 (0)