Combobox Review
ComboBox is used as an alternative to a select box, where many items must be chosen from, or to assist in finding values for text input.
The ComboBox offers a list of items to choose from and it also exposes the value of selected item(s) via its value property. Please note that this value is distinct from the contained Input's value. The two values can be synced with the force attribute.
Examples #
<duet-layout center>
<div slot="main">
<duet-card>
<duet-combobox id="duet-combobox">
<duet-input label="Combobox Postalcode example" icon="form-location" expand caption="use arrow keys to navigate, type to search list"></duet-input>
</duet-combobox>
</duet-card>
</div>
</duet-layout>
<script>
(function() {
var combobox = document.querySelectorAll("#duet-combobox")
// Zipcode map to place
combobox[0].items = [
{id:0, value: "49200", name: "Heinlahti"},
{id:1, value: "49210", name: "Huutjärvi"},
{id:2, value: "49220", name: "Siltakylä"},
{id:3, value: "49240", name: "Purola"},
{id:4, value: "49270", name: "Something"},
{id:5, value: "49290", name: "Vastila"}
]
combobox[0].addEventListener("duetChange", function (e) {
console.log("Combobox value changed:", e.detail)
})
})()
</script>
<duet-layout center>
<div slot="main">
<duet-card>
<duet-combobox
items='[{"id":"0", "value": "49200", "name": "Heinlahti"},{"id":"1", "value": "49210", "name": "Huutjärvi"}]'
>
<duet-input label="Anna postinumerosi" label-hidden icon="form-location"></duet-input>
</duet-combobox>
</duet-card>
</div>
</duet-layout>
<duet-layout center>
<div slot="main">
<duet-card>
<duet-combobox id="duet-combobox-custom-formatter" force>
<duet-input label="Combobox Postalcode with custom formatter & tags" placeholder="select a postal code" icon="form-location" expand caption="use arrow keys to navigate, type to search list"></duet-input>
</duet-combobox>
</duet-card>
</div>
</duet-layout>
<script>
(function(){
var combobox = document.querySelectorAll("#duet-combobox-custom-formatter")
// Zipcode map to place
combobox[0].items = [
{id:0, value: "49200", name: "Heinlahti", tags: ["something else", "Heinlahti"]},
{id:1, value: "49210", name: "Huutjärvi", tags: ["49200", "Heinlahti"]},
{id:2, value: "49220", name: "Siltakylä", tags: ["49200", "Heinlahti"]},
{id:3, value: "49240", name: "Purola", tags: ["49200", "Heinlahti"]},
{id:4, value: "49270", name: "Something", tags: ["49200", "Heinlahti"]},
{id:5, value: "49290", name: "Vastila", tags: ["49200", "Heinlahti"]}
]
combobox[0].formatter = (item ) => {
return item.value + "" + item.name
}
combobox[0].addEventListener("duetChange", function (e) {
console.log("Combobox value changed:", e.detail)
})
})()
</script>
<duet-layout center>
<div slot="main">
<duet-card>
<duet-combobox id="duet-combobox-custom-formatter-with-errorchecking" force>
<duet-input label="Combobox Postalcode with custom formatter & tags" placeholder="select a postal code" icon="form-location" expand caption="this will error out if value selected is not the first with id:49200"></duet-input>
</duet-combobox>
</duet-card>
</div>
</duet-layout>
<script>
(function(){
var combobox = document.querySelectorAll("#duet-combobox-custom-formatter-with-errorchecking")
var input = document.querySelectorAll("duet-input")
// Zipcode map to place
combobox[0].items = [
{id:0, value: "49200", name: "Heinlahti", tags: ["something else", "Heinlahti"]},
{id:1, value: "49210", name: "Huutjärvi", tags: ["49200", "Heinlahti"]},
{id:2, value: "49220", name: "Siltakylä", tags: ["49200", "Heinlahti"]},
{id:3, value: "49240", name: "Purola", tags: ["49200", "Heinlahti"]},
{id:4, value: "49270", name: "Something", tags: ["49200", "Heinlahti"]},
{id:5, value: "49290", name: "Vastila", tags: ["49200", "Heinlahti"]}
]
combobox[0].formatter = (item ) => {
return item.value + " ⊨ " + item.name
}
combobox[0].addEventListener("duetChange", function (e) {
if(e.detail.value !== "49200") {
input[0].error = "Only postalcode with id:49200 is allowed as selection"
}else {
input[0].error = undefined
}
console.log("Combobox value changed:", e.detail)
})
}())
</script>
<duet-layout center>
<div slot="main">
<duet-card>
<duet-combobox min-characters="3" id="duet-combobox-with-min-characters" >
<duet-input label="Combobox Postalcode example" icon="form-location" expand caption="use arrow keys to navigate, type to search list"></duet-input>
</duet-combobox>
</duet-card>
</div>
</duet-layout>
<script>
(function(){
var combobox = document.querySelectorAll("#duet-combobox-with-min-characters")
// Zipcode map to place
combobox[0].items = [
{id:0, value: "49200", name: "Heinlahti"},
{id:1, value: "49210", name: "Huutjärvi"},
{id:2, value: "49220", name: "Siltakylä"},
{id:3, value: "49240", name: "Purola"},
{id:4, value: "49270", name: "Something"},
{id:5, value: "49290", name: "Vastila"}
]
combobox[0].addEventListener("duetChange", function (e) {
console.log("Combobox value changed:", e.detail)
})
}())
</script>
<duet-layout center>
<div slot="main">
<duet-card>
<duet-combobox open-list-on-click id="duet-combobox-open-on-click">
<duet-input label="Combobox Postalcode example" icon="form-location" expand caption="use arrow keys to navigate, type to search list"></duet-input>
</duet-combobox>
</duet-card>
</div>
</duet-layout>
<script>
(function(){
var combobox = document.querySelectorAll("#duet-combobox-open-on-click")
// Zipcode map to place
combobox[0].items = [
{id:0, value: "49200", name: "Heinlahti"},
{id:1, value: "49210", name: "Huutjärvi"},
{id:2, value: "49220", name: "Siltakylä"},
{id:3, value: "49240", name: "Purola"},
{id:4, value: "49270", name: "Something"},
{id:5, value: "49290", name: "Vastila"}
]
combobox[0].addEventListener("duetChange", function (e) {
console.log("Combobox value changed:", e.detail)
})
}())
</script>
<duet-layout center>
<div slot="main">
<duet-card>
<duet-combobox value="49240" id="duet-combobox-preselected-value">
<duet-input label="Combobox Postalcode example" icon="form-location" expand caption="use arrow keys to navigate, type to search list"></duet-input>
</duet-combobox>
</duet-card>
</div>
</duet-layout>
<script>
(function(){
var combobox = document.querySelectorAll("#duet-combobox-preselected-value")
// Zipcode map to place
combobox[0].items = [
{id:0, value: "49200", name: "Heinlahti"},
{id:1, value: "49210", name: "Huutjärvi"},
{id:2, value: "49220", name: "Siltakylä"},
{id:3, value: "49240", name: "Purola"},
{id:4, value: "49270", name: "Something"},
{id:5, value: "49290", name: "Vastila"}
]
combobox[0].addEventListener("duetChange", function (e) {
console.log("Combobox value changed:", e.detail)
})
}())
</script>
<duet-layout center>
<div slot="main">
<duet-card>
<!--
Combobox will generate the text input if you don't include it. In this case you need to add the label and
optional capition to the combobox component.
-->
<duet-combobox
filter-type="includes"
label="Choose disease"
caption="use arrow keys to navigate, type to search list"
multiple
open-list-on-click
></duet-combobox>
</duet-card>
</div>
</duet-layout>
<script>
(function() {
var combobox = document.querySelector("duet-combobox")
var items = [
{ "value":"B33.3", "name":"Muut retrovirusinfektiot", "id":"1" },
{ "value":"B33.8", "name":"Muu määritetty virussairaus", "id":"1" }, // for duplicate id' you will get a warning
{ "value":"B34", "name":"Virussair.sijainti ei määrit" }, // if no id is provided, it will be generated
{ "value":"B34.0", "name":"Määrittämätön adenovirusinfekt", "id":"3" },
{ "value":"B34.1", "name":"Määrittämätön enterovirusinfek", "id":"4" },
{ "value":"B34.2", "name":"Määrittämätön koronavirusinfek", "id":"5" },
{ "value":"B34.3", "name":"Määrittämätön parvovirusinfekt", "id":"6" },
{ "value":"B34.4", "name":"Määrittämätön papovavirusinfek", "id":"7" },
{ "value":"B34.8", "name":"Muu virusinf. sijainti ei määr", "id":"8" },
{ "value":"B34.9", "name":"Määrittämätön virusinfektio", "id":"9" },
{ "value":"B35-B49", "name":"Sienitaudit", "id":"10" },
{ "value":"B35", "name":"Ihon silsasairaus", "id":"11" },
{ "value":"B35.0", "name":"Pälvisilsa ja/tai partasilsa", "id":"12" },
{ "value":"B35.1", "name":"Kynsisilsa", "id":"13" },
{ "value":"B35.2", "name":"Käsisilsa", "id":"14" },
{ "value":"B35.3", "name":"Jalkasilsa", "id":"15" },
{ "value":"B35.4", "name":"Vartalosilsa", "id":"16" },
{ "value":"B35.5", "name":"Tinea imbricata", "id":"17" },
{ "value":"B35.6", "name":"Nivustaipeen silsa", "id":"18" },
{ "value":"B35.8", "name":"Muu ihon silsasairaus", "id":"19" },
{ "value":"B35.9", "name":"Määrittämätön ihon silsasair", "id":"20" },
{ "value":"B36", "name":"Muut pinnalliset sienitaudit", "id":"21" },
{ "value":"B36.0", "name":"Savipuoli", "id":"22" },
{ "value":"B36.1", "name":"Mustasilsa", "id":"23" },
{ "value":"B36.2", "name":"Valkosilsa", "id":"24" }
]
combobox.items = items
combobox.addEventListener("duetChange", function (e) {
console.log("Combobox value changed:", e.detail)
})
})()
</script>
Properties #
Property | Attribute | Description | Type | Default |
---|---|---|---|---|
accessibleLabelDefaults | accessible-label-defaults | Defaults for the accessible labels for the select items | DuetLangObject | string | { en: { heading: "Select:", item: "{name}, ({current} of {total})", itemFiltered: "{name}, ({current} of {total}, {hiddenItems} filtered)", }, fi: { heading: "Valitse:", item: "{name}, ({current}/{total})", itemFiltered: "{name}, ({current}/{total}, {hiddenItems} suodatettu)", }, sv: { heading: "Välj:", item: "{name}, ({current} av {total})", itemFiltered: "{name}, ({current} av {total}, {hiddenItems} filtrerade)", }, } |
accessibleLabels | -- | Accessible labels for the select items | { [x: string]: string; } | getLocaleString( this.accessibleLabelDefaults, getLanguage() ) |
caption | caption | Caption for the input if input is not provided as a slotted element. | string | "" |
filterType | filter-type | Defines if filtering of items should be done by includes or startsWith | "includes" | "startsWith" | "startsWith" |
force | force | Force the user to make a selection (typing things in the input field will only be used for list search). This property also sync input value with selected value on blur. | boolean | false |
formatter | -- | A hook to overwrite how the values are displayed in the input field after a User select an item | (item: DuetComboboxItem) => string | (item: DuetComboboxItem) => item && item.name ? item.name : "" |
items | items | Array of item objects. Each item should have properties name, value and optionally id. If id is not provided, it will be generated. Alternatively, an array of strings can be provided, in which case the strings will be used for name, value and id. | any | undefined |
label | label | Label for the input if input is not provided as a slotted element. | string | "" |
minCharacters | min-characters | Defines minimum number of characters that must be given to show search results | number | 1 |
multiple | multiple | Allow multiple selections. Selections are displayed as DuetChips. | boolean | false |
openListOnClick | open-list-on-click | Defines if items list should always open after clicking on input | boolean | false |
theme | theme | Theme of the combobox. | "" | "default" | "turva" | "" |
value | value | Value of selected item/s. If multiple is true, value is an array of selected items, else it's a string. | string | string[] | undefined |
Events #
Event | Description | Type |
---|---|---|
duetChange | Emitted when selected item changed. | CustomEvent<{ originalEvent?: Event; component: "duet-combobox"; value: any; item: DuetComboboxItem; }> |
Methods #
formatItem(item: DuetComboboxItem) => Promise<string>
#
Exposes a formatter function to format the item value displayed after a user selects an item
Parameters #
Name | Type | Description |
---|---|---|
item | { id?: string; value: any; name: string; html?: string; tags?: string[]; } | : DuetComboboxItem |
Returns #
Type: Promise<string>
Usage #
This section includes guidelines for designers and developers about the usage of this component in different contexts.
Normal usage with javascript #
Don't use the standard .value attribute to do any validation on, you should use the duetChange event instead - it provides better data for filtering
When to use #
- As a more userfirendly alternative to a standard select box, however it should only be used when the number of items are unwieldy.
When not to use #
- For small list of items
- For items that have groupings
Accessibility #
This component has been validated to meet the WCAG 2.1 AA accessibility guidelines. You can find additional information regarding accessibility of this component below.
- You can use all the features of duet-input as normal
- Accessibility is provided via a dynamic select of chosen items via aria-selected
- The select text is hidden from screen readers and a label is read aloud instead, which informs the user of which item he/she selected out of how many items in the list
- it has been implemented to support https://www.levelaccess.com/differences-aria-1-0-1-1-changes-rolecombobox/
Integration
For integration, event and theming guidelines, please see Using Components. This documentation explains how to implement and use Duet’s components across different technologies like Angular, React or Vanilla JavaScript.
Tutorials
Follow these practical tutorials to learn how to build simple page layouts using Duet’s CSS Framework, Web Components and other features:
Building Layouts
TutorialsUsing CLI Tools
TutorialsCreating Custom Patterns
TutorialsServer Side Rendering
TutorialsSharing Prototypes
TutorialsUsage With Markdown
Troubleshooting
If you experience any issues while using a component, please head over to the Support page for more guidelines and help.