SelectCard
A SelectCard is a controlled component that allows users to select one or more options from a set of options. It is useful for presenting a list of options in a card-like format.
Updated in eds-core: 1.15.0
Quick Start
- Installation
npm install @adaptavant/eds-core
- Import
import { SelectCard } from '@adaptavant/eds-core';
ControlType
The controlType
prop specifies the input type rendered within each card of the SelectCard group. It supports two values checkbox
and radio
. Default value is "radio".
const themeOptionsForRadio = [
{
value: 'light',
name: 'theme',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeLightIcon size="20" />
</div>
<Text className="text-body-12">Light</Text>
</>
),
},
{
value: 'dark',
name: 'theme',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeDarkIcon size="20" />
</div>
<Text className="text-body-12">Dark</Text>
</>
),
},
{
value: 'system',
name: 'theme',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<DeviceDesktopIcon size="20" />
</div>
<Text className="text-body-12">System</Text>
</>
),
},
];
const themeOptions = [
{
value: 'light',
name: 'sun',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeLightIcon size="20" />
</div>
<Text className="text-body-12">Light</Text>
</>
),
},
{
value: 'dark',
name: 'moon',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeDarkIcon size="20" />
</div>
<Text className="text-body-12">Dark</Text>
</>
),
},
{
value: 'system',
name: 'system',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<DeviceDesktopIcon size="20" />
</div>
<Text className="text-body-12">System</Text>
</>
),
},
];
const [selectedThemeOption, setSelectedThemeOption] = React.useState(['']);
const [selectedThemeOptionRadio, setSelectedThemeOptionRadio] = React.useState('');
return (
<Box className="flex flex-col gap-4">
<SelectCard
controlType="radio"
onChange={setSelectedThemeOptionRadio}
options={themeOptionsForRadio}
value={selectedThemeOptionRadio}
/>
<SelectCard
controlType="checkbox"
onChange={setSelectedThemeOption}
options={themeOptions}
value={selectedThemeOption}
/>
</Box>
);
Orientation
The orientation
prop determines the layout of the cards within the SelectCard component. It accepts the following values:
- horizontal: Arranges the cards in a horizontal layout. (Default)
- vertical: Arranges the cards in a vertical layout.
const addonList = [
{
value: 'online',
name: 'online',
directSlot: (
<>
<Box>
<Text className="text-body-12 font-strong">Online appointment</Text>
<Text className="text-body-12 text-secondary">Book appointments online</Text>
</Box>
<Text className="text-body-12 text-secondary">Free</Text>
</>
),
},
{
value: 'ivr',
name: 'ivr',
directSlot: (
<>
<Box>
<Text className="text-body-12 font-strong">IVR</Text>
<Text className="text-body-12 text-secondary">Set Interactive Voice Response for customers</Text>
</Box>
<Text className="text-body-12 text-secondary">Free</Text>
</>
),
},
{
value: 'business',
name: 'business-line',
directSlot: (
<>
<Box>
<Text className="text-body-12 font-strong">Business line</Text>
<Text className="text-body-12 text-secondary">Dedicate local business line</Text>
</Box>
<Text className="text-body-12 text-secondary">$5 per number</Text>
</>
),
},
];
const [selectedAddonList, setSelectedAddonList] = React.useState([]);
return (
<SelectCard
className="w-full"
controlType="checkbox"
onChange={(selectedItem) => {
setSelectedAddonList(selectedItem);
// You can perform additional actions here
}}
options={addonList}
orientation="vertical"
value={selectedAddonList}
/>
);
Value & OnChange
The value
prop represents the currently selected value(s) in the SelectCard component. Since SelectCard is a controlled component, this prop is required to manage the component's state externally.
- For radio controlType: Pass a single string to represent the selected card.
- For checkbox controlType: Pass an array of strings to represent the selected cards.
When initializing the SelectCard component in consumer applications, you can set default values by providing them through the value prop.
The onChange
callback is triggered whenever the selection changes within the SelectCard component. It provides the updated value(s) as an argument:
- For radio controlType: Returns the newly selected string value.
- For checkbox controlType: Returns an updated array of selected values.
const themeOptions = [
{
value: 'light',
name: 'sun',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeLightIcon size="20" />
</div>
<Text className="text-body-12">Light</Text>
</>
),
},
{
value: 'dark',
name: 'moon',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeDarkIcon size="20" />
</div>
<Text className="text-body-12">Dark</Text>
</>
),
},
{
value: 'system',
name: 'system',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<DeviceDesktopIcon size="20" />
</div>
<Text className="text-body-12">System</Text>
</>
),
},
];
const [selectedThemeOption, setSelectedThemeOption] = React.useState(['light', 'dark']); // Default selected values
return (
<SelectCard
controlType="checkbox"
onChange={(selectedItem) => {
setSelectedThemeOption(selectedItem);
// You can perform additional actions here
}}
options={themeOptions}
value={selectedThemeOption}
/>
);
Dynamic content
SelectCard has slots for dynamic contents to be rendered in view only when a particular card is selected(checked), pass dynamicSlot
in your options
array to achieve this behaviour.
function NameInput() {
const [errorMsg, setErrorMsg] = React.useState(null);
const handleInputOnChange = (e) => {
setErrorMsg(
e.target.value.length > 25
? 'Name should not be more than 25 characters'
: null
);
};
return (
<Field errorMessage={errorMsg} label="Your name" labelVisibility="hidden">
<TextInput onChange={handleInputOnChange} placeholder="John Doe" />
</Field>
);
};
function CreditCardInput() {
const [errorMsg, setErrorMsg] = React.useState(null);
const handleInputOnChange = (e) => {
setErrorMsg(
e.target.value.length !== 16
? 'Credit card number should be 16 digits'
: null
);
};
return (
<Field
errorMessage={errorMsg}
label="Credit Card Number"
labelVisibility="hidden"
>
<TextInput
onChange={handleInputOnChange}
placeholder="1234 5678 9012 3456"
/>
</Field>
);
}
const dynamicOptions = [
{
value: 'pay-via-card',
name: 'paymentType',
directSlot: (
<>
<Text className="text-body-16 sm:text-body-12">Pay via card</Text>
<Box className="flex items-center">
<AmexColorIcon size="20" />
<VisaColorIcon size="20" />
<MastercardColorIcon size="20" />
</Box>
</>
),
dynamicSlot: (
<Box className="flex space-x-4">
<NameInput />
<CreditCardInput />
</Box>
),
transitionHeightClass: 'h-14', // When not passed card will grow without any css transition
},
{
value: 'pay-via-UPI',
name: 'paymentType',
directSlot: 'Pay via upi',
dynamicSlot: (
<Box className="flex space-x-4">
<NameInput />
<CreditCardInput />
</Box>
),
transitionHeightClass: 'h-14', // give maximum dynamicContent can grow - classNames will be used for transition card height
},
{
value: 'pay-via-cash',
name: 'paymentType',
directSlot: 'Pay with cash',
isDisabled: true, // allows you to disable particular card alone
},
];
const [selectPaymentType, setSelectedPaymentType] = React.useState('');
return (
<SelectCard
className="w-full"
controlType="radio"
onChange={(newSelectPaymentType) => {
setSelectedPaymentType(newSelectPaymentType);
}}
legend= "Select Payment Type"
options={dynamicOptions}
orientation="vertical"
value={selectPaymentType}
/>
);
Disabled
Use the isDisabled
prop and set it to "true" to disable all cards in the SelectCard group. To disable an individual card, add the isdisabled
property to the respective option object in the "options" array.
const themeOptions = [
{
value: 'light',
name: 'sun',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeLightIcon size="20" />
</div>
<Text className="text-body-12">Light</Text>
</>
),
},
{
value: 'dark',
name: 'moon',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeDarkIcon size="20" />
</div>
<Text className="text-body-12">Dark</Text>
</>
),
},
{
value: 'system',
name: 'system',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<DeviceDesktopIcon size="20" />
</div>
<Text className="text-body-12">System</Text>
</>
),
},
];
const themeOptionsTwo = [
{
value: 'light2',
name: 'sun',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeLightIcon size="20" />
</div>
<Text className="text-body-12">Light</Text>
</>
),
},
{
value: 'dark2',
name: 'moon',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<ModeDarkIcon size="20" />
</div>
<Text className="text-body-12">Dark</Text>
</>
),
},
{
value: 'system2',
name: 'system',
directSlot: (
<>
<div className="w-5 h-5 flex items-center justify-center">
<DeviceDesktopIcon size="20" />
</div>
<Text className="text-body-12">System</Text>
</>
),
isDisabled: true, // allows you to disable particular card alone
},
];
const [selectedThemeOption, setSelectedThemeOption] = React.useState('');
return (
<Box className="flex flex-col gap-4">
<SelectCard
controlType="checkbox"
isDisabled={true} // Disabled all cards
onChange={(selectedItem) => {
setSelectedThemeOption(selectedItem);
}}
options={themeOptions}
value={selectedThemeOption}
/>
<SelectCard
controlType="checkbox"
onChange={(selectedItem) => {
setSelectedThemeOption(selectedItem);
}}
options={themeOptionsTwo} // Disabled single card in list
value={selectedThemeOption}
/>
</Box>
);
Style API
Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.
Please refer to the Style API documentation for more insights.
SelectCard parts
function NameInput() {
return (
<Field label="Your name" labelVisibility="hidden">
<TextInput placeholder="John Doe" />
</Field>
);
};
const dynamicOptions = [
{
value: 'pay-via-card',
name: 'paymentType',
directSlot: (
<>
<Text className="text-body-16 sm:text-body-12">Pay via card</Text>
<Box className="flex items-center">
<AmexColorIcon size="20" />
<VisaColorIcon size="20" />
<MastercardColorIcon size="20" />
</Box>
</>
),
dynamicSlot: <NameInput />,
// @ts-expect-error
classNames: { // classNames to target primitive parts. Note: This is not a valid prop for SelectCardProps thats why we have "ts-error" comment
icon: 'fill-caution',
control: 'bg-critical hover:bg-critical-hover border-[orange] border-2',
},
},
];
const [selectPaymentType, setSelectedPaymentType] = React.useState('');
return (
<SelectCard
className="w-96 m-auto"
classNames={{
legend: 'text-positive sm:font-stronger',
controlWrapper: 'bg-positive px-0.5 rounded-4px',
card: 'px-2 bg-positive-hover',
cardRow: 'bg-caution-secondary',
focusIndicator: 'border-4 border-input-critical',
directContentWrapper: 'text-link bg-positive-secondary-pressed ps-2',
dynamicContentWrapper: 'bg-caution-hover',
}}
controlType="radio"
onChange={(newSelectPaymentType) => {
setSelectedPaymentType(newSelectPaymentType);
}}
legend= "Select Payment Type"
options={dynamicOptions}
orientation="vertical"
value={selectPaymentType}
/>
);
Stylable Parts | Description |
---|---|
root | The root container of all cards, fieldset that is wrapping all inner element. |
legend | Legend element in fieldset. First child of fieldset. |
controlWrapper | Targets root part(AlignChildToText) of primitives. |
control | The container for the control element, which visually represents the checked/unchecked state. Pass in "options" object. |
icon | The icon displayed within the checkbox/radio when it is checked. Pass in "options" object. |
card | Wrapper element of each card, consist of control, directSlot and dynamicSlot |
focusIndicator | Targets focus ring of the card. |
directContentWrapper | Wrapper element for directSlot. |
dynamicContentWrapper | Wrapper element for dynamicSlot. |