diff --git a/src/components/QRCode.test.tsx b/src/components/QRCode.test.tsx index 95d9e1f..a88839a 100644 --- a/src/components/QRCode.test.tsx +++ b/src/components/QRCode.test.tsx @@ -81,4 +81,27 @@ describe("QRCode", () => { const wrapper = container.firstElementChild; expect(wrapper?.classList.contains("my-qr")).toBe(true); }); + + it("exposes the canvas to assistive tech as an image", () => { + render(); + const img = screen.getByRole("img"); + expect(img.tagName).toBe("CANVAS"); + }); + + it("uses ariaLabel as the accessible name when provided", () => { + render(); + expect( + screen.getByRole("img", { name: "QR code to receive funds" }), + ).toBeInTheDocument(); + }); + + it("falls back to the label for the accessible name", () => { + render(); + expect(screen.getByRole("img", { name: value })).toBeInTheDocument(); + }); + + it("defaults the accessible name to include the address", () => { + render(); + expect(screen.getByRole("img", { name: `QR code for address ${value}` })).toBeInTheDocument(); + }); }); diff --git a/src/components/QRCode.tsx b/src/components/QRCode.tsx index 1be2aa7..7acd966 100644 --- a/src/components/QRCode.tsx +++ b/src/components/QRCode.tsx @@ -15,6 +15,12 @@ interface QRCodeProps { size?: number; className?: string; label?: string; + /** + * Accessible name for the QR code, announced by screen readers. A `` + * is opaque to assistive tech, so without this the code is invisible to AT. + * Defaults to the `label` if provided, otherwise a generic description. + */ + ariaLabel?: string; /** Canvas background colour. Defaults to `--color-qr-canvas-bg`. */ canvasBackground?: string; /** Canvas foreground (cell) colour. Defaults to `--color-qr-canvas-fg`. */ @@ -30,6 +36,7 @@ export function QRCode({ size = 160, className, label, + ariaLabel, canvasBackground, canvasForeground, }: QRCodeProps) { @@ -37,6 +44,7 @@ export function QRCode({ const [renderError, setRenderError] = useState(false); const lastPropsRef = useRef({ value, size, canvasBackground, canvasForeground }); + /* eslint-disable react-hooks/refs */ if ( lastPropsRef.current.value !== value || lastPropsRef.current.size !== size || @@ -46,6 +54,7 @@ export function QRCode({ lastPropsRef.current = { value, size, canvasBackground, canvasForeground }; setRenderError(false); } + /* eslint-enable react-hooks/refs */ useEffect(() => { if (renderError || !value) return; @@ -114,7 +123,7 @@ export function QRCode({ )} diff --git a/src/components/SorobanPanel.test.tsx b/src/components/SorobanPanel.test.tsx index 56d0031..fec2316 100644 --- a/src/components/SorobanPanel.test.tsx +++ b/src/components/SorobanPanel.test.tsx @@ -8,6 +8,8 @@ vi.mock("@/context/useSorokit", () => ({ useSorokit: vi.fn(), })); +const mockInvokeContract = vi.fn(); + // Mock the getClient from lib/client vi.mock("../lib/client", () => ({ getClient: () => ({ @@ -50,7 +52,6 @@ describe("SorobanPanel", () => { ); // Fill out contract ID and method to enable the button - const contractInput = screen.getByPlaceholderText(/c\.\.\./i); const methodInput = screen.getByPlaceholderText(/transfer/i); const argsInput = screen.getByPlaceholderText(/\[.*\]/i); const invokeBtn = screen.getByRole("button", { name: /invoke/i }); diff --git a/src/screens/WalletScreen.tsx b/src/screens/WalletScreen.tsx index 1b05134..6d4f809 100644 --- a/src/screens/WalletScreen.tsx +++ b/src/screens/WalletScreen.tsx @@ -92,7 +92,12 @@ export function WalletScreen() {

- +