Skip to content

Commit fdd8b1f

Browse files
Merge branch 'devin/1753802867-radiogroup-recipe' of https://git-manager.devin.ai/proxy/github.com/launchdarkly/launchpad-ui into devin/1753802867-radiogroup-recipe
2 parents 52462ef + 374e709 commit fdd8b1f

File tree

5 files changed

+119
-121
lines changed

5 files changed

+119
-121
lines changed

packages/components/src/Radio.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface RadioProps extends AriaRadioProps {
2222
const RadioContext = createContext<ContextValue<RadioProps, HTMLLabelElement>>(null);
2323

2424
const RadioIcon = ({ isSelected }: Partial<RadioRenderProps>) => (
25-
<div className={radioIconStyles()}>
25+
<div data-radio-icon className={radioIconStyles()}>
2626
{isSelected ? (
2727
<svg aria-hidden="true" className={styles.icon} viewBox="0 0 16 16">
2828
<path

packages/components/src/styles/RadioGroup.module.css

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@
1313
}
1414

1515
.card {
16-
/* Card variant styles for RadioGroup */
16+
align-items: stretch;
17+
gap: var(--lp-spacing-300);
1718
}
1819

1920
/* Card variant styling - targets child Radio components when parent has data-variant="card" */
2021
.group[data-variant='card'] label[data-rac] {
2122
border: var(--lp-border-width-200) solid var(--lp-color-border-ui-primary);
22-
border-radius: var(--lp-border-radius-regular);
23-
padding: var(--lp-spacing-400);
23+
border-radius: var(--lp-border-radius-medium);
2424
background: var(--lp-color-bg-ui-primary);
2525
transition: all var(--lp-duration-100) ease-in-out;
2626
flex-direction: column;
2727
align-items: flex-start;
28-
gap: var(--lp-spacing-300);
28+
gap: 0;
2929

3030
&[data-hovered] {
3131
border-color: var(--lp-color-border-interactive-primary-hover);
@@ -44,8 +44,17 @@
4444
}
4545

4646
&[data-selected] {
47-
border-color: var(--lp-color-border-interactive-primary-base);
47+
border-color: var(--lp-color-text-interactive-base);
4848
background: var(--lp-color-bg-interactive-primary-subtle);
49+
50+
& [slot='description'] {
51+
display: block;
52+
}
53+
54+
& [slot='heading'] {
55+
border-bottom-left-radius: 0;
56+
border-bottom-right-radius: 0;
57+
}
4958
}
5059

5160
&[data-disabled] {
@@ -55,43 +64,34 @@
5564
cursor: not-allowed;
5665
}
5766

58-
& .circle {
59-
position: absolute;
60-
top: var(--lp-spacing-400);
61-
right: var(--lp-spacing-400);
62-
width: var(--lp-size-20);
63-
height: var(--lp-size-20);
64-
}
65-
66-
&:not(:has([slot='description'])) {
67-
border-bottom-left-radius: 0;
68-
border-bottom-right-radius: 0;
69-
}
70-
7167
&:has([slot='heading']) {
7268
display: grid;
7369
grid-template-areas:
7470
'heading radio'
7571
'description description';
7672
grid-template-columns: 1fr auto;
77-
gap: var(--lp-spacing-300);
7873
align-items: flex-start;
74+
position: relative;
7975

80-
& .circle {
76+
& [data-radio-icon] {
77+
transition: all var(--lp-duration-100) ease-in-out;
8178
grid-area: radio;
82-
position: static;
79+
position: absolute;
80+
right: var(--lp-spacing-500);
81+
align-self: center;
8382
}
8483
}
8584

8685
&:has([slot='heading']):not(:has([slot='description'])) {
87-
grid-template-areas: 'heading radio';
86+
grid-template-areas: 'heading';
8887
}
8988

9089
& [slot='heading'] {
9190
grid-area: heading;
9291
display: flex;
9392
align-items: center;
94-
gap: var(--lp-spacing-300);
93+
gap: var(--lp-spacing-500);
94+
padding: var(--lp-spacing-400) var(--lp-spacing-500);
9595
}
9696

9797
& [slot='heading'] [slot='icon'] {
@@ -103,7 +103,6 @@
103103

104104
& [slot='heading'] [slot='label'] {
105105
font: var(--lp-text-label-1-semibold);
106-
color: var(--lp-color-text-ui-primary);
107106
}
108107

109108
& [slot='heading'] [slot='subtitle'] {
@@ -118,6 +117,12 @@
118117
font: var(--lp-text-body-2-regular);
119118
color: var(--lp-color-text-ui-secondary);
120119
margin-top: var(--lp-spacing-200);
120+
background: var(--lp-color-bg-interactive-selected);
121+
padding: var(--lp-spacing-500);
122+
display: none;
123+
border-bottom-left-radius: var(--lp-border-radius-medium);
124+
border-bottom-right-radius: var(--lp-border-radius-medium);
125+
transition: all var(--lp-duration-100) ease-in-out;
121126
}
122127

123128
&[data-selected] [slot='heading'] [slot='subtitle'] {

packages/components/stories/RadioGroup.stories.tsx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { Meta, StoryObj } from '@storybook/react-vite';
22
import type { ComponentType } from 'react';
33

4+
import { Icon } from '@launchpad-ui/icons';
5+
import { vars } from '@launchpad-ui/vars';
46
import { userEvent, within } from 'storybook/test';
57

68
import { Button } from '../src/Button';
@@ -80,3 +82,90 @@ export const Validation: Story = {
8082
await userEvent.click(canvas.getByRole('button'));
8183
},
8284
};
85+
86+
export const Card: Story = {
87+
args: {
88+
variant: 'card',
89+
defaultValue: 'feature',
90+
},
91+
render: (args) => {
92+
return (
93+
<div
94+
style={{
95+
width: vars.size['320'],
96+
}}
97+
>
98+
<RadioGroup {...args}>
99+
<Label>Experiment type</Label>
100+
<div
101+
style={{
102+
display: 'flex',
103+
flexDirection: 'column',
104+
gap: vars.spacing[300],
105+
}}
106+
>
107+
<Radio value="feature">
108+
<div slot="heading">
109+
<div slot="icon">
110+
<Icon name="flag" size="medium" />
111+
</div>
112+
<div>
113+
<div slot="label">Feature change</div>
114+
<div slot="subtitle">A/B test different variations</div>
115+
</div>
116+
</div>
117+
<div slot="description">Compare treatments to see which one wins</div>
118+
</Radio>
119+
<Radio value="funnel">
120+
<div slot="heading">
121+
<div slot="icon">
122+
<Icon name="flask" size="medium" />
123+
</div>
124+
<div>
125+
<div slot="label">Funnel optimization</div>
126+
<div slot="subtitle">Multi-step conversion tracking</div>
127+
</div>
128+
</div>
129+
<div slot="description">Track the success of a multi-step user flow</div>
130+
</Radio>
131+
<Radio value="export">
132+
<div slot="heading">
133+
<div slot="icon">
134+
<Icon name="data" size="medium" />
135+
</div>
136+
<div>
137+
<div slot="label">Data Export only</div>
138+
<div slot="subtitle">Raw data for analysis</div>
139+
</div>
140+
</div>
141+
<div slot="description">Create custom experiment analysis in your warehouse</div>
142+
</Radio>
143+
<Radio value="snowflake" isDisabled>
144+
<div slot="heading">
145+
<div slot="icon">
146+
<Icon name="circle" size="medium" />
147+
</div>
148+
<div>
149+
<div slot="label">Snowflake native</div>
150+
<div slot="subtitle">Warehouse-powered insights</div>
151+
</div>
152+
</div>
153+
<div slot="description">Analysis powered by your Snowflake warehouse</div>
154+
</Radio>
155+
<Radio value="simple">
156+
<div slot="heading">
157+
<div slot="icon">
158+
<Icon name="gear" size="medium" />
159+
</div>
160+
<div>
161+
<div slot="label">Simple option</div>
162+
<div slot="subtitle">Basic configuration</div>
163+
</div>
164+
</div>
165+
</Radio>
166+
</div>
167+
</RadioGroup>
168+
</div>
169+
);
170+
},
171+
};

packages/components/stories/recipes/RadioCardGroup.mdx

Lines changed: 0 additions & 8 deletions
This file was deleted.

packages/components/stories/recipes/composition.stories.tsx

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { ListBox, ListBoxItem } from '../../src/ListBox';
2222
import { Modal, ModalOverlay } from '../../src/Modal';
2323
import { Perceivable } from '../../src/Perceivable';
2424
import { Popover } from '../../src/Popover';
25-
import { Radio } from '../../src/Radio';
2625
import { RadioButton } from '../../src/RadioButton';
2726
import { RadioGroup } from '../../src/RadioGroup';
2827
import { RadioIconButton } from '../../src/RadioIconButton';
@@ -153,93 +152,6 @@ export const RadioButtonGroup: Story = {
153152
name: 'RadioButtonGroup',
154153
};
155154

156-
export const RadioCardGroup: Story = {
157-
args: {
158-
children: (
159-
<div
160-
style={{
161-
display: 'flex',
162-
flexDirection: 'column',
163-
gap: vars.spacing[400],
164-
maxWidth: vars.size[480],
165-
}}
166-
>
167-
<RadioGroup defaultValue="feature" variant="card">
168-
<Label>Experiment type</Label>
169-
<div
170-
style={{
171-
display: 'flex',
172-
flexDirection: 'column',
173-
gap: vars.spacing[300],
174-
}}
175-
>
176-
<Radio value="feature">
177-
<div slot="heading">
178-
<div slot="icon">
179-
<Icon name="flag" size="medium" />
180-
</div>
181-
<div>
182-
<div slot="label">Feature change</div>
183-
<div slot="subtitle">A/B test different variations</div>
184-
</div>
185-
</div>
186-
<div slot="description">Compare treatments to see which one wins</div>
187-
</Radio>
188-
<Radio value="funnel">
189-
<div slot="heading">
190-
<div slot="icon">
191-
<Icon name="flask" size="medium" />
192-
</div>
193-
<div>
194-
<div slot="label">Funnel optimization</div>
195-
<div slot="subtitle">Multi-step conversion tracking</div>
196-
</div>
197-
</div>
198-
<div slot="description">Track the success of a multi-step user flow</div>
199-
</Radio>
200-
<Radio value="export">
201-
<div slot="heading">
202-
<div slot="icon">
203-
<Icon name="data" size="medium" />
204-
</div>
205-
<div>
206-
<div slot="label">Data Export only</div>
207-
<div slot="subtitle">Raw data for analysis</div>
208-
</div>
209-
</div>
210-
<div slot="description">Create custom experiment analysis in your warehouse</div>
211-
</Radio>
212-
<Radio value="snowflake" isDisabled>
213-
<div slot="heading">
214-
<div slot="icon">
215-
<Icon name="circle" size="medium" />
216-
</div>
217-
<div>
218-
<div slot="label">Snowflake native</div>
219-
<div slot="subtitle">Warehouse-powered insights</div>
220-
</div>
221-
</div>
222-
<div slot="description">Analysis powered by your Snowflake warehouse</div>
223-
</Radio>
224-
<Radio value="simple">
225-
<div slot="heading">
226-
<div slot="icon">
227-
<Icon name="gear" size="medium" />
228-
</div>
229-
<div>
230-
<div slot="label">Simple option</div>
231-
<div slot="subtitle">Basic configuration</div>
232-
</div>
233-
</div>
234-
</Radio>
235-
</div>
236-
</RadioGroup>
237-
</div>
238-
),
239-
},
240-
name: 'RadioCardGroup',
241-
};
242-
243155
export const ListBoxTooltip: Story = {
244156
render: () => {
245157
const [isOpen, setOpen] = useState(false);

0 commit comments

Comments
 (0)