Skip to content

Commit

Permalink
fix(SfSelect): correct usage of v-model
Browse files Browse the repository at this point in the history
  • Loading branch information
Szymon-dziewonski committed Jul 25, 2023
1 parent 2b97ecf commit 9c937a7
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/four-elephants-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@storefront-ui/vue': patch
---

Correct usage of `v-model` in SfSelect
26 changes: 15 additions & 11 deletions apps/docs/components/components/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ If you need to make this field required, it is crucial to communicate this inten
<!-- react -->
<<<../../preview/next/pages/showcases/Select/SelectSizes.tsx#source
<!-- end react -->

</Showcase>

### Custom chevron
Expand All @@ -40,6 +41,7 @@ You can replace the default chevron with your own custom content by using the <!
<!-- react -->
<<<../../preview/next/pages/showcases/Select/SelectCustomChevron.tsx#source
<!-- end react -->

</Showcase>

### Invalid state
Expand All @@ -54,15 +56,15 @@ You can replace the default chevron with your own custom content by using the <!
<!-- react -->
<<<../../preview/next/pages/showcases/Select/SelectInvalid.tsx#source
<!-- end react -->

</Showcase>

## Notes

All non-prop attributes and styles added to `SfSelect` component are passed directly to native input element. This means that you can add all of the input attributes directly to `SfSelect`. If you want to style the wrapper `div`, you can pass your classes via the `wrapperClassName` prop.
All non-prop attributes and styles added to `SfSelect` component are passed directly to native input element. This means that you can add all of the input attributes directly to `SfSelect`. If you want to style the wrapper `div`, you can pass your classes via the `wrapperClassName` prop.

If you only have a few options, consider using the [`Radio`](radio.html) component instead of `Select`.


## Accessibility notes

Since this component uses the native `<select>` element, it inherits its accessibility features. For example, keyboard users can focus the select with `tab`, open with `space`, navigate the options using `arrows`, close the options menu with `Escape`, and select an option with `Enter` or `Space`.
Expand All @@ -77,15 +79,15 @@ Since this component uses the native `<select>` element, it inherits its accessi

## Props

| Prop name | Type | Default value | Possible values |
| ---------------- | -------- | ------------- | -------------------------------------- |
| `value` | `string` | | |
| `size` | `SfSelectSize` | `'base'` | `'sm'`, `'base'`,`'lg'` |
| `disabled` | `boolean` | `false` | |
| `invalid` | `boolean` | `false` | |
| `required` | `boolean` | `false` | |
| `placeholder` | `string` | | |
| `wrapperClassName` | `string` | | |
| Prop name | Type | Default value | Possible values |
| ------------------- | ----------------- | ------------- | -------------------------------------- |
| `modelValue` | `string` | | |
| `size` | `SfSelectSize` | `'base'` | `'sm'`, `'base'`,`'lg'` |
| `disabled` | `boolean` | `false` | |
| `invalid` | `boolean` | `false` | |
| `required` | `boolean` | `false` | |
| `placeholder` | `string` | | |
| `wrapperClassName` | `string` | | |
<!-- react -->
| `onChange` | `Function` | | |
| `slotChevron` | `ReactNode` | | |
Expand All @@ -112,12 +114,14 @@ Since this component uses the native `<select>` element, it inherits its accessi

::: slot source
<SourceCode>

<!-- vue -->
<<<../../../packages/sfui/frameworks/vue/components/SfSelect/SfSelect.vue
<!-- end vue -->

<!-- react -->
<<< ../../../packages/sfui/frameworks/react/components/SfSelect/SfSelect.tsx
<!-- end react -->

</SourceCode>
:::
2 changes: 1 addition & 1 deletion apps/preview/nuxt/pages/showcases/Filters/Category.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<li v-for="(category, index) in categories" :key="category.key">
<SfListItem
size="sm"
as="a"
tag="a"
:href="category.link"
:class="[
'first-of-type:mt-2 rounded-md active:bg-primary-100',
Expand Down
5 changes: 3 additions & 2 deletions apps/preview/nuxt/pages/showcases/Filters/Color.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
<input v-model="selectedValues" :value="value" class="appearance-none peer" type="checkbox" />
<span
class="inline-flex items-center justify-center p-1 transition duration-300 rounded-full cursor-pointer ring-1 ring-neutral-200 ring-inset outline-offset-2 outline-secondary-600 peer-checked:ring-2 peer-checked:ring-primary-700 peer-hover:bg-primary-100 peer-[&:not(:checked):hover]:ring-primary-200 peer-active:bg-primary-200 peer-active:ring-primary-300 peer-disabled:cursor-not-allowed peer-disabled:bg-disabled-100 peer-disabled:opacity-50 peer-disabled:ring-1 peer-disabled:ring-disabled-200 peer-disabled:hover:ring-disabled-200 peer-checked:hover:ring-primary-700 peer-checked:active:ring-primary-700 peer-focus:outline"
><SfThumbnail size="sm" :class="value"
/></span>
>
<SfThumbnail size="sm" :class="value" />
</span>
</template>
<p>
<span class="mr-2 typography-text-sm">{{ label }}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<template v-if="type === 'category'">
<ul class="mt-2 mb-6">
<li>
<SfListItem size="sm" as="button" type="button">
<SfListItem size="sm" tag="button" type="button">
<div class="flex items-center">
<SfIconArrowBack size="sm" class="text-neutral-500 mr-3" />
Back to {{ details[0].label }}
Expand All @@ -51,7 +51,7 @@
<li v-for="({ id, link, label, counter }, categoryIndex) in details" :key="id">
<SfListItem
size="sm"
as="a"
tag="a"
:href="link"
:class="[
'first-of-type:mt-2 rounded-md active:bg-primary-100',
Expand Down
3 changes: 2 additions & 1 deletion apps/preview/nuxt/pages/showcases/Filters/Sorting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ const options = ref([
{ label: 'Customer Rating', value: 'customer rating' },
{ label: 'Bestsellers', value: 'bestsellers' },
]);
const selected = ref('');
const selected = ref(options.value[0].value);
</script>
2 changes: 1 addition & 1 deletion apps/preview/nuxt/pages/showcases/Select/SelectInvalid.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<label>
<span class="pb-1 text-sm font-medium text-neutral-900 font-body"> Label </span>
<SfSelect :invalid="true" placeholder="-- Select --">
<SfSelect invalid placeholder="-- Select --">
<option v-for="{ value, label } in options" :key="value" :value="value">
{{ label }}
</option>
Expand Down
30 changes: 13 additions & 17 deletions packages/sfui/frameworks/vue/components/SfSelect/SfSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default {
};
</script>
<script lang="ts" setup>
import { ref, type PropType } from 'vue';
import { ref, type PropType, computed } from 'vue';
import { SfSelectSize, SfIconExpandMore, useFocusVisible } from '@storefront-ui/vue';
const props = defineProps({
Expand All @@ -28,7 +28,7 @@ const props = defineProps({
type: Boolean,
default: false,
},
value: {
modelValue: {
type: String,
default: '',
},
Expand All @@ -41,19 +41,16 @@ const emit = defineEmits<{
(event: 'update:modelValue', param: string): void;
}>();
const selected = ref(props.value);
const chevronRotated = ref(false);
const { isFocusVisible } = useFocusVisible();
const clickHandler = () => (chevronRotated.value = true);
const blurHandler = () => (chevronRotated.value = false);
const keydownHandler = () => (chevronRotated.value = true);
const onOpen = () => (chevronRotated.value = true);
const onClose = () => (chevronRotated.value = false);
const changedValue = (event: Event) => {
selected.value = (event.target as HTMLSelectElement).value;
chevronRotated.value = false;
emit('update:modelValue', (event.target as HTMLSelectElement).value);
};
const modelProxy = computed({
get: () => props.modelValue,
set: (value: string) => emit('update:modelValue', value),
});
</script>

<template>
Expand All @@ -69,8 +66,7 @@ const changedValue = (event: Event) => {
>
<select
v-bind="$attrs"
:value="value || selected"
:required="required"
v-model="modelProxy"
:disabled="disabled"
:class="[
'appearance-none disabled:cursor-not-allowed cursor-pointer pl-4 pr-3.5 text-neutral-900 ring-inset focus:ring-primary-700 focus:ring-2 outline-none bg-transparent rounded-md ring-1 ring-neutral-300 hover:ring-primary-700 active:ring-2 active:ring-primary-700 disabled:bg-disabled-100 disabled:text-disabled-900 disabled:ring-disabled-200',
Expand All @@ -82,10 +78,10 @@ const changedValue = (event: Event) => {
},
]"
data-testid="select-input"
@blur="blurHandler"
@click="clickHandler"
@change="changedValue"
@keydown.space="keydownHandler"
@blur="onClose"
@change="onClose"
@click="onOpen"
@keydown.space="onOpen"
>
<option
v-if="placeholder"
Expand Down

0 comments on commit 9c937a7

Please sign in to comment.