Skip to content
This repository was archived by the owner on Mar 25, 2025. It is now read-only.

Commit ba59742

Browse files
Brian Ochanboilund
authored andcommitted
feat(toggleButtonGroup): add exclusiveness to options
- Adds an exclusive boolean prop to the ToggleButtonGroup. When set, if an option is already selected, any persistent toggles on the same option will be ignored.
1 parent de2bd2e commit ba59742

File tree

6 files changed

+397
-31
lines changed

6 files changed

+397
-31
lines changed

packages/core/src/ToggleButtonGroup/__snapshots__/index.test.tsx.snap

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,3 +540,221 @@ exports[`ToggleButtonGroup ToggleButtonGroupWithField 1`] = `
540540
</div>
541541
</div>
542542
`;
543+
544+
exports[`ToggleButtonGroup ToggleButtonGroupWithFieldExclusive 1`] = `
545+
.c3 {
546+
font-family: Open Sans,Helvetica,arial,sans-serif;
547+
font-size: 13px;
548+
line-height: 22px;
549+
overflow: hidden;
550+
text-overflow: ellipsis;
551+
margin: 0;
552+
font-size: 13px;
553+
line-height: 18px;
554+
white-space: nowrap;
555+
}
556+
557+
.c7 {
558+
font-family: Open Sans,Helvetica,arial,sans-serif;
559+
font-size: 13px;
560+
line-height: 22px;
561+
overflow: hidden;
562+
text-overflow: ellipsis;
563+
margin: 0;
564+
font-size: 13px;
565+
line-height: 22px;
566+
}
567+
568+
.c2 {
569+
height: 40px;
570+
display: -webkit-box;
571+
display: -webkit-flex;
572+
display: -ms-flexbox;
573+
display: flex;
574+
-webkit-align-items: center;
575+
-webkit-box-align: center;
576+
-ms-flex-align: center;
577+
align-items: center;
578+
color: rgb(82,82,82);
579+
cursor: default;
580+
}
581+
582+
.c5 {
583+
box-sizing: border-box;
584+
max-width: 100%;
585+
position: relative;
586+
cursor: pointer;
587+
-webkit-transition: all 200ms;
588+
transition: all 200ms;
589+
}
590+
591+
.c5:not(:last-child) {
592+
border-right: 1px solid rgb(184,184,184);
593+
}
594+
595+
.c6 {
596+
background-color: rgb(255,255,255);
597+
height: 32px;
598+
width: 100%;
599+
display: -webkit-box;
600+
display: -webkit-flex;
601+
display: -ms-flexbox;
602+
display: flex;
603+
-webkit-align-items: center;
604+
-webkit-box-align: center;
605+
-ms-flex-align: center;
606+
align-items: center;
607+
-webkit-box-pack: center;
608+
-webkit-justify-content: center;
609+
-ms-flex-pack: center;
610+
justify-content: center;
611+
padding: 0 8px;
612+
overflow: hidden;
613+
white-space: nowrap;
614+
-webkit-transition: all 200ms;
615+
transition: all 200ms;
616+
color: rgb(102,102,102);
617+
fill: rgb(102,102,102);
618+
}
619+
620+
.c6:hover {
621+
background-color: rgba(204,204,204,0.16);
622+
}
623+
624+
.c6:focus {
625+
background-color: rgba(204,204,204,0.16);
626+
outline-color: rgb(69,39,160);
627+
}
628+
629+
.c6:active {
630+
background-color: rgba(204,204,204,0.24);
631+
}
632+
633+
.c4 {
634+
width: 100%;
635+
display: grid;
636+
grid-template-columns: repeat(auto-fit,minmax(24px,1fr));
637+
-webkit-user-select: none;
638+
-moz-user-select: none;
639+
-ms-user-select: none;
640+
user-select: none;
641+
border: 1px solid rgb(184,184,184);
642+
border-radius: 2px;
643+
}
644+
645+
.c9 {
646+
position: fixed;
647+
top: 0;
648+
width: 360px;
649+
border-radius: 4px;
650+
margin: 8px 16px;
651+
right: 0;
652+
}
653+
654+
.c0 {
655+
color: rgb(41,41,41);
656+
}
657+
658+
.c1 {
659+
position: absolute;
660+
z-index: 0;
661+
}
662+
663+
.c8 {
664+
position: absolute;
665+
z-index: 1;
666+
}
667+
668+
<div
669+
className="c0"
670+
id="practical-root"
671+
>
672+
<div>
673+
<div
674+
className="c1"
675+
id="layer-5"
676+
>
677+
<div>
678+
<div
679+
className="c2"
680+
>
681+
<span
682+
className="c3"
683+
>
684+
Exclusive selection
685+
</span>
686+
</div>
687+
<div
688+
className="c4"
689+
data-cy="toggleButtonGroupExclusive"
690+
>
691+
<div
692+
className="c5"
693+
disabled={false}
694+
onClick={[Function]}
695+
selected={false}
696+
>
697+
<div
698+
className="c6"
699+
selected={false}
700+
tabIndex={1}
701+
>
702+
<p
703+
className="c7"
704+
>
705+
Option 1
706+
</p>
707+
</div>
708+
</div>
709+
<div
710+
className="c5"
711+
disabled={false}
712+
onClick={[Function]}
713+
selected={false}
714+
>
715+
<div
716+
className="c6"
717+
selected={false}
718+
tabIndex={1}
719+
>
720+
<p
721+
className="c7"
722+
>
723+
Option 2
724+
</p>
725+
</div>
726+
</div>
727+
<div
728+
className="c5"
729+
disabled={false}
730+
onClick={[Function]}
731+
selected={false}
732+
>
733+
<div
734+
className="c6"
735+
selected={false}
736+
tabIndex={1}
737+
>
738+
<p
739+
className="c7"
740+
>
741+
Option 3
742+
</p>
743+
</div>
744+
</div>
745+
</div>
746+
</div>
747+
</div>
748+
</div>
749+
<div>
750+
<div
751+
className="c8"
752+
id="layer-6"
753+
>
754+
<div
755+
className="c9"
756+
/>
757+
</div>
758+
</div>
759+
</div>
760+
`;

packages/core/src/ToggleButtonGroup/index.test.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,38 @@ const TestControllerWithField: React.FC = () => {
7676
)
7777
}
7878

79+
const TestControlledWithFieldExclusive: React.FC = () => {
80+
const [exclusiveValue, setExclusiveValue] = useState([])
81+
82+
const onExclusiveChange = useCallback(id => {
83+
setExclusiveValue([id])
84+
}, [])
85+
86+
return (
87+
<ToggleButtonGroupWithField
88+
data-cy="toggleButtonGroupExclusive"
89+
label="Exclusive selection"
90+
onChange={onExclusiveChange}
91+
exclusive
92+
options={[
93+
{
94+
id: 'option1',
95+
content: <Typography>Option 1</Typography>,
96+
},
97+
{
98+
id: 'option2',
99+
content: <Typography>Option 2</Typography>,
100+
},
101+
{
102+
id: 'option3',
103+
content: <Typography>Option 3</Typography>,
104+
},
105+
]}
106+
values={exclusiveValue}
107+
/>
108+
)
109+
}
110+
79111
describe('ToggleButtonGroup', () => {
80112
test('ToggleButtonGroup', () => {
81113
const test = TestRender(<TestController />)
@@ -85,4 +117,8 @@ describe('ToggleButtonGroup', () => {
85117
const test = TestRender(<TestControllerWithField />)
86118
expect(test).toMatchSnapshot()
87119
})
120+
test('ToggleButtonGroupWithFieldExclusive', () => {
121+
const test = TestRender(<TestControlledWithFieldExclusive />)
122+
expect(test).toMatchSnapshot()
123+
})
88124
})

packages/core/src/ToggleButtonGroup/index.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ interface ToggleButtonProps<T extends string | number> {
116116
* @param id The given id prop
117117
*/
118118
readonly onClick: (id: T) => void
119+
/**
120+
* If `true`, only allow one of the child ToggleButton values to be selected.
121+
* @default false
122+
*/
123+
readonly exclusive?: boolean
119124
/**
120125
* ToggleButton content.
121126
*/
@@ -136,6 +141,7 @@ function ToggleButton<T extends string | number>({
136141
disabled = false,
137142
onClick,
138143
content,
144+
exclusive = false,
139145
}: ToggleButtonProps<T>): JSX.Element {
140146
const selected = useMemo(
141147
() => values.some(value => value === id),
@@ -147,8 +153,13 @@ function ToggleButton<T extends string | number>({
147153
if (disabled) {
148154
return
149155
}
156+
157+
if (exclusive && selected) {
158+
return
159+
}
160+
150161
onClick(id)
151-
}, [disabled, id, onClick])
162+
}, [disabled, id, exclusive, selected, onClick])
152163

153164
return (
154165
<ToggleButtonContainer
@@ -193,6 +204,11 @@ interface ToggleButtonGroupProps<T extends string | number>
193204
* Array of id values that should be toggled.
194205
*/
195206
readonly values: ReadonlyArray<T>
207+
/**
208+
* If `true`, only allow one of the child ToggleButton values to be selected.
209+
* @default false
210+
*/
211+
readonly exclusive?: boolean
196212
/**
197213
* Callback fired when user clicks a toggle button.
198214
* @param id The toggle button id prop
@@ -204,6 +220,7 @@ export function ToggleButtonGroup<T extends string | number>({
204220
options,
205221
onChange,
206222
values,
223+
exclusive = false,
207224
...props
208225
}: ToggleButtonGroupProps<T>): JSX.Element {
209226
return (
@@ -215,6 +232,7 @@ export function ToggleButtonGroup<T extends string | number>({
215232
{...option}
216233
values={values}
217234
onClick={onChange}
235+
exclusive={exclusive}
218236
/>
219237
)
220238
})}

packages/docs/src/mdx/coreComponents/ToggleButtonGroup.mdx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ To emphasize groups of related Toggle buttons, a group should share a common con
2323

2424
export const DemoComponent = ({}) => {
2525
const [values, setToggledValues] = useState(['24'])
26+
const [exclusiveValue, setExclusiveValue] = useState([])
2627
const onChange = useCallback(
2728
id => {
2829
const found = values.includes(id)
@@ -34,6 +35,12 @@ export const DemoComponent = ({}) => {
3435
},
3536
[values]
3637
)
38+
const onExclusiveChange = useCallback(
39+
id => {
40+
setExclusiveValue([id])
41+
},
42+
[exclusiveValue]
43+
)
3744
return (
3845
<>
3946
<ToggleButtonGroup
@@ -93,6 +100,23 @@ export const DemoComponent = ({}) => {
93100
]}
94101
values={values}
95102
/>
103+
<ToggleButtonGroupWithField
104+
label="Exclusive selection"
105+
onChange={onExclusiveChange}
106+
exclusive
107+
options={[
108+
{
109+
id: 'option1',
110+
content: <Typography>Option 1</Typography>,
111+
},
112+
{ id: 'option2', content: <Typography>Option 2</Typography> },
113+
{
114+
id: 'option3',
115+
content: <Typography>Option 3</Typography>,
116+
},
117+
]}
118+
values={exclusiveValue}
119+
/>
96120
</>
97121
)
98122
}

0 commit comments

Comments
 (0)