Skip to content

Commit 29734bb

Browse files
committed
lint and removed XS
1 parent 1a7e962 commit 29734bb

File tree

4 files changed

+130
-74
lines changed

4 files changed

+130
-74
lines changed

packages/@react-spectrum/s2/src/SelectBox.tsx

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ const selectBoxStyles = style({
5151
width: {
5252
default: {
5353
size: {
54-
XS: 100,
5554
S: 128,
5655
M: 136,
5756
L: 160,
@@ -65,7 +64,6 @@ const selectBoxStyles = style({
6564
height: {
6665
default: {
6766
size: {
68-
XS: 100,
6967
S: 128,
7068
M: 136,
7169
L: 160,
@@ -88,7 +86,6 @@ const selectBoxStyles = style({
8886
},
8987
padding: {
9088
size: {
91-
XS: 12,
9289
S: 16,
9390
M: 20,
9491
L: 24,
@@ -207,11 +204,6 @@ const descriptionText = style({
207204
lineHeight: 'body'
208205
});
209206

210-
const checkboxContainer = style({
211-
position: 'absolute',
212-
top: 16,
213-
left: 16
214-
});
215207

216208
const SelectBoxRenderPropsContext = createContext<{
217209
isHovered?: boolean,
@@ -257,14 +249,17 @@ export const SelectBox = /*#__PURE__*/ forwardRef(function SelectBox(props: Sele
257249
style={UNSAFE_style}>
258250

259251
{showCheckbox && (
260-
<div className={checkboxContainer}>
261-
<div style={{pointerEvents: 'none'}}>
262-
<Checkbox
263-
isSelected={isSelected}
264-
isDisabled={isDisabled}
265-
size={size === 'XS' ? 'S' : size}
266-
isReadOnly />
267-
</div>
252+
<div
253+
className={style({
254+
position: 'absolute',
255+
top: 16,
256+
left: 16
257+
})}>
258+
<Checkbox
259+
isSelected={isSelected}
260+
isDisabled={isDisabled}
261+
size={size}
262+
isReadOnly />
268263
</div>
269264
)}
270265
{orientation === 'horizontal' ? (

packages/@react-spectrum/s2/src/SelectBoxGroup.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
Text
1717
} from 'react-aria-components';
1818
import {DOMRef, DOMRefValue, HelpTextProps, Orientation, Selection, SpectrumLabelableProps} from '@react-types/shared';
19+
import {FieldLabel} from './Field';
1920
import {getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
2021
import React, {createContext, forwardRef, ReactElement, ReactNode, useEffect, useId, useMemo} from 'react';
2122
import {SelectBoxRenderPropsContext} from './SelectBox';
@@ -52,7 +53,7 @@ export interface SelectBoxGroupProps extends StyleProps, SpectrumLabelableProps,
5253
* The size of the SelectBoxGroup.
5354
* @default 'M'
5455
*/
55-
size?: 'XS' | 'S' | 'M' | 'L' | 'XL',
56+
size?: 'S' | 'M' | 'L' | 'XL',
5657
/**
5758
* The axis the SelectBox elements should align with.
5859
* @default 'vertical'
@@ -87,12 +88,16 @@ export interface SelectBoxGroupProps extends StyleProps, SpectrumLabelableProps,
8788
/**
8889
* Whether the SelectBoxGroup is in an invalid state.
8990
*/
90-
isInvalid?: boolean
91+
isInvalid?: boolean,
92+
/**
93+
* Contextual help text for the SelectBoxGroup.
94+
*/
95+
contextualHelp?: ReactNode
9196
}
9297

9398
interface SelectBoxContextValue {
9499
allowMultiSelect?: boolean,
95-
size?: 'XS' | 'S' | 'M' | 'L' | 'XL',
100+
size?: 'S' | 'M' | 'L' | 'XL',
96101
orientation?: Orientation,
97102
isDisabled?: boolean,
98103
selectedKeys?: Selection,
@@ -214,6 +219,7 @@ export const SelectBoxGroup = /*#__PURE__*/ forwardRef(function SelectBoxGroup(p
214219

215220
let {
216221
label,
222+
contextualHelp,
217223
children,
218224
onSelectionChange,
219225
defaultValue,
@@ -318,10 +324,12 @@ export const SelectBoxGroup = /*#__PURE__*/ forwardRef(function SelectBoxGroup(p
318324
isInvalid={hasValidationErrors} />
319325

320326
{label && (
321-
<Text slot="label" id={`${gridId}-label`}>
327+
<FieldLabel
328+
id={`${gridId}-label`}
329+
isRequired={isRequired}
330+
contextualHelp={contextualHelp}>
322331
{label}
323-
{isRequired && <span aria-label="required"> *</span>}
324-
</Text>
332+
</FieldLabel>
325333
)}
326334

327335
<GridList

packages/@react-spectrum/s2/stories/SelectBoxGroup.stories.tsx

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717

1818
import {action} from '@storybook/addon-actions';
1919
import AlertNotice from '../spectrum-illustrations/linear/AlertNotice';
20-
import {createIcon, SelectBox, SelectBoxGroup, Text} from '../src';
20+
import {Button, createIcon, SelectBox, SelectBoxGroup, Text} from '../src';
2121
import type {Meta, StoryObj} from '@storybook/react';
2222
import Paperairplane from '../spectrum-illustrations/linear/Paperairplane';
23-
import React from 'react';
23+
import React, {useState} from 'react';
2424
import Server from '../spectrum-illustrations/linear/Server';
2525
import StarFilledSVG from '../s2wf-icons/S2_Icon_StarFilled_20_N.svg';
2626
import StarSVG from '../s2wf-icons/S2_Icon_Star_20_N.svg';
@@ -29,7 +29,7 @@ const StarIcon = createIcon(StarSVG);
2929
const StarFilledIcon = createIcon(StarFilledSVG);
3030

3131
const meta: Meta<typeof SelectBoxGroup> = {
32-
title: 'SelectBoxGroup (Collection)',
32+
title: 'SelectBoxGroup',
3333
component: SelectBoxGroup,
3434
parameters: {
3535
layout: 'centered'
@@ -42,7 +42,7 @@ const meta: Meta<typeof SelectBoxGroup> = {
4242
},
4343
size: {
4444
control: 'select',
45-
options: ['XS', 'S', 'M', 'L', 'XL']
45+
options: ['S', 'M', 'L', 'XL']
4646
},
4747
orientation: {
4848
control: 'select',
@@ -63,7 +63,7 @@ const meta: Meta<typeof SelectBoxGroup> = {
6363
numColumns: 2,
6464
gutterWidth: 'default',
6565
isRequired: false,
66-
isDisabled: false,
66+
isDisabled: false
6767
}
6868
};
6969

@@ -133,15 +133,13 @@ export const MultipleSelection: Story = {
133133
// Grid Navigation Testing
134134
export const GridNavigation: Story = {
135135
args: {
136-
label: 'Test Grid Navigation (Use Arrow Keys)',
136+
label: 'Test Grid Navigation',
137137
numColumns: 3
138138
},
139139
render: (args) => (
140140
<div style={{maxWidth: 600}}>
141141
<p style={{marginBottom: 16, fontSize: 14, color: '#666'}}>
142-
Focus any item (best done by clicking to the left of the group and hitting the tab key) and use arrow keys to navigate:
143-
{/* <br />• ↑↓ moves vertically (same column)
144-
<br />• ←→ moves horizontally (same row) */}
142+
Focus any item (best done by clicking to the side of the group and hitting the tab key) and using arrow keys to navigate:
145143
</p>
146144
<SelectBoxGroup {...args} onSelectionChange={action('onSelectionChange')}>
147145
<SelectBox value="1">
@@ -167,38 +165,6 @@ export const GridNavigation: Story = {
167165
)
168166
};
169167

170-
// Form Integration
171-
export const FormIntegration: Story = {
172-
args: {
173-
label: 'Select your option',
174-
name: 'user_preference',
175-
isRequired: true
176-
},
177-
render: (args) => (
178-
<form
179-
onSubmit={(e) => {
180-
e.preventDefault();
181-
const formData = new FormData(e.currentTarget);
182-
action('Form submitted')(Object.fromEntries(formData));
183-
}}>
184-
<SelectBoxGroup {...args} onSelectionChange={action('onSelectionChange')}>
185-
<SelectBox value="option1">
186-
<Text slot="text">Option 1</Text>
187-
</SelectBox>
188-
<SelectBox value="option2">
189-
<Text slot="text">Option 2</Text>
190-
</SelectBox>
191-
<SelectBox value="option3">
192-
<Text slot="text">Option 3</Text>
193-
</SelectBox>
194-
</SelectBoxGroup>
195-
<button type="submit" style={{marginTop: 16}}>
196-
Submit Form
197-
</button>
198-
</form>
199-
)
200-
};
201-
202168
export const FormValidation: Story = {
203169
args: {
204170
label: 'Required Selection',
@@ -222,7 +188,7 @@ export const FormValidation: Story = {
222188
export const SizeVariations: Story = {
223189
render: () => (
224190
<div style={{display: 'flex', flexDirection: 'column', gap: 32}}>
225-
{(['XS', 'S', 'M', 'L', 'XL'] as const).map((size) => (
191+
{(['S', 'M', 'L', 'XL'] as const).map((size) => (
226192
<SelectBoxGroup
227193
key={size}
228194
size={size}
@@ -311,9 +277,8 @@ export const IndividualDisabled: Story = {
311277
)
312278
};
313279

314-
// Controlled Mode - Convert to proper component to use React hooks
315280
function ControlledStory() {
316-
const [value, setValue] = React.useState('option2');
281+
const [value, setValue] = useState('option2');
317282

318283
return (
319284
<div>
@@ -346,9 +311,8 @@ export const Controlled: Story = {
346311
render: () => <ControlledStory />
347312
};
348313

349-
// Dynamic Icons - Convert to proper component to use React hooks
350314
function DynamicIconsStory() {
351-
const [selectedValues, setSelectedValues] = React.useState<Set<string>>(
315+
const [selectedValues, setSelectedValues] = useState<Set<string>>(
352316
new Set()
353317
);
354318

@@ -379,7 +343,6 @@ export const DynamicIcons: Story = {
379343
render: () => <DynamicIconsStory />
380344
};
381345

382-
// Multiple Columns
383346
export const MultipleColumns: Story = {
384347
args: {
385348
label: 'Choose options',
@@ -398,3 +361,55 @@ export const MultipleColumns: Story = {
398361
</div>
399362
)
400363
};
364+
365+
function FormSubmissionExample() {
366+
const [selectedValues, setSelectedValues] = useState<string[]>(['newsletter']);
367+
const [submittedData, setSubmittedData] = useState<string[] | null>(null);
368+
369+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
370+
e.preventDefault();
371+
const formData = new FormData(e.currentTarget);
372+
const preferences = formData.getAll('preferences') as string[];
373+
setSubmittedData(preferences);
374+
action('form-submitted')(preferences);
375+
};
376+
377+
return (
378+
<div style={{maxWidth: 400}}>
379+
<form onSubmit={handleSubmit}>
380+
<SelectBoxGroup
381+
selectionMode="multiple"
382+
value={selectedValues}
383+
onSelectionChange={(val) => setSelectedValues(val as string[])}
384+
name="preferences"
385+
label="Email Preferences"
386+
isRequired>
387+
<SelectBox value="newsletter">
388+
<Text slot="text">Newsletter</Text>
389+
</SelectBox>
390+
<SelectBox value="marketing">
391+
<Text slot="text">Marketing Updates</Text>
392+
</SelectBox>
393+
<SelectBox value="product">
394+
<Text slot="text">Product News</Text>
395+
</SelectBox>
396+
<SelectBox value="security">
397+
<Text slot="text">Security Alerts</Text>
398+
</SelectBox>
399+
</SelectBoxGroup>
400+
401+
<Button type="submit" variant="accent" >
402+
Save Preferences
403+
</Button>
404+
</form>
405+
406+
{submittedData && (
407+
<Text>Submitted: {submittedData.join(', ')}</Text>
408+
)}
409+
</div>
410+
);
411+
}
412+
413+
export const FormSubmission: Story = {
414+
render: () => <FormSubmissionExample />
415+
};

packages/@react-spectrum/s2/test/SelectBoxGroup.test.tsx

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import {act, render, screen, waitFor} from '@testing-library/react';
2+
import {Button, Text} from '../src';
13
import React from 'react';
2-
import {render, screen, waitFor, act} from '@testing-library/react';
34
import {SelectBox} from '../src/SelectBox';
45
import {SelectBoxGroup} from '../src/SelectBoxGroup';
5-
import {Text} from '../src';
66
import userEvent from '@testing-library/user-event';
77

88
function SingleSelectBox() {
@@ -250,11 +250,9 @@ describe('SelectBoxGroup', () => {
250250
</SelectBox>
251251
</SelectBoxGroup>
252252
);
253-
const grid = screen.getByRole('grid', {name: 'Required test required'});
253+
const grid = screen.getByRole('grid', {name: 'Required test'});
254254
expect(grid).toBeInTheDocument();
255-
256255
expect(screen.getByText('Required test')).toBeInTheDocument();
257-
expect(screen.getByText('*')).toBeInTheDocument();
258256
});
259257

260258
it('supports error message and validation', () => {
@@ -422,6 +420,46 @@ describe('SelectBoxGroup', () => {
422420
expect(hiddenInput).toBeInTheDocument();
423421
expect(hiddenInput).toHaveValue('option1');
424422
});
423+
424+
it('works with form submission using S2 Button', async () => {
425+
const onSubmit = jest.fn();
426+
render(
427+
<form
428+
onSubmit={(e) => {
429+
e.preventDefault();
430+
const formData = new FormData(e.currentTarget);
431+
const values = formData.getAll('preferences');
432+
onSubmit(values);
433+
}}>
434+
<SelectBoxGroup
435+
selectionMode="multiple"
436+
onSelectionChange={() => {}}
437+
value={['option1', 'option3']}
438+
name="preferences"
439+
label="User Preferences">
440+
<SelectBox value="option1">
441+
<Text slot="text">Newsletter</Text>
442+
</SelectBox>
443+
<SelectBox value="option2">
444+
<Text slot="text">Marketing</Text>
445+
</SelectBox>
446+
<SelectBox value="option3">
447+
<Text slot="text">Updates</Text>
448+
</SelectBox>
449+
</SelectBoxGroup>
450+
<Button type="submit" variant="accent">
451+
Submit Preferences
452+
</Button>
453+
</form>
454+
);
455+
456+
const submitButton = screen.getByRole('button', {name: 'Submit Preferences'});
457+
expect(submitButton).toBeInTheDocument();
458+
459+
await userEvent.click(submitButton);
460+
461+
expect(onSubmit).toHaveBeenCalledWith(['option1', 'option3']);
462+
});
425463
});
426464

427465
describe('Individual SelectBox behavior', () => {

0 commit comments

Comments
 (0)