Skip to content

Introduce shared PDF report components #12967

Description

@shervElmi

Feature Description

The PDF report is built with @react-pdf/renderer. Each section and widget styles its own text, links, buttons, badges, and cards from the raw Text, View, and Link primitives. The same styles and colors repeat across many files, so the report is hard to keep consistent, and each new widget repeats them.

Add a small set of shared PDF components that mirror the dashboard components. A text component mirrors Typography. Components for links, buttons, change badges, and chips mirror Link, Button, ChangeBadge, and Chip. Keep the report's colors and text styles in one theme.

Each section and widget then builds from these components, so the report stays consistent with the Figma design and a new widget reuses the components instead of repeating the styles.


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • Each repeated part of the report renders through one shared component:
    • A text component, similar to the dashboard Typography component, renders the report's text.
    • A link component, similar to the dashboard Link component, renders the report's links.
    • A button component, similar to the dashboard Button component, renders the report's buttons.
    • A change badge component, similar to the dashboard ChangeBadge component, renders the report's change badges.
    • A chip component, similar to the dashboard Chip component, renders the report's chips.
    • A card component renders the report's cards.
  • The PDF colors and text styles live in one place. Every component reads them from there, and no file holds a duplicate color or text-style value.
  • Every PDF section and widget builds from the shared components, and no section or widget keeps its own copy of a style the components provide.

Implementation Brief

  • Update assets/js/components/pdf-export/pdf-theme.ts

    • Replace the PDF_COLOR_* constants, PDF_HEADER_COLORS, and the current colors map with one colors map of the Figma report values:

      export const colors = {
       textPrimary: '#161b18',
       textMuted: '#6c726e',
       link: '#108080',
       border: '#ebeef0',
       pageBackground: '#f3f5f7',
       surface: '#ffffff',
       positiveBadgeBackground: '#d8ffc0',
       positiveBadgeText: '#1f4c04',
       negativeBadgeBackground: '#ffded3',
       negativeBadgeText: '#7a1e00',
       noticeBackground: '#e3d1ff',
       noticeText: '#462083',
      } as const;
    • Replace fontSizes with a typography map matching $typography-settings, keyed by type then size:

      • Each entry has fontSize, fontWeight, letterSpacing, and lineHeight.
      • Resolve every $fs-*, $ls-*, and $lh-* to a plain number, with fontWeight 400 or 500.
    • Add a typographyFontFamilies map keyed by type, matching $typography-font-families:

      export const typographyFontFamilies = {
       display: PDF_FONT_FAMILY_DISPLAY,
       headline: PDF_FONT_FAMILY_DISPLAY,
       title: PDF_FONT_FAMILY_TEXT,
       body: PDF_FONT_FAMILY_TEXT,
       label: PDF_FONT_FAMILY_TEXT,
      } as const;
  • Create assets/js/components/pdf-export/shared-react-pdf-components/PDFTypography.tsx

    • A text component like Typography, with type (default 'body'), size (default 'medium'), and style props.

    • Renders a <Text> with:

      style={ [
       typography[ type ][ size ],
       { fontFamily: typographyFontFamilies[ type ], color: colors.textPrimary },
       style,
      ] }
  • Create assets/js/components/pdf-export/shared-react-pdf-components/PDFLink.tsx

    • A link component like Link, with href, type, size, trailingIcon, and style props.
    • Renders a <Link src={ href }> with flexDirection: 'row', alignItems: 'center', and no underline, wrapping the text and the optional trailingIcon.
    • Renders the text as a PDFTypography with the given type and size and style={ [ { color: colors.link }, style ] }.
  • Create assets/js/components/pdf-export/shared-react-pdf-components/PDFButton.tsx

    • A button like Button, with href, backgroundColor, labelColor, and children props.
    • Renders a <View> with the backgroundColor, borderRadius: 100, paddingVertical: 8, and paddingHorizontal: 16, wrapping a <Link src={ href }> with no underline. The link renders children as a PDFTypography label medium with style={ { color: labelColor } }.
  • Create assets/js/components/pdf-export/shared-react-pdf-components/PDFChangeBadge.tsx

    • A change badge like ChangeBadge, with a change prop (the formatted string) and an isNegative prop.
    • Sets the background and text color from isNegative:
      • true: colors.negativeBadgeBackground with colors.negativeBadgeText.
      • false: colors.positiveBadgeBackground with colors.positiveBadgeText.
    • Renders a <View> with the background, borderRadius: 100, paddingVertical: 4, and paddingHorizontal: 8, wrapping change as a PDFTypography body small in the text color.
  • Create assets/js/components/pdf-export/shared-react-pdf-components/PDFCard.tsx

    • Accepts style and children props, and renders the report's white cards.
    • Renders a <View> with style={ [ { backgroundColor: colors.surface, borderRadius: 16, padding: 24 }, style ] }.
  • Rename assets/js/components/pdf-export/shared-react-pdf-components/PDFHeaderSectionChip.tsx to PDFChip.tsx

    • Rename the component, its props interface, and its default export to PDFChip, like Chip, keeping the label and optional Icon props and no link target.
    • Renders a <View> with borderWidth: 1, borderColor: colors.border, borderRadius: 100, paddingVertical: 8, and paddingHorizontal: 12.
    • Renders the optional Icon at size 20 in colors.textPrimary with a 4 gap to the label, then the label as a PDFTypography label small with style={ { fontFamily: typographyFontFamilies.display, letterSpacing: 0.5 } }.
  • Update assets/js/components/pdf-export/section-icons.ts

    • In makeIcon, read the default icon color from colors.textPrimary in place of PDF_HEADER_COLORS.chipIcon.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFWidgetSection.tsx

    • Render the heading with PDFTypography body large.
    • Render the card with PDFCard, passing cardStyle through as its style.
    • Move the card's marginBottom: 20 onto the outer <View>.
    • Remove the local styles.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/DashboardReport.tsx

    • Render the area title with PDFTypography headline medium, and the "No report data available." and "Data unavailable." lines with PDFTypography body small in colors.textMuted.
    • Read the page background from colors.pageBackground.
    • Remove the sectionTitle and emptyText rules from styles.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFHeader.tsx

    • Render the "Your site's performance" line with PDFTypography body medium in colors.textMuted, and the date range with PDFTypography headline small.
    • Render the site address with PDFLink body medium in colors.textMuted when dashboardURL is set, and with PDFTypography body medium in colors.textMuted otherwise.
    • Render the "View dashboard in Site Kit" link with PDFLink label medium and the chevron <Svg> as its trailingIcon, with the chevron fill reading colors.link.
    • Render the section chips with PDFChip, and read the divider color from colors.border.
    • Remove the local text styles.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFFooter.tsx

    • Render the three footer links with PDFLink body small in colors.textMuted.
    • Remove the local link style.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFSiteKitLogo.tsx

    • Render the "Site Kit" logo text with PDFTypography headline small, passing its marginLeft: 7 through style.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFEmailReportingNotice.tsx

    • Render the title with PDFTypography label medium, and the two body lines with PDFTypography body medium, all in colors.noticeText.
    • Render the button with PDFButton, passing colors.noticeText as the background and colors.surface as the label color.
    • Read the card background from colors.noticeBackground, set the container borderRadius: 16 and paddingVertical: 14, and read the star icon fill from colors.noticeText.
    • Remove the local COLORS and text styles.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFMetricTile.tsx

    • Render the title with PDFTypography title small, the value with headline medium, and the change label with body small in colors.textMuted.
    • Render the change badge with PDFChangeBadge, passing change and isNegative.
    • Remove the local COLORS, the text styles, and the chip and chipText styles.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFMetricChartTile.tsx

    • Render the card with PDFCard.
    • Render the title with PDFTypography title small, the value with headline medium, and the legend and change labels with body small in colors.textMuted.
    • Render the change badge with PDFChangeBadge, mapping changeDirection === 'down' to isNegative.
    • Render the "Data unavailable" placeholder with PDFTypography body small in colors.textMuted on colors.pageBackground.
    • Remove the local COLORS, the card and text styles, and the ChangeArrow component.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFTable.tsx

    • Render the header cells with PDFTypography title small and the body cells with PDFTypography body medium, passing textAlign: 'right' through style for the right-aligned columns.
    • Read the row dividers from colors.border.
    • Remove the local headerTextStyles, bodyTextStyles, and COLORS.
  • Update assets/js/modules/analytics-4/components/dashboard/DashboardAllTrafficWidgetGA4/indexPDF.tsx

    • Render the "No data available." text with PDFTypography body small in colors.textMuted.
    • Remove the noData rule from styles.
  • Update assets/js/modules/analytics-4/components/module/ModulePopularPagesWidgetGA4/ModulePopularPagesWidgetGA4PDF.tsx

    • Render the rank with PDFTypography body medium.
    • Render the page title link with PDFLink body medium, and the page URL link with PDFLink body small.
    • Render the "No data available." text with PDFTypography body small in colors.textMuted.
    • Remove borderRadius from the table's cardStyle.
    • Remove the local bodyTextStyles, the rank, title, url, and noData styles, and PAGE_LINK_COLOR.
  • Update assets/js/modules/search-console/components/dashboard/SearchFunnelWidgetGA4/indexPDF.tsx

    • Render the "Search traffic over time" sub-heading with PDFTypography body large.
    • Render the "Data unavailable" text with PDFTypography body small in colors.textMuted.
    • Remove the local heading and noData text rules from styles.

Test Coverage

  • Add assets/js/components/pdf-export/shared-react-pdf-components/PDFTypography.test.tsx
    • Following PDFMetricTile.test.tsx for the react-test-renderer setup:
      • PDFTypography renders a <Text> with the font family, size, weight, line height, and letter spacing of the given type and size.
      • PDFTypography defaults to body medium when type and size are omitted.
      • PDFTypography merges the style prop over the token style.
  • Add assets/js/components/pdf-export/shared-react-pdf-components/PDFLink.test.tsx
    • PDFLink links to href with no underline and the link color, renders the trailingIcon when one is given, and takes the color from the style prop.
  • Add assets/js/components/pdf-export/shared-react-pdf-components/PDFButton.test.tsx
    • PDFButton renders the link to href, and reads the background and label color from its props.
  • Add assets/js/components/pdf-export/shared-react-pdf-components/PDFChangeBadge.test.tsx
    • PDFChangeBadge uses the positive colors for a rising change and the negative colors for a falling change.
  • Add assets/js/components/pdf-export/shared-react-pdf-components/PDFCard.test.tsx
    • PDFCard renders the colors.surface background with borderRadius: 16 and padding: 24, and merges the style prop.
  • Rename assets/js/components/pdf-export/shared-react-pdf-components/PDFHeaderSectionChip.test.tsx to PDFChip.test.tsx
    • Update it for PDFChip: the label rendered through PDFTypography in the display family at letter-spacing 0.5, the colors.border border, the size 20 icon, and no link target.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFMetricChartTile.test.tsx
    • Cover the change badge now using the positive and negative badge colors, with no arrow.
  • Update assets/js/components/pdf-export/shared-react-pdf-components/PDFEmailReportingNotice.test.tsx
    • Cover the notice background, title, body, and button colors now read from colors.
  • Update assets/js/modules/analytics-4/components/dashboard/DashboardAllTrafficWidgetGA4/indexPDF.test.tsx, assets/js/modules/analytics-4/components/module/ModulePopularPagesWidgetGA4/ModulePopularPagesWidgetGA4PDF.test.tsx, and assets/js/modules/search-console/components/dashboard/SearchFunnelWidgetGA4/indexPDF.test.tsx
    • Update the empty-state assertions for the text now rendered in colors.textMuted through PDFTypography.
  • Update any remaining PDF component test that asserts a color or font value the shared components now own.

QA Brief

Changelog entry

Metadata

Metadata

Assignees

No one assigned

    Labels

    Team SIssues for Squad 1Type: EnhancementImprovement of an existing featureType: InfrastructureEngineering infrastructure & tooling

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions