Skip to content

Commit c2a5890

Browse files
jasonbasuilRachel Roppolo
authored andcommitted
feature/UIDS-416 add stories for additional SingleSelect use cases (#432)
* adds Multi Select with Checkbox Option example story * adds Custom Value Container story * exports Option and ValueContainer as replaceable components from the DS
1 parent d392afd commit c2a5890

File tree

8 files changed

+304
-5
lines changed

8 files changed

+304
-5
lines changed

spec/__snapshots__/Storyshots.test.js.snap

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5519,6 +5519,160 @@ exports[`Storyshots Design System/Selects/Creatable Default 1`] = `
55195519
</div>
55205520
`;
55215521

5522+
exports[`Storyshots Design System/Selects/Single Custom Option With Checkbox 1`] = `
5523+
<div
5524+
style={
5525+
Object {
5526+
"padding": "1rem",
5527+
}
5528+
}
5529+
>
5530+
<label
5531+
htmlFor="multi-select"
5532+
id="select-label-custom-option"
5533+
>
5534+
Custom option with checkbox
5535+
</label>
5536+
<div
5537+
className="SingleSelect css-2b097c-container"
5538+
id="multi-select"
5539+
onKeyDown={[Function]}
5540+
>
5541+
<div
5542+
className=" css-15ub2n0-control"
5543+
onMouseDown={[Function]}
5544+
onTouchEnd={[Function]}
5545+
>
5546+
<div
5547+
className=" css-g1d714-ValueContainer"
5548+
>
5549+
<div
5550+
className=" css-1269n2i-placeholder"
5551+
>
5552+
Select...
5553+
</div>
5554+
<input
5555+
aria-autocomplete="list"
5556+
aria-labelledby="select-label"
5557+
className="css-62g3xt-dummyInput"
5558+
disabled={false}
5559+
id="react-select-10-input"
5560+
onBlur={[Function]}
5561+
onChange={[Function]}
5562+
onFocus={[Function]}
5563+
readOnly={true}
5564+
tabIndex="0"
5565+
value=""
5566+
/>
5567+
</div>
5568+
<div
5569+
className=" css-1hb7zxy-IndicatorsContainer"
5570+
>
5571+
<span
5572+
className=" css-43ykx9-indicatorSeparator"
5573+
/>
5574+
<div
5575+
aria-hidden="true"
5576+
className=" css-7sl878-indicatorContainer"
5577+
onMouseDown={[Function]}
5578+
onTouchEnd={[Function]}
5579+
>
5580+
<svg
5581+
aria-hidden="true"
5582+
className="css-6q0nyr-Svg"
5583+
focusable="false"
5584+
height={20}
5585+
viewBox="0 0 20 20"
5586+
width={20}
5587+
>
5588+
<path
5589+
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
5590+
/>
5591+
</svg>
5592+
</div>
5593+
</div>
5594+
</div>
5595+
</div>
5596+
</div>
5597+
`;
5598+
5599+
exports[`Storyshots Design System/Selects/Single Custom Value Container 1`] = `
5600+
<div
5601+
style={
5602+
Object {
5603+
"padding": "1rem",
5604+
}
5605+
}
5606+
>
5607+
<label
5608+
htmlFor="custom-value-container-select"
5609+
id="select-label-custom-value-container"
5610+
>
5611+
Custom value container
5612+
</label>
5613+
<div
5614+
className="SingleSelect css-2b097c-container"
5615+
id="custom-value-container-select"
5616+
onKeyDown={[Function]}
5617+
>
5618+
<div
5619+
className=" css-15ub2n0-control"
5620+
onMouseDown={[Function]}
5621+
onTouchEnd={[Function]}
5622+
>
5623+
<div
5624+
className=" css-g1d714-ValueContainer"
5625+
>
5626+
<div
5627+
className=" css-1269n2i-placeholder"
5628+
>
5629+
Select...
5630+
</div>
5631+
<input
5632+
aria-autocomplete="list"
5633+
aria-labelledby="select-label"
5634+
className="css-62g3xt-dummyInput"
5635+
disabled={false}
5636+
id="react-select-11-input"
5637+
onBlur={[Function]}
5638+
onChange={[Function]}
5639+
onFocus={[Function]}
5640+
readOnly={true}
5641+
tabIndex="0"
5642+
value=""
5643+
/>
5644+
</div>
5645+
<div
5646+
className=" css-1hb7zxy-IndicatorsContainer"
5647+
>
5648+
<span
5649+
className=" css-43ykx9-indicatorSeparator"
5650+
/>
5651+
<div
5652+
aria-hidden="true"
5653+
className=" css-7sl878-indicatorContainer"
5654+
onMouseDown={[Function]}
5655+
onTouchEnd={[Function]}
5656+
>
5657+
<svg
5658+
aria-hidden="true"
5659+
className="css-6q0nyr-Svg"
5660+
focusable="false"
5661+
height={20}
5662+
viewBox="0 0 20 20"
5663+
width={20}
5664+
>
5665+
<path
5666+
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
5667+
/>
5668+
</svg>
5669+
</div>
5670+
</div>
5671+
</div>
5672+
</div>
5673+
</div>
5674+
`;
5675+
55225676
exports[`Storyshots Design System/Selects/Single Default 1`] = `
55235677
<div
55245678
style={

src/Select/Option.jsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import { components } from 'react-select';
3+
4+
import CheckboxButton from 'src/CheckboxButton';
5+
import './Option.scss';
6+
7+
// Replaceable Components
8+
// Option: Component responsible for displaying an option in the menu with a Checkbox.
9+
//
10+
// This is a replaceable component. If you need to deviate from this replaceable component,
11+
// reach out to the DS team first before creating your own replaceable component.
12+
//
13+
// See: https://react-select.com/components#replaceable-components
14+
15+
/* eslint-disable react/prop-types */
16+
const Option = ({ ...props }) => (
17+
<components.Option {...props}>
18+
<div className="Option">
19+
<label>{props.label}</label>
20+
<CheckboxButton
21+
checked={props.isSelected}
22+
id={props.label}
23+
onChange={() => null}
24+
/>
25+
</div>
26+
</components.Option>
27+
);
28+
/* eslint-enable react/prop-types */
29+
30+
export default Option;

src/Select/Option.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.Option {
2+
display: flex;
3+
justify-content: space-between;
4+
align-items: center;
5+
}

src/Select/SingleSelect.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const SingleSelect = ({
1111
'aria-label': ariaLabel,
1212
'aria-labelledby': ariaLabelledBy,
1313
className,
14+
closeMenuOnSelect,
1415
defaultValue,
1516
disabled,
1617
getOptionLabel,
@@ -19,6 +20,7 @@ const SingleSelect = ({
1920
id,
2021
inputId,
2122
isLoading,
23+
isMulti,
2224
isSearchable,
2325
modal,
2426
name,
@@ -34,13 +36,15 @@ const SingleSelect = ({
3436
aria-label={ariaLabel}
3537
aria-labelledby={ariaLabelledBy}
3638
className={classNames('SingleSelect', className)}
39+
closeMenuOnSelect={closeMenuOnSelect}
3740
defaultValue={defaultValue}
3841
getOptionLabel={getOptionLabel}
3942
getOptionValue={getOptionValue}
4043
id={id}
4144
inputId={inputId}
4245
isClearable={isClearable}
4346
isDisabled={disabled || isLoading}
47+
isMulti={isMulti}
4448
isSearchable={isSearchable}
4549
menuPortalTarget={modal ? document.body : undefined}
4650
name={name}
@@ -64,6 +68,7 @@ SingleSelect.propTypes = {
6468
'aria-label': propTypes.string,
6569
'aria-labelledby': propTypes.string,
6670
className: propTypes.string,
71+
closeMenuOnSelect: propTypes.bool,
6772
defaultValue: propTypes.object,
6873
disabled: propTypes.bool,
6974
getOptionLabel: propTypes.func,
@@ -72,6 +77,7 @@ SingleSelect.propTypes = {
7277
inputId: propTypes.string,
7378
isClearable: propTypes.bool,
7479
isLoading: propTypes.bool,
80+
isMulti: propTypes.bool,
7581
isSearchable: propTypes.bool,
7682
modal: propTypes.bool,
7783
name: propTypes.string,
@@ -87,6 +93,7 @@ SingleSelect.defaultProps = {
8793
'aria-label': undefined,
8894
'aria-labelledby': undefined,
8995
className: undefined,
96+
closeMenuOnSelect: true,
9097
defaultValue: undefined,
9198
disabled: false,
9299
getOptionLabel: undefined,
@@ -95,6 +102,7 @@ SingleSelect.defaultProps = {
95102
id: undefined,
96103
inputId: undefined,
97104
isLoading: false,
105+
isMulti: undefined,
98106
isSearchable: false,
99107
modal: false,
100108
name: undefined,

src/Select/SingleSelect.stories.jsx

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import React, { Fragment } from 'react';
22

33
import SingleSelect from 'src/Select/SingleSelect';
44

5+
import Option from './Option';
6+
import ValueContainer from './ValueContainer';
7+
58
const onChange = () => {};
69

710
export default {
@@ -10,11 +13,12 @@ export default {
1013
};
1114

1215
const options = [
13-
{ label: 'Bob', value: 1 },
14-
{ label: 'Dave', value: 2 },
15-
{ label: 'Jeff', value: 3 },
16-
{ label: 'Dennis', value: 4 },
17-
{ label: 'Basel', value: 5 },
16+
{ label: 'White', value: 1 },
17+
{ label: 'Black / African American', value: 2 },
18+
{ label: 'American Indian / Alaska Native', value: 3 },
19+
{ label: 'Asian', value: 4 },
20+
{ label: 'Native Hawaiian / Pacific Islander', value: 5 },
21+
{ label: 'Hispanic / Latinx', value: 6 },
1822
];
1923

2024
export const Default = () => (
@@ -53,3 +57,49 @@ export const MultiSelect = () => (
5357
/>
5458
</Fragment>
5559
);
60+
61+
export const CustomOptionWithCheckbox = () => (
62+
<Fragment>
63+
<label htmlFor="multi-select" id="select-label-custom-option">Custom option with checkbox</label>
64+
<SingleSelect
65+
aria-labelledby="select-label"
66+
closeMenuOnSelect={false}
67+
components={{
68+
Option,
69+
}}
70+
hideSelectedOptions={false}
71+
id="multi-select"
72+
isMulti
73+
options={options}
74+
onChange={onChange}
75+
/>
76+
</Fragment>
77+
);
78+
79+
export const CustomValueContainer = () => (
80+
<Fragment>
81+
<label htmlFor="custom-value-container-select" id="select-label-custom-value-container">
82+
Custom value container
83+
</label>
84+
<SingleSelect
85+
aria-labelledby="select-label"
86+
closeMenuOnSelect={false}
87+
components={{
88+
Option,
89+
ValueContainer: (props) => (
90+
<ValueContainer
91+
{...props}
92+
/* eslint-disable react/prop-types */
93+
valueText={`participant${props.getValue().length > 1 ? 's' : ''} selected`}
94+
/* eslint-enable react/prop-types */
95+
/>
96+
),
97+
}}
98+
hideSelectedOptions={false}
99+
id="custom-value-container-select"
100+
isMulti
101+
options={options}
102+
onChange={onChange}
103+
/>
104+
</Fragment>
105+
);

src/Select/ValueContainer.jsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
import { components } from 'react-select';
3+
import propTypes from 'prop-types';
4+
5+
// Replaceable Components
6+
//
7+
// Value Container: Container responsible for loading the placeholder value and the input.
8+
//
9+
// This is a replaceable component. If you need to deviate from this replaceable component,
10+
// reach out to the DS team first before creating your own replaceable component.
11+
//
12+
// See: https://react-select.com/components#replaceable-components
13+
14+
/* eslint-disable react/prop-types */
15+
const ValueContainer = ({ children, valueText, ...props }) => {
16+
const { getValue, hasValue } = props;
17+
const numValues = getValue().length;
18+
19+
if (!hasValue) {
20+
return (
21+
<components.ValueContainer {...props}>
22+
{children}
23+
</components.ValueContainer>
24+
);
25+
}
26+
return (
27+
<components.ValueContainer {...props}>
28+
{`${numValues} ${valueText}`}
29+
</components.ValueContainer>
30+
);
31+
};
32+
/* eslint-enable react/prop-types */
33+
34+
export default ValueContainer;
35+
36+
ValueContainer.propTypes = {
37+
children: propTypes.node,
38+
valueText: propTypes.string,
39+
};
40+
41+
ValueContainer.defaultProps = {
42+
children: undefined,
43+
valueText: 'selected',
44+
};

0 commit comments

Comments
 (0)