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

Commit 72a8f8f

Browse files
authored
Show tooltips on narrow tabbed views (#12624)
* Show tooltips on narrow tabbed views * Also only show on left-side tabs * Unused import * Comments * Add test * More test * Assert tooltip appears in playwright test
1 parent 650b9cb commit 72a8f8f

File tree

4 files changed

+100
-1
lines changed

4 files changed

+100
-1
lines changed

playwright/e2e/settings/general-user-settings-tab.spec.ts

+6
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ test.describe("General user settings tab", () => {
120120
await expect(uut).toMatchScreenshot("general-smallscreen.png");
121121
});
122122

123+
test("should show tooltips on narrow screen", async ({ page, uut }) => {
124+
await page.setViewportSize({ width: 700, height: 600 });
125+
await page.getByRole("tab", { name: "General" }).hover();
126+
await expect(page.getByRole("tooltip")).toHaveText("General");
127+
});
128+
123129
test("should support adding and removing a profile picture", async ({ uut, page }) => {
124130
const profileSettings = uut.locator(".mx_UserProfileSettings");
125131
// Upload a picture

src/components/structures/TabbedView.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import AutoHideScrollbar from "./AutoHideScrollbar";
2424
import { PosthogScreenTracker, ScreenName } from "../../PosthogTrackers";
2525
import { NonEmptyArray } from "../../@types/common";
2626
import { RovingAccessibleButton, RovingTabIndexProvider } from "../../accessibility/RovingTabIndex";
27+
import { useWindowWidth } from "../../hooks/useWindowWidth";
2728

2829
/**
2930
* Represents a tab for the TabbedView.
@@ -87,10 +88,11 @@ function TabPanel<T extends string>({ tab }: ITabPanelProps<T>): JSX.Element {
8788
interface ITabLabelProps<T extends string> {
8889
tab: Tab<T>;
8990
isActive: boolean;
91+
showToolip: boolean;
9092
onClick: () => void;
9193
}
9294

93-
function TabLabel<T extends string>({ tab, isActive, onClick }: ITabLabelProps<T>): JSX.Element {
95+
function TabLabel<T extends string>({ tab, isActive, showToolip, onClick }: ITabLabelProps<T>): JSX.Element {
9496
const classes = classNames("mx_TabbedView_tabLabel", {
9597
mx_TabbedView_tabLabel_active: isActive,
9698
});
@@ -112,6 +114,7 @@ function TabLabel<T extends string>({ tab, isActive, onClick }: ITabLabelProps<T
112114
aria-selected={isActive}
113115
aria-controls={id}
114116
element="li"
117+
title={showToolip ? label : undefined}
115118
>
116119
{tabIcon}
117120
<span className="mx_TabbedView_tabLabel_text" id={`${id}_label`}>
@@ -152,12 +155,16 @@ export default function TabbedView<T extends string>(props: IProps<T>): JSX.Elem
152155
return props.tabs.find((tab) => tab.id === id);
153156
};
154157

158+
const windowWidth = useWindowWidth();
159+
155160
const labels = props.tabs.map((tab) => (
156161
<TabLabel
157162
key={"tab_label_" + tab.id}
158163
tab={tab}
159164
isActive={tab.id === props.activeTabId}
160165
onClick={() => props.onChange(tab.id)}
166+
// This should be the same as the the CSS breakpoint at which the tab labels are hidden
167+
showToolip={windowWidth < 1024 && tabLocation == TabLocation.LEFT}
161168
/>
162169
));
163170
const tab = getTabById(props.activeTabId);

src/hooks/useWindowWidth.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Copyright 2024 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import React from "react";
18+
19+
import UIStore, { UI_EVENTS } from "../stores/UIStore";
20+
21+
/**
22+
* Hook that gets the width of the viewport using UIStore
23+
*
24+
* @returns the current window width
25+
*/
26+
export const useWindowWidth = (): number => {
27+
const [width, setWidth] = React.useState(UIStore.instance.windowWidth);
28+
29+
React.useEffect(() => {
30+
UIStore.instance.on(UI_EVENTS.Resize, () => {
31+
setWidth(UIStore.instance.windowWidth);
32+
});
33+
34+
return () => {
35+
UIStore.instance.removeListener(UI_EVENTS.Resize, () => {
36+
setWidth(UIStore.instance.windowWidth);
37+
});
38+
};
39+
}, []);
40+
41+
return width;
42+
};

test/hooks/useWindowWidth-test.ts

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
Copyright 2024 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { renderHook } from "@testing-library/react-hooks";
18+
import { act } from "@testing-library/react";
19+
20+
import UIStore, { UI_EVENTS } from "../../src/stores/UIStore";
21+
import { useWindowWidth } from "../../src/hooks/useWindowWidth";
22+
23+
describe("useWindowWidth", () => {
24+
beforeEach(() => {
25+
UIStore.instance.windowWidth = 768;
26+
});
27+
28+
it("should return the current width of window, according to UIStore", () => {
29+
const { result } = renderHook(() => useWindowWidth());
30+
31+
expect(result.current).toBe(768);
32+
});
33+
34+
it("should update the value when UIStore's value changes", () => {
35+
const { result } = renderHook(() => useWindowWidth());
36+
37+
act(() => {
38+
UIStore.instance.windowWidth = 1024;
39+
UIStore.instance.emit(UI_EVENTS.Resize);
40+
});
41+
42+
expect(result.current).toBe(1024);
43+
});
44+
});

0 commit comments

Comments
 (0)