feat(back-button): add dark variant and update tests for BackButton component
This commit is contained in:
@@ -13,6 +13,9 @@ describe("shared Tailwind components", () => {
|
|||||||
const actionHtml = renderToStaticMarkup(
|
const actionHtml = renderToStaticMarkup(
|
||||||
<BackButton onClick={() => undefined} variant="ghost" />,
|
<BackButton onClick={() => undefined} variant="ghost" />,
|
||||||
);
|
);
|
||||||
|
const darkHtml = renderToStaticMarkup(
|
||||||
|
<BackButton href="/splash" variant="dark" ariaLabel="Back to home" />,
|
||||||
|
);
|
||||||
|
|
||||||
expect(linkHtml).toContain('href="/chat"');
|
expect(linkHtml).toContain('href="/chat"');
|
||||||
expect(linkHtml).toContain('aria-label="Back to chat"');
|
expect(linkHtml).toContain('aria-label="Back to chat"');
|
||||||
@@ -22,6 +25,10 @@ describe("shared Tailwind components", () => {
|
|||||||
expect(actionHtml).toContain("<button");
|
expect(actionHtml).toContain("<button");
|
||||||
expect(actionHtml).toContain("size-8");
|
expect(actionHtml).toContain("size-8");
|
||||||
expect(actionHtml).toContain("hover:opacity-70");
|
expect(actionHtml).toContain("hover:opacity-70");
|
||||||
|
expect(darkHtml).toContain('href="/splash"');
|
||||||
|
expect(darkHtml).toContain('aria-label="Back to home"');
|
||||||
|
expect(darkHtml).toContain("bg-[rgba(13,11,20,0.7)]");
|
||||||
|
expect(darkHtml).toContain("text-white");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders CharacterAvatar with dynamic sizing and image utilities", () => {
|
it("renders CharacterAvatar with dynamic sizing and image utilities", () => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ interface BackButtonBaseProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
iconSize?: number;
|
iconSize?: number;
|
||||||
variant?: "floating" | "soft" | "ghost";
|
variant?: "floating" | "soft" | "ghost" | "dark";
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackButtonLinkProps = BackButtonBaseProps & {
|
type BackButtonLinkProps = BackButtonBaseProps & {
|
||||||
@@ -32,6 +32,8 @@ const VARIANT_CLASS_NAMES = {
|
|||||||
"size-(--responsive-icon-button-size,42px) border border-[rgba(25,19,22,0.08)] bg-[rgba(255,255,255,0.82)] text-[#21191d] shadow-[0_10px_22px_rgba(50,30,40,0.08)] hover:-translate-y-px hover:bg-white hover:shadow-[0_14px_26px_rgba(50,30,40,0.12)] active:translate-y-px",
|
"size-(--responsive-icon-button-size,42px) border border-[rgba(25,19,22,0.08)] bg-[rgba(255,255,255,0.82)] text-[#21191d] shadow-[0_10px_22px_rgba(50,30,40,0.08)] hover:-translate-y-px hover:bg-white hover:shadow-[0_14px_26px_rgba(50,30,40,0.12)] active:translate-y-px",
|
||||||
ghost:
|
ghost:
|
||||||
"size-8 bg-transparent text-black hover:opacity-70 active:scale-96",
|
"size-8 bg-transparent text-black hover:opacity-70 active:scale-96",
|
||||||
|
dark:
|
||||||
|
"size-(--responsive-icon-button-size,42px) border border-[rgba(255,255,255,0.12)] bg-[rgba(13,11,20,0.7)] text-white shadow-[0_10px_24px_rgba(0,0,0,0.2)] backdrop-blur-md hover:bg-[rgba(28,24,39,0.82)] active:scale-96",
|
||||||
} satisfies Record<NonNullable<BackButtonBaseProps["variant"]>, string>;
|
} satisfies Record<NonNullable<BackButtonBaseProps["variant"]>, string>;
|
||||||
|
|
||||||
export function BackButton({
|
export function BackButton({
|
||||||
|
|||||||
@@ -160,10 +160,15 @@ describe("chat Tailwind components", () => {
|
|||||||
expect(guestHtml).toContain('aria-label="Sign up to unlock more features"');
|
expect(guestHtml).toContain('aria-label="Sign up to unlock more features"');
|
||||||
expect(guestHtml).toContain("bg-(--color-accent,#f84d96)");
|
expect(guestHtml).toContain("bg-(--color-accent,#f84d96)");
|
||||||
expect(guestHtml).toContain("size-(--icon-size-sm,16px)");
|
expect(guestHtml).toContain("size-(--icon-size-sm,16px)");
|
||||||
|
expect(guestHtml).not.toContain('href="/splash"');
|
||||||
|
expect(guestHtml).not.toContain('aria-label="Back to home"');
|
||||||
|
expect(guestHtml).not.toContain('aria-label="Menu"');
|
||||||
expect(memberHtml).toContain("Offer");
|
expect(memberHtml).toContain("Offer");
|
||||||
|
expect(memberHtml).toContain('href="/splash"');
|
||||||
|
expect(memberHtml).toContain('aria-label="Back to home"');
|
||||||
expect(memberHtml).toContain('aria-label="Menu"');
|
expect(memberHtml).toContain('aria-label="Menu"');
|
||||||
expect(memberHtml).toContain("bg-[rgba(13,11,20,0.7)]");
|
expect(memberHtml).toContain("bg-[rgba(13,11,20,0.7)]");
|
||||||
expect(memberHtml).toContain("size-(--icon-size-xl,32px)");
|
expect(memberHtml).toContain("size-(--responsive-icon-button-size,42px)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders ChatInputTextField with textarea utilities", () => {
|
it("renders ChatInputTextField with textarea utilities", () => {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { Lock, Menu } from "lucide-react";
|
import { Lock, Menu } from "lucide-react";
|
||||||
|
|
||||||
|
import { BackButton } from "@/app/_components";
|
||||||
import { ROUTES } from "@/router/routes";
|
import { ROUTES } from "@/router/routes";
|
||||||
import { useAppNavigator } from "@/router/use-app-navigator";
|
import { useAppNavigator } from "@/router/use-app-navigator";
|
||||||
|
|
||||||
@@ -19,9 +20,9 @@ export interface ChatHeaderProps {
|
|||||||
export function ChatHeader({ isGuest, offerBanner }: ChatHeaderProps) {
|
export function ChatHeader({ isGuest, offerBanner }: ChatHeaderProps) {
|
||||||
const navigator = useAppNavigator();
|
const navigator = useAppNavigator();
|
||||||
|
|
||||||
if (isGuest) {
|
|
||||||
return (
|
return (
|
||||||
<header className="flex shrink-0 flex-col items-stretch bg-transparent p-0">
|
<header className="flex shrink-0 flex-col items-stretch bg-transparent p-0">
|
||||||
|
{isGuest ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex w-full cursor-pointer items-center justify-center gap-(--spacing-sm,8px) border-0 bg-(--color-accent,#f84d96) px-(--spacing-md,12px) py-(--spacing-sm,8px) text-center text-(length:--responsive-caption,var(--font-size-sm,12px)) font-medium text-white"
|
className="flex w-full cursor-pointer items-center justify-center gap-(--spacing-sm,8px) border-0 bg-(--color-accent,#f84d96) px-(--spacing-md,12px) py-(--spacing-sm,8px) text-center text-(length:--responsive-caption,var(--font-size-sm,12px)) font-medium text-white"
|
||||||
@@ -35,29 +36,28 @@ export function ChatHeader({ isGuest, offerBanner }: ChatHeaderProps) {
|
|||||||
/>
|
/>
|
||||||
<span>Sign up to unlock more features</span>
|
<span>Sign up to unlock more features</span>
|
||||||
</button>
|
</button>
|
||||||
</header>
|
) : (
|
||||||
);
|
offerBanner
|
||||||
}
|
)}
|
||||||
|
|
||||||
return (
|
{!isGuest ? (
|
||||||
<header className="flex shrink-0 flex-col items-stretch bg-transparent p-0">
|
<div className="flex items-center justify-between px-(--spacing-md,12px) py-(--spacing-sm,8px)">
|
||||||
{offerBanner}
|
<BackButton
|
||||||
|
href={ROUTES.splash}
|
||||||
|
variant="dark"
|
||||||
|
ariaLabel="Back to home"
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="ml-(--spacing-md,12px) flex items-center justify-between px-(--spacing-md,12px) py-(--spacing-sm,8px)">
|
|
||||||
{/* 菜单按钮(点击 → push 到 /sidebar,与 Dart MenuButton 一致) */}
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex cursor-pointer items-center justify-center rounded-(--radius-full,999px) border-0 bg-[rgba(13,11,20,0.7)] p-(--spacing-sm,8px) text-(--color-text-primary,#fff)"
|
className="flex size-(--responsive-icon-button-size,42px) cursor-pointer items-center justify-center rounded-(--radius-full,999px) border border-[rgba(255,255,255,0.12)] bg-[rgba(13,11,20,0.7)] text-(--color-text-primary,#fff) shadow-[0_10px_24px_rgba(0,0,0,0.2)] backdrop-blur-md transition-[background,transform] duration-150 hover:bg-[rgba(28,24,39,0.82)] active:scale-96 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
|
||||||
onClick={() => navigator.push(ROUTES.sidebar)}
|
onClick={() => navigator.push(ROUTES.sidebar)}
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
>
|
>
|
||||||
<Menu
|
<Menu size={24} aria-hidden="true" />
|
||||||
className="size-(--icon-size-xl,32px)"
|
|
||||||
size={24}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
) : null}
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user