-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SELF-302: Add
KeyValueInput
Component (#514)
* feat: add key value input component * feat: add key value input props
- Loading branch information
1 parent
0e52e93
commit 48feccd
Showing
10 changed files
with
209 additions
and
3 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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { ArgTypes, Canvas, PRIMARY_STORY } from '@storybook/addon-docs'; | ||
import { Primary } from './KeyValueInput.stories'; | ||
|
||
# KeyValueInput | ||
|
||
<Canvas of={Primary} /> | ||
|
||
## Props | ||
|
||
<ArgTypes story={PRIMARY_STORY} /> |
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 |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Meta, StoryObj } from '@storybook/vue3'; | ||
import mdx from './KeyValueInput.mdx'; | ||
// @ts-ignore No types from Vue file | ||
import KeyValueInput from './KeyValueInput.vue'; | ||
|
||
const meta: Meta<typeof KeyValueInput> = { | ||
title: 'Components/KeyValueInput', | ||
component: KeyValueInput, | ||
parameters: { | ||
docs: { | ||
page: mdx | ||
} | ||
} | ||
}; | ||
|
||
export default meta; | ||
|
||
const keyValueModel: [string, string][] = []; | ||
|
||
export const Primary: StoryObj<typeof KeyValueInput> = { | ||
args: { | ||
label: 'Key Value Input' | ||
}, | ||
render: (args) => ({ | ||
components: { KeyValueInput }, | ||
data: () => ({ keyValueModel }), | ||
setup: () => ({ args }), | ||
template: `<KeyValueInput v-bind="args" v-model="keyValueModel" style="width: 30rem;" />` | ||
}) | ||
}; |
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 |
---|---|---|
@@ -0,0 +1,135 @@ | ||
<template> | ||
<div data-testid="uic-key-value-input"> | ||
<fieldset class="w-full"> | ||
<legend class="uic-label">{{ label }}</legend> | ||
<ul class="flex flex-col gap-4 w-full"> | ||
<li | ||
v-for="([key, value], index) in modelValue" | ||
:key="`key-value-${index}`" | ||
class="grid gap-6 items-start" | ||
style="grid-template-columns: 1fr 1fr min-content" | ||
> | ||
<TextInput | ||
:id="`key-${index}`" | ||
:data-testid="`uic-key-value-input-key-${index}`" | ||
:disabled | ||
:error="Boolean(keyErrors?.[index])" | ||
:helper-text="keyErrors?.[index]" | ||
:label="keyLabel" | ||
:model-value="key" | ||
sr-only-label | ||
@update:modelValue="handleKeyChange(index, $event)" | ||
@blur="$emit('keyBlur', index)" | ||
@focus="$emit('keyFocus', index)" | ||
@input="$emit('keyInput', index)" | ||
/> | ||
<TextInput | ||
:id="`value-${index}`" | ||
:data-testid="`uic-key-value-input-value-${index}`" | ||
:disabled | ||
:error="Boolean(valueErrors?.[index])" | ||
:helper-text="valueErrors?.[index]" | ||
:label="valueLabel" | ||
:model-value="value" | ||
sr-only-label | ||
@update:modelValue="handleValueChange(index, $event)" | ||
@blur="$emit('valueBlur', index)" | ||
@focus="$emit('valueFocus', index)" | ||
@input="$emit('valueInput', index)" | ||
/> | ||
<IconButton | ||
:data-testid="`uic-key-value-input-delete-${index}`" | ||
:disabled | ||
icon="Delete" | ||
type="button" | ||
color="error" | ||
variant="outlined" | ||
@click="handleDelete(index)" | ||
/> | ||
</li> | ||
</ul> | ||
</fieldset> | ||
<Button | ||
class="mt-2" | ||
data-testid="uic-key-value-add-button" | ||
size="medium" | ||
type="button" | ||
variant="ghost" | ||
@click="handleAddField" | ||
> | ||
<Icon icon="Plus" class="mr-1" size="sm" /> | ||
{{ addLabel }} | ||
</Button> | ||
</div> | ||
</template> | ||
<script setup lang="ts"> | ||
import Button from '@/components/Button/Button.vue'; | ||
import { Icon } from '@/components/Icon'; | ||
import { IconButton } from '@/components/IconButton'; | ||
import TextInput from '@/components/TextInput/TextInput.vue'; | ||
const props = withDefaults( | ||
defineProps<{ | ||
addLabel?: string; | ||
disabled?: boolean; | ||
/** Errors correspond with the field's index. */ | ||
keyErrors?: Record<number, string>; | ||
keyLabel?: string; | ||
label: string; | ||
/** | ||
* The `modelValue` is typed this way to allow invalid duplicated keys to be | ||
* shown with errors. To convert this to an object use | ||
* `Object.fromEntries(...)` | ||
*/ | ||
modelValue: [string, string][]; | ||
/** Errors correspond with the field's index. */ | ||
valueErrors?: Record<number, string>; | ||
valueLabel?: string; | ||
}>(), | ||
{ | ||
addLabel: 'Add field', | ||
disabled: false, | ||
keyErrors: undefined, | ||
keyLabel: 'Key', | ||
valueErrors: undefined, | ||
valueLabel: 'Value' | ||
} | ||
); | ||
const emit = defineEmits<{ | ||
(e: 'update:modelValue', value: [string, string][]): void; // eslint-disable-line no-unused-vars | ||
(e: 'keyFocus', index: number): void; // eslint-disable-line no-unused-vars | ||
(e: 'valueFocus', index: number): void; // eslint-disable-line no-unused-vars | ||
(e: 'keyBlur', index: number): void; // eslint-disable-line no-unused-vars | ||
(e: 'valueBlur', index: number): void; // eslint-disable-line no-unused-vars | ||
(e: 'keyInput', index: number): void; // eslint-disable-line no-unused-vars | ||
(e: 'valueInput', index: number): void; // eslint-disable-line no-unused-vars | ||
}>(); | ||
const handleKeyChange = (index: number, newKey: string) => { | ||
const newModelValue = [...props.modelValue]; | ||
newModelValue[index][0] = newKey; | ||
emit('update:modelValue', newModelValue); | ||
}; | ||
const handleValueChange = (index: number, newValue: string) => { | ||
const newModelValue = [...props.modelValue]; | ||
newModelValue[index][1] = newValue; | ||
emit('update:modelValue', newModelValue); | ||
}; | ||
const handleAddField = () => { | ||
emit('update:modelValue', [...props.modelValue, ['', '']]); | ||
}; | ||
const handleDelete = (index: number) => { | ||
const newModelValue = [...props.modelValue]; | ||
newModelValue.splice(index, 1); | ||
emit('update:modelValue', newModelValue); | ||
}; | ||
</script> |
19 changes: 19 additions & 0 deletions
19
src/components/KeyValueInput/__tests__/KeyValueInput.spec.ts
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 |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import '@testing-library/jest-dom'; | ||
import { render } from '@testing-library/vue'; | ||
import KeyValueInput from '../KeyValueInput.vue'; | ||
|
||
const DEFAULT_PROPS: InstanceType<typeof KeyValueInput>['$props'] = { | ||
label: 'Test label', | ||
modelValue: [['', '']] | ||
}; | ||
|
||
describe('KeyValueInput', () => { | ||
it('renders', () => { | ||
const { getByTestId } = render(KeyValueInput, { props: DEFAULT_PROPS }); | ||
expect(getByTestId('uic-key-value-input')).toBeVisible(); | ||
expect(getByTestId('uic-key-value-input-key-0')).toBeVisible(); | ||
expect(getByTestId('uic-key-value-input-value-0')).toBeVisible(); | ||
expect(getByTestId('uic-key-value-input-delete-0')).toBeVisible(); | ||
expect(getByTestId('uic-key-value-add-button')).toBeVisible(); | ||
}); | ||
}); |
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 |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as KeyValueInput } from './KeyValueInput.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