-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(vfg): convert field-select + more tests [khcp-11336] (#1584)
Convert VFG `FieldSelect` to use Kongponents + add more VFG component test coverage. Addresses [KHCP-11336](https://konghq.atlassian.net/browse/KHCP-11336).
- Loading branch information
1 parent
8c1e99f
commit c57df8e
Showing
11 changed files
with
511 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
248 changes: 129 additions & 119 deletions
248
packages/core/forms/src/components/fields/FieldSelect.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,138 +1,148 @@ | ||
<template lang="pug"> | ||
select.form-control(v-model="value", :disabled="disabled || null", :name="schema.inputName", :id="getFieldID(schema)", :class="schema.fieldClasses", v-attributes="'input'") | ||
option(v-if="!selectOptions.hideNoneSelectedText", :disabled="schema.required || null", :value="null") {{ selectOptions.noneSelectedText || "<Nothing selected>" }} | ||
|
||
template(v-for="item in items") | ||
optgroup(v-if="item.group", :label="getGroupName(item)") | ||
option(v-if="item.ops", v-for="i in item.ops", :value="getItemValue(i)") {{ getItemName(i) }} | ||
|
||
option(v-if="!item.group", :value="getItemValue(item)") {{ getItemName(item) }} | ||
<template> | ||
<KSelect | ||
:id="getFieldID(schema)" | ||
v-model="inputValue" | ||
:class="schema.fieldClasses" | ||
:clearable="!schema.required" | ||
:disabled="disabled || undefined" | ||
:items="items" | ||
:kpop-attributes="{ 'data-testid': `${getFieldID(schema)}-items` }" | ||
:label-attributes="{ info: schema.help }" | ||
:name="schema.inputName" | ||
:placeholder="!selectOptions.hideNoneSelectedText ? selectOptions.noneSelectedText || 'Nothing Selected' : undefined" | ||
:required="schema.required || undefined" | ||
width="100%" | ||
/> | ||
</template> | ||
|
||
<script> | ||
<script lang="ts" setup> | ||
import { computed, toRefs, type PropType } from 'vue' | ||
import type { SelectItem } from '@kong/kongponents' | ||
import isObject from 'lodash-es/isObject' | ||
import isNil from 'lodash-es/isNil' | ||
import find from 'lodash-es/find' | ||
import abstractField from './abstractField' | ||
export default { | ||
mixins: [abstractField], | ||
computed: { | ||
selectOptions() { | ||
return this.schema.selectOptions || {} | ||
}, | ||
import composables from '../../composables' | ||
items() { | ||
const values = this.schema.values | ||
if (typeof values === 'function') { | ||
return this.groupValues(values.apply(this, [this.model, this.schema])) | ||
} else return this.groupValues(values) | ||
}, | ||
const props = defineProps({ | ||
disabled: { | ||
type: Boolean, | ||
default: false, | ||
}, | ||
formOptions: { | ||
type: Object as PropType<Record<string, any>>, | ||
default: () => undefined, | ||
}, | ||
model: { | ||
type: Object as PropType<Record<string, any>>, | ||
default: () => undefined, | ||
}, | ||
schema: { | ||
type: Object as PropType<Record<string, any>>, | ||
required: true, | ||
}, | ||
vfg: { | ||
type: Object, | ||
required: true, | ||
}, | ||
/** | ||
* TODO: stronger type | ||
* TODO: pass this down to KInput error and errorMessage | ||
*/ | ||
errors: { | ||
type: Array, | ||
default: () => [], | ||
}, | ||
hint: { | ||
type: String, | ||
default: '', | ||
}, | ||
}) | ||
methods: { | ||
formatValueToField(value) { | ||
if (isNil(value)) { | ||
return null | ||
} | ||
return value | ||
}, | ||
groupValues(values) { | ||
const array = [] | ||
let arrayElement = {} | ||
values.forEach(item => { | ||
arrayElement = null | ||
if (item.group && isObject(item)) { | ||
// There is in a group. | ||
// Find element with this group. | ||
arrayElement = find(array, i => i.group === item.group) | ||
if (arrayElement) { | ||
// There is such a group. | ||
arrayElement.ops.push({ | ||
id: item.id, | ||
name: item.name, | ||
}) | ||
} else { | ||
// There is not such a group. | ||
// Initialising. | ||
arrayElement = { | ||
group: '', | ||
ops: [], | ||
} | ||
// Set group. | ||
arrayElement.group = item.group | ||
// Set Group element. | ||
arrayElement.ops.push({ | ||
id: item.id, | ||
name: item.name, | ||
}) | ||
const emit = defineEmits<{ | ||
(event: 'modelUpdated', value: any, model: Record<string, any>): void | ||
}>() | ||
// Add array. | ||
array.push(arrayElement) | ||
} | ||
} else { | ||
// There is not in a group. | ||
array.push(item) | ||
} | ||
}) | ||
const selectOptions = computed((): Record<string, any> => props.schema.selectOptions || {}) | ||
// With Groups. | ||
return array | ||
}, | ||
const formatValueToField = (value: Record<string, any>) => { | ||
if (isNil(value)) { | ||
return null | ||
} | ||
return value | ||
} | ||
getGroupName(item) { | ||
if (item && item.group) { | ||
return item.group | ||
} | ||
const propsRefs = toRefs(props) | ||
throw new Error('Group name is missing! https://icebob.gitbooks.io/vueformgenerator/content/fields/select.html#select-field-with-object-items') | ||
}, | ||
const { getFieldID, clearValidationErrors, value: inputValue } = composables.useAbstractFields({ | ||
model: propsRefs.model, | ||
schema: props.schema, | ||
formOptions: props.formOptions, | ||
formatValueToField, | ||
emitModelUpdated: (data: { value: any, model: Record<string, any> }): void => { | ||
emit('modelUpdated', data.value, data.model) | ||
}, | ||
}) | ||
defineExpose({ | ||
clearValidationErrors, | ||
}) | ||
const items = computed((): SelectItem[] => { | ||
// values to be used in the select items | ||
const selectOptions = props.schema.values | ||
if (typeof selectOptions === 'function') { | ||
return getItemsFromValues(selectOptions.apply(this, [props.model, props.schema])) | ||
} else { | ||
return getItemsFromValues(selectOptions) | ||
} | ||
}) | ||
const getItemsFromValues = (values: Record<string, any>[] | string[] | number[]): SelectItem[] => { | ||
const itemArray: SelectItem[] = [] | ||
values.forEach(item => { | ||
itemArray.push({ | ||
label: getItemName(item), | ||
value: getItemValue(item), | ||
disabled: typeof item === 'object' ? item.disabled || undefined : undefined, | ||
group: typeof item === 'object' ? String(item.group || '').toUpperCase() || undefined : undefined, | ||
}) | ||
}) | ||
// With Groups. | ||
return itemArray | ||
} | ||
getItemValue(item) { | ||
if (isObject(item)) { | ||
if (typeof this.schema.selectOptions !== 'undefined' && typeof this.schema.selectOptions.value !== 'undefined') { | ||
return item[this.schema.selectOptions.value] | ||
} else { | ||
// Use 'id' instead of 'value' cause of backward compatibility | ||
if (typeof item.id !== 'undefined') { | ||
return item.id | ||
} else { | ||
throw new Error('`id` is not defined. If you want to use another key name, add a `value` property under `selectOptions` in the schema. https://icebob.gitbooks.io/vueformgenerator/content/fields/select.html#select-field-with-object-items') | ||
} | ||
} | ||
const getItemValue = (item: string | number | Record<string, any>): string | number => { | ||
if (isObject(item)) { | ||
if (selectOptions.value && typeof selectOptions.value.value !== 'undefined') { | ||
return item[selectOptions.value.value] | ||
} else { | ||
// Use 'id' instead of 'value' cause of backward compatibility | ||
if (typeof item.id !== 'undefined') { | ||
return String(item.id) | ||
} else { | ||
return item | ||
throw new Error('`id` is not defined. If you want to use another key name, add a `value` property under `selectOptions` in the schema. https://icebob.gitbooks.io/vueformgenerator/content/fields/select.html#select-field-with-object-items') | ||
} | ||
}, | ||
} | ||
} else { | ||
return item | ||
} | ||
} | ||
getItemName(item) { | ||
if (isObject(item)) { | ||
if (typeof this.schema.selectOptions !== 'undefined' && typeof this.schema.selectOptions.name !== 'undefined') { | ||
return item[this.schema.selectOptions.name] | ||
} else { | ||
if (typeof item.name !== 'undefined') { | ||
return item.name | ||
} else { | ||
throw new Error('`name` is not defined. If you want to use another key name, add a `name` property under `selectOptions` in the schema. https://icebob.gitbooks.io/vueformgenerator/content/fields/select.html#select-field-with-object-items') | ||
} | ||
} | ||
const getItemName = (item: string | number | Record<string, any>): string => { | ||
if (isObject(item)) { | ||
if (selectOptions.value && typeof selectOptions.value.name !== 'undefined') { | ||
return item[selectOptions.value.name] | ||
} else { | ||
if (typeof item.name !== 'undefined') { | ||
return String(item.name) | ||
} else { | ||
return item | ||
throw new Error('`name` is not defined. If you want to use another key name, add a `name` property under `selectOptions` in the schema. https://icebob.gitbooks.io/vueformgenerator/content/fields/select.html#select-field-with-object-items') | ||
} | ||
}, | ||
}, | ||
} | ||
} else { | ||
return String(item) | ||
} | ||
} | ||
</script> | ||
|
||
<style lang="sass"> | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.