diff --git a/.playground/pages/composant/time.vue b/.playground/pages/composant/time.vue new file mode 100644 index 0000000..c16995e --- /dev/null +++ b/.playground/pages/composant/time.vue @@ -0,0 +1,87 @@ + + + + Simple + + + + + Avec label + + + + + Valeur initiale + + + + + Required + + + + + Desactive + + + + + Readonly + + + + + Erreur + + + + + Succes + + + + + + diff --git a/app/components/malio/Time.test.ts b/app/components/malio/Time.test.ts new file mode 100644 index 0000000..277f20b --- /dev/null +++ b/app/components/malio/Time.test.ts @@ -0,0 +1,79 @@ +import {describe, expect, it} from 'vitest' +import {mount} from '@vue/test-utils' +import type {DefineComponent} from 'vue' +import Time from './Time.vue' + +type TimeProps = { + id?: string + label?: string + name?: string + modelValue?: string | null + inputClass?: string + labelClass?: string + groupClass?: string + required?: boolean + disabled?: boolean + readonly?: boolean + hint?: string + error?: string + success?: string +} + +const TimeForTest = Time as DefineComponent + +const mountTime = (props: TimeProps = {}) => + mount(TimeForTest, {props}) + +describe('MalioTime', () => { + it('renders two text inputs and a separator', () => { + const wrapper = mountTime() + + expect(wrapper.findAll('input')).toHaveLength(2) + expect(wrapper.text()).toContain(':') + }) + + it('uses separate ids for hours and minutes inputs', () => { + const wrapper = mountTime({label: 'Horaire'}) + const inputs = wrapper.findAll('input') + + expect(inputs[0].attributes('id')).toContain('-hours') + expect(inputs[1].attributes('id')).toContain('-minutes') + expect(wrapper.get('label').attributes('for')).toBe(inputs[0].attributes('id')) + }) + + it('clamps values to 24 hours and 59 minutes', async () => { + const wrapper = mountTime({modelValue: ''}) + const inputs = wrapper.findAll('input') + + await inputs[0].setValue('99') + await inputs[1].setValue('88') + + expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['23:59']) + expect((inputs[0].element as HTMLInputElement).value).toBe('23') + expect((inputs[1].element as HTMLInputElement).value).toBe('59') + }) + + it('pads single digits on blur', async () => { + const wrapper = mountTime({modelValue: ''}) + const inputs = wrapper.findAll('input') + + await inputs[0].setValue('7') + await inputs[0].trigger('blur') + await inputs[1].setValue('5') + await inputs[1].trigger('blur') + + expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['07:05']) + expect((inputs[0].element as HTMLInputElement).value).toBe('07') + expect((inputs[1].element as HTMLInputElement).value).toBe('05') + }) + + it('applies the primary border to the focused field', async () => { + const wrapper = mountTime() + const inputs = wrapper.findAll('input') + + await inputs[0].trigger('focus') + + expect(inputs[0].classes()).toContain('border-m-primary') + expect(inputs[1].classes()).not.toContain('border-m-primary') + }) +}) diff --git a/app/components/malio/Time.vue b/app/components/malio/Time.vue new file mode 100644 index 0000000..8f63685 --- /dev/null +++ b/app/components/malio/Time.vue @@ -0,0 +1,255 @@ + + + + + {{ label }} + + + + + + : + + + + + + + {{ error || success || hint }} + + + + + diff --git a/app/story/inputTime.story.vue b/app/story/inputTime.story.vue new file mode 100644 index 0000000..784968d --- /dev/null +++ b/app/story/inputTime.story.vue @@ -0,0 +1,89 @@ + + + + + Simple + + + + + Avec label + + + + + Valeur initiale + + + + + Required + + + + + Desactive + + + + + Readonly + + + + + Erreur + + + + + Succes + + + + + + +
+ {{ error || success || hint }} +