From 28b788c584470598bf342cab406b4b70a56ad936 Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:14:05 -0600 Subject: [PATCH] Quote Block accessibility and error handling --- app/components/QuoteBlock.js | 141 +++++++++++++++++++++++++++++------ 1 file changed, 120 insertions(+), 21 deletions(-) diff --git a/app/components/QuoteBlock.js b/app/components/QuoteBlock.js index ac373fe..8341a6e 100644 --- a/app/components/QuoteBlock.js +++ b/app/components/QuoteBlock.js @@ -1,6 +1,6 @@ "use client"; -import React from "react"; +import React, { useState } from "react"; import Image from "next/image"; import QuoteDecor from "./QuoteDecor"; @@ -11,7 +11,13 @@ const QuoteBlock = ({ author = "Jo Freeman", source = "The Tyranny of Structurelessness", avatarSrc = "assets/Quote_Avatar.svg", + id, + fallbackAvatarSrc = "assets/Quote_Avatar.svg", // Fallback avatar + onError, // Error callback }) => { + const [imageError, setImageError] = useState(false); + const [imageLoading, setImageLoading] = useState(true); + // Variant configurations const variants = { compact: { @@ -63,8 +69,60 @@ const QuoteBlock = ({ const config = variants[variant] || variants.standard; + // Use provided ID or generate a stable one based on content + const baseId = id || `quote-${author.toLowerCase().replace(/\s+/g, "-")}`; + const quoteId = `${baseId}-content`; + const authorId = `${baseId}-author`; + + // Error handling functions + const handleImageError = (error) => { + console.warn( + `QuoteBlock: Failed to load avatar image for ${author}:`, + error + ); + setImageError(true); + setImageLoading(false); + + // Call error callback if provided + if (onError) { + onError({ + type: "image_load_error", + message: `Failed to load avatar for ${author}`, + author, + avatarSrc, + error, + }); + } + }; + + const handleImageLoad = () => { + setImageLoading(false); + setImageError(false); + }; + + // Validate required props + if (!quote || !author) { + console.error("QuoteBlock: Missing required props (quote or author)"); + if (onError) { + onError({ + type: "missing_props", + message: "QuoteBlock requires quote and author props", + quote: !!quote, + author: !!author, + }); + } + return null; // Don't render if missing required props + } + + // Determine which avatar to use + const currentAvatarSrc = imageError ? fallbackAvatarSrc : avatarSrc; + return ( -
+
@@ -74,19 +132,57 @@ const QuoteBlock = ({ className="pointer-events-none absolute z-0 left-0 top-0 w-full h-full" + aria-hidden="true" /> )}
- {`${author} -
+ {/* Avatar with error handling */} +
+ {`Portrait + + {/* Loading state */} + {imageLoading && ( +
+ )} + + {/* Error state - show initials */} + {imageError && !imageLoading && ( +
+ + {author + .split(" ") + .map((n) => n[0]) + .join("") + .toUpperCase()} + +
+ )} +
+ +

@@ -94,21 +190,24 @@ const QuoteBlock = ({

-
-

+ {author} -

-

- {source} -

-
+ + {source && ( +

+ {source} +

+ )} +
-
+
); };