Skip to content

Commit ca00081

Browse files
author
Fergus Bisset
committed
feat(ProductCard): add server-safe version for Next.js Server Components
- Created ProductCard.server.tsx without event handlers (onClick, onKeyDown) - Server version uses href navigation instead of event callbacks - Updated SSR barrel to export server-safe version by default - Enhanced SSR validation to detect event handler props - Added comprehensive documentation (SERVER_VS_CLIENT.md) - All SSR tests passing (58 files, 216 tests) BREAKING CHANGE: ProductCard from /ssr barrel no longer supports onClick or button onClick callbacks. Use href for navigation or import from main barrel with 'use client' directive for interactive behavior. Fixes Next.js App Router error: 'Event handlers cannot be passed to Client Component props'
1 parent f78a960 commit ca00081

File tree

6 files changed

+1411
-173
lines changed

6 files changed

+1411
-173
lines changed

dist/meta/components.json

Lines changed: 140 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,81 @@
11
{
2-
"version": "0.0.41",
3-
"generatedAt": "2025-10-22T08:35:33.145Z",
2+
"version": "0.0.42",
3+
"generatedAt": "2025-10-22T08:36:30.094Z",
44
"count": 18,
55
"components": {
6+
"Tag": {
7+
"name": "Tag",
8+
"category": "meta",
9+
"since": "0.1.1",
10+
"a11yNotes": [
11+
"Close button (when closable) is a native button with aria-label \"Remove\"."
12+
],
13+
"props": [
14+
{
15+
"name": "text",
16+
"type": "string",
17+
"description": "Plain text content"
18+
},
19+
{
20+
"name": "html",
21+
"type": "string",
22+
"description": "HTML string content (overrides text)"
23+
},
24+
{
25+
"name": "children",
26+
"type": "ReactNode|string",
27+
"description": "Children content (highest precedence)"
28+
},
29+
{
30+
"name": "color",
31+
"type": "'default'|'white'|'grey'|'green'|'aqua-green'|'blue'|'purple'|'pink'|'red'|'orange'|'yellow'",
32+
"defaultValue": "default",
33+
"description": "Visual color variant",
34+
"enum": [
35+
"default",
36+
"white",
37+
"grey",
38+
"green",
39+
"aqua-green",
40+
"blue",
41+
"purple",
42+
"pink",
43+
"red",
44+
"orange",
45+
"yellow"
46+
]
47+
},
48+
{
49+
"name": "noBorder",
50+
"type": "boolean",
51+
"defaultValue": "false",
52+
"description": "Removes border style"
53+
},
54+
{
55+
"name": "closable",
56+
"type": "boolean",
57+
"defaultValue": "false",
58+
"description": "Displays close (×) control"
59+
},
60+
{
61+
"name": "onClose",
62+
"type": "() => void",
63+
"description": "Callback when close button clicked"
64+
},
65+
{
66+
"name": "disabled",
67+
"type": "boolean",
68+
"defaultValue": "false",
69+
"description": "Disables close interaction"
70+
},
71+
{
72+
"name": "className",
73+
"type": "string",
74+
"description": "Additional CSS classes"
75+
}
76+
],
77+
"source": "src/components/Tag/Tag.schema.ts"
78+
},
679
"Textarea": {
780
"name": "Textarea",
881
"category": "form",
@@ -119,78 +192,67 @@
119192
],
120193
"source": "src/components/Textarea/Textarea.schema.ts"
121194
},
122-
"Tag": {
123-
"name": "Tag",
124-
"category": "meta",
195+
"Radios": {
196+
"name": "Radios",
197+
"category": "form",
125198
"since": "0.1.1",
126199
"a11yNotes": [
127-
"Close button (when closable) is a native button with aria-label \"Remove\"."
200+
"Keyboard arrow navigation cycles through options.",
201+
"Conditional content container is hidden with --hidden class when inactive."
128202
],
129203
"props": [
130204
{
131-
"name": "text",
205+
"name": "name",
132206
"type": "string",
133-
"description": "Plain text content"
207+
"required": true,
208+
"description": "Group name"
134209
},
135210
{
136-
"name": "html",
211+
"name": "value",
137212
"type": "string",
138-
"description": "HTML string content (overrides text)"
139-
},
140-
{
141-
"name": "children",
142-
"type": "ReactNode|string",
143-
"description": "Children content (highest precedence)"
213+
"description": "Controlled selected value"
144214
},
145215
{
146-
"name": "color",
147-
"type": "'default'|'white'|'grey'|'green'|'aqua-green'|'blue'|'purple'|'pink'|'red'|'orange'|'yellow'",
148-
"defaultValue": "default",
149-
"description": "Visual color variant",
150-
"enum": [
151-
"default",
152-
"white",
153-
"grey",
154-
"green",
155-
"aqua-green",
156-
"blue",
157-
"purple",
158-
"pink",
159-
"red",
160-
"orange",
161-
"yellow"
162-
]
216+
"name": "defaultValue",
217+
"type": "string",
218+
"description": "Uncontrolled initial value"
163219
},
164220
{
165-
"name": "noBorder",
221+
"name": "hasError",
166222
"type": "boolean",
167223
"defaultValue": "false",
168-
"description": "Removes border style"
224+
"description": "Error style"
169225
},
170226
{
171-
"name": "closable",
172-
"type": "boolean",
173-
"defaultValue": "false",
174-
"description": "Displays close (×) control"
227+
"name": "describedBy",
228+
"type": "string",
229+
"description": "aria-describedby id list"
175230
},
176231
{
177-
"name": "onClose",
178-
"type": "() => void",
179-
"description": "Callback when close button clicked"
232+
"name": "className",
233+
"type": "string",
234+
"description": "Extra CSS classes"
180235
},
181236
{
182-
"name": "disabled",
237+
"name": "size",
238+
"type": "'normal'|'small'",
239+
"defaultValue": "normal",
240+
"description": "Size variant"
241+
},
242+
{
243+
"name": "inline",
183244
"type": "boolean",
184245
"defaultValue": "false",
185-
"description": "Disables close interaction"
246+
"description": "Inline layout"
186247
},
187248
{
188-
"name": "className",
189-
"type": "string",
190-
"description": "Additional CSS classes"
249+
"name": "options",
250+
"type": "Array<{ value: string; text: string; hint?: string; disabled?: boolean; conditional?: any }>",
251+
"required": true,
252+
"description": "Radio options"
191253
}
192254
],
193-
"source": "src/components/Tag/Tag.schema.ts"
255+
"source": "src/components/Radios/Radios.schema.ts"
194256
},
195257
"Select": {
196258
"name": "Select",
@@ -281,68 +343,6 @@
281343
],
282344
"source": "src/components/Select/Select.schema.ts"
283345
},
284-
"Radios": {
285-
"name": "Radios",
286-
"category": "form",
287-
"since": "0.1.1",
288-
"a11yNotes": [
289-
"Keyboard arrow navigation cycles through options.",
290-
"Conditional content container is hidden with --hidden class when inactive."
291-
],
292-
"props": [
293-
{
294-
"name": "name",
295-
"type": "string",
296-
"required": true,
297-
"description": "Group name"
298-
},
299-
{
300-
"name": "value",
301-
"type": "string",
302-
"description": "Controlled selected value"
303-
},
304-
{
305-
"name": "defaultValue",
306-
"type": "string",
307-
"description": "Uncontrolled initial value"
308-
},
309-
{
310-
"name": "hasError",
311-
"type": "boolean",
312-
"defaultValue": "false",
313-
"description": "Error style"
314-
},
315-
{
316-
"name": "describedBy",
317-
"type": "string",
318-
"description": "aria-describedby id list"
319-
},
320-
{
321-
"name": "className",
322-
"type": "string",
323-
"description": "Extra CSS classes"
324-
},
325-
{
326-
"name": "size",
327-
"type": "'normal'|'small'",
328-
"defaultValue": "normal",
329-
"description": "Size variant"
330-
},
331-
{
332-
"name": "inline",
333-
"type": "boolean",
334-
"defaultValue": "false",
335-
"description": "Inline layout"
336-
},
337-
{
338-
"name": "options",
339-
"type": "Array<{ value: string; text: string; hint?: string; disabled?: boolean; conditional?: any }>",
340-
"required": true,
341-
"description": "Radio options"
342-
}
343-
],
344-
"source": "src/components/Radios/Radios.schema.ts"
345-
},
346346
"Panel": {
347347
"name": "Panel",
348348
"category": "content",
@@ -449,33 +449,6 @@
449449
],
450450
"source": "src/components/InsetText/InsetText.schema.ts"
451451
},
452-
"Hint": {
453-
"name": "Hint",
454-
"category": "typography",
455-
"since": "0.1.1",
456-
"a11yNotes": [
457-
"Provides supporting contextual help text associated with a form field or section."
458-
],
459-
"props": [
460-
{
461-
"name": "id",
462-
"type": "string",
463-
"description": "HTML id attribute"
464-
},
465-
{
466-
"name": "className",
467-
"type": "string",
468-
"description": "Additional CSS classes"
469-
},
470-
{
471-
"name": "children",
472-
"type": "ReactNode|string",
473-
"required": true,
474-
"description": "Hint content"
475-
}
476-
],
477-
"source": "src/components/Hint/Hint.schema.ts"
478-
},
479452
"Input": {
480453
"name": "Input",
481454
"category": "form",
@@ -618,6 +591,33 @@
618591
],
619592
"source": "src/components/Input/Input.schema.ts"
620593
},
594+
"Hint": {
595+
"name": "Hint",
596+
"category": "typography",
597+
"since": "0.1.1",
598+
"a11yNotes": [
599+
"Provides supporting contextual help text associated with a form field or section."
600+
],
601+
"props": [
602+
{
603+
"name": "id",
604+
"type": "string",
605+
"description": "HTML id attribute"
606+
},
607+
{
608+
"name": "className",
609+
"type": "string",
610+
"description": "Additional CSS classes"
611+
},
612+
{
613+
"name": "children",
614+
"type": "ReactNode|string",
615+
"required": true,
616+
"description": "Hint content"
617+
}
618+
],
619+
"source": "src/components/Hint/Hint.schema.ts"
620+
},
621621
"Heading": {
622622
"name": "Heading",
623623
"category": "typography",
@@ -1114,10 +1114,13 @@
11141114
}
11151115
},
11161116
"categories": {
1117+
"meta": [
1118+
"Tag"
1119+
],
11171120
"form": [
11181121
"Textarea",
1119-
"Select",
11201122
"Radios",
1123+
"Select",
11211124
"Label",
11221125
"Input",
11231126
"Fieldset",
@@ -1126,9 +1129,6 @@
11261129
"DateInput",
11271130
"Button"
11281131
],
1129-
"meta": [
1130-
"Tag"
1131-
],
11321132
"content": [
11331133
"Panel",
11341134
"InsetText",

0 commit comments

Comments
 (0)