Skip to content

Commit dfb0ce0

Browse files
committed
add tabpanel aria props to dashboard wrapper instead
1 parent abc118f commit dfb0ce0

File tree

2 files changed

+67
-37
lines changed
  • src/app
    • [locale]/(redesign)/(authenticated)/user/(dashboard)/dashboard
    • components/client

2 files changed

+67
-37
lines changed

src/app/[locale]/(redesign)/(authenticated)/user/(dashboard)/dashboard/View.tsx

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
getDashboardSummary,
2424
} from "../../../../../../functions/server/dashboard";
2525
import { getExposureStatus } from "../../../../../../components/server/StatusPill";
26-
import { TabList } from "../../../../../../components/client/TabList";
26+
import { TabList, TabPanel } from "../../../../../../components/client/TabList";
2727
import { filterExposures } from "./filterExposures";
2828
import { SubscriberBreach } from "../../../../../../../utils/subscriberBreaches";
2929
import { getLocale } from "../../../../../../functions/universal/getLocale";
@@ -35,6 +35,7 @@ import { getNextGuidedStep } from "../../../../../../functions/server/getRelevan
3535
import { useTelemetry } from "../../../../../../hooks/useTelemetry";
3636
import { UserAnnouncementWithDetails } from "../../../../../../../db/tables/user_announcements";
3737
import { PlusShutdownBanner } from "../../../../../../components/client/PlusShutdownBanner";
38+
import { Item, useTabListState } from "react-stately";
3839

3940
export type TabType = "action-needed" | "fixed";
4041

@@ -89,16 +90,6 @@ export const View = (props: Props) => {
8990
const [activeExposureCardKey, setActiveExposureCardKey] = useState<
9091
string | null
9192
>(null);
92-
const tabsData: TabData[] = [
93-
{
94-
name: l10n.getString("dashboard-tab-label-action-needed"),
95-
key: "action-needed",
96-
},
97-
{
98-
name: l10n.getString("dashboard-tab-label-fixed"),
99-
key: "fixed",
100-
},
101-
];
10293

10394
const breachesDataArray = props.userBreaches.flat();
10495

@@ -124,6 +115,33 @@ export const View = (props: Props) => {
124115
});
125116

126117
const tabSpecificExposures = getTabSpecificExposures(activeTab);
118+
const tabsData: TabData[] = [
119+
{
120+
name: l10n.getString("dashboard-tab-label-action-needed"),
121+
key: "action-needed",
122+
},
123+
{
124+
name: l10n.getString("dashboard-tab-label-fixed"),
125+
key: "fixed",
126+
},
127+
];
128+
129+
const tabListState = useTabListState({
130+
selectedKey: activeTab,
131+
onSelectionChange: (key) => {
132+
setActiveTab(key as TabType);
133+
recordTelemetry("dashboard", "view", {
134+
dashboard_tab: key as TabType,
135+
breach_count: breachesDataArray.length,
136+
});
137+
},
138+
children: tabsData.map((t) => (
139+
<Item key={t.key} title={t.name}>
140+
<></>
141+
</Item>
142+
)),
143+
});
144+
127145
const filteredExposures = filterExposures(tabSpecificExposures, filters);
128146
const exposureCardElems = filteredExposures.map(
129147
(exposure: SubscriberBreach) => {
@@ -187,21 +205,15 @@ export const View = (props: Props) => {
187205
enabledFeatureFlags={props.enabledFeatureFlags}
188206
announcements={announcements}
189207
>
190-
<TabList
191-
tabs={tabsData}
192-
onSelectionChange={(selectedKey) => {
193-
setActiveTab(selectedKey as TabType);
194-
recordTelemetry("dashboard", "view", {
195-
dashboard_tab: selectedKey as TabType,
196-
breach_count: breachesDataArray.length,
197-
});
198-
}}
199-
selectedKey={activeTab}
200-
/>
208+
<TabList tabs={tabsData} state={tabListState} />
201209
</Toolbar>
202210
<PlusShutdownBanner countryCode={props.countryCode} />
203211

204-
<div className={styles.dashboardContent}>
212+
<TabPanel
213+
className={styles.dashboardContent}
214+
state={tabListState}
215+
id={activeTab}
216+
>
205217
<DashboardTopBanner
206218
tabType={activeTab}
207219
hasExposures={hasExposures}
@@ -252,7 +264,7 @@ export const View = (props: Props) => {
252264
) : (
253265
<ul className={styles.exposureList}>{exposureCardElems}</ul>
254266
)}
255-
</div>
267+
</TabPanel>
256268
</div>
257269
);
258270
};

src/app/components/client/TabList.tsx

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
import { Key, ReactNode, useRef } from "react";
6-
import { AriaTabListOptions, useTab, useTabList } from "react-aria";
7-
import { Item, useTabListState } from "react-stately";
6+
import {
7+
AriaTabListOptions,
8+
AriaTabPanelProps,
9+
useTab,
10+
useTabList,
11+
useTabPanel,
12+
} from "react-aria";
13+
import { Item, Node } from "react-stately";
814
import styles from "./TabList.module.scss";
915
import { TabListState, TabListStateOptions } from "@react-stately/tabs";
1016

@@ -13,6 +19,7 @@ export type TabsProps = (
1319
| AriaTabListOptions<object>
1420
) & {
1521
variant?: "primary" | "secondary";
22+
state: TabListState<object>;
1623
};
1724

1825
export type TabListProps = TabsProps & {
@@ -24,30 +31,26 @@ export type TabListProps = TabsProps & {
2431
};
2532

2633
export interface TabParams {
27-
item: {
28-
key: Parameters<typeof useTab>[0]["key"];
29-
rendered: ReactNode;
30-
};
34+
item: Node<object>;
3135
state: TabListState<object>;
3236
}
3337

3438
function Tab({ item, state }: TabParams) {
35-
const { key, rendered } = item;
3639
const ref = useRef(null);
40+
const { key } = item;
3741
const { tabProps } = useTab({ key }, state, ref);
3842

3943
return (
4044
<div {...tabProps} ref={ref} className={styles.tab}>
41-
{rendered}
45+
{item.rendered}
4246
</div>
4347
);
4448
}
4549

4650
function Tabs(props: TabsProps) {
47-
const state = useTabListState(props);
4851
const ref = useRef(null);
49-
const { tabListProps } = useTabList(props, state, ref);
50-
const { collection, selectedItem } = state;
52+
const { tabListProps } = useTabList(props, props.state, ref);
53+
const { collection } = props.state;
5154

5255
return (
5356
<div
@@ -56,7 +59,7 @@ function Tabs(props: TabsProps) {
5659
>
5760
<div {...tabListProps} ref={ref} className={styles.tabs}>
5861
{[...collection].map((item) => (
59-
<Tab key={item.key} item={item} state={state} />
62+
<Tab key={item.key} item={item} state={props.state} />
6063
))}
6164
</div>
6265
</div>
@@ -65,7 +68,7 @@ function Tabs(props: TabsProps) {
6568

6669
export const TabList = ({ tabs, ...props }: TabListProps) => {
6770
return (
68-
<Tabs {...props}>
71+
<Tabs id="tablist" {...props}>
6972
{tabs.map((tab) => (
7073
<Item key={tab.key} title={tab.name}>
7174
{tab.content}
@@ -74,3 +77,18 @@ export const TabList = ({ tabs, ...props }: TabListProps) => {
7477
</Tabs>
7578
);
7679
};
80+
export interface TabPanelProps extends AriaTabPanelProps {
81+
state: TabListState<object>;
82+
className?: string; // etc
83+
children: ReactNode;
84+
}
85+
86+
export function TabPanel({ state, ...props }: TabPanelProps) {
87+
const ref = useRef(null);
88+
const { tabPanelProps } = useTabPanel(props, state, ref);
89+
return (
90+
<div {...tabPanelProps} ref={ref} className={props.className}>
91+
{props.children}
92+
</div>
93+
);
94+
}

0 commit comments

Comments
 (0)