Skip to content

Commit

Permalink
Merge pull request #4963 from nextcloud-libraries/enh/a11y/select-label
Browse files Browse the repository at this point in the history
enh(NcSelect): Add visible input label
  • Loading branch information
Pytal committed Jan 3, 2024
2 parents 24a2810 + 77de015 commit 8bf8598
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 62 deletions.
3 changes: 0 additions & 3 deletions l10n/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,6 @@ msgstr ""
msgid "Search emoji"
msgstr ""

msgid "Search for options"
msgstr ""

msgid "Search for time zone"
msgstr ""

Expand Down
117 changes: 62 additions & 55 deletions src/components/NcSelect/NcSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,19 @@ General purpose multiselect component.
```vue
<template>
<div class="grid">
<div v-for="{ name, props } in selectArray"
<div v-for="{ props } in selectArray"
class="container">
<label :for="props.inputId">{{ name }}</label>
<NcSelect v-bind="props"
v-model="props.value" />
</div>
</div>
</template>

<script>
import GenRandomId from '../../utils/GenRandomId.js'
const getRandomId = () => {
return `select-${GenRandomId()}`
}
const selectArray = [
{
name: 'Simple',
props: {
inputId: getRandomId(),
inputLabel: 'Simple',
options: [
'foo',
'bar',
Expand All @@ -62,9 +54,8 @@ const selectArray = [
},
{
name: 'Simple (top placement)',
props: {
inputId: getRandomId(),
inputLabel: 'Simple (top placement)',
placement: 'top',
options: [
'foo',
Expand All @@ -77,9 +68,8 @@ const selectArray = [
},
{
name: 'Multiple (with placeholder)',
props: {
inputId: getRandomId(),
inputLabel: 'Multiple (with placeholder)',
multiple: true,
placeholder: 'Select multiple options',
options: [
Expand All @@ -93,9 +83,8 @@ const selectArray = [
},
{
name: 'Multiple (objects, pre-selected, stay open on select)',
props: {
inputId: getRandomId(),
inputLabel: 'Multiple (objects, pre-selected, stay open on select)',
multiple: true,
closeOnSelect: false,
options: [
Expand Down Expand Up @@ -187,13 +176,11 @@ parent container is limited to `350px`
<template>
<div class="grid">
<div class="container">
<label :for="data1.props.inputId">{{ data1.name }}</label>
<NcSelect :no-wrap="false"
v-bind="data1.props"
v-model="data1.props.value" />
</div>
<div class="container">
<label :for="data2.props.inputId">{{ data2.name }}</label>
<NcSelect :no-wrap="true"
v-bind="data2.props"
v-model="data2.props.value" />
Expand All @@ -202,16 +189,9 @@ parent container is limited to `350px`
</template>

<script>
import GenRandomId from '../../utils/GenRandomId.js'
const getRandomId = () => {
return `select-${GenRandomId()}`
}
const data1 = {
name: 'Wrapped (Default)',
props: {
inputId: getRandomId(),
inputLabel: 'Wrapped (Default)',
multiple: true,
closeOnSelect: false,
options: [
Expand Down Expand Up @@ -242,9 +222,8 @@ const data1 = {
}
const data2 = {
name: 'Not wrapped',
props: {
inputId: getRandomId(),
inputLabel: 'Not wrapped',
multiple: true,
closeOnSelect: false,
options: [
Expand Down Expand Up @@ -305,9 +284,8 @@ export default {
```vue
<template>
<div class="grid">
<div v-for="{ name, props } in selectArray"
<div v-for="{ props } in selectArray"
class="container">
<label :for="props.inputId">{{ name }}</label>
<NcSelect v-bind="props"
v-model="props.value" />
</div>
Expand All @@ -318,17 +296,10 @@ export default {
import AccountGroup from '@mdi/svg/svg/account-group.svg?raw'
import Email from '@mdi/svg/svg/email.svg?raw'
import GenRandomId from '../../utils/GenRandomId.js'
const getRandomId = () => {
return `select-${GenRandomId()}`
}
const selectArray = [
{
name: 'User select',
props: {
inputId: getRandomId(),
inputLabel: 'User select',
userSelect: true,
options: [
{
Expand Down Expand Up @@ -394,9 +365,8 @@ const selectArray = [
},
{
name: 'Multiple user select (stay open on select)',
props: {
inputId: getRandomId(),
inputLabel: 'Multiple user select (stay open on select)',
userSelect: true,
multiple: true,
closeOnSelect: false,
Expand Down Expand Up @@ -491,6 +461,12 @@ export default {
v-bind="propsToForward"
v-on="$listeners"
@search="searchString => search = searchString">
<template v-if="!labelOutside && inputLabel" #header>
<label :for="inputId"
class="select__label">
{{ inputLabel }}
</label>
</template>
<template #search="{ attributes, events }">
<input :class="['vs__search', inputClass]"
v-bind="attributes"
Expand Down Expand Up @@ -538,6 +514,7 @@ export default {
<script>
import '@nextcloud/vue-select/dist/vue-select.css'
import Vue from 'vue'
import { VueSelect } from '@nextcloud/vue-select'
import {
autoUpdate,
Expand Down Expand Up @@ -572,6 +549,7 @@ export default {
props: {
// Add VueSelect props to $props
...VueSelect.props,
...VueSelect.mixins.reduce((allProps, mixin) => ({ ...allProps, ...mixin.props }), {}),
/**
* `aria-label` for the clear input button
Expand All @@ -583,10 +561,12 @@ export default {
/**
* `aria-label` for the search input
*
* A descriptive `inputLabel` is preferred as this is not visible.
*/
ariaLabelCombobox: {
type: String,
default: t('Search for options'),
default: null,
},
/**
Expand Down Expand Up @@ -719,14 +699,30 @@ export default {
/**
* Input element id
*
* @see https://vue-select.org/api/props.html#inputid
*/
inputId: {
type: String,
default: () => `select-input-${GenRandomId()}`,
},
/**
* Visible label for the input element
*
* @todo Set default for @nextcloud/vue 9
*/
inputLabel: {
type: String,
default: null,
},
/**
* Pass true if you are using an external label
*/
labelOutside: {
type: Boolean,
default: false,
},
/**
* Display a visible border around dropdown options
* which have keyboard focus
Expand Down Expand Up @@ -1005,27 +1001,34 @@ export default {
},
propsToForward() {
const {
// Props handled by this component
inputClass,
noWrap,
placement,
userSelect,
// Props to forward
...initialPropsToForward
} = this.$props
const vueSelectKeys = [
...Object.keys(VueSelect.props),
...VueSelect.mixins.flatMap(mixin => Object.keys(mixin.props ?? {})),
]
const initialPropsToForward = Object.fromEntries(
Object.entries(this.$props)
.filter(([key, _value]) => vueSelectKeys.includes(key)),
)
const propsToForward = {
...initialPropsToForward,
// Custom overrides of vue-select props
calculatePosition: this.localCalculatePosition,
filterBy: this.localFilterBy,
label: this.localLabel,
}
return propsToForward
},
},
mounted() {
if (!this.labelOutside && !this.inputLabel && !this.ariaLabelCombobox) {
Vue.util.warn('[NcSelect] An `inputLabel` or `ariaLabelCombobox` should be set.')
}
if (this.inputLabel && this.ariaLabelCombobox) {
Vue.util.warn('[NcSelect] Only one of `inputLabel` or `ariaLabelCombobox` should to be set.')
}
},
methods: {
t,
},
Expand Down Expand Up @@ -1106,6 +1109,11 @@ body {
min-width: 260px;
margin: 0;
.select__label {
display: block;
margin-bottom: 2px;
}
.vs__selected {
height: 32px;
padding: 0 8px 0 12px;
Expand Down Expand Up @@ -1233,5 +1241,4 @@ body {
.user-select .vs__selected {
padding: 0 2px !important;
}
</style>
8 changes: 4 additions & 4 deletions src/components/NcSelectTags/NcSelectTags.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
```vue
<template>
<div class="wrapper">
<NcSelectTags v-model="value" :multiple="false" />
<NcSelectTags v-model="value" input-label="Tag" :multiple="false" />
{{ value }}
</div>
</template>
Expand All @@ -47,7 +47,7 @@ export default {
```vue
<template>
<div class="wrapper">
<NcSelectTags v-model="value" :multiple="true" />
<NcSelectTags v-model="value" input-label="Tags" :multiple="true" />
{{ value }}
</div>
</template>
Expand All @@ -71,7 +71,7 @@ Because of compatibility reasons only 5 tag entries are shown by default. If you
```vue
<template>
<div class="wrapper">
<NcSelectTags v-model="value" :limit="null" />
<NcSelectTags v-model="value" input-label="Tags" :limit="null" />
{{ value }}
</div>
</template>
Expand All @@ -94,7 +94,7 @@ It's also possible to apply any custom filter logic by setting the `optionsFilte
```vue
<template>
<div class="wrapper">
<NcSelectTags v-model="value" :options-filter="customFilter" />
<NcSelectTags v-model="value" input-label="Tags" :options-filter="customFilter" />
{{ value }}
</div>
</template>
Expand Down

0 comments on commit 8bf8598

Please sign in to comment.