Skip to main content

SelectableBox

A box that has selection states. It can be used as an alternative to a radio button or checkbox set.

The SelectableBox can contain any kind of content as long as it is not clickable. In other words, there should be no clickable targets distinct from selection.

Basic Usage

As Checkbox

() => {
const type = 'checkbox';
const allCheeseOptions = ['swiss', 'cheddar', 'pepperjack'];
const [checkedCheeses, { add, remove, set, clear }] = useCheckboxSetValues(['swiss']);

const handleChange = (e) => {
e.target.checked ? add(e.target.value) : remove(e.target.value);
};

const isInvalid = () => checkedCheeses.includes('swiss');
const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });

return (
<SelectableBox.Set
className="bg-light-200 p-3"
value={checkedCheeses}
type={type}
onChange={handleChange}
name="cheeses"
columns={isExtraSmall ? 1 : 2}
ariaLabel="cheese selection"
>
<SelectableBox value="swiss" type={type} aria-label="swiss checkbox">
<h3>It is my first SelectableBox</h3>
<p>Swiss</p>
</SelectableBox>
<SelectableBox value="cheddar" inputHidden={false} type={type} aria-label="cheddar checkbox">
<h3>Cheddar</h3>
</SelectableBox>
<SelectableBox
value="pepperjack"
inputHidden={false}
type={type}
isInvalid={isInvalid()}
aria-label="pepperjack checkbox"
>
<h3>Pepperjack</h3>
</SelectableBox>
</SelectableBox.Set>
);
}

As Radio

() => {
const [value, setValue] = useState('green');
const handleChange = (e) => setValue(e.target.value);
const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });

return (
<SelectableBox.Set
value={value}
onChange={handleChange}
name="colors"
columns={isExtraSmall ? 1 : 3}
ariaLabel="color selection"
>
<SelectableBox value="red" aria-label="red checkbox">
<h3>It is Red color</h3>
<p>Select me</p>
</SelectableBox>
<SelectableBox value="green" inputHidden={false} aria-label="green radio-button">
<h3>Green</h3>
<p>Leaves and grass</p>
</SelectableBox>
<SelectableBox value="blue" inputHidden={false} aria-label="blue radio-button">
<h3>Blue</h3>
<p>The sky</p>
</SelectableBox>
</SelectableBox.Set>
);
}

As Checkbox

As Checkbox with isIndeterminate

() => {
const type = 'checkbox';
const allCheeseOptions = ['swiss', 'cheddar', 'pepperjack'];
const [checkedCheeses, { add, remove, set, clear }] = useCheckboxSetValues(['swiss']);

const allChecked = allCheeseOptions.every(value => checkedCheeses.includes(value));
const someChecked = allCheeseOptions.some(value => checkedCheeses.includes(value));
const isIndeterminate = someChecked && !allChecked;
const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.small.maxWidth });

const handleChange = (e) => {
e.target.checked ? add(e.target.value) : remove(e.target.value);
};

const handleCheckAllChange = ({ checked }) => {
checked ? set(allCheeseOptions) : clear();
};

return (
<>
<SelectableBox
className="mb-3"
checked={allChecked}
isIndeterminate={isIndeterminate}
onClick={handleCheckAllChange}
inputHidden={false}
type={type}
aria-label="all options checkbox"
>
All the cheese
</SelectableBox>
<SelectableBox.Set
value={checkedCheeses}
type={type}
onChange={handleChange}
name="cheeses"
columns={isExtraSmall ? 1 : 3}
ariaLabel="cheese selection"
>
<SelectableBox value="swiss" type={type} aria-label="swiss checkbox">
<h3>It is my first SelectableBox</h3>
<p>Swiss</p>
</SelectableBox>
<SelectableBox value="cheddar" inputHidden={false} type={type} aria-label="cheddar checkbox">
<h3>Cheddar</h3>
</SelectableBox>
<SelectableBox value="pepperjack" inputHidden={false} type={type} aria-label="pepperjack checkbox">
<h3>Pepperjack</h3>
</SelectableBox>
</SelectableBox.Set>
</>
);
}

As Checkbox with ariaLabelledby

() => {
const type = 'checkbox';
const allCheeseOptions = ['swiss', 'cheddar', 'pepperjack'];
const [checkedCheeses, { add, remove, set, clear }] = useCheckboxSetValues(['swiss']);

const handleChange = (e) => {
e.target.checked ? add(e.target.value) : remove(e.target.value);
};

const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });

return (
<Stack className="bg-light-200 p-3">
<h3 id="cheese selection" className="mb-4">
Select your favorite cheese:
</h3>
<SelectableBox.Set
value={checkedCheeses}
type={type}
onChange={handleChange}
name="cheeses"
columns={isExtraSmall ? 1 : 3}
ariaLabelledby="cheese selection"
>
<SelectableBox value="swiss" inputHidden={false} type={type} aria-label="swiss checkbox">
<h3>Swiss</h3>
</SelectableBox>
<SelectableBox value="cheddar" inputHidden={false} type={type} aria-label="cheddar checkbox">
<h3>Cheddar</h3>
</SelectableBox>
<SelectableBox value="pepperjack" inputHidden={false} type={type} aria-label="pepperjack checkbox">
<h3>Pepperjack</h3>
</SelectableBox>
</SelectableBox.Set>
</Stack>
);
}

Without active outline

The showActiveBoxState property only affects SelectableBox that have inputHidden set to false. If a component has no input, the border is always rendered in an active state.

() => {
const [value, setValue] = useState('apples');
const handleChange = (e) => setValue(e.target.value);
const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });

return (
<SelectableBox.Set
value={value}
onChange={handleChange}
name="fruits"
columns={isExtraSmall ? 1 : 3}
ariaLabel="fruits selection"
>
<SelectableBox
value="apples"
inputHidden={false}
showActiveBoxState={false}
aria-label="apple radio-button"
>
<h3>Apples</h3>
</SelectableBox>
<SelectableBox
value="oranges"
inputHidden={false}
showActiveBoxState={false}
aria-label="orange radio-button"
>
<h3>Oranges</h3>
</SelectableBox>
<SelectableBox
value="bananas"
inputHidden={false}
showActiveBoxState={false}
aria-label="banana radio-button"
>
<h3>Bananas</h3>
</SelectableBox>
</SelectableBox.Set>
);
}