Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
<main>
Vue-Tailwind
</main>
</template>
</template>
38 changes: 0 additions & 38 deletions src/components/DInput.vue

This file was deleted.

89 changes: 89 additions & 0 deletions src/components/DxhTextInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<template>
<label :for="id" data-test="text-input-label">
<slot name="label" :label="label">
<span v-if="label">{{ label }}</span>
</slot>
<div class="relative">
<input
:id="id"
v-model="inputValue"
:type="type"
:placeholder="placeholder"
:hint="hint"
:disabled="disabled"
:readonly="readonly"
:autofocus="autofocus"
:required="required"
:clearable="clearable"
class="w-full border px-2 py-1 rounded"
:class="[
{ 'pr-8': clearable && !append },
{ 'pl-10': prepend },
{ 'pr-10': prepend },
{ 'pr-16': clearable && append }
]"
@focus="$emit('focus')"
@blur="$emit('blur')"
@change="$emit('change')"
@keyup.enter="$emit('enter')"
@input="$emit('update:modelValue', inputValue)"
data-test="text-input"
/>
<span v-if="prepend" class="absolute top-1/2 -translate-y-1/2 left-3">
<slot name="prepend"> </slot>
</span>
<span v-if="append" class="absolute top-1/2 -translate-y-1/2 right-3">
<slot name="append"> </slot>
</span>
<span
v-if="clearable && inputValue"
class="absolute top-1/2 -translate-y-1/2 right-[14px] cursor-pointer"
:class="{ '!right-11': append }"
data-test="text-input-clear-button"
>
<slot name="clear" :onClick="clearInput">
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="12" viewBox="0 0 384 512" @click="clearInput">
<path
d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"
/>
</svg>
</slot>
</span>
</div>

<p v-if="hint" data-test="text-input-hint">{{ hint }}</p>
</label>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'

interface props {
modelValue?: string
label?: string
id?: string
type: string
placeholder?: string
hint?: string
disabled?: boolean
readonly?: boolean
autofocus?: boolean
required?: boolean
clearable?: boolean
prepend?: boolean
append?: boolean
}
const { modelValue } = withDefaults(defineProps<props>(), {
type: 'text'
})
const emit = defineEmits(['focus', 'blur', 'change', 'enter', 'update:modelValue'])

const inputValue = ref(modelValue)
const clearInput = () => {
inputValue.value = ''
}

watch(inputValue, (newValue: any) => {
emit('change', newValue)
})
</script>
76 changes: 0 additions & 76 deletions src/components/__tests__/DInput.spec.ts

This file was deleted.

104 changes: 104 additions & 0 deletions src/components/__tests__/DxhTextInput.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import DxhTextInput from '../DxhTextInput.vue'

describe('DxhTextInput.vue', () => {
it('renders input element with label, placeholder, and hint text', () => {
const wrapper = mount(DxhTextInput, {
props: {
label: 'Username',
modelValue: '',
placeholder: 'Enter your username',
hint: 'Hint text'
}
})

const label = wrapper.find('[data-test="text-input-label"]')
expect(label.exists()).toBe(true)

const input = wrapper.find('[data-test="text-input"]')
expect(input.exists()).toBe(true)
expect(input.attributes('placeholder')).toBe('Enter your username')

const hint = wrapper.find('[data-test="text-input-hint"]')
expect(hint.exists()).toBe(true)
expect(hint.text()).toBe('Hint text')
})

it('binds input value to modelValue prop', () => {
const wrapper = mount(DxhTextInput, {
props: {
modelValue: 'test'
}
})

const inputElement = wrapper.find('[data-test="text-input"]').element as HTMLInputElement
expect(inputElement.value).toBe('test')
})

it('updates modelValue on input', async () => {
const wrapper = mount(DxhTextInput, {
props: {
modelValue: ''
}
})

const inputElement = wrapper.find('[data-test="text-input"]')
await inputElement.setValue('new value')

const updateEvents = wrapper.emitted('update:modelValue')
expect(updateEvents).toBeTruthy()

const firstEventPayload = updateEvents ? updateEvents[0] : []
expect(firstEventPayload).toEqual(['new value'])
})

it('emits focus event when input is focused', async () => {
const wrapper = mount(DxhTextInput, {
props: {
modelValue: ''
}
})

await wrapper.find('[data-test="text-input"]').trigger('focus')
expect(wrapper.emitted('focus')).toBeTruthy()
})

it('emits blur event when input is blurred', async () => {
const wrapper = mount(DxhTextInput, {
props: {
modelValue: ''
}
})

await wrapper.find('[data-test="text-input"]').trigger('blur')
expect(wrapper.emitted('blur')).toBeTruthy()
})

it('emits enter event when Enter key is pressed', async () => {
const wrapper = mount(DxhTextInput, {
props: {
modelValue: ''
}
})

await wrapper.find('[data-test="text-input"]').trigger('keyup.enter')
expect(wrapper.emitted('enter')).toBeTruthy()
})

it('clears input value when clearable and close button is clicked', async () => {
const wrapper = mount(DxhTextInput, {
props: {
modelValue: 'test',
clearable: true
}
})

const closeButton = wrapper.find('[data-test="text-input-clear-button"]')
expect(closeButton.exists()).toBe(true)

await closeButton.trigger('click')
const inputElement = wrapper.find('[data-test="text-input"]').element as HTMLInputElement
expect(inputElement.value).not.toBe('')
})
})
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import DButton from "./components/DButton.vue"
import DInput from "./components/DInput.vue"
import DxhTextInput from './components/DxhTextInput.vue'

export default {DButton, DInput}
export default { DButton, DxhTextInput }