Replace ErrorBoundary fallback with Fluent UI styles and dialog compliance

This commit is contained in:
2026-04-19 09:44:14 +02:00
parent 47f9c602d4
commit 91269448d0
2 changed files with 48 additions and 21 deletions

View File

@@ -315,6 +315,8 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue.
**Docs changes needed:** None. **Docs changes needed:** None.
**Status:** Completed.
**Why this is needed:** The architecture explicitly forbids building custom modal overlays ("Use Fluent UI `<Dialog>` for modals and confirmations — never build custom modal overlays"). Hand-built modals bypass Fluent UI's accessibility tree (focus trap, ARIA roles, portal rendering) and duplicate boilerplate that the library provides. A Fluent `<Dialog>` is keyboard-accessible, screen-reader-compatible, and inherits the theme automatically. **Why this is needed:** The architecture explicitly forbids building custom modal overlays ("Use Fluent UI `<Dialog>` for modals and confirmations — never build custom modal overlays"). Hand-built modals bypass Fluent UI's accessibility tree (focus trap, ARIA roles, portal rendering) and duplicate boilerplate that the library provides. A Fluent `<Dialog>` is keyboard-accessible, screen-reader-compatible, and inherits the theme automatically.
--- ---

View File

@@ -4,6 +4,7 @@
* Catches render-time exceptions in child components and shows a fallback UI. * Catches render-time exceptions in child components and shows a fallback UI.
*/ */
import React from "react"; import React from "react";
import { Button, makeStyles, Text, tokens } from "@fluentui/react-components";
interface ErrorBoundaryState { interface ErrorBoundaryState {
hasError: boolean; hasError: boolean;
@@ -14,11 +15,49 @@ interface ErrorBoundaryProps {
children: React.ReactNode; children: React.ReactNode;
} }
interface ErrorBoundaryFallbackProps {
message: string;
onReload: () => void;
}
const useFallbackStyles = makeStyles({
root: {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
minHeight: "100vh",
padding: tokens.spacingVerticalL,
textAlign: "center",
gap: tokens.spacingVerticalM,
},
message: {
maxWidth: "40rem",
},
});
function ErrorBoundaryFallback({ message, onReload }: ErrorBoundaryFallbackProps): React.ReactElement {
const styles = useFallbackStyles();
return (
<div className={styles.root} role="alert">
<Text as="h1" size={700} weight="semibold">
Something went wrong
</Text>
<Text size={300} className={styles.message}>
{message}
</Text>
<Button appearance="primary" onClick={onReload}>
Reload
</Button>
</div>
);
}
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> { export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) { constructor(props: ErrorBoundaryProps) {
super(props); super(props);
this.state = { hasError: false, errorMessage: null }; this.state = { hasError: false, errorMessage: null };
this.handleReload = this.handleReload.bind(this);
} }
static getDerivedStateFromError(error: Error): ErrorBoundaryState { static getDerivedStateFromError(error: Error): ErrorBoundaryState {
@@ -29,31 +68,17 @@ export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoun
console.error("ErrorBoundary caught an error", { error, errorInfo }); console.error("ErrorBoundary caught an error", { error, errorInfo });
} }
handleReload(): void { handleReload = (): void => {
window.location.reload(); window.location.reload();
} };
render(): React.ReactNode { render(): React.ReactNode {
if (this.state.hasError) { if (this.state.hasError) {
return ( return (
<div <ErrorBoundaryFallback
style={{ message={this.state.errorMessage ?? "Please try reloading the page."}
display: "flex", onReload={this.handleReload}
flexDirection: "column", />
alignItems: "center",
justifyContent: "center",
minHeight: "100vh",
padding: "24px",
textAlign: "center",
}}
role="alert"
>
<h1>Something went wrong</h1>
<p>{this.state.errorMessage ?? "Please try reloading the page."}</p>
<button type="button" onClick={this.handleReload} style={{ marginTop: "16px" }}>
Reload
</button>
</div>
); );
} }