diff --git a/src/TheBuilder.WebAnalytics/Client/src/analytics/analytics-breakdown-grid.element.ts b/src/TheBuilder.WebAnalytics/Client/src/analytics/analytics-breakdown-grid.element.ts index 3a3cf9c..3812f5e 100644 --- a/src/TheBuilder.WebAnalytics/Client/src/analytics/analytics-breakdown-grid.element.ts +++ b/src/TheBuilder.WebAnalytics/Client/src/analytics/analytics-breakdown-grid.element.ts @@ -3,13 +3,14 @@ import { UmbElementMixin } from "@umbraco-cms/backoffice/element-api"; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import type { AnalyticsBreakdown, AnalyticsDimension, AnalyticsEventsReport, AnalyticsFlagsReport } from "../api/types.gen.js"; import { breakdownMetricTotal, topBreakdownRows } from "./breakdown-rows.js"; -import { selectedCardDimension, type AcquisitionView, type DashboardCard, UTM_OPTIONS } from "./dashboard-cards.js"; +import { selectedCardDimension, type AcquisitionView, type DashboardCard } from "./dashboard-cards.js"; import type { AnalyticsFilter, AudienceDimension, DashboardMetric, UtmDimension } from "./dashboard-url-state.js"; import { topEventRows } from "./event-rows.js"; import "./breakdown-table.element.js"; import "./event-table.element.js"; import "./flag-card.element.js"; -import { stateData, type AsyncState } from "./async-state.js"; +import { isInitialLoading, stateData, type AsyncState } from "./async-state.js"; +import type { ReportTabGroup } from "./report-tabs.js"; @customElement("web-analytics-breakdown-grid") export class WebAnalyticsBreakdownGridElement extends UmbElementMixin(LitElement) { @@ -33,106 +34,71 @@ export class WebAnalyticsBreakdownGridElement extends UmbElementMixin(LitElement this.dispatchEvent(new CustomEvent(name, { bubbles: true, composed: true, detail })); } - #onTabKeydown(event: KeyboardEvent): void { - if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return; - event.preventDefault(); - const tabs = Array.from((event.currentTarget as HTMLElement).parentElement?.querySelectorAll("[role=tab]") ?? []); - const currentIndex = tabs.indexOf(event.currentTarget as HTMLButtonElement); - const targetIndex = event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 - : event.key === "ArrowLeft" ? (currentIndex - 1 + tabs.length) % tabs.length - : (currentIndex + 1) % tabs.length; - tabs[targetIndex]?.click(); - tabs[targetIndex]?.focus(); + #tabsForCard(card: Extract): ReportTabGroup { + return { + ariaLabel: "Audience technology", + idPrefix: `${card.id}-card-tab`, + options: card.options.map(({ dimension, label }) => ({ value: dimension, label })), + selected: this.audienceDimension, + }; } - #renderTabs(card: Extract) { - const selected = card.id === "audience" ? this.audienceDimension : this.utmDimension; - const options = card.id === "utm" ? UTM_OPTIONS : card.options; - return html` -
- ${options.map(({ dimension, label }) => html` - - `)} -
- `; - } - - #renderAcquisitionTabs(utmAvailable: boolean) { + #acquisitionTabs(utmAvailable: boolean): ReportTabGroup { const selected = utmAvailable ? this.acquisitionView : "referrers"; - return html` -
- - ${utmAvailable ? html` - - ` : ""} -
- `; + return { + ariaLabel: "Traffic source", + idPrefix: "acquisition-card-tab", + options: [ + { value: "referrers", label: "Referrers" }, + ...(utmAvailable ? [{ value: "utm", label: "UTM" }] : []), + ], + selected, + }; } - #renderUtmTabs(card: Extract) { - return html` -
- ${card.options.map(({ dimension, label }) => html` - - `)} -
- `; + #utmTabs(card: Extract): ReportTabGroup { + return { + appearance: "secondary", + ariaLabel: "UTM parameter", + idPrefix: "utm-card-tab", + options: card.options.map(({ dimension, label }) => ({ value: dimension, label })), + selected: this.utmDimension, + }; } #renderCard(card: DashboardCard) { const selected = selectedCardDimension(card, this.audienceDimension, this.utmDimension); const state = this.breakdowns[selected.dimension]; - const loading = !state || state.status === "idle" || state.status === "loading"; + const loading = isInitialLoading(state); const allRows = state ? stateData(state)?.rows ?? [] : []; const rows = topBreakdownRows(allRows, 10); const total = breakdownMetricTotal(allRows, this.metric); const unavailable = state?.status === "error" ? state.message : undefined; const planLimited = card.kind === "tabbed-breakdown" && card.planLimited; const linkValues = selected.dimension === "RequestPath" || selected.dimension === "Route"; + const headingTabs = card.kind === "tabbed-breakdown" ? this.#tabsForCard(card) : undefined; return html` - +
) => card.kind === "tabbed-breakdown" && this.#dispatch(card.id === "audience" ? "audience-change" : "utm-change", { dimension: event.detail.value })} .unavailable=${unavailable}> - ${card.kind === "tabbed-breakdown" ? this.#renderTabs(card) : ""} ${planLimited && unavailable ? html`

UTM reporting availability depends on your analytics plan and reporting window.

` : ""}