forked from baron/baron-sso
사이드바 접기 기능 추가
This commit is contained in:
@@ -116,6 +116,24 @@ describe("devfront AppLayout", () => {
|
||||
expect(document.documentElement.classList.contains("light")).toBe(true);
|
||||
});
|
||||
|
||||
it("toggles the sidebar and persists the collapsed state", async () => {
|
||||
const container = await renderLayout();
|
||||
|
||||
const collapseButton = container.querySelector(
|
||||
'button[aria-label="Collapse sidebar"]',
|
||||
) as HTMLButtonElement;
|
||||
await act(async () => {
|
||||
collapseButton.click();
|
||||
});
|
||||
|
||||
expect(window.localStorage.getItem("baron_shell_sidebar_collapsed")).toBe(
|
||||
"true",
|
||||
);
|
||||
expect(
|
||||
container.querySelector('button[aria-label="Expand sidebar"]'),
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it("toggles profile menu, navigates to profile, toggles theme, and logs out", async () => {
|
||||
const container = await renderLayout();
|
||||
|
||||
|
||||
@@ -19,11 +19,13 @@ import {
|
||||
buildShellProfileSummary,
|
||||
buildShellSessionStatus,
|
||||
readShellSessionExpiryEnabled,
|
||||
readShellSidebarCollapsed,
|
||||
readShellTheme,
|
||||
type ShellSidebarNavItem,
|
||||
type ShellTranslator,
|
||||
shellLayoutClasses,
|
||||
writeShellSessionExpiryEnabled,
|
||||
writeShellSidebarCollapsed,
|
||||
} from "../../../../common/shell";
|
||||
import { fetchMe } from "../../features/auth/authApi";
|
||||
import { t } from "../../lib/i18n";
|
||||
@@ -118,6 +120,9 @@ function AppLayout() {
|
||||
const isDevelopmentRuntime = import.meta.env.MODE === "development";
|
||||
const [theme, setTheme] = useState<"light" | "dark">(readShellTheme);
|
||||
const [isProfileMenuOpen, setIsProfileMenuOpen] = useState(false);
|
||||
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(() =>
|
||||
readShellSidebarCollapsed(false),
|
||||
);
|
||||
const [, setDevelopmentRenderRevision] = useState(0);
|
||||
const [isSessionExpiryEnabled, setIsSessionExpiryEnabled] = useState(() =>
|
||||
readShellSessionExpiryEnabled(!isDevelopmentRuntime),
|
||||
@@ -352,26 +357,42 @@ function AppLayout() {
|
||||
return next;
|
||||
});
|
||||
};
|
||||
const handleSidebarToggle = () => {
|
||||
setIsSidebarCollapsed((prev) => {
|
||||
const next = !prev;
|
||||
writeShellSidebarCollapsed(next);
|
||||
return next;
|
||||
});
|
||||
};
|
||||
const sidebarNavContent = (
|
||||
<div className={shellLayoutClasses.navList}>
|
||||
{navItems.map(({ labelKey, labelFallback, to, icon: Icon }) => (
|
||||
<NavLink
|
||||
key={to}
|
||||
to={to}
|
||||
end={to === "/"}
|
||||
className={({ isActive }) =>
|
||||
[
|
||||
shellLayoutClasses.navItemBase,
|
||||
isActive
|
||||
? shellLayoutClasses.navItemActive
|
||||
: shellLayoutClasses.navItemIdle,
|
||||
].join(" ")
|
||||
}
|
||||
>
|
||||
<Icon size={18} />
|
||||
<span>{t(labelKey, labelFallback)}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
{navItems.map(({ labelKey, labelFallback, to, icon: Icon }) => {
|
||||
const label = t(labelKey, labelFallback);
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
key={to}
|
||||
to={to}
|
||||
end={to === "/"}
|
||||
className={({ isActive }) =>
|
||||
[
|
||||
shellLayoutClasses.navItemBase,
|
||||
isSidebarCollapsed
|
||||
? shellLayoutClasses.navItemBaseCollapsed
|
||||
: "",
|
||||
isActive
|
||||
? shellLayoutClasses.navItemActive
|
||||
: shellLayoutClasses.navItemIdle,
|
||||
].join(" ")
|
||||
}
|
||||
title={label}
|
||||
aria-label={label}
|
||||
>
|
||||
<Icon size={18} />
|
||||
<span className={isSidebarCollapsed ? "sr-only" : ""}>{label}</span>
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
const sidebarFooterContent = (
|
||||
@@ -379,22 +400,39 @@ function AppLayout() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleLogout}
|
||||
className={shellLayoutClasses.logoutButton}
|
||||
className={
|
||||
isSidebarCollapsed
|
||||
? shellLayoutClasses.logoutButtonCollapsed
|
||||
: shellLayoutClasses.logoutButton
|
||||
}
|
||||
title={t("ui.shell.nav.logout", "Logout")}
|
||||
>
|
||||
<LogOut size={18} />
|
||||
<span>{t("ui.shell.nav.logout", "Logout")}</span>
|
||||
<span className={isSidebarCollapsed ? "sr-only" : ""}>
|
||||
{t("ui.shell.nav.logout", "Logout")}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={shellLayoutClasses.root}>
|
||||
<div
|
||||
className={
|
||||
isSidebarCollapsed
|
||||
? shellLayoutClasses.rootCollapsed
|
||||
: shellLayoutClasses.root
|
||||
}
|
||||
>
|
||||
<AppSidebar
|
||||
brandLabel={t("ui.dev.brand", "Baron Sign In")}
|
||||
brandTitle={t("ui.dev.console_title", "Developer Console")}
|
||||
brandIcon={<ShieldHalf size={20} />}
|
||||
navContent={sidebarNavContent}
|
||||
footerContent={sidebarFooterContent}
|
||||
collapsed={isSidebarCollapsed}
|
||||
onToggleCollapsed={handleSidebarToggle}
|
||||
collapseLabel={t("ui.shell.sidebar.collapse", "사이드바 접기")}
|
||||
expandLabel={t("ui.shell.sidebar.expand", "사이드바 펼치기")}
|
||||
/>
|
||||
|
||||
<div className={shellLayoutClasses.content}>
|
||||
|
||||
Reference in New Issue
Block a user