Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bedd29d
Add all PDF tile configurations.
zutigrm Jul 21, 2026
96efcbd
Add tests for every tile.
zutigrm Jul 21, 2026
8925e6c
Order by area, module-filter, outage.
zutigrm Jul 21, 2026
ea3757b
Cover ordering, filter, outage, abort.
zutigrm Jul 21, 2026
79fb06e
Tile data loader factory; doc.
zutigrm Jul 21, 2026
5fc23d5
Update loader call signature.
zutigrm Jul 21, 2026
290ffa2
Four-column grid, uniform tile height.
zutigrm Jul 21, 2026
f0402cc
Align tile render-prop typing.
zutigrm Jul 21, 2026
5a630ec
Remove inline PDF tile configs.
zutigrm Jul 21, 2026
f4c5c71
Move and expand tile tests.
zutigrm Jul 21, 2026
98ee97e
Pass viewableModules into getData.
zutigrm Jul 21, 2026
bf22a55
Add change badge helper.
zutigrm Jul 21, 2026
de027f8
Add ranked-list PDF tile.
zutigrm Jul 21, 2026
d2527be
Add table tile tests.
zutigrm Jul 21, 2026
3ccd1b0
Add text metric PDF tile.
zutigrm Jul 21, 2026
d8b5499
Add text tile tests.
zutigrm Jul 21, 2026
4322c83
Add card min-height styling.
zutigrm Jul 21, 2026
98c8606
Fold maxLines into text style.
zutigrm Jul 21, 2026
b2ec515
Read the new PDF tiles map.
zutigrm Jul 21, 2026
e03fc98
Update aggregate widget assertions.
zutigrm Jul 21, 2026
7560a1b
Add optional viewableModules getData param.
zutigrm Jul 21, 2026
2afa959
Export earnings report options helper.
zutigrm Jul 21, 2026
ae30031
Export engaged source report options.
zutigrm Jul 21, 2026
902012b
Export least-engaging report options.
zutigrm Jul 21, 2026
49045b4
Export most-engaging report options.
zutigrm Jul 21, 2026
e244fec
Export new-visitors report options.
zutigrm Jul 21, 2026
13776c1
Export pages-per-visit report options.
zutigrm Jul 21, 2026
354444a
Export popular-authors report options.
zutigrm Jul 21, 2026
1537294
Export popular-content report options.
zutigrm Jul 21, 2026
b31c25c
Export popular-products report options.
zutigrm Jul 21, 2026
3d7c8b9
Export returning-visitors report options.
zutigrm Jul 21, 2026
b28e50c
Export top-categories report options.
zutigrm Jul 21, 2026
b997970
Export cities add-to-cart options.
zutigrm Jul 21, 2026
326d8c8
Export cities leads options, events.
zutigrm Jul 21, 2026
328ffd9
Export cities purchases report options.
zutigrm Jul 21, 2026
e63c511
Export top-cities report options.
zutigrm Jul 21, 2026
86b152d
Export converting source report options.
zutigrm Jul 21, 2026
12e908c
Export top-countries report options.
zutigrm Jul 21, 2026
6cd0ab5
Export device purchases report options.
zutigrm Jul 21, 2026
9a0226c
Export pages leads options, events.
zutigrm Jul 21, 2026
915bce2
Rename and export trending options.
zutigrm Jul 21, 2026
5497f71
Export returning-visitor pages options.
zutigrm Jul 21, 2026
0450507
Export source add-to-cart options.
zutigrm Jul 21, 2026
5600d1d
Export source leads options, events.
zutigrm Jul 21, 2026
3c6de4d
Export source purchases report options.
zutigrm Jul 21, 2026
c052cc3
Export top-traffic-source report options.
zutigrm Jul 21, 2026
597e88c
Export visit-length report options.
zutigrm Jul 21, 2026
a6601e8
Export visits-per-visitor report options.
zutigrm Jul 21, 2026
8c855e8
Export popular-keywords report options.
zutigrm Jul 21, 2026
c529755
Merge remote-tracking branch 'origin/develop' into enhancement/12555-…
zutigrm Jul 21, 2026
5bcea5e
Update code comment styles.
tofumatt Jul 21, 2026
fa8d85f
Update code comment.
tofumatt Jul 21, 2026
b74ea44
Update code comment.
tofumatt Jul 21, 2026
ec88b10
Merge branch 'enhancement/12555-kmw-extended' of github.com:google/si…
tofumatt Jul 21, 2026
d76b8b0
Update code comment.
tofumatt Jul 21, 2026
0930995
Rename variable.
tofumatt Jul 21, 2026
81ac2be
Merge branches 'enhancement/12555-kmw-extended' and 'enhancement/1255…
tofumatt Jul 21, 2026
4c56309
Add JSDoc string.
tofumatt Jul 21, 2026
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
20 changes: 10 additions & 10 deletions assets/js/components/KeyMetrics/KeyMetricsPDF.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,29 +144,29 @@ describe( 'KeyMetricsPDF', () => {
} );
} );

it( 'drops a tile whose data is null rather than showing a placeholder', () => {
const TileComponent = jest.fn( () => null );

it( 'renders exactly the tiles it is given, so the grid reflows with no gaps', () => {
// The aggregate loader drops no-data tiles before this component, so every
// tile here renders and there is no per-tile placeholder.
const tree = render( [
{
slug: 'metricA',
title: 'New visitors',
TileComponent,
TileComponent: NumericTile,
data: { value: '42' },
},
{
slug: 'metricB',
title: 'Returning visitors',
TileComponent,
data: null,
slug: 'metricC',
title: 'Visit length',
TileComponent: NumericTile,
data: { value: '3m' },
},
] );

const text = findTextStrings( tree ).join( ' ' );
expect( text ).toContain( 'New visitors: 42' );
expect( text ).toContain( 'Visit length: 3m' );
// No placeholder copy is ever rendered.
expect( text ).not.toContain( 'Data unavailable' );
// The failed tile is dropped; only the tile with data renders.
expect( TileComponent ).toHaveBeenCalledTimes( 1 );
} );

it( 'renders no tiles when the tile list is empty', () => {
Expand Down
37 changes: 19 additions & 18 deletions assets/js/components/KeyMetrics/KeyMetricsPDF.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ const styles = createPDFStyles( {
marginBottom: 24,
},
tileInner: {
// A small gutter between the grid columns.
// Fills the tile's height so its card stretches to the row's tallest tile,
// giving every tile in a row a uniform height as on the dashboard. The
// margin is a small gutter between the grid columns.
flexGrow: 1,
marginRight: 24,
},
} );
Expand All @@ -50,25 +53,23 @@ const KeyMetricsPDF: FC< PDFWidgetComponentProps > = ( { data } ) => {

return (
<View style={ styles.container }>
{ tiles
.filter( ( tile ) => tile.data )
.map( ( tile ) => {
const TileComponent = tile.TileComponent;
{ tiles.map( ( tile ) => {
const TileComponent = tile.TileComponent;

return (
<View key={ tile.slug } style={ styles.tile }>
<View style={ styles.tileInner }>
<TileComponent
title={ tile.title }
{ ...( tile.data as Record<
string,
unknown
> ) }
/>
</View>
return (
<View key={ tile.slug } style={ styles.tile }>
<View style={ styles.tileInner }>
<TileComponent
title={ tile.title }
{ ...( tile.data as Record<
string,
unknown
> ) }
/>
</View>
);
} ) }
</View>
);
} ) }
</View>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe( 'createKeyMetricTileDataLoader', () => {
);
const result = await getTileData( { registry, dates: DATES, signal } );

expect( buildReports ).toHaveBeenCalledWith( DATES );
expect( buildReports ).toHaveBeenCalledWith( DATES, registry );
expect( dispatch ).toHaveBeenCalledWith( 'test/store-a' );
expect( dispatch ).toHaveBeenCalledWith( 'test/store-b' );
expect( fetchGetReport ).toHaveBeenCalledWith(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,28 @@ interface FetchReportResult {
* threading the export's abort `signal` through each request, then hands the
* resolved report responses (in the same order) to `extract`, which normalises
* them into the shape the tile's `TileComponent` consumes. It returns `null`
* early when the export is canceled, and throws when any report fails so the
* aggregate loader can skip just that tile.
* early when the export is canceled, and throws when one of those fetched
* reports fails so the aggregate loader can skip just that tile. `extract`
* returns `null` when its report has no data, which the aggregate loader treats
* the same way — the tile is left out of the report entirely.
*
* Note this covers only the reports `buildReports` returns. A report a tile
* resolves inside `buildReports` (e.g. to discover page paths) is fetched via
* `resolveSelect`, which yields `undefined` rather than throwing on failure, so
* such a failure reads as no data, not as a thrown error.
*
* @since n.e.x.t
*
* @param buildReports Returns the reports to fetch for the given date range.
* @param extract Maps the resolved report responses to the tile's data.
* @param buildReports Returns the reports to fetch for the date range. It receives the registry too, so a tile whose report options depend on resolved state can await it; it may return a promise.
* @param extract Maps the resolved report responses to the tile's data, or `null` when the report has no data.
* @return A `getTileData( { registry, dates, signal } )` loader.
*/
export default function createKeyMetricTileDataLoader< TData >(
buildReports: ( dates: PDFReportDates ) => TileReportRequest[],
extract: ( reports: unknown[] ) => TData
buildReports: (
dates: PDFReportDates,
registry: GetPDFDataParams[ 'registry' ]
) => TileReportRequest[] | Promise< TileReportRequest[] >,
extract: ( reports: unknown[] ) => TData | null
): ( params: GetPDFDataParams ) => Promise< TData | null > {
return async function getTileData( {
registry,
Expand All @@ -74,7 +84,11 @@ export default function createKeyMetricTileDataLoader< TData >(
return null;
}

const requests = buildReports( dates );
const requests = await buildReports( dates, registry );

if ( signal.aborted ) {
return null;
}

const results: FetchReportResult[] = await Promise.all(
requests.map( ( { moduleStore, options } ) =>
Expand Down
Loading
Loading