- Accordion
- Avatar
- Badge
- Breadcrumb
- Button
- Calendar
- ChatContainer
- ChatInput
- ChatMessage
- ChatMultiChoiceQuestion
- ChatMultiOptionQuestion
- ChatThinking
- Checkbox
- Combobox
- Container
- CurrencyInput
- DistributionSlider
- Drawer
- Dropdown
- FilePicker
- Grid
- Heading
- Image
- Input
- InputGroup
- Label
- Logo
- MapPin
- Markdown
- Modal
- NativeSelect
- NumberInput
- OptionSlider
- OtpInput
- PhoneInput
- Popover
- Progress
- PropertyCalendar
- RadioGroup
- RadioGroupCards
- ResponsiveModal
- ScrollArea
- SearchBar
- SearchBarFallback
- SearchInput
- Select
- Separator
- Spinner
- Switch
- Table
- Tabs
- Text
- Textarea
- TimePicker
- Toast
- Toggle
- ToggleCard
- ToggleGroup
- Toolbar
- Tooltip
ChatMultiOptionQuestion
A multi-select chat question with a "select all" affordance, indeterminate state, and an inline submit footer.
pnpm add @wandercom/design-system-web
import { ChatMultiOptionQuestion } from '@wandercom/design-system-web/ui/chat-multi-option-question';
export function Example() {
const [selected, setSelected] = useState<string[]>([]);
return (
<ChatMultiOptionQuestion
description="Select all the pages you want to build."
onChange={setSelected}
onSubmit={(values) => respond(values)}
options={[
{ value: 'home', label: 'Home' },
{ value: 'about', label: 'About' },
]}
question="What pages do you want to show?"
value={selected}
/>
);
}The high-level ChatMultiOptionQuestion composes a prompt, a "select all" row, the option list, and a footer with a live count + submit button. Drop down to the subcomponents (ChatMultiOptionQuestionRoot, ChatMultiOptionQuestionSelectAll, …) when you need a custom layout.
The default question renders the full Figma layout: prompt + description, "Select all" row over a divider, the option list, a divider, and a footer with the count and submit arrow. Submit is disabled until at least one option is selected.
<ChatMultiOptionQuestion
description="Select all the pages you want to build."
onChange={setSelected}
onSubmit={(values) => respond(values)}
options={pages}
question="What pages do you want to show?"
value={selected}
/>Hide the "Select all" row when the question only has a handful of options or when toggling everything at once doesn’t make sense. The footer still renders whenever onSubmit is provided.
<ChatMultiOptionQuestion
allowSelectAll={false}
onChange={setSelected}
onSubmit={(values) => respond(values)}
options={amenities}
question="Which amenities matter most?"
value={selected}
/>For full control over layout — custom select-all label, removing the divider, replacing the footer — use the subcomponents directly.
<ChatMultiOptionQuestionRoot
onChange={setSelected}
onSubmit={handleSubmit}
options={rooms}
value={selected}
>
<ChatMultiOptionQuestionPrompt description="Pick every room we should photograph.">
Which rooms are ready to shoot?
</ChatMultiOptionQuestionPrompt>
<div className="flex flex-col gap-2">
<ChatMultiOptionQuestionSelectAll>All rooms in the home</ChatMultiOptionQuestionSelectAll>
<ChatMultiOptionQuestionDivider />
<ChatMultiOptionQuestionOptions />
<ChatMultiOptionQuestionDivider />
<ChatMultiOptionQuestionFooter />
</div>
</ChatMultiOptionQuestionRoot>- Select all is
trueonly when every option is selected. With a partial selection, the checkbox renders an indeterminate dash and clicking it selects every option. Clicking it again with everything selected clears the selection. - The
valuearray is normalized to the order of the suppliedoptionsso consumers can render summaries without re-sorting. ChatMultiOptionQuestionSubmitis auto-disabled when the selection is empty. Passdisabled={false}to override, or passshowFooter={false}on the high-level component to hide the footer entirely.
- The root renders as a
<fieldset>and is labeled viaaria-labelledbypointing at the prompt heading when aPromptis mounted, so screen readers announce the question on entry. When noPromptis rendered, passaria-labelonChatMultiOptionQuestionRootto label the group instead. To use a native<legend>instead ofaria-labelledby, renderChatMultiOptionQuestionPromptwithasChildand pass a<legend>as its child. - Each option row is a
<label>associated with the row's checkbox viahtmlFor/id, so the entire row is a single tap target and screen readers announce label + state together. The checkbox renders Radix'srole="checkbox"button under the hood. - The "select all" row uses Radix's indeterminate state and is announced as
aria-checked="mixed"when partial. - The submit button ships with an
aria-labelof"Submit". Override via thearia-labelprop onChatMultiOptionQuestionSubmitor the high-level component. - Disabling the root via
disabledonChatMultiOptionQuestionRootpropagatesdisabledto every checkbox via the native<fieldset>cascade.
ChatMultiOptionQuestion accepts everything ChatMultiOptionQuestionRoot does, plus question, description, allowSelectAll, and showFooter which drive the default composition.
options
value
onChange
onSubmit?:
question
description?:
allowSelectAll?:
selectAllLabel?:
showFooter?:
disabled?:
className?:
children
description?:
asChild?:
className?:
Inherits all <div> props.
children?:
Inherits all <label> props except htmlFor and onChange (managed by the root).
value
children
disabled?:
Inherits all <label> props except htmlFor and onChange.
children?:
className?:
Inherits all <div> props. Renders a 1px horizontal rule with aria-hidden="true".
asChild?:
className?:
children?:
asChild?:
className?:
format?:
asChild?:
className?:
Inherits the Button API except size, variant, type, asChild, and children, which are fixed by the component.
icon?:
aria-label?:
disabled?:
className?:
type ChatMultiOptionQuestionItem = {
value: string;
label: ReactNode;
};