Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/pages/ReceiptShare.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,14 @@ export default function ReceiptShare() {
}, [reducedMotion]);

return (
<div className="mx-auto flex max-w-4xl flex-col gap-4 px-4 py-4 sm:gap-6 sm:px-6 sm:py-6 lg:max-w-5xl lg:px-8">
<div className="receiptshare-page mx-auto flex max-w-4xl flex-col gap-4 px-4 py-4 sm:gap-6 sm:px-6 sm:py-6 lg:max-w-5xl lg:px-8">
{/* ARIA live region for screen-reader status announcements (#650) */}
<LiveRegion
message={announcement}
data-testid="receiptshare-live-region"
/>

<div className="flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
<div className="receiptshare-chrome flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
<div className="space-y-1.5">
<h1 className="text-xl font-semibold tracking-tight text-foreground sm:text-2xl">
Receipt Share
Expand All @@ -130,7 +130,7 @@ export default function ReceiptShare() {
Track status of your claim receipts and settlements.
</p>
</div>
<div className="flex items-center gap-2">
<div className="receiptshare-chrome flex items-center gap-2">
{status === "error" && (
<Button variant="outline" size="sm" onClick={handleRetry} className="rounded-full">
Retry
Expand All @@ -153,7 +153,7 @@ export default function ReceiptShare() {
aria-labelledby="receipts-heading"
className="space-y-3 rounded-2xl border border-border/60 bg-card/70 p-4 shadow-sm sm:space-y-4 sm:p-5"
>
<div className="flex items-center gap-2">
<div className="receiptshare-chrome flex items-center gap-2">
<h2 id="receipts-heading" className="text-base font-semibold text-foreground sm:text-lg">
Receipt Statuses
</h2>
Expand Down Expand Up @@ -182,7 +182,7 @@ export default function ReceiptShare() {

{status === "error" && (
<div className="rounded-xl border border-destructive/30 bg-destructive/5 p-4">
<div className="flex items-center gap-2">
<div className="receiptshare-chrome flex items-center gap-2">
<AlertCircle className="h-5 w-5 text-destructive" />
<p className="text-sm font-medium text-destructive">Failed to load receipt statuses</p>
</div>
Expand Down
67 changes: 67 additions & 0 deletions src/pages/__tests__/ReceiptShare.print.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @file src/pages/__tests__/ReceiptShare.print.test.tsx
*
* Focused tests for ReceiptShare print styles (GrantFox FWC26 b#031).
*
* Strategy
* --------
* CSS @media print rules are not evaluated by jsdom, so we verify that:
* 1. src/styles/print.css exists and contains the required .receiptshare-page
* and .receiptshare-chrome selectors.
* 2. src/pages/ReceiptShare.tsx applies the expected print-oriented class
* names to the root container and chrome elements.
* 3. The print stylesheet is imported/loaded globally.
*/

import fs from "node:fs";
import path from "node:path";

const read = (relPath: string) =>
fs.readFileSync(path.join(process.cwd(), relPath), "utf8");

describe("src/styles/print.css — ReceiptShare print rules", () => {
const CSS_PATH = "src/styles/print.css";
let css: string;

beforeAll(() => {
css = read(CSS_PATH);
});

it("file exists under src/styles/", () => {
expect(fs.existsSync(path.join(process.cwd(), CSS_PATH))).toBe(true);
});

it("contains a @media print block for .receiptshare-page", () => {
expect(css).toContain(".receiptshare-page");
});

it("hides chrome via .receiptshare-chrome", () => {
expect(css).toContain(".receiptshare-chrome");
});

it("hides the live region via data-testid", () => {
expect(css).toContain('receiptshare-live-region');
});

it("resets background/color to light-mode values", () => {
expect(css).toContain("background: #fff !important");
expect(css).toContain("color: #000 !important");
});
});

describe("src/pages/ReceiptShare.tsx — print-oriented class names", () => {
const TSX_PATH = "src/pages/ReceiptShare.tsx";
let tsx: string;

beforeAll(() => {
tsx = read(TSX_PATH);
});

it("applies .receiptshare-page on the root container", () => {
expect(tsx).toContain("receiptshare-page");
});

it("applies .receiptshare-chrome on the header", () => {
expect(tsx).toContain("receiptshare-chrome");
});
});
40 changes: 40 additions & 0 deletions src/styles/print.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,43 @@
height: auto !important;
}
}

/* ReceiptShare print presentation. The header, chrome, and live-region are
* hidden; receipt cards retain their structure and break gracefully. */
@media print {
.receiptshare-page {
max-width: none !important;
padding: 0 !important;
background: #fff !important;
color: #000 !important;
}

.receiptshare-page [class*="text-muted-foreground"] {
color: #374151 !important;
}

.receiptshare-page [class*="bg-card"],
.receiptshare-page [class*="bg-background"],
.receiptshare-page [class*="bg-muted"] {
background: #fff !important;
}

.receiptshare-chrome,
.receiptshare-page [data-testid="receiptshare-live-region"] {
display: none !important;
}

.receiptshare-page ul {
margin: 0;
padding: 0;
}

.receiptshare-page li {
break-inside: avoid;
}

.receiptshare-page a {
color: #000 !important;
text-decoration: none !important;
}
}