Skip to content

Commit

Permalink
feat(select): add type generics | add unit tests (#963)
Browse files Browse the repository at this point in the history
* feat(select): add type generics | update examples

* test(select): add select tests
  • Loading branch information
mlmoravek authored Jun 17, 2024
1 parent c0e429d commit a5bdedc
Show file tree
Hide file tree
Showing 13 changed files with 379 additions and 128 deletions.
48 changes: 24 additions & 24 deletions packages/docs/components/Select.md

Large diffs are not rendered by default.

8 changes: 3 additions & 5 deletions packages/oruga/src/components/input/Input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,7 @@ const {
// inject parent field component if used inside one
const { parentField, statusVariant, statusVariantIcon } = injectField();
const vmodel = defineModel<ModelValue>({
default: undefined,
});
const vmodel = defineModel<ModelValue>({ default: undefined });
// if id is given set as `for` property on o-field wrapper
if (props.id) parentField?.value?.setInputId(props.id);
Expand All @@ -311,9 +309,9 @@ onMounted(() => {
(value) => {
if (parentField?.value) parentField.value.setFilled(!!value);
if (props.autosize) resize();
if (!isValid.value) nextTick(() => checkHtml5Validity());
if (!isValid.value) checkHtml5Validity();
},
{ immediate: true },
{ immediate: true, flush: "post" },
);
});
Expand Down
48 changes: 23 additions & 25 deletions packages/oruga/src/components/select/Select.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts" generic="T">
import { computed, watch, onMounted, ref, nextTick, type PropType } from "vue";
import OIcon from "../icon/Icon.vue";
Expand Down Expand Up @@ -29,12 +29,12 @@ const props = defineProps({
override: { type: Boolean, default: undefined },
/** @model */
modelValue: {
type: [String, Number, Boolean, Object, Array],
default: null,
type: [String, Number, Boolean, Object, Array] as PropType<T | T[]>,
default: undefined,
},
/** Select options, unnecessary when default slot is used */
options: {
type: Array as PropType<string[] | OptionsItem[]>,
type: Array as PropType<string[] | OptionsItem<T>[]>,
default: undefined,
},
/**
Expand All @@ -55,9 +55,9 @@ const props = defineProps({
},
/** Text when nothing is selected */
placeholder: { type: String, default: undefined },
/** Allow multiple selection */
/** Allow multiple selection - same as native multiple */
multiple: { type: Boolean, default: false },
/** Same as native disabled */
/** Disable the input - same as native disabled */
disabled: { type: Boolean, default: false },
/** Makes input full width when inside a grouped or addon field */
expanded: { type: Boolean, default: false },
Expand Down Expand Up @@ -191,10 +191,7 @@ const emits = defineEmits<{
* modelValue prop two-way binding
* @param value {string | number | boolean | object | Array<any>} updated modelValue prop
*/
(
e: "update:modelValue",
value: string | number | boolean | object | Array<any>,
): void;
(e: "update:modelValue", value: T | T[]): void;
/**
* on input focus event
* @param event {Event} native event
Expand Down Expand Up @@ -225,36 +222,33 @@ const emits = defineEmits<{
const selectRef = ref<HTMLInputElement>();
// use form input functionality
const { checkHtml5Validity, onBlur, onFocus, onInvalid, setFocus } =
const { checkHtml5Validity, onBlur, onFocus, onInvalid, setFocus, isValid } =
useInputHandler(selectRef, emits, props);
// inject parent field component if used inside one
const { parentField, statusVariant, statusVariantIcon } = injectField();
const vmodel = defineModel<string | number | boolean | object | Array<unknown>>(
{ default: undefined },
);
const vmodel = defineModel<T | T[]>({ default: undefined });
const placeholderVisible = computed(() => vmodel.value === null);
const placeholderVisible = computed(() => !props.multiple && !vmodel.value);
onMounted(() => {
/**
* When v-model is changed:
* 1. Set parent field filled state.
* 2. Resize textarea input
* 3. Check html5 valdiation
* 2. Check html5 valdiation
*/
watch(
() => vmodel.value,
vmodel,
(value) => {
if (parentField?.value) parentField.value.setFilled(!!value);
checkHtml5Validity();
if (!isValid.value) checkHtml5Validity();
},
{ immediate: true },
{ immediate: true, flush: "post" },
);
});
const selectOptions = computed<OptionsItem[]>(() => {
const selectOptions = computed<OptionsItem<T>[]>(() => {
if (!props.options || !Array.isArray(props.options)) return [];
return props.options.map((option) =>
Expand Down Expand Up @@ -282,12 +276,16 @@ const rightIconVariant = computed(() =>
: statusVariant.value,
);
function iconClick(emit, event): void {
function iconClick(emit, event: Event): void {
emits(emit, event);
nextTick(() => setFocus());
}
function rightIconClick(event): void {
function leftIconClick(event: Event): void {
if (props.iconClickable) iconClick("icon-click", event);
}
function rightIconClick(event: Event): void {
if (props.iconRightClickable) iconClick("icon-right-click", event);
}
Expand Down Expand Up @@ -343,7 +341,7 @@ const selectClasses = defineClasses(
"arrowClass",
"o-sel-arrow",
null,
computed(() => !props.iconRight && !props.multiple),
computed(() => !hasIconRight.value && !props.multiple),
],
);
Expand All @@ -366,7 +364,7 @@ defineExpose({ focus: setFocus, value: vmodel.value });
:icon="icon"
:pack="iconPack"
:size="size"
@click="iconClick('icon-click', $event)" />
@click="leftIconClick($event)" />

<select
v-bind="$attrs"
Expand Down
97 changes: 26 additions & 71 deletions packages/oruga/src/components/select/examples/base.vue
Original file line number Diff line number Diff line change
@@ -1,68 +1,37 @@
<script setup lang="ts">
import { ref } from "vue";
const selectedOptions = ref([]);
// @ts-expect-error Examples are loaded differently.
import type { OptionsItem } from "../../../../dist/oruga";
const options: OptionsItem[] = [
{ label: "Flint", value: "flint" },
{ label: "Silver", value: "silver" },
{ label: "Vane", value: "vane" },
{ label: "Billy", value: "billy" },
{ label: "Jack", value: "silver", attrs: { disabled: true } },
];
</script>

<template>
<section>
<o-field label="Simple">
<o-select placeholder="Select a name">
<o-select placeholder="Select a name" required>
<option value="flint">Flint</option>
<option value="silver">Silver</option>
</o-select>
</o-field>

<o-field label="Grouped">
<o-select placeholder="Select a character" icon="user">
<optgroup label="Black Sails">
<option value="flint">Flint</option>
<option value="silver">Silver</option>
<option value="vane">Vane</option>
<option value="billy">Billy</option>
<option value="jack">Jack</option>
</optgroup>

<optgroup label="Breaking Bad">
<option value="heisenberg">Heisenberg</option>
<option value="jesse">Jesse</option>
<option value="saul">Saul</option>
<option value="mike">Mike</option>
</optgroup>

<optgroup label="Game of Thrones">
<option value="tyrion-lannister">Tyrion Lannister</option>
<option value="jamie-lannister">Jamie Lannister</option>
<option value="daenerys-targaryen">
Daenerys Targaryen
</option>
<option value="jon-snow">Jon Snow</option>
</optgroup>
</o-select>
</o-field>

<o-field
label="Error"
variant="danger"
message="Something went wrong with this field">
<o-select placeholder="Select a character">
<option value="flint">Flint</option>
<option value="silver">Silver</option>
</o-select>
</o-field>

<o-field label="Rounded">
<o-select placeholder="Select a character" rounded>
<option value="flint">Flint</option>
<option value="silver">Silver</option>
</o-select>
<o-select placeholder="Select a character" :options="options" />
</o-field>

<o-field label="Disabled">
<o-select placeholder="Select a character" disabled>
<option value="flint">Flint</option>
<option value="silver">Silver</option>
</o-select>
<o-select
placeholder="Select a character"
disabled
:options="options" />
</o-field>

<o-field label="Disabled option">
Expand All @@ -72,32 +41,18 @@ const selectedOptions = ref([]);
</o-select>
</o-field>

<o-field label="Expanded">
<o-select placeholder="Select a character" expanded>
<option value="flint">Flint</option>
<option value="silver">Silver</option>
</o-select>
</o-field>

<o-field label="Size 'large'">
<o-select placeholder="Large" size="large">
<option value="flint">Flint</option>
<option value="silver">Silver</option>
</o-select>
<o-field label="Rounded">
<o-select
placeholder="Select a character"
rounded
:options="options" />
</o-field>

<o-field label="Multiple">
<o-select v-model="selectedOptions" multiple native-size="8">
<option value="flint">Flint</option>
<option value="silver">Silver</option>
<option value="vane">Vane</option>
<option value="billy">Billy</option>
<option value="jack">Jack</option>
<option value="heisenberg">Heisenberg</option>
<option value="jesse">Jesse</option>
<option value="saul">Saul</option>
<option value="mike">Mike</option>
</o-select>
<o-field label="Expanded">
<o-select
placeholder="Select a character"
expanded
:options="options" />
</o-field>
</section>
</template>
42 changes: 42 additions & 0 deletions packages/oruga/src/components/select/examples/grouped.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup lang="ts">
import { ref } from "vue";
const selected = ref([]);
</script>

<template>
<section>
<o-field>
<o-select
v-model="selected"
placeholder="Select a character"
icon="user">
<optgroup label="Black Sails">
<option value="flint">Flint</option>
<option value="silver">Silver</option>
<option value="vane">Vane</option>
<option value="billy">Billy</option>
<option value="jack">Jack</option>
</optgroup>

<optgroup label="Breaking Bad">
<option value="heisenberg">Heisenberg</option>
<option value="jesse">Jesse</option>
<option value="saul">Saul</option>
<option value="mike">Mike</option>
</optgroup>

<optgroup label="Game of Thrones">
<option value="tyrion-lannister">Tyrion Lannister</option>
<option value="jamie-lannister">Jamie Lannister</option>
<option value="daenerys-targaryen">
Daenerys Targaryen
</option>
<option value="jon-snow">Jon Snow</option>
</optgroup>
</o-select>
</o-field>

<p><b>Selected:</b> {{ selected }}</p>
</section>
</template>
18 changes: 18 additions & 0 deletions packages/oruga/src/components/select/examples/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
import Base from "./base.vue";
import BaseCode from "./base.vue?raw";
import Grouped from "./grouped.vue";
import GroupedCode from "./grouped.vue?raw";
import Multiple from "./multiple.vue";
import MultipleCode from "./multiple.vue?raw";
import Sizes from "./sizes.vue";
import SizesCode from "./sizes.vue?raw";
import Variants from "./variants.vue";
import VariantsCode from "./variants.vue?raw";
</script>
Expand All @@ -10,6 +19,15 @@ import VariantsCode from "./variants.vue?raw";
<h3 id="base">Base</h3>
<ExampleViewer :component="Base" :code="BaseCode" />

<h3 id="variants">Grouped</h3>
<ExampleViewer :component="Grouped" :code="GroupedCode" />

<h3 id="variants">Multiple</h3>
<ExampleViewer :component="Multiple" :code="MultipleCode" />

<h3 id="variants">Sizes</h3>
<ExampleViewer :component="Sizes" :code="SizesCode" />

<h3 id="variants">Variants</h3>
<ExampleViewer :component="Variants" :code="VariantsCode" />
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { ref } from "vue";
const selectedOptions = ref(null);
const inspectData = [
{
class: "rootClass",
Expand Down
25 changes: 25 additions & 0 deletions packages/oruga/src/components/select/examples/multiple.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup lang="ts">
import { ref } from "vue";
const selected = ref([]);
</script>

<template>
<section>
<o-field label="Multiple">
<o-select v-model="selected" multiple native-size="8">
<option value="flint">Flint</option>
<option value="silver">Silver</option>
<option value="vane">Vane</option>
<option value="billy">Billy</option>
<option value="jack">Jack</option>
<option value="heisenberg">Heisenberg</option>
<option value="jesse">Jesse</option>
<option value="saul">Saul</option>
<option value="mike">Mike</option>
</o-select>
</o-field>

<p><b>Selected:</b> {{ selected }}</p>
</section>
</template>
Loading

0 comments on commit a5bdedc

Please sign in to comment.