feat : composant TimeWheels deux molettes HH:MM avec bande centrale (MUI-39)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
import {describe, expect, it} from 'vitest'
|
||||||
|
import {mount} from '@vue/test-utils'
|
||||||
|
import TimeWheels from './TimeWheels.vue'
|
||||||
|
import TimeWheel from './TimeWheel.vue'
|
||||||
|
|
||||||
|
const mountWheels = (modelValue = '09:30') =>
|
||||||
|
mount(TimeWheels, {props: {modelValue}, attachTo: document.body})
|
||||||
|
|
||||||
|
describe('MalioTimeWheels', () => {
|
||||||
|
it('rend deux molettes (heures + minutes) et un séparateur', () => {
|
||||||
|
const wrapper = mountWheels('09:30')
|
||||||
|
const wheels = wrapper.findAllComponents(TimeWheel)
|
||||||
|
expect(wheels).toHaveLength(2)
|
||||||
|
expect(wheels[0].props('ariaLabel')).toBe('Heures')
|
||||||
|
expect(wheels[1].props('ariaLabel')).toBe('Minutes')
|
||||||
|
expect(wrapper.text()).toContain(':')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('splitte modelValue vers les bonnes molettes', () => {
|
||||||
|
const wrapper = mountWheels('09:30')
|
||||||
|
const wheels = wrapper.findAllComponents(TimeWheel)
|
||||||
|
expect(wheels[0].props('modelValue')).toBe(9)
|
||||||
|
expect(wheels[1].props('modelValue')).toBe(30)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('recompose et émet HH:MM quand l\'heure change', async () => {
|
||||||
|
const wrapper = mountWheels('09:30')
|
||||||
|
const wheels = wrapper.findAllComponents(TimeWheel)
|
||||||
|
wheels[0].vm.$emit('update:modelValue', 14)
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['14:30'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('recompose et émet HH:MM quand la minute change', async () => {
|
||||||
|
const wrapper = mountWheels('09:30')
|
||||||
|
const wheels = wrapper.findAllComponents(TimeWheel)
|
||||||
|
wheels[1].vm.$emit('update:modelValue', 5)
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['09:05'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('par défaut 00:00 quand modelValue est vide', () => {
|
||||||
|
const wrapper = mountWheels('')
|
||||||
|
const wheels = wrapper.findAllComponents(TimeWheel)
|
||||||
|
expect(wheels[0].props('modelValue')).toBe(0)
|
||||||
|
expect(wheels[1].props('modelValue')).toBe(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
data-test="time-wheels"
|
||||||
|
class="relative flex items-center justify-center gap-3 py-2"
|
||||||
|
>
|
||||||
|
<!-- bande centrale (overlay, traverse les 2 colonnes) -->
|
||||||
|
<div
|
||||||
|
class="pointer-events-none absolute inset-x-2 top-1/2 z-0 h-10 -translate-y-1/2 rounded-lg bg-m-primary/10"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MalioTimeWheel
|
||||||
|
:model-value="hours"
|
||||||
|
:values="HOURS"
|
||||||
|
aria-label="Heures"
|
||||||
|
class="relative z-10"
|
||||||
|
@update:model-value="onHours"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="relative z-10 text-lg font-bold text-black">:</span>
|
||||||
|
|
||||||
|
<MalioTimeWheel
|
||||||
|
:model-value="minutes"
|
||||||
|
:values="MINUTES"
|
||||||
|
aria-label="Minutes"
|
||||||
|
class="relative z-10"
|
||||||
|
@update:model-value="onMinutes"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {computed} from 'vue'
|
||||||
|
import MalioTimeWheel from './TimeWheel.vue'
|
||||||
|
import {formatTime, parseTime} from '../composables/timeFormat'
|
||||||
|
|
||||||
|
defineOptions({name: 'MalioTimeWheels', inheritAttrs: false})
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{modelValue?: string | null}>(),
|
||||||
|
{modelValue: ''},
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits<{(e: 'update:modelValue', value: string): void}>()
|
||||||
|
|
||||||
|
const HOURS = Array.from({length: 24}, (_, i) => i)
|
||||||
|
const MINUTES = Array.from({length: 60}, (_, i) => i)
|
||||||
|
|
||||||
|
const parts = computed(() => parseTime(props.modelValue) ?? {hours: 0, minutes: 0})
|
||||||
|
const hours = computed(() => parts.value.hours)
|
||||||
|
const minutes = computed(() => parts.value.minutes)
|
||||||
|
|
||||||
|
const onHours = (value: number) => emit('update:modelValue', formatTime(value, minutes.value))
|
||||||
|
const onMinutes = (value: number) => emit('update:modelValue', formatTime(hours.value, value))
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user