Skip to content

Commit 48bae19

Browse files
committed
Updation on copilot reviews
Signed-off-by: kartik <kartikrautan0@gmail.com>
1 parent c455148 commit 48bae19

File tree

9 files changed

+169
-73
lines changed

9 files changed

+169
-73
lines changed

src/components/Navbar.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,18 @@ const MenuItemGroup = ({
140140
const Button = ({
141141
children,
142142
onClick,
143-
className = ""
143+
className = "",
144+
"aria-label": ariaLabel
144145
}: {
145146
children: React.ReactNode;
146147
onClick?: () => void;
147148
className?: string;
149+
"aria-label"?: string;
148150
}) => (
149151
<button
150152
className={`flex items-center ${className}`}
151153
onClick={onClick}
154+
aria-label={ariaLabel}
152155
>
153156
{children}
154157
</button>
@@ -370,26 +373,26 @@ function Navbar() {
370373
{/* Language Switcher */}
371374
<div
372375
className={`h-16 flex items-center justify-center rounded-md cursor-pointer ${screens.md
373-
? "px-5 border-l border-white border-opacity-10 pl-4 pr-4"
374-
: "px-2.5 pl-1.5 pr-1.5"
376+
? "px-5 border-l border-white border-opacity-10 pl-4 pr-4"
377+
: "px-2.5 pl-1.5 pr-1.5"
375378
} ${hovered === "language" ? "bg-white bg-opacity-10" : "bg-transparent"
376379
}`}
377380
onMouseEnter={() => setHovered("language")}
378381
onMouseLeave={() => setHovered(null)}
379382
>
380383
<Dropdown overlay={languageMenu} trigger={["click"]}>
381-
<Button className="bg-transparent border-none text-white h-16 flex items-center cursor-pointer">
384+
<Button className="bg-transparent border-none text-white h-16 flex items-center cursor-pointer" aria-label={t('navbar.language')}>
382385
<MdLanguage className={`text-xl text-white ${screens.md ? "mr-1.5" : "mr-0"
383-
}`} />
386+
}`} aria-hidden="true" />
384387
<span className={screens.md ? "inline" : "hidden"}>{currentLang.flag} {currentLang.label}</span>
385388
</Button>
386389
</Dropdown>
387390
</div>
388391

389392
<div
390393
className={`h-16 flex items-center justify-center rounded-md cursor-pointer ${screens.md
391-
? "px-5 border-l border-white border-opacity-10 pl-4 pr-4"
392-
: "px-2.5 pl-1.5 pr-1.5"
394+
? "px-5 border-l border-white border-opacity-10 pl-4 pr-4"
395+
: "px-2.5 pl-1.5 pr-1.5"
393396
} ${hovered === "discord" ? "bg-white bg-opacity-10" : "bg-transparent"
394397
}`}
395398
onMouseEnter={() => setHovered("discord")}
@@ -409,8 +412,8 @@ function Navbar() {
409412

410413
<div
411414
className={`h-16 flex items-center justify-center rounded-md cursor-pointer ${screens.md
412-
? "px-5 border-l border-white border-opacity-10 pl-4 pr-4"
413-
: "px-2.5 pl-1.5 pr-1.5"
415+
? "px-5 border-l border-white border-opacity-10 pl-4 pr-4"
416+
: "px-2.5 pl-1.5 pr-1.5"
414417
} ${hovered === "github" ? "bg-white bg-opacity-10" : "bg-transparent"
415418
}`}
416419
onMouseEnter={() => setHovered("github")}

src/components/PlaygroundSidebar.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const PlaygroundSidebar = () => {
8080

8181
interface NavItem {
8282
title: string;
83+
tourKey: string;
8384
icon?: React.ComponentType<{ size: number }>;
8485
component?: React.ReactNode;
8586
onClick?: () => void;
@@ -89,24 +90,28 @@ const PlaygroundSidebar = () => {
8990
const navTop: NavItem[] = [
9091
{
9192
title: t('sidebar.editor'),
93+
tourKey: 'editor',
9294
icon: IoCodeSlash,
9395
onClick: handleEditorToggle,
9496
active: isEditorsVisible
9597
},
9698
{
9799
title: t('sidebar.preview'),
100+
tourKey: 'preview',
98101
icon: VscOutput,
99102
onClick: handlePreviewToggle,
100103
active: isPreviewVisible
101104
},
102105
{
103106
title: t('sidebar.problems'),
107+
tourKey: 'problems',
104108
icon: FiTerminal,
105109
onClick: () => setProblemPanelVisible(!isProblemPanelVisible),
106110
active: isProblemPanelVisible
107111
},
108112
{
109113
title: t('sidebar.aiAssistant'),
114+
tourKey: 'ai-assistant',
110115
component: (
111116
<div className="flex items-center justify-center">
112117
<div className="relative w-6 h-6">
@@ -127,29 +132,34 @@ const PlaygroundSidebar = () => {
127132
},
128133
{
129134
title: t('sidebar.fullscreen'),
135+
tourKey: 'fullscreen',
130136
component: <FullScreenModal />
131137
},
132138
];
133139

134140
interface NavBottomItem {
135141
title: string;
142+
tourKey: string;
136143
icon: React.ComponentType<{ size: number }>;
137144
onClick: () => void;
138145
}
139146

140147
const navBottom: NavBottomItem[] = [
141148
{
142149
title: t('sidebar.share'),
150+
tourKey: 'share',
143151
icon: FiShare2,
144152
onClick: () => void handleShare()
145153
},
146154
{
147155
title: t('sidebar.startTour'),
156+
tourKey: 'start-tour',
148157
icon: FaCirclePlay,
149158
onClick: () => void handleStartTour()
150159
},
151160
{
152161
title: t('sidebar.settings'),
162+
tourKey: 'settings',
153163
icon: FiSettings,
154164
onClick: handleSettings
155165
},
@@ -158,15 +168,15 @@ const PlaygroundSidebar = () => {
158168
return [
159169
<aside key="sidebar" className="playground-sidebar">
160170
<nav className="playground-sidebar-nav">
161-
{navTop.map(({ title, icon: Icon, component, onClick, active }) => (
162-
<Tooltip key={title} title={title} placement="right">
171+
{navTop.map(({ title, tourKey, icon: Icon, component, onClick, active }) => (
172+
<Tooltip key={tourKey} title={title} placement="right">
163173
<div
164174
role="button"
165175
aria-label={title}
166176
tabIndex={0}
167177
onClick={onClick}
168178
className={`group playground-sidebar-nav-item ${active ? 'playground-sidebar-nav-item-active' : 'playground-sidebar-nav-item-inactive'
169-
} tour-${title.toLowerCase().replace(' ', '-')}`}
179+
} tour-${tourKey}`}
170180
>
171181
{component ? (
172182
<div className="playground-sidebar-nav-item-icon-container">
@@ -182,14 +192,14 @@ const PlaygroundSidebar = () => {
182192
</nav>
183193

184194
<nav className="playground-sidebar-nav-bottom">
185-
{navBottom.map(({ title, icon: Icon, onClick }) => (
186-
<Tooltip key={title} title={title} placement="right">
195+
{navBottom.map(({ title, tourKey, icon: Icon, onClick }) => (
196+
<Tooltip key={tourKey} title={title} placement="right">
187197
<div
188198
role="button"
189199
aria-label={title}
190200
tabIndex={0}
191201
onClick={onClick}
192-
className={`group playground-sidebar-nav-bottom-item tour-${title.toLowerCase().replace(' ', '-')}`}
202+
className={`group playground-sidebar-nav-bottom-item tour-${tourKey}`}
193203
>
194204
<Icon size={18} />
195205
<span className="playground-sidebar-nav-item-title">{title}</span>

src/components/ProblemPanel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface ProblemItem {
1414
}
1515

1616
const ProblemPanel: React.FC = () => {
17-
const { t } = useTranslation();
17+
const { t, i18n } = useTranslation();
1818
const { error, backgroundColor, textColor } = useAppStore((state) => ({
1919
error: state.error,
2020
backgroundColor: state.backgroundColor,
@@ -74,7 +74,7 @@ const ProblemPanel: React.FC = () => {
7474

7575

7676
const formatTimestamp = (timestamp: Date) => {
77-
return timestamp.toLocaleTimeString('en-US', {
77+
return timestamp.toLocaleTimeString(i18n.language, {
7878
hour12: false,
7979
hour: '2-digit',
8080
minute: '2-digit',

src/i18n.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ void i18n
1515
fr: { translation: frTranslation },
1616
es: { translation: esTranslation },
1717
},
18+
supportedLngs: ['en', 'fr', 'es'],
19+
load: 'languageOnly',
1820
fallbackLng: 'en',
1921
interpolation: {
2022
escapeValue: false,

src/tests/components/Footer.test.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ import { render } from "@testing-library/react";
22
import { describe, it, expect, vi, beforeAll } from "vitest";
33
import Footer from "../../components/Footer";
44

5-
vi.mock('react-i18next', () => ({
6-
useTranslation: () => ({
7-
t: (key: string) => key,
8-
i18n: { language: 'en', changeLanguage: vi.fn() }
9-
})
10-
}));
5+
vi.mock('react-i18next', async () => {
6+
const { reactI18nextMock } = await import("../../utils/testing/i18nMock");
7+
return reactI18nextMock;
8+
});
119

1210
vi.mock("../../public/logo.png", () => ({
1311
default: "logo.png",
Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
1-
import { render, screen } from "@testing-library/react";
1+
import { render, screen, fireEvent } from "@testing-library/react";
22
import "@testing-library/jest-dom";
33
import Navbar from "../../components/Navbar";
44
import { MemoryRouter } from "react-router-dom";
5-
import { vi } from "vitest";
6-
7-
vi.mock("react-i18next", () => ({
8-
useTranslation: () => ({
9-
t: (key: string) => {
10-
const map: Record<string, string> = {
11-
"navbar.templatePlayground": "Template Playground",
12-
"navbar.github": "Github", // the original test asserted for Github with a capital G
13-
};
14-
return map[key] || key;
15-
},
16-
i18n: { language: "en", changeLanguage: vi.fn() },
17-
}),
18-
}));
5+
import { vi, describe, it, expect, afterEach } from "vitest";
6+
import { mockChangeLanguage } from "../../utils/testing/i18nMock";
7+
8+
vi.mock("react-i18next", async () => {
9+
const { reactI18nextMock } = await import("../../utils/testing/i18nMock");
10+
return {
11+
useTranslation: () => ({
12+
t: (key: string) => {
13+
const map: Record<string, string> = {
14+
"navbar.templatePlayground": "Template Playground",
15+
"navbar.github": "GitHub",
16+
"navbar.help": "Help",
17+
"navbar.learn": "Learn Now",
18+
"navbar.discord": "Discord",
19+
"navbar.about": "About",
20+
"navbar.community": "Community",
21+
"navbar.issues": "Issues",
22+
"navbar.documentation": "Documentation",
23+
"navbar.info": "Info",
24+
"navbar.language": "Language",
25+
};
26+
return map[key] || key;
27+
},
28+
i18n: reactI18nextMock.useTranslation().i18n,
29+
}),
30+
};
31+
});
1932

2033
const renderNavbar = () => {
2134
render(
@@ -25,6 +38,11 @@ const renderNavbar = () => {
2538
);
2639
};
2740

41+
afterEach(() => {
42+
mockChangeLanguage.mockClear();
43+
localStorage.clear();
44+
});
45+
2846
describe("Navbar", () => {
2947
it("renders logo and title on small screens", () => {
3048
renderNavbar();
@@ -36,10 +54,10 @@ describe("Navbar", () => {
3654
expect(title).toBeInTheDocument();
3755
});
3856

39-
it("renders Github link on all screens", () => {
57+
it("renders GitHub link on all screens", () => {
4058
renderNavbar();
4159

42-
const githubLink = screen.getByRole("link", { name: /Github/i });
60+
const githubLink = screen.getByRole("link", { name: /GitHub/i });
4361
expect(githubLink).toBeInTheDocument();
4462
});
4563

@@ -55,3 +73,32 @@ describe("Navbar", () => {
5573
});
5674
});
5775
});
76+
77+
describe("Language Switching", () => {
78+
it("selecting a language calls i18n.changeLanguage with the correct code", () => {
79+
renderNavbar();
80+
81+
// Click the language switcher button to open the dropdown
82+
const langButton = screen.getByText("English", { exact: false }).closest("button");
83+
expect(langButton).toBeInTheDocument();
84+
fireEvent.click(langButton!);
85+
86+
// Click the "Français" option in the dropdown
87+
const frenchOption = screen.getByText("Français");
88+
fireEvent.click(frenchOption);
89+
90+
// Assert changeLanguage was called with 'fr'
91+
expect(mockChangeLanguage).toHaveBeenCalledWith("fr");
92+
});
93+
94+
it("persisted language from localStorage is reflected in the UI on render", () => {
95+
// Pre-set localStorage to simulate a previously saved language
96+
localStorage.setItem("i18nextLng", "es");
97+
98+
renderNavbar();
99+
100+
// The displayed language label should reflect Spanish
101+
const spanishLabel = screen.getByText("Español", { exact: false });
102+
expect(spanishLabel).toBeInTheDocument();
103+
});
104+
});

src/tests/components/PlaygroundSidebar.test.tsx

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,28 @@ import "@testing-library/jest-dom";
33
import PlaygroundSidebar from "../../components/PlaygroundSidebar";
44
import { vi } from "vitest";
55

6-
vi.mock('react-i18next', () => ({
7-
useTranslation: () => ({
8-
t: (key: string) => {
9-
const map: Record<string, string> = {
10-
'sidebar.editor': 'Editor',
11-
'sidebar.preview': 'Preview',
12-
'sidebar.problems': 'Problems',
13-
'sidebar.aiAssistant': 'AI Assistant',
14-
'sidebar.share': 'Share',
15-
'sidebar.startTour': 'Start Tour',
16-
'sidebar.settings': 'Settings',
17-
'sidebar.fullscreen': 'Fullscreen'
18-
};
19-
return map[key] || key;
20-
},
21-
i18n: { language: 'en', changeLanguage: vi.fn() }
22-
})
23-
}));
6+
7+
vi.mock('react-i18next', async () => {
8+
const { reactI18nextMock } = await import("../../utils/testing/i18nMock");
9+
return {
10+
useTranslation: () => ({
11+
t: (key: string) => {
12+
const map: Record<string, string> = {
13+
'sidebar.editor': 'Editor',
14+
'sidebar.preview': 'Preview',
15+
'sidebar.problems': 'Problems',
16+
'sidebar.aiAssistant': 'AI Assistant',
17+
'sidebar.share': 'Share',
18+
'sidebar.startTour': 'Start Tour',
19+
'sidebar.settings': 'Settings',
20+
'sidebar.fullscreen': 'Fullscreen'
21+
};
22+
return map[key] || key;
23+
},
24+
i18n: reactI18nextMock.useTranslation().i18n,
25+
})
26+
};
27+
});
2428

2529
// Mock the store
2630
vi.mock("../../store/store", () => ({

0 commit comments

Comments
 (0)