Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit a629ce3

Browse files
authored
Use generics to better type TabbedView (#10726)
1 parent fcf2fe2 commit a629ce3

File tree

12 files changed

+56
-52
lines changed

12 files changed

+56
-52
lines changed

src/TextForEvent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { WIDGET_LAYOUT_EVENT_TYPE } from "./stores/widgets/WidgetLayoutStore";
3232
import { RightPanelPhases } from "./stores/right-panel/RightPanelStorePhases";
3333
import defaultDispatcher from "./dispatcher/dispatcher";
3434
import { MatrixClientPeg } from "./MatrixClientPeg";
35-
import { ROOM_SECURITY_TAB } from "./components/views/dialogs/RoomSettingsDialog";
35+
import { RoomSettingsTab } from "./components/views/dialogs/RoomSettingsDialog";
3636
import AccessibleButton, { ButtonEvent } from "./components/views/elements/AccessibleButton";
3737
import RightPanelStore from "./stores/right-panel/RightPanelStore";
3838
import { highlightEvent, isLocationEvent } from "./utils/EventUtils";
@@ -236,7 +236,7 @@ function textForTombstoneEvent(ev: MatrixEvent): (() => string) | null {
236236
const onViewJoinRuleSettingsClick = (): void => {
237237
defaultDispatcher.dispatch({
238238
action: "open_room_settings",
239-
initial_tab_id: ROOM_SECURITY_TAB,
239+
initial_tab_id: RoomSettingsTab.Security,
240240
});
241241
};
242242

src/components/structures/TabbedView.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { RovingAccessibleButton, RovingTabIndexProvider } from "../../accessibil
2929
/**
3030
* Represents a tab for the TabbedView.
3131
*/
32-
export class Tab {
32+
export class Tab<T extends string> {
3333
/**
3434
* Creates a new tab.
3535
* @param {string} id The tab's ID.
@@ -39,7 +39,7 @@ export class Tab {
3939
* @param {string} screenName The screen name to report to Posthog.
4040
*/
4141
public constructor(
42-
public readonly id: string,
42+
public readonly id: T,
4343
public readonly label: string,
4444
public readonly icon: string | null,
4545
public readonly body: React.ReactNode,
@@ -52,20 +52,20 @@ export enum TabLocation {
5252
TOP = "top",
5353
}
5454

55-
interface IProps {
56-
tabs: NonEmptyArray<Tab>;
57-
initialTabId?: string;
55+
interface IProps<T extends string> {
56+
tabs: NonEmptyArray<Tab<T>>;
57+
initialTabId?: T;
5858
tabLocation: TabLocation;
59-
onChange?: (tabId: string) => void;
59+
onChange?: (tabId: T) => void;
6060
screenName?: ScreenName;
6161
}
6262

63-
interface IState {
64-
activeTabId: string;
63+
interface IState<T extends string> {
64+
activeTabId: T;
6565
}
6666

67-
export default class TabbedView extends React.Component<IProps, IState> {
68-
public constructor(props: IProps) {
67+
export default class TabbedView<T extends string> extends React.Component<IProps<T>, IState<T>> {
68+
public constructor(props: IProps<T>) {
6969
super(props);
7070

7171
const initialTabIdIsValid = props.tabs.find((tab) => tab.id === props.initialTabId);
@@ -78,7 +78,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
7878
tabLocation: TabLocation.LEFT,
7979
};
8080

81-
private getTabById(id: string): Tab | undefined {
81+
private getTabById(id: T): Tab<T> | undefined {
8282
return this.props.tabs.find((tab) => tab.id === id);
8383
}
8484

@@ -87,7 +87,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
8787
* @param {Tab} tab the tab to show
8888
* @private
8989
*/
90-
private setActiveTab(tab: Tab): void {
90+
private setActiveTab(tab: Tab<T>): void {
9191
// make sure this tab is still in available tabs
9292
if (!!this.getTabById(tab.id)) {
9393
if (this.props.onChange) this.props.onChange(tab.id);
@@ -97,7 +97,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
9797
}
9898
}
9999

100-
private renderTabLabel(tab: Tab): JSX.Element {
100+
private renderTabLabel(tab: Tab<T>): JSX.Element {
101101
const isActive = this.state.activeTabId === tab.id;
102102
const classes = classNames("mx_TabbedView_tabLabel", {
103103
mx_TabbedView_tabLabel_active: isActive,
@@ -130,11 +130,11 @@ export default class TabbedView extends React.Component<IProps, IState> {
130130
);
131131
}
132132

133-
private getTabId(tab: Tab): string {
133+
private getTabId(tab: Tab<T>): string {
134134
return `mx_tabpanel_${tab.id}`;
135135
}
136136

137-
private renderTabPanel(tab: Tab): React.ReactNode {
137+
private renderTabPanel(tab: Tab<T>): React.ReactNode {
138138
const id = this.getTabId(tab);
139139
return (
140140
<div className="mx_TabbedView_tabPanel" key={id} id={id} aria-labelledby={`${id}_label`}>

src/components/views/context_menus/RoomContextMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import ExportDialog from "../dialogs/ExportDialog";
3838
import { useFeatureEnabled } from "../../../hooks/useSettings";
3939
import { usePinnedEvents } from "../right_panel/PinnedMessagesCard";
4040
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
41-
import { ROOM_NOTIFICATIONS_TAB } from "../dialogs/RoomSettingsDialog";
41+
import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog";
4242
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
4343
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
4444
import DMRoomMap from "../../../utils/DMRoomMap";
@@ -199,7 +199,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
199199
dis.dispatch({
200200
action: "open_room_settings",
201201
room_id: room.roomId,
202-
initial_tab_id: ROOM_NOTIFICATIONS_TAB,
202+
initial_tab_id: RoomSettingsTab.Notifications,
203203
});
204204
onFinished();
205205

src/components/views/dialogs/InviteDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1494,7 +1494,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
14941494

14951495
let dialogContent;
14961496
if (this.props.kind === InviteKind.CallTransfer) {
1497-
const tabs: NonEmptyArray<Tab> = [
1497+
const tabs: NonEmptyArray<Tab<TabId>> = [
14981498
new Tab(TabId.UserDirectory, _td("User Directory"), "mx_InviteDialog_userDirectoryIcon", usersSection),
14991499
];
15001500

src/components/views/dialogs/RoomSettingsDialog.tsx

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,16 @@ import { NonEmptyArray } from "../../../@types/common";
4040
import { PollHistoryTab } from "../settings/tabs/room/PollHistoryTab";
4141
import ErrorBoundary from "../elements/ErrorBoundary";
4242

43-
export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
44-
export const ROOM_VOIP_TAB = "ROOM_VOIP_TAB";
45-
export const ROOM_SECURITY_TAB = "ROOM_SECURITY_TAB";
46-
export const ROOM_ROLES_TAB = "ROOM_ROLES_TAB";
47-
export const ROOM_NOTIFICATIONS_TAB = "ROOM_NOTIFICATIONS_TAB";
48-
export const ROOM_BRIDGES_TAB = "ROOM_BRIDGES_TAB";
49-
export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB";
50-
export const ROOM_POLL_HISTORY_TAB = "ROOM_POLL_HISTORY_TAB";
43+
export const enum RoomSettingsTab {
44+
General = "ROOM_GENERAL_TAB",
45+
Voip = "ROOM_VOIP_TAB",
46+
Security = "ROOM_SECURITY_TAB",
47+
Roles = "ROOM_ROLES_TAB",
48+
Notifications = "ROOM_NOTIFICATIONS_TAB",
49+
Bridges = "ROOM_BRIDGES_TAB",
50+
Advanced = "ROOM_ADVANCED_TAB",
51+
PollHistory = "ROOM_POLL_HISTORY_TAB",
52+
}
5153

5254
interface IProps {
5355
roomId: string;
@@ -118,12 +120,12 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
118120
this.forceUpdate();
119121
};
120122

121-
private getTabs(): NonEmptyArray<Tab> {
122-
const tabs: Tab[] = [];
123+
private getTabs(): NonEmptyArray<Tab<RoomSettingsTab>> {
124+
const tabs: Tab<RoomSettingsTab>[] = [];
123125

124126
tabs.push(
125127
new Tab(
126-
ROOM_GENERAL_TAB,
128+
RoomSettingsTab.General,
127129
_td("General"),
128130
"mx_RoomSettingsDialog_settingsIcon",
129131
<GeneralRoomSettingsTab room={this.state.room} />,
@@ -133,7 +135,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
133135
if (SettingsStore.getValue("feature_group_calls")) {
134136
tabs.push(
135137
new Tab(
136-
ROOM_VOIP_TAB,
138+
RoomSettingsTab.Voip,
137139
_td("Voice & Video"),
138140
"mx_RoomSettingsDialog_voiceIcon",
139141
<VoipRoomSettingsTab room={this.state.room} />,
@@ -142,7 +144,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
142144
}
143145
tabs.push(
144146
new Tab(
145-
ROOM_SECURITY_TAB,
147+
RoomSettingsTab.Security,
146148
_td("Security & Privacy"),
147149
"mx_RoomSettingsDialog_securityIcon",
148150
<SecurityRoomSettingsTab room={this.state.room} closeSettingsFn={() => this.props.onFinished(true)} />,
@@ -151,7 +153,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
151153
);
152154
tabs.push(
153155
new Tab(
154-
ROOM_ROLES_TAB,
156+
RoomSettingsTab.Roles,
155157
_td("Roles & Permissions"),
156158
"mx_RoomSettingsDialog_rolesIcon",
157159
<RolesRoomSettingsTab room={this.state.room} />,
@@ -160,7 +162,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
160162
);
161163
tabs.push(
162164
new Tab(
163-
ROOM_NOTIFICATIONS_TAB,
165+
RoomSettingsTab.Notifications,
164166
_td("Notifications"),
165167
"mx_RoomSettingsDialog_notificationsIcon",
166168
(
@@ -176,7 +178,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
176178
if (SettingsStore.getValue("feature_bridge_state")) {
177179
tabs.push(
178180
new Tab(
179-
ROOM_BRIDGES_TAB,
181+
RoomSettingsTab.Bridges,
180182
_td("Bridges"),
181183
"mx_RoomSettingsDialog_bridgesIcon",
182184
<BridgeSettingsTab room={this.state.room} />,
@@ -187,7 +189,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
187189

188190
tabs.push(
189191
new Tab(
190-
ROOM_POLL_HISTORY_TAB,
192+
RoomSettingsTab.PollHistory,
191193
_td("Poll history"),
192194
"mx_RoomSettingsDialog_pollsIcon",
193195
<PollHistoryTab room={this.state.room} onFinished={() => this.props.onFinished(true)} />,
@@ -197,7 +199,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
197199
if (SettingsStore.getValue(UIFeature.AdvancedSettings)) {
198200
tabs.push(
199201
new Tab(
200-
ROOM_ADVANCED_TAB,
202+
RoomSettingsTab.Advanced,
201203
_td("Advanced"),
202204
"mx_RoomSettingsDialog_warningIcon",
203205
(
@@ -211,7 +213,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
211213
);
212214
}
213215

214-
return tabs as NonEmptyArray<Tab>;
216+
return tabs as NonEmptyArray<Tab<RoomSettingsTab>>;
215217
}
216218

217219
public render(): React.ReactNode {

src/components/views/dialogs/SpacePreferencesDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const SpacePreferencesAppearanceTab: React.FC<Pick<IProps, "space">> = ({ space
7070
};
7171

7272
const SpacePreferencesDialog: React.FC<IProps> = ({ space, initialTabId, onFinished }) => {
73-
const tabs: NonEmptyArray<Tab> = [
73+
const tabs: NonEmptyArray<Tab<SpacePreferenceTab>> = [
7474
new Tab(
7575
SpacePreferenceTab.Appearance,
7676
_td("Appearance"),

src/components/views/dialogs/SpaceSettingsDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
8080
<AdvancedRoomSettingsTab room={space} closeSettingsFn={onFinished} />,
8181
)
8282
: null,
83-
].filter(Boolean) as NonEmptyArray<Tab>;
83+
].filter(Boolean) as NonEmptyArray<Tab<SpaceSettingsTab>>;
8484
}, [cli, space, onFinished]);
8585

8686
return (

src/components/views/dialogs/UserSettingsDialog.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
8181
this.setState({ newSessionManagerEnabled: newValue });
8282
};
8383

84-
private getTabs(): NonEmptyArray<Tab> {
85-
const tabs: Tab[] = [];
84+
private getTabs(): NonEmptyArray<Tab<UserTab>> {
85+
const tabs: Tab<UserTab>[] = [];
8686

8787
tabs.push(
8888
new Tab(
@@ -208,7 +208,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
208208
),
209209
);
210210

211-
return tabs as NonEmptyArray<Tab>;
211+
return tabs as NonEmptyArray<Tab<UserTab>>;
212212
}
213213

214214
public render(): React.ReactNode {

src/components/views/elements/DesktopCapturerSourcePicker.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ export interface PickerIProps {
8585
onFinished(sourceId?: string): void;
8686
}
8787

88+
type TabId = "screen" | "window";
89+
8890
export default class DesktopCapturerSourcePicker extends React.Component<PickerIProps, PickerIState> {
8991
public interval: number;
9092

@@ -134,7 +136,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
134136
this.props.onFinished();
135137
};
136138

137-
private getTab(type: "screen" | "window", label: string): Tab {
139+
private getTab(type: TabId, label: string): Tab<TabId> {
138140
const sources = this.state.sources
139141
.filter((source) => source.id.startsWith(type))
140142
.map((source) => {
@@ -152,7 +154,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
152154
}
153155

154156
public render(): React.ReactNode {
155-
const tabs: NonEmptyArray<Tab> = [
157+
const tabs: NonEmptyArray<Tab<TabId>> = [
156158
this.getTab("screen", _t("Share entire screen")),
157159
this.getTab("window", _t("Application window")),
158160
];

src/components/views/rooms/NewRoomIntro.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { Action } from "../../../dispatcher/actions";
3333
import SpaceStore from "../../../stores/spaces/SpaceStore";
3434
import { showSpaceInvite } from "../../../utils/space";
3535
import EventTileBubble from "../messages/EventTileBubble";
36-
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
36+
import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog";
3737
import { MatrixClientPeg } from "../../../MatrixClientPeg";
3838
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
3939
import { UIComponent } from "../../../settings/UIFeature";
@@ -268,7 +268,7 @@ const NewRoomIntro: React.FC = () => {
268268
event.preventDefault();
269269
defaultDispatcher.dispatch({
270270
action: "open_room_settings",
271-
initial_tab_id: ROOM_SECURITY_TAB,
271+
initial_tab_id: RoomSettingsTab.Security,
272272
});
273273
}
274274

0 commit comments

Comments
 (0)