+ );
+}
+
+/**
+ * The **Traffic-like** instance: rolling presets, custom range, and comparison
+ * in the slot.
+ *
+ * The subtitle is a static arg here; in product it derives from the *applied*
+ * preset/range and, once the interval control lands, the active interval.
+ */
+export const Default: Story = {
+ args: {
+ title: 'Site traffic',
+ subtitle: 'Last 30 days',
+ },
+ render: ( { title, subtitle } ) => (
+
+ ),
+};
+
+/**
+ * The **Insights-like** instance: the year surface (all time plus calendar
+ * years) in the slot.
+ *
+ * Per the design's instances table, this surface carries *no comparison
+ * control*.
+ */
+export const YearSurface: Story = {
+ args: {
+ title: 'Insights',
+ subtitle: 'All time',
+ },
+ render: ( { title, subtitle } ) => (
+
+ ),
+};
+
+/**
+ * The left half on its own: nothing in the controls slot.
+ */
+export const WithoutControls: Story = {
+ args: {
+ title: 'Site traffic',
+ subtitle: 'Last 30 days',
+ },
+ render: ( { title, subtitle } ) => ,
+};
diff --git a/projects/packages/premium-analytics/routes/dashboard/components/dashboard-sections/dashboard-sections.module.scss b/projects/packages/premium-analytics/routes/dashboard/components/dashboard-sections/dashboard-sections.module.scss
index 8ef23a11b6df..4f7d2997fe03 100644
--- a/projects/packages/premium-analytics/routes/dashboard/components/dashboard-sections/dashboard-sections.module.scss
+++ b/projects/packages/premium-analytics/routes/dashboard/components/dashboard-sections/dashboard-sections.module.scss
@@ -7,8 +7,14 @@
flex-direction: column;
flex: 1 1 auto;
min-block-size: 0;
+
+ // Own stacking context, so in-panel layers (the sticky section header,
+ // widget focus rings) never compete with the page header above.
+ position: relative;
+ z-index: 0;
}
.tabList {
padding-inline: var(--wpds-dimension-padding-2xl);
+ z-index: 0;
}
diff --git a/projects/packages/premium-analytics/routes/dashboard/components/dashboard-sections/dashboard-sections.tsx b/projects/packages/premium-analytics/routes/dashboard/components/dashboard-sections/dashboard-sections.tsx
index ddb860ce7ef6..cb4301b7a4fd 100644
--- a/projects/packages/premium-analytics/routes/dashboard/components/dashboard-sections/dashboard-sections.tsx
+++ b/projects/packages/premium-analytics/routes/dashboard/components/dashboard-sections/dashboard-sections.tsx
@@ -35,11 +35,7 @@ type DashboardSectionsProps = {
* Tabs are keyed by the section `slug` (the URL-facing identifier the active
* section state uses), not the namespaced `id`.
*
- * @param props - Component props.
- * @param props.sections - The sections to render, in order.
- * @param props.value - The currently active section ID.
- * @param props.onChange - Called with the new section ID when the user selects a different section.
- * @param props.children - Section panel content.
+ * @param {DashboardSectionsProps} props - The props for the DashboardSections component.
* @return The section tab bar element.
*/
export function DashboardSections( {
diff --git a/projects/packages/premium-analytics/routes/dashboard/stage.module.scss b/projects/packages/premium-analytics/routes/dashboard/stage.module.scss
index ff1abbdad86d..5ef1034965ab 100644
--- a/projects/packages/premium-analytics/routes/dashboard/stage.module.scss
+++ b/projects/packages/premium-analytics/routes/dashboard/stage.module.scss
@@ -12,7 +12,7 @@
.content {
flex: 1 1 auto;
min-block-size: 0;
- overflow-y: auto;
+
// Reserve paint space for the widget host's outer focus outline.
padding-block-start: calc(var(--wpds-border-width-focus) + 2px);
// Match the horizontal padding of the page header and the section tabs so the
@@ -21,10 +21,8 @@
padding-inline: var(--wpds-dimension-padding-2xl);
}
-.dateFilters {
- // Sits below the section tabs and above the widgets. The tab list already
- // adds a bottom margin, so only pad below here to keep the spacing even.
- // Inline padding matches the tabs and widget grid so it lines up.
+.sectionHeader {
+ // Separates the header row from the widget grid below. The inline gutter
+ // comes from the panel, which already pads to match the tabs.
padding-block-end: var(--wpds-dimension-gap-lg);
- padding-inline: var(--wpds-dimension-padding-2xl);
}
diff --git a/projects/packages/premium-analytics/routes/dashboard/stage.tsx b/projects/packages/premium-analytics/routes/dashboard/stage.tsx
index 8c02a5a275eb..534c3db44df4 100644
--- a/projects/packages/premium-analytics/routes/dashboard/stage.tsx
+++ b/projects/packages/premium-analytics/routes/dashboard/stage.tsx
@@ -1,6 +1,6 @@
import { GlobalErrorProvider } from '@jetpack-premium-analytics/data';
import { useReportDateFilters } from '@jetpack-premium-analytics/routing';
-import { DateFiltersPanel, SectionTabPanel } from '@jetpack-premium-analytics/ui';
+import { DateFiltersPanel, SectionHeader, SectionTabPanel } from '@jetpack-premium-analytics/ui';
import { Page, Breadcrumbs } from '@wordpress/admin-ui';
import { Spinner } from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';
@@ -92,10 +92,6 @@ function Dashboard(): JSX.Element {
items={ [ { label: __( 'Analytics', 'jetpack-premium-analytics-pkg' ) } ] }
/>
}
- subTitle={ __(
- 'Track your site performance and visitor insights.',
- 'jetpack-premium-analytics-pkg'
- ) }
actions={ }
className={ styles.dashboard }
>
@@ -104,24 +100,18 @@ function Dashboard(): JSX.Element {
value={ activeSection }
onChange={ setActiveSection }
>
- { /*
- * The date filters drive every section, so they render once
- * below the section tabs and above the widgets, sharing the
- * URL search state across all sections.
- *
- * The wrapper div is also the responsive-measurement target:
- * DateFiltersPanel reads its width to pick mobile/wide layouts
- * instead of relying on the viewport.
- */ }
-