Browse Source

refactor: replace hardcoded layout margins with global CSS utility classes and standardize page background variants

master
mortezaei 2 weeks ago
parent
commit
806e15f0d5
  1. 2
      src/app/[lang]/preview-upload/page.tsx
  2. 23
      src/app/globals.css
  3. 2
      src/app/preview-upload/page.tsx
  4. 15
      src/app/questions-list/[slug]/question-detail-client.tsx
  5. 67
      src/app/questions-list/[slug]/question-dimensions.test.tsx
  6. 2
      src/app/questions-list/[slug]/test-intro-client.tsx
  7. 12
      src/app/questions-list/questions-list-client.tsx
  8. 20
      src/components/Componentes/page-background.tsx
  9. 4
      src/components/Componentes/page-loading-skeleton.tsx
  10. 2
      src/components/Componentes/test-loading-screen.tsx
  11. 2
      src/components/Componentes/test-questions-flow.tsx

2
src/app/[lang]/preview-upload/page.tsx

@ -17,6 +17,8 @@ const mockQuestion: QuestionField = {
description: "",
tooltip: "Supporting documents for identity verification",
extras: {
placeHolder: "",
range: [0, 0],
options: [".pdf", ".jpg", ".jpeg", ".png"],
},
options: [],

23
src/app/globals.css

@ -368,6 +368,14 @@ body[data-page-background="none"] .app-shell,
.app-shell:has(.page-background-none),
body:has(.page-background-none),
html:has(.page-background-none) {
background-image: none !important;
background-color: #F5F5F5 !important;
}
body[data-page-background="cream"] .app-shell,
.app-shell:has(.page-background-cream),
body:has(.page-background-cream),
html:has(.page-background-cream) {
background-image: none !important;
background-color: #F7F1F0 !important;
}
@ -472,8 +480,8 @@ body.section-overlay-open .app-shell {
--app-shell-px: 17px;
padding-inline: var(--app-shell-px);
box-sizing: border-box;
background-color: var(--background);
background-image: var(--default-page-background-image);
background-color: #F7F1F0;
background-image: none;
background-position: top;
background-repeat: no-repeat;
background-size: cover;
@ -532,6 +540,16 @@ body.section-overlay-open .app-shell {
margin-inline: calc(-1 * var(--app-shell-px, 17px));
}
.questions-list-main {
margin-inline: calc(-1 * var(--app-shell-px, 17px));
padding-inline: var(--app-shell-px, 17px);
}
.questions-list-header {
margin-inline: calc(-1 * var(--app-shell-px, 17px));
padding-inline: var(--app-shell-px, 17px);
}
.question-progress,
.question-snap-content {
transition:
@ -553,6 +571,7 @@ body.section-overlay-open .app-shell {
}
.page-background-none,
.page-background-cream,
.page-background-custom,
.page-background-default {
display: none;

2
src/app/preview-upload/page.tsx

@ -17,6 +17,8 @@ const mockQuestion: QuestionField = {
description: "",
tooltip: "Supporting documents for identity verification",
extras: {
placeHolder: "",
range: [0, 0],
options: [".pdf", ".jpg", ".jpeg", ".png"],
},
options: [],

15
src/app/questions-list/[slug]/question-detail-client.tsx

@ -268,6 +268,9 @@ export default function QuestionDetailClient({
const [hasTestProgress, setHasTestProgress] = useState(false);
const queryClient = useQueryClient();
const profileId = useCurrentProfileId();
const isOverlay = Boolean(useSectionOverlay());
const renderPageBackground = () =>
!isOverlay ? <PageBackground disabled variant="cream" /> : null;
const handleExit = useCallback(() => {
void queryClient.invalidateQueries({
@ -518,7 +521,7 @@ export default function QuestionDetailClient({
return (
<>
<PageBackground disabled />
{renderPageBackground()}
<main className="question-detail-main flex h-svh flex-col overflow-hidden bg-[#F7F1F0]">
<StickyHeader
sticky={false}
@ -563,7 +566,7 @@ export default function QuestionDetailClient({
if (!isAssessment) {
return (
<>
<PageBackground disabled />
{renderPageBackground()}
<main className="question-detail-main flex h-svh flex-col overflow-hidden bg-[#F7F1F0]">
<StickyHeader
sticky={false}
@ -599,7 +602,7 @@ export default function QuestionDetailClient({
return (
<>
<PageBackground disabled />
{renderPageBackground()}
<main className="question-detail-main flex h-svh flex-col overflow-hidden bg-[#F7F1F0]">
<StickyHeader
sticky={false}
@ -666,7 +669,7 @@ export default function QuestionDetailClient({
return (
<>
<PageBackground disabled />
{renderPageBackground()}
<main className="question-detail-main flex h-svh flex-col items-center justify-center gap-4 bg-[#F7F1F0] px-6 text-center">
<p className="font-semibold text-[#1B1B1B]">
{isError
@ -778,7 +781,7 @@ export default function QuestionDetailClient({
return (
<>
<PageBackground disabled />
{renderPageBackground()}
<main className="question-detail-main flex h-svh flex-col overflow-hidden bg-[#F7F1F0]">
<StickyHeader
sticky={false}
@ -953,7 +956,7 @@ export default function QuestionDetailClient({
return (
<>
<PageBackground disabled />
{renderPageBackground()}
<QuestionAnswersProvider
slug={item.slug}

67
src/app/questions-list/[slug]/question-dimensions.test.tsx

@ -5,6 +5,7 @@ import path from "path";
import { FixToTheEnd } from "@/components/Componentes/fix-to-the-end";
import { PageHeader } from "@/components/Componentes/page-header";
import StickyHeader from "@/components/Componentes/sticky-header";
import { PageBackground } from "@/components/Componentes/page-background";
import { I18nProvider } from "@/translations/provider";
afterEach(() => {
@ -120,4 +121,70 @@ describe("Dimensions & Padding Integrity Verification", () => {
expect(mainMatches).not.toBeNull();
expect(mainMatches!.length).toBeGreaterThanOrEqual(4);
});
it("globals.css defines #F5F5F5 for page-background-none and #F7F1F0 for page-background-cream", () => {
const cssPath = path.resolve(process.cwd(), "src/app/globals.css");
const cssContent = fs.readFileSync(cssPath, "utf-8");
// page-background-none sets #F5F5F5
expect(cssContent).toMatch(
/body:has\(\.page-background-none\),\s*html:has\(\.page-background-none\)\s*\{[\s\S]*?background-color:\s*#F5F5F5\s*!important;/,
);
// page-background-cream sets #F7F1F0
expect(cssContent).toMatch(
/body:has\(\.page-background-cream\),\s*html:has\(\.page-background-cream\)\s*\{[\s\S]*?background-color:\s*#F7F1F0\s*!important;/,
);
// section-overlay uses #F7F1F0 without background-image
expect(cssContent).toMatch(
/\.section-overlay\s*\{[\s\S]*?background-color:\s*#F7F1F0;[\s\S]*?background-image:\s*none;/,
);
// questions-list-main and questions-list-header exist
expect(cssContent).toMatch(
/\.questions-list-main\s*\{[\s\S]*?margin-inline:\s*calc\(-1\s*\*\s*var\(--app-shell-px,\s*17px\)\);/,
);
expect(cssContent).toMatch(
/\.questions-list-header\s*\{[\s\S]*?margin-inline:\s*calc\(-1\s*\*\s*var\(--app-shell-px,\s*17px\)\);/,
);
});
it("questions-list-client uses questions-list-main and questions-list-header without hardcoded -mx-[17px]", () => {
const listClientPath = path.resolve(
process.cwd(),
"src/app/questions-list/questions-list-client.tsx",
);
const content = fs.readFileSync(listClientPath, "utf-8");
expect(content).not.toMatch(/<main[^>]*className="[^"]*-mx-\[17px\]/);
expect(content).not.toMatch(/<header[^>]*className="[^"]*-mx-\[17px\]/);
const mainMatches = content.match(
/<main[^>]*className="[^"]*questions-list-main/g,
);
expect(mainMatches).not.toBeNull();
expect(mainMatches!.length).toBe(3);
const headerMatches = content.match(
/<header[^>]*className="[^"]*questions-list-header/g,
);
expect(headerMatches).not.toBeNull();
expect(headerMatches!.length).toBe(3);
});
it("PageBackground handles disabled, variant cream and default correctly", () => {
const { container: c1 } = render(<PageBackground disabled />);
expect(c1.querySelector(".page-background-none")).toBeInTheDocument();
expect(c1.querySelector(".page-background-cream")).not.toBeInTheDocument();
const { container: c2 } = render(
<PageBackground disabled variant="cream" />,
);
expect(c2.querySelector(".page-background-none")).toBeInTheDocument();
expect(c2.querySelector(".page-background-cream")).toBeInTheDocument();
const { container: c3 } = render(<PageBackground />);
expect(c3.querySelector(".page-background-default")).toBeInTheDocument();
});
});

2
src/app/questions-list/[slug]/test-intro-client.tsx

@ -31,7 +31,7 @@ export default function TestIntroClient({
return (
<>
<PageBackground disabled />
<PageBackground disabled variant="cream" />
<main className="question-detail-main flex h-svh flex-col overflow-hidden bg-[#F7F1F0]">
<StickyHeader sticky={false} className="shrink-0">
<div className="flex items-center gap-4">

12
src/app/questions-list/questions-list-client.tsx

@ -721,7 +721,7 @@ export default function QuestionsListClient({
<PageBackground disabled />
<main
style={{ paddingBottom: "calc(100px + var(--safe-bottom))" }}
className="-mx-[17px] relative flex h-dvh min-h-0 flex-col overflow-y-auto overflow-x-hidden bg-[#F5F5F5] px-[17px] pt-0 overscroll-y-contain"
className="questions-list-main relative flex h-dvh min-h-0 flex-col overflow-y-auto overflow-x-hidden bg-[#F5F5F5] pt-0 overscroll-y-contain"
>
<div className="pointer-events-none absolute inset-x-0 top-0 h-[240px] bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.98)_0%,rgba(255,255,255,0.7)_42%,rgba(255,255,255,0)_100%)]" />
<div className="pointer-events-none absolute -top-16 left-1/2 h-[220px] w-[220px] -translate-x-1/2 rounded-full bg-white/70 blur-3xl" />
@ -730,7 +730,7 @@ export default function QuestionsListClient({
style={{
paddingTop: "max(12px, calc(var(--safe-top) + 4px))",
}}
className="sticky top-0 z-20 -mx-[17px] flex items-center justify-between bg-[#F5F5F5]/90 px-[17px] pb-3 backdrop-blur-md"
className="questions-list-header sticky top-0 z-20 flex items-center justify-between bg-[#F5F5F5]/90 pb-3 backdrop-blur-md"
>
<NavigationButton
icon="back"
@ -795,7 +795,7 @@ export default function QuestionsListClient({
<PageBackground disabled />
<main
style={{ paddingBottom: "calc(100px + var(--safe-bottom))" }}
className="-mx-[17px] relative min-h-screen overflow-x-clip bg-[#F5F5F5] px-[17px] pt-0"
className="questions-list-main relative min-h-screen overflow-x-clip bg-[#F5F5F5] pt-0"
>
<div className="pointer-events-none absolute inset-x-0 top-0 h-[240px] bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.98)_0%,rgba(255,255,255,0.7)_42%,rgba(255,255,255,0)_100%)]" />
<div className="pointer-events-none absolute -top-16 left-1/2 h-[220px] w-[220px] -translate-x-1/2 rounded-full bg-white/70 blur-3xl" />
@ -804,7 +804,7 @@ export default function QuestionsListClient({
style={{
paddingTop: "max(12px, calc(var(--safe-top) + 4px))",
}}
className="sticky top-0 z-20 -mx-[17px] flex items-center justify-between bg-[#F5F5F5]/90 px-[17px] pb-3 backdrop-blur-md"
className="questions-list-header sticky top-0 z-20 flex items-center justify-between bg-[#F5F5F5]/90 pb-3 backdrop-blur-md"
>
<NavigationButton
icon="back"
@ -914,7 +914,7 @@ export default function QuestionsListClient({
<main
style={{ paddingBottom: "calc(100px + var(--safe-bottom))" }}
className="-mx-[17px] relative flex h-dvh min-h-0 flex-col overflow-y-auto overflow-x-hidden bg-[#F5F5F5] px-[17px] pt-0 overscroll-y-contain"
className="questions-list-main relative flex h-dvh min-h-0 flex-col overflow-y-auto overflow-x-hidden bg-[#F5F5F5] pt-0 overscroll-y-contain"
>
<div className="pointer-events-none absolute inset-x-0 top-0 h-[240px] bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.98)_0%,rgba(255,255,255,0.7)_42%,rgba(255,255,255,0)_100%)]" />
<div className="pointer-events-none absolute -top-16 left-1/2 h-[220px] w-[220px] -translate-x-1/2 rounded-full bg-white/70 blur-3xl" />
@ -923,7 +923,7 @@ export default function QuestionsListClient({
style={{
paddingTop: "max(12px, calc(var(--safe-top) + 4px))",
}}
className="sticky top-0 z-20 -mx-[17px] flex items-center justify-between bg-[#F5F5F5]/90 px-[17px] pb-3 backdrop-blur-md"
className="questions-list-header sticky top-0 z-20 flex items-center justify-between bg-[#F5F5F5]/90 pb-3 backdrop-blur-md"
>
<NavigationButton
icon="back"

20
src/components/Componentes/page-background.tsx

@ -6,11 +6,13 @@ import { useEffect } from "react";
type PageBackgroundProps = {
image?: string;
disabled?: boolean;
variant?: "cream" | "gray" | "default";
};
export function PageBackground({
image,
disabled = false,
variant,
}: PageBackgroundProps) {
useEffect(() => {
if (!image) return;
@ -23,16 +25,20 @@ export function PageBackground({
};
}, [image]);
let bgClass = "page-background-default";
if (disabled) {
bgClass =
variant === "cream"
? "page-background-none page-background-cream"
: "page-background-none";
} else if (image) {
bgClass = "page-background-custom";
}
return (
<span
aria-hidden="true"
className={
disabled
? "page-background-none"
: image
? "page-background-custom"
: "page-background-default"
}
className={bgClass}
style={
image
? ({ "--page-background-image": `url("${image}")` } as CSSProperties)

4
src/components/Componentes/page-loading-skeleton.tsx

@ -14,7 +14,7 @@ export function PageLoadingSkeleton({
if (variant === "questions" || variant === "test") {
return (
<>
<PageBackground disabled />
<PageBackground disabled variant="cream" />
<main
aria-busy="true"
className="question-detail-main flex min-h-svh flex-col px-[17px] pt-[max(12px,calc(var(--safe-top)+4px))] pb-[calc(24px+var(--safe-bottom))] bg-[#F7F1F0]"
@ -65,7 +65,7 @@ export function PageLoadingSkeleton({
return (
<>
<PageBackground disabled />
<PageBackground disabled variant="cream" />
<main
aria-busy="true"
className="question-detail-main flex min-h-svh flex-col px-[23px] pt-[max(12px,calc(var(--safe-top)+4px))] pb-[calc(24px+var(--safe-bottom))]"

2
src/components/Componentes/test-loading-screen.tsx

@ -12,7 +12,7 @@ type TestLoadingScreenProps = {
export default function TestLoadingScreen(_props: TestLoadingScreenProps) {
return (
<>
<PageBackground disabled />
<PageBackground disabled variant="cream" />
<main
aria-busy="true"
className="question-detail-main flex h-svh flex-col items-center justify-center bg-[#F7F1F0] px-6 text-center select-none"

2
src/components/Componentes/test-questions-flow.tsx

@ -237,7 +237,7 @@ export default function TestQuestionsFlow({
return (
<>
<PageBackground disabled />
<PageBackground disabled variant="cream" />
<main className="question-detail-main flex h-svh flex-col overflow-hidden bg-[#F7F1F0]">
{/* Header */}
<StickyHeader sticky={false} className="shrink-0">

Loading…
Cancel
Save