Listbox
Renders list of options in a accessible way.
install | version | usage | style |
---|---|---|---|
yarn add @ciceksepeti/cui-listbox | 1.0.0 | import {Listbox, ListboxItem } from '@ciceksepeti/cui-listbox' | import '@ciceksepeti/cui-listbox/style.css' |
Content​
Renders list of elements. Listbox is a polymorphic component. That's why can be rendered as any valid HTML tag
Examples​
Default​
- Controlled
- Uncontrolled
Example of the default behavior of Listbox component that controlled. Renders as 'button' by default.
note
import React, { useState } from 'react';
import { Listbox, ListboxItem } from '@ciceksepeti/cui-listbox';
import '@ciceksepeti/cui-listbox/styles.css';
export function CUIListboxDefaultControlled() {
let [value, setValue] = useState('apple');
return (
<>
<Listbox
value={value}
onChange={(value) => setValue(value)}
aria-labelledby="listbox1"
>
<ListboxItem value="apple">apple</ListboxItem>
<ListboxItem value="orange">orange</ListboxItem>
<ListboxItem value="cherry">cherry</ListboxItem>
<ListboxItem value="banana">banana</ListboxItem>
</Listbox>
<p>Current State: {value}</p>
</>
);
}
Example of the default behavior of Listbox component that uncontrolled. Renders as 'button' by default.
note
import React, { useRef } from 'react';
import { Listbox, ListboxItem, ListboxInput } from '@ciceksepeti/cui-listbox';
import '@ciceksepeti/cui-listbox/styles.css';
export function CUIListboxDefaultUncontrolled() {
const inputRef = useRef(null);
return (
<Listbox aria-labelledby="listbox 10">
<ListboxInput ref={inputRef} />
<ListboxItem value="apple">apple</ListboxItem>
<ListboxItem value="orange">orange</ListboxItem>
<ListboxItem value="cherry">cherry</ListboxItem>
<ListboxItem value="banana">banana</ListboxItem>
</Listbox>
<button
onClick={() => alert(inputRef.current?.value)}
style={{ marginLeft: '1rem' }}
>
Submit
</button>
);
}
Prefix​
Example of a Listbox component with prefix. Renders as 'button' by default.
import React from 'react';
import { Listbox, ListboxItem } from '@ciceksepeti/cui-listbox';
import '@ciceksepeti/cui-listbox/styles.css';
export function Example() {
return (
<Listbox
name="listbox1"
prefix={<span style={{ marginRight: 5 }}>#</span>}
style={{
width: 200,
}}
as="button"
aria-labelledby="listbox2"
>
<ListboxItem value="item1">item 1</ListboxItem>
<ListboxItem value="item2">item 2</ListboxItem>
<ListboxItem value="item3">item 3</ListboxItem>
<ListboxItem value="item4">item 4</ListboxItem>
</Listbox>
);
}
Search​
Example of a searchable Listbox component. Renders as 'button' by default.
import React, { useState } from 'react';
import { Listbox, ListboxItem } from '@ciceksepeti/cui-listbox';
import '@ciceksepeti/cui-listbox/styles.css';
export function Example() {
return (
<Listbox
as="button"
aria-labelledby="listbox3"
name="listbox3"
style={{
width: 200,
}}
>
<input
onChange={(e) => {
setSearch(e.target.value);
}}
placeholder="Search"
style={{
margin: '10px 0 10px 10px',
}}
type="text"
value={search}
/>
{items
.filter(({ label }) => label.includes(search))
.map(({ value, label }) => (
<ListboxItem key={value} value={value}>
{label}
</ListboxItem>
))}
</Listbox>
);
}
Compound​
Listbox
is a compound component. CactusUI providesListboxButton
,ListboxInput
andListboxPopover
to user create own Listbox. Props and their required and default values of these subcomponents are described props section of the page.
- Controlled
- Uncontrolled
import React, { useState } from 'react';
import {
ListboxButton,
ListboxPopover,
ListboxInput,
ListboxItem,
} from '@ciceksepeti/cui-listbox';
import '@ciceksepeti/cui-listbox/styles.css';
export function CUIListboxCompoundControlled() {
const [value, setValue] = useState('orange');
return (
<>
<ListboxButton
aria-labelledby="listbox4"
value={value}
onChange={(value) => setValue(value)}
>
<ListboxPopover>
<ListboxItem value="apple">apple</ListboxItem>
<ListboxItem value="orange">orange</ListboxItem>
<ListboxItem value="cherry">cherry</ListboxItem>
<ListboxItem value="banana">banana</ListboxItem>
</ListboxPopover>
</ListboxButton>
<span style={{ marginLeft: '1rem' }}>Current State : {value}</span>
</>
);
}
import React, { useRef } from 'react';
import {
ListboxButton,
ListboxPopover,
ListboxItem,
ListboxInput,
} from '@ciceksepeti/cui-listbox';
import '@ciceksepeti/cui-listbox/styles.css';
export function CUIListboxCompoundUncontrolled() {
const inputRef = useRef(null);
return (
<>
<ListboxButton aria-labelledby="listbox4" defaultValue="banana">
<ListboxInput ref={inputRef} />
<ListboxPopover>
<ListboxItem value="apple">apple</ListboxItem>
<ListboxItem value="orange">orange</ListboxItem>
<ListboxItem value="cherry">cherry</ListboxItem>
<ListboxItem value="banana">banana</ListboxItem>
</ListboxPopover>
</ListboxButton>
<button
onClick={() => alert(inputRef.current?.value)}
style={{ marginLeft: '1rem' }}
>
Submit
</button>
);
</>
}
Props​
Listbox Props​
Name | Type | Default | Required | Description |
name | string | - | - | Name of the listbox |
portal | boolean | false | - | Whether or not to render popover within a portal. |
required | boolean | false | - | Whether or not the listbox input's form field is required. |
disabled | boolean | false | - | Whether or not the listbox input is disabled. |
value | string | - | - | Current value of the listbox input. |
defaultValue | string | - | - | Default value of the listbox input. |
arrow | React.ReactNode | false | - | Set arrow shown of listbox input's form field. |
prefix | React.ReactNode | false | - | Set prefix shown of listbox input's form field. |
children | React.ReactNode | - | required | Element shown as Listbox Option. As default, Listbox accepts ListboxItem as children. |
as | string | div | - | Changes html tag of Listbox component which renders to DOM |
Listbox Events​
Name | Type | Description |
onChange | (value:string) => void | Gets called when selected item changes. |
Listbox Accessibility​
info
Name | Type | Default | Description |
aria-label | string | - | Defines a string value that labels the current element. |
aria-labelledby | string | - | Identifies the element (or elements) that labels the current element. |
ListboxItem Props​
Name | Type | Default | Required | Description |
value | string | - | required | Value of the listbox item. |
disabled | boolean | false | - | Whether or not the listbox item is disabled. |
children | React.ReactNode | - | required | Element shown as Listbox Item. |
as | string | div | - | Changes html tag of ListboxItem component which renders to DOM |
ListboxItem Events​
Name | Type | Description |
onMouseLeave | (event: React.SyntheticEvent) => any | Gets called when cursor leaves the listbox item. |
onMouseEnter | (event: React.SyntheticEvent) => any | Gets called when cursor enters the listbox item. |
onTouchStart | (event: React.SyntheticEvent) => any | Gets called when touch starts on the listbox item. |
ListboxButton Props​
Name | Type | Default | Required | Description |
value | string | - | - | Current value of the listbox input. |
defaultValue | string | - | - | Default value of the listbox. |
disabled | boolean | false | - | Whether or not the listbox item is disabled. |
children | React.ReactNode | - | required | Element shown as Listbox Button. |
as | string | button | - | Changes html tag of ListboxButton component which renders to DOM |
ListboxButton Events​
Name | Type | Description |
onChange | (value:string) => void | Gets called when selected item changes. |
ListboxPopover Props​
Name | Type | Default | Required | Description |
as | string | div | - | Changes html tag of popover component which renders to DOM |
children | React.ReactNode | - | required | Elements shown inside ListboxPopover. |
Playground​
Styling​
Element Selectors​
tip
Name | Type |
[data-cui-listbox-popover][hidden] | Element Selector |
[data-cui-listbox-button] | Element Selector |
[data-cui-listbox-button][aria-disabled='true'] | Element Selector |
[data-cui-listbox-list] | Element Selector |
[data-cui-listbox-popover] | Element Selector |
[data-cui-listbox-arrow] | Element Selector |
[data-cui-listbox-arrow][data-expanded='false']::before | Element and Pseudo Selectors |
[data-cui-listbox-arrow][data-expanded='true']::before | Element and Pseudo Selectors |
[data-cui-listbox-item] | Element Selector |
[data-cui-listbox-item][aria-disabled='true'] | Element Selector |
[data-cui-listbox-item][aria-selected='true'] | Element Selector |
[data-cui-listbox-item][data-active='true'] | Element Selector |
Default Selector Values​
[data-cui-listbox-popover][hidden] {
display: none;
}
[data-cui-listbox-button] {
color: black;
cursor: pointer;
padding: 10px 15px;
border-radius: 4px;
align-items: center;
display: inline-flex;
background-color: white;
border: 1px solid #eee;
justify-content: space-between;
}
[data-cui-listbox-button][aria-disabled='true'] {
opacity: 0.5;
}
[data-cui-listbox-list] {
margin: 0;
padding: 0;
list-style: none;
}
[data-cui-listbox-popover] {
margin: 5px 0px;
border-radius: 4px;
border: 1px solid #eee;
background-color: white;
}
[data-cui-listbox-arrow] {
font-size: 12px;
color: #6e6e6e;
display: inline-block;
transform: rotate(90deg);
-ms-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
}
[data-cui-listbox-arrow][data-expanded='false']::before {
content: '\276F';
}
[data-cui-listbox-arrow][data-expanded='true']::before {
content: '\276E';
}
[data-cui-listbox-item] {
cursor: pointer;
padding: 5px 10px;
text-align: start;
}
[data-cui-listbox-item][aria-disabled='true'] {
opacity: 0.5;
}
[data-cui-listbox-item][aria-selected='true'] {
background-color: steelblue;
}
[data-cui-listbox-item][data-active='true'] {
font-weight: bold;
}