Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs (#35): Improve JSDoc comments for components #36

Merged
merged 2 commits into from Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 57 additions & 0 deletions docs/SelectMulti.stories.ts
@@ -1,5 +1,6 @@
import SelectMulti from '../src/SelectMulti.vue'

import { SelectOption } from '../src/types'
import { longDefaultListOfOptions } from './storybookUtilities'

export default {
Expand Down Expand Up @@ -34,3 +35,59 @@ Primary.args = {
disabled: false,
displayPillsBelowInput: false,
};


const WithCustomOptionsTemplate = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { SelectMulti },
data() {
return { values: [{}] }
},
methods: {
getLabelForSearching(option: SelectOption): string {
return `label: ${option.label}, with value: ${option.value}`
}
},
template: `
<div class="wrapper">
<SelectMulti
v-model="values"
:options="options"
:label="label"
:labelIsVisible="labelIsVisible"
:optionLabelForSearching="getLabelForSearching"
:placeholder="placeholder"
:disabled="disabled"
:displayPillsBelowInput="displayPillsBelowInput"
>
<template v-slot:selectedOption="{ option }" >
<strong>{{ option.label }}</strong> <em>{{ option.value }}</em>
</template>
<template v-slot:option="{ option }" >
<strong>label: {{ option.label }}<strong>, <em>with value: {{ option.value }}</em>
</template>
</SelectMulti>
</div>`
});

export const WithCustomOptions = WithCustomOptionsTemplate.bind({});
WithCustomOptions.args = {
label: 'My SelectMulti with custom options',
options: longDefaultListOfOptions,
labelIsVisible: true,
placeholder: 'Select Options',
disabled: false,
displayPillsBelowInput: false,
};

WithCustomOptions.story = {
parameters: {
docs: {
storyDescription: `
Sometimes, we need to display each option in the select with custom formatting,
or in a way different from the standard display of option.label. When this is the case,
an option scoped slot is provided for custom template code, as well as a selectedOption slot
`
}
}
}
51 changes: 0 additions & 51 deletions docs/SelectMultiWithCustomOptions.stories.ts

This file was deleted.

51 changes: 51 additions & 0 deletions docs/SelectSingle.stories.ts
Expand Up @@ -30,3 +30,54 @@ Primary.args = {
labelIsVisible: true,
disabled: false
};


const WithCustomOptionsTemplate = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { SelectSingle },
data() {
return {
value: {
value: 'option-two',
label: 'Option Two'
}
}
},
template: `
<div class="wrapper">
<SelectSingle
v-model="value"
:options="options"
:label="label"
:labelIsVisible="labelIsVisible"
:disabled="disabled"
>
<template v-slot:selectedOption="{ option }" >
<strong>{{ option.label }}</strong> <em>{{ option.value }}</em>
</template>
<template v-slot:option="{ option }" >
<strong>label: {{ option.label }}</strong>, <em>with value: {{ option.value }}</em>
</template>
</SelectSingle>
</div>`
});

export const WithCustomOptions = WithCustomOptionsTemplate.bind({});
WithCustomOptions.args = {
label: 'My SelectSingle with custom options',
options: longDefaultListOfOptions,
labelIsVisible: true,
disabled: false
};

WithCustomOptions.story = {
parameters: {
docs: {
storyDescription: `
Sometimes, we need to display each option in the select with custom formatting,
or in a way different from the standard display of option.label. When this is the case,
an option scoped slot is provided for custom template code, as well as a selectedOption slot
`
}
}
}
47 changes: 0 additions & 47 deletions docs/SelectSingleWithCustomOptions.stories.ts

This file was deleted.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "@politico/vue-accessible-selects",
"version": "0.4.5",
"version": "0.5.0",
"description": "Select & Multi Select implementations for Vue, focused especially on implementing accessibility best practices",
"files": [
"dist",
Expand Down
34 changes: 21 additions & 13 deletions src/SelectMulti.vue
Expand Up @@ -31,6 +31,9 @@
}
}

/**
* Component to select multiple options from a dropdown, developed with accessibility & usability as the primary focus
*/
// `PURE` designation to enable tree-shaking
export default /*#__PURE__*/(Vue as VueConstructor<ISelectMulti>).extend({
name: 'SelectMulti',
Expand All @@ -48,7 +51,7 @@
required: true
},
options: {
type: Array as () => SelectOption[],
type: Array as PropType<SelectOption[]>,
required: true
},
labelIsVisible: {
Expand All @@ -67,7 +70,7 @@
* in order to accurately filter the available options
* @example
* ```
* :optionLabelForSearching="option => option.label + option.value"
* :optionLabelForSearching="a => a.label + '-' + a.value"
* ```
*/
optionLabelForSearching: {
Expand All @@ -81,7 +84,7 @@
},
/** Generally, there's no need to set this via a prop - it will be set automatically when using v-model */
values: {
type: Array as () => SelectOption[],
type: Array as PropType<SelectOption[]>,
required: false,
default: () => []
}
Expand All @@ -108,6 +111,7 @@
return this.values
},
set(values: SelectOption[]) {
// Used just for v-model, no need to subscribe to handle event
this.$emit('change', values)
}
}
Expand All @@ -129,6 +133,10 @@
if (this.inputValue !== curValue) {
this.inputValue = curValue
this.activeIndex = 0
/**
* emits the current user-provided search string,
* primarily useful for making autocomplete calls
*/
this.$emit('searchChange', this.inputValue)
}

Expand All @@ -137,7 +145,6 @@
this.updateMenuState(menuState, false)
}
},

onInputKeyDown(event: KeyboardEvent) {
const max = this.filteredOptions.length - 1

Expand All @@ -160,7 +167,6 @@
return this.updateMenuState(true)
}
},

onInputBlur() {
if (this.ignoreBlur) {
this.ignoreBlur = false
Expand All @@ -169,36 +175,37 @@

this.updateMenuState(false, false)
},

onOptionChange(index: number) {
this.activeIndex = index
},

onOptionClick(index: number) {
this.onOptionChange(index)
this.updateOption(index)
},

onOptionMouseDown(event: MouseEvent) {
this.ignoreBlur = true
this.callFocus = true
event.stopPropagation()
},

onMenuMouseDown(event: MouseEvent) {
event.preventDefault()
},

removeOption(index: number) {
/**
* emits the most recently removed value,
* *generally not necessary*, if state can be handled w/ v-model alone
*/
this.$emit('remove', this.selectedOptions[index])
this.selectedOptions = [...this.selectedOptions.filter((_, i) => i !== index)]
},

selectOption(option: SelectOption) {
/**
* emits the most recently selected value
* *generally not necessary*, if state can be handled w/ v-model alone
*/
this.$emit('select', option)
this.selectedOptions = [...this.selectedOptions, option]
},

updateOption(index: number) {
const option = this.filteredOptions[index]
const optionIndex = this.selectedOptions.indexOf(option)
Expand All @@ -213,7 +220,6 @@
this.activeIndex = this.filteredOptions.indexOf(option)
}
},

updateMenuState(open: boolean, callFocus = true) {
this.open = open
this.callFocus = callFocus
Expand All @@ -238,6 +244,7 @@
:aria-describedby="`${htmlId}-selected-option-pills`"
@click="removeOption(index)"
>
<!-- @slot Display the currently selected options via custom template code -->
<slot name="selectedOption" :option="option">
{{ option.label }}
</slot>
Expand Down Expand Up @@ -289,6 +296,7 @@
@click="onOptionClick(index)"
@mousedown="onOptionMouseDown"
>
<!-- @slot Display individual options via custom template code -->
<slot name="option" :option="option">
{{ option.label }}
</slot>
Expand Down