11import React , { Fragment , useState } from "react" ;
22import type { Meta , StoryObj } from "@storybook/react" ;
3+ import type { RadioRenderProps } from "react-aria-components" ;
34
45import { IPreviewArgs } from "./utils" ;
56
67import CustomRadio , {
78 CustomRadioItem ,
89 RadioIndicator ,
10+ type CustomRadioOption ,
911} from "../lib/form/custom-radio" ;
1012import Card from "../lib/container/card" ;
1113import TextField from "../lib/form/text-field" ;
@@ -21,11 +23,40 @@ export default meta;
2123
2224type Story = StoryObj < typeof meta > & IPreviewArgs ;
2325
24- /** `items` API — the simplest case. Each option is a full card with the indicator on
25- * the right; the whole card is the click target (it is the radio's `<label>`). Because
26- * the card is the target, the focus ring and selected emphasis go on the card (driven by
27- * the render props), not on the small circle — so `RadioIndicator` gets `focusRing={false}`
28- * to avoid a double ring. This mirrors react-aria's own card-radio example. */
26+ /** A full-card option with the indicator on the right; the whole card is the click target
27+ * (it is the radio's `<label>`). Because the card is the target, the focus ring and selected
28+ * emphasis go on the card (driven by the render props), not on the small circle — so
29+ * `RadioIndicator` gets `focusRing={false}` to avoid a double ring. Defined at module scope
30+ * (not inside the story's `render`) so it keeps a stable identity across renders. */
31+ const CreationMethodCard = ( {
32+ title,
33+ ...rp
34+ } : RadioRenderProps & { title : string } ) => (
35+ < Card
36+ hover
37+ className = { cn (
38+ "flex h-fit w-[420px] items-center gap-4 p-4" ,
39+ rp . isSelected && "border-klerosUIComponentsPrimaryBlue" ,
40+ rp . isFocusVisible &&
41+ "ring-klerosUIComponentsPrimaryBlue ring-2 ring-offset-2" ,
42+ ) }
43+ >
44+ < span className = "text-klerosUIComponentsPrimaryText grow text-base" >
45+ { title }
46+ </ span >
47+ < RadioIndicator { ...rp } focusRing = { false } />
48+ </ Card >
49+ ) ;
50+
51+ const CREATION_METHOD_ITEMS : CustomRadioOption [ ] = [
52+ { value : "scratch" , title : "Create a case from scratch" } ,
53+ { value : "duplicate" , title : "Duplicate an existing case" } ,
54+ ] . map ( ( { value, title } ) => ( {
55+ value,
56+ content : ( rp ) => < CreationMethodCard title = { title } { ...rp } /> ,
57+ } ) ) ;
58+
59+ /** `items` API — the simplest case. This mirrors react-aria's own card-radio example. */
2960export const Cards : Story = {
3061 args : { themeUI : "dark" , backgroundUI : "light" } ,
3162 render : function Render ( ) {
@@ -35,28 +66,7 @@ export const Cards: Story = {
3566 aria-label = "Creation method"
3667 value = { value }
3768 onChange = { setValue }
38- items = { [
39- { value : "scratch" , title : "Create a case from scratch" } ,
40- { value : "duplicate" , title : "Duplicate an existing case" } ,
41- ] . map ( ( { value : v , title } ) => ( {
42- value : v ,
43- content : ( rp ) => (
44- < Card
45- hover
46- className = { cn (
47- "flex h-fit w-[420px] items-center gap-4 p-4" ,
48- rp . isSelected && "border-klerosUIComponentsPrimaryBlue" ,
49- rp . isFocusVisible &&
50- "ring-klerosUIComponentsPrimaryBlue ring-2 ring-offset-2" ,
51- ) }
52- >
53- < span className = "text-klerosUIComponentsPrimaryText grow text-base" >
54- { title }
55- </ span >
56- < RadioIndicator { ...rp } focusRing = { false } />
57- </ Card >
58- ) ,
59- } ) ) }
69+ items = { CREATION_METHOD_ITEMS }
6070 />
6171 ) ;
6272 } ,
0 commit comments