Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Create an InputField component
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruvkb committed Oct 28, 2021
1 parent 6470632 commit 13029a4
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 0 deletions.
82 changes: 82 additions & 0 deletions src/components/InputField/InputField.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<div
class="input-field group flex flex-row items-center gap-4 pe-4 hover:bg-dark-charcoal-06 h-12 border border-dark-charcoal-20 rounded-sm overflow-hidden focus-within:ring focus-within:ring-pink focus-within:border-white"
:class="[
{
'border-s-0 rounded-s-none': connectionSides.includes('start'),
'border-e-0 rounded-e-none': connectionSides.includes('end'),
},
]"
>
<!-- eslint-disable vuejs-accessibility/form-control-has-label -->
<!-- The `inputId` prop is provided so that the user of the component can associate a label -->
<input
v-bind="$attrs"
:id="inputId"
v-model="text"
type="text"
class="flex-grow leading-none font-semibold bg-tx ps-4 h-full focus:outline-none"
/>
<!-- eslint-enable vuejs-accessibility/form-control-has-label -->
<div
class="info font-semibold text-xs text-dark-charcoal-70 group-hover:text-dark-charcoal"
>
<!-- @slot Extra information goes here -->
<slot />
</div>
</div>
</template>

<script>
import { computed } from '@nuxtjs/composition-api'
/**
* Provides a control to enter text as input.
*/
export default {
name: 'InputField',
inheritAttrs: false,
model: {
prop: 'value',
event: 'input',
},
props: {
/**
* the textual content of the input field
*/
value: {
type: String,
default: '',
},
/**
* the ID to associate with the internal `<input>` element; This should be
* used to associate a label and is recommended for a11y.
*/
inputId: {
type: String,
},
/**
* list of sides where the field is connected to other controls
*/
connectionSides: {
type: Array,
default: () => [],
validator: (val) => val.every((item) => ['start', 'end'].includes(item)),
},
},
setup(props, { emit }) {
const text = computed({
get() {
return props.value
},
set(value) {
emit('input', value)
},
})
return {
text,
}
},
}
</script>
120 changes: 120 additions & 0 deletions src/components/InputField/meta/InputField.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {
ArgsTable,
Canvas,
Description,
Meta,
Story,
} from '@storybook/addon-docs'

import InputField from '~/components/InputField/InputField.vue'

<Meta
title="Components/Input field"
component={InputField}
argTypes={{
input: {
action: 'input',
},
}}
/>

export const Template = (args, { argTypes }) => ({
template: `
<InputField v-bind="$props" v-on="$props">
Extra info
</InputField>
`,
components: { InputField },
props: Object.keys(argTypes),
})

# Input field

<Description of={InputField} />

<ArgsTable of={InputField} />

The component emits an `input` event with the new contents of the field whenever
the field receives an input.

<Canvas>
<Story
name="Default"
args={{
value: 'Text goes here',
}}
>
{Template.bind({})}
</Story>
</Canvas>

The recommended way to use it is with `v-model` mapping to a `String`.

export const vModelTemplate = () => ({
template: `
<div>
<InputField v-model="text">
{{ text.length }}
</InputField>
{{ text }}
</div>
`,
components: { InputField },
data() {
return {
text: 'Hello, World!',
}
},
})

<Canvas>
<Story name="v-model">{vModelTemplate.bind({})}</Story>
</Canvas>

The component is a transparent wrapper over `<input>` so all attributes of the
input element can be applied to it, e.g. `placeholder`. It is recommended not to
change the type as the field is specifically designed for text.

<Canvas>
<Story
name="With placeholder"
args={{
placeholder: 'Enter something here',
}}
>
{Template.bind({})}
</Story>
</Canvas>

For a11y, it is recommended that the input field be associated with a label. The
approach for this is to set the `inputId` prop and use the ID as the `for`
attribute of a `<label>` element.

export const labelTemplate = () => ({
template: `
<div>
<label for="field">Label:</label>
<InputField input-id="field"/>
</div>
`,
components: { InputField },
})

<Canvas>
<Story name="With label">{labelTemplate.bind({})}</Story>
</Canvas>

The field can be conjoined with other controls on either the start or the end
side and as such it provides the `connectionSides` prop to make the connections
seamless. The field has no border or rounded corners on the connected sides.

<Canvas>
<Story
name="With connections"
args={{
connectionSides: ['start', 'end'],
}}
>
{Template.bind({})}
</Story>
</Canvas>

0 comments on commit 13029a4

Please sign in to comment.