Skip to content

Commit 435f19b

Browse files
committed
Address PR review: hydration fix, type safety, accessibility
- Fix hydration mismatch: use useEffect + isLoaded flag to defer version-dependent rendering until client has loaded localStorage - Fix type: replace any[] with A2UIComponent[] for v0.9 defaults - Fix accessibility: make role/tabIndex conditional on onClick - Add license header to pnpm-lock.yaml for CI check
1 parent c6ad70b commit 435f19b

File tree

6 files changed

+32
-12
lines changed

6 files changed

+32
-12
lines changed

tools/composer/pnpm-lock.yaml

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/composer/src/app/components/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ function ComponentContent({ component, specVersion }: { component: ComponentDoc;
194194
}
195195

196196
export default function ComponentsPage() {
197-
const { specVersion } = useSpecVersion();
197+
const { specVersion, isLoaded } = useSpecVersion();
198198
const componentsData = specVersion === '0.9' ? COMPONENTS_DATA_V09 : COMPONENTS_DATA;
199199
const [selectedComponent, setSelectedComponent] = useState('Row');
200200

@@ -210,7 +210,7 @@ export default function ComponentsPage() {
210210
onSelect={setSelectedComponent}
211211
data={componentsData}
212212
/>
213-
{component && <ComponentContent component={component} specVersion={specVersion} />}
213+
{isLoaded && component && <ComponentContent component={component} specVersion={specVersion} />}
214214
</div>
215215
);
216216
}

tools/composer/src/app/gallery/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default function GalleryPage() {
2929
const [selectedWidget, setSelectedWidget] = useState<Widget | null>(null);
3030
const { addWidget } = useWidgets();
3131
const router = useRouter();
32-
const { specVersion } = useSpecVersion();
32+
const { specVersion, isLoaded } = useSpecVersion();
3333

3434
const galleryWidgets = specVersion === '0.9' ? V09_GALLERY_WIDGETS : V08_GALLERY_WIDGETS;
3535

@@ -57,7 +57,7 @@ export default function GalleryPage() {
5757
<div className="flex-1 overflow-auto p-6">
5858
<h1 className="mb-6 text-2xl font-semibold">Gallery</h1>
5959
<div className="columns-1 gap-4 sm:columns-2 lg:columns-3 xl:columns-4 2xl:columns-5" style={{ columnWidth: '308px' }}>
60-
{galleryWidgets.map((item) => (
60+
{!isLoaded ? null : galleryWidgets.map((item) => (
6161
<div key={item.widget.id} className="mb-4 break-inside-avoid">
6262
<GalleryWidget
6363
widget={item.widget}

tools/composer/src/components/gallery/gallery-widget.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ export function GalleryWidget({ widget, height = 200, onClick }: GalleryWidgetPr
3131

3232
return (
3333
<div
34-
role="button"
35-
tabIndex={0}
34+
role={onClick ? 'button' : undefined}
35+
tabIndex={onClick ? 0 : undefined}
3636
onClick={onClick}
37-
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onClick?.(); }}
38-
className="w-full text-left rounded-xl border border-white bg-white/80 p-4 shadow-sm transition-all hover:shadow-md hover:border-muted-foreground/30 cursor-pointer overflow-hidden"
37+
onKeyDown={(e) => { if (onClick && (e.key === 'Enter' || e.key === ' ')) onClick(); }}
38+
className={`w-full text-left rounded-xl border border-white bg-white/80 p-4 shadow-sm transition-all hover:shadow-md hover:border-muted-foreground/30 overflow-hidden ${onClick ? 'cursor-pointer' : ''}`}
3939
style={{ minHeight: height }}
4040
>
4141
<div className="flex flex-col gap-2 h-full">

tools/composer/src/components/main/create-widget.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ const DEFAULT_COMPONENTS_V08: A2UIComponent[] = [
5151
},
5252
];
5353

54-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
55-
const DEFAULT_COMPONENTS_V09: any[] = [
54+
const DEFAULT_COMPONENTS_V09: A2UIComponent[] = [
5655
{
5756
id: "root",
5857
component: "Card",

tools/composer/src/contexts/spec-version-context.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
* Controls which version (v0.8 or v0.9) the entire composer uses for
2121
* rendering, gallery samples, component docs, and new widget creation.
2222
* Persisted to localStorage.
23+
*
24+
* Uses useEffect to load from localStorage after hydration to avoid
25+
* server/client mismatch. The `isLoaded` flag lets consumers defer
26+
* version-dependent rendering until the client value is available.
2327
*/
2428
'use client';
2529

@@ -32,19 +36,22 @@ const DEFAULT_VERSION: SpecVersion = '0.9';
3236
interface SpecVersionContextValue {
3337
specVersion: SpecVersion;
3438
setSpecVersion: (version: SpecVersion) => void;
39+
isLoaded: boolean;
3540
}
3641

3742
const SpecVersionContext = createContext<SpecVersionContextValue | null>(null);
3843

3944
export function SpecVersionProvider({ children }: { children: ReactNode }) {
4045
const [specVersion, setSpecVersionState] = useState<SpecVersion>(DEFAULT_VERSION);
46+
const [isLoaded, setIsLoaded] = useState(false);
4147

42-
// Load from localStorage on mount
48+
// Load from localStorage after hydration
4349
useEffect(() => {
4450
const stored = localStorage.getItem(STORAGE_KEY);
4551
if (stored === '0.8' || stored === '0.9') {
4652
setSpecVersionState(stored);
4753
}
54+
setIsLoaded(true);
4855
}, []);
4956

5057
const setSpecVersion = useCallback((version: SpecVersion) => {
@@ -53,7 +60,7 @@ export function SpecVersionProvider({ children }: { children: ReactNode }) {
5360
}, []);
5461

5562
return (
56-
<SpecVersionContext.Provider value={{ specVersion, setSpecVersion }}>
63+
<SpecVersionContext.Provider value={{ specVersion, setSpecVersion, isLoaded }}>
5764
{children}
5865
</SpecVersionContext.Provider>
5966
);

0 commit comments

Comments
 (0)