feat : DateTime utilise les molettes au lieu de l'input time natif (MUI-39)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|||||||
import {mount} from '@vue/test-utils'
|
import {mount} from '@vue/test-utils'
|
||||||
import type {DefineComponent} from 'vue'
|
import type {DefineComponent} from 'vue'
|
||||||
import DateTime_ from './DateTime.vue'
|
import DateTime_ from './DateTime.vue'
|
||||||
|
import TimeWheels from '../time/internal/TimeWheels.vue'
|
||||||
|
|
||||||
type DateTimeProps = {
|
type DateTimeProps = {
|
||||||
id?: string
|
id?: string
|
||||||
@@ -49,11 +50,11 @@ describe('MalioDateTime', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('popover', () => {
|
describe('popover', () => {
|
||||||
it('ouvre la grille et l\'input heure au clic', async () => {
|
it('ouvre la grille et les molettes au clic', async () => {
|
||||||
const wrapper = mountDateTime()
|
const wrapper = mountDateTime()
|
||||||
await wrapper.get('[data-test="date-input"]').trigger('click')
|
await wrapper.get('[data-test="date-input"]').trigger('click')
|
||||||
expect(wrapper.find('[data-test="month-grid"]').exists()).toBe(true)
|
expect(wrapper.find('[data-test="month-grid"]').exists()).toBe(true)
|
||||||
expect(wrapper.find('[data-test="time-input"]').exists()).toBe(true)
|
expect(wrapper.find('[data-test="time-wheels"]').exists()).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -69,8 +70,8 @@ describe('MalioDateTime', () => {
|
|||||||
it('applique l\'heure réglée avant le clic du jour', async () => {
|
it('applique l\'heure réglée avant le clic du jour', async () => {
|
||||||
const wrapper = mountDateTime()
|
const wrapper = mountDateTime()
|
||||||
await wrapper.get('[data-test="date-input"]').trigger('click')
|
await wrapper.get('[data-test="date-input"]').trigger('click')
|
||||||
await wrapper.get('[data-test="time-input"]').setValue('09:15')
|
wrapper.findComponent(TimeWheels).vm.$emit('update:modelValue', '09:15')
|
||||||
// pas d'émission tant qu'aucun jour n'est choisi
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
||||||
await wrapper.get('[data-test="day"][data-iso="2026-05-19"]').trigger('click')
|
await wrapper.get('[data-test="day"][data-iso="2026-05-19"]').trigger('click')
|
||||||
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['2026-05-19T09:15:00'])
|
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['2026-05-19T09:15:00'])
|
||||||
@@ -79,15 +80,15 @@ describe('MalioDateTime', () => {
|
|||||||
it('met à jour l\'heure quand une date est déjà choisie', async () => {
|
it('met à jour l\'heure quand une date est déjà choisie', async () => {
|
||||||
const wrapper = mountDateTime({modelValue: '2026-05-20T14:30:00'})
|
const wrapper = mountDateTime({modelValue: '2026-05-20T14:30:00'})
|
||||||
await wrapper.get('[data-test="date-input"]').trigger('click')
|
await wrapper.get('[data-test="date-input"]').trigger('click')
|
||||||
await wrapper.get('[data-test="time-input"]').setValue('08:45')
|
wrapper.findComponent(TimeWheels).vm.$emit('update:modelValue', '08:45')
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['2026-05-20T08:45:00'])
|
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['2026-05-20T08:45:00'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('initialise l\'input heure depuis la valeur', async () => {
|
it('initialise les molettes depuis la valeur', async () => {
|
||||||
const wrapper = mountDateTime({modelValue: '2026-05-20T14:30:00'})
|
const wrapper = mountDateTime({modelValue: '2026-05-20T14:30:00'})
|
||||||
await wrapper.get('[data-test="date-input"]').trigger('click')
|
await wrapper.get('[data-test="date-input"]').trigger('click')
|
||||||
const time = wrapper.get('[data-test="time-input"]').element as HTMLInputElement
|
expect(wrapper.findComponent(TimeWheels).props('modelValue')).toBe('14:30')
|
||||||
expect(time.value).toBe('14:30')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -28,25 +28,21 @@
|
|||||||
:max="max?.slice(0, 10)"
|
:max="max?.slice(0, 10)"
|
||||||
@select="onSelectDay"
|
@select="onSelectDay"
|
||||||
/>
|
/>
|
||||||
<!-- Bloc heure intérimaire : input natif, isolé pour remplacement futur par le sélecteur dédié. -->
|
<div class="mt-[26px]">
|
||||||
<div class="mt-[26px] flex-col items-center gap-2">
|
<TimeWheels
|
||||||
<input
|
:model-value="timeValue || '00:00'"
|
||||||
:id="timeInputId"
|
@update:model-value="onTimeChange"
|
||||||
data-test="time-input"
|
/>
|
||||||
type="time"
|
|
||||||
:value="timeValue"
|
|
||||||
class="w-full border border-m-muted bg-white px-2 py-1 text-base outline-none focus:border-m-primary"
|
|
||||||
@input="onTimeInput"
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</CalendarField>
|
</CalendarField>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {computed, ref, useId, watch} from 'vue'
|
import {computed, ref, watch} from 'vue'
|
||||||
import CalendarField from './internal/CalendarField.vue'
|
import CalendarField from './internal/CalendarField.vue'
|
||||||
import MonthGrid from './internal/MonthGrid.vue'
|
import MonthGrid from './internal/MonthGrid.vue'
|
||||||
|
import TimeWheels from '../time/internal/TimeWheels.vue'
|
||||||
import {composeDateTime, formatIsoDateTimeToDisplay, isValidIsoDateTime, splitDateTime} from './composables/datetimeFormat'
|
import {composeDateTime, formatIsoDateTimeToDisplay, isValidIsoDateTime, splitDateTime} from './composables/datetimeFormat'
|
||||||
|
|
||||||
defineOptions({name: 'MalioDateTime', inheritAttrs: false})
|
defineOptions({name: 'MalioDateTime', inheritAttrs: false})
|
||||||
@@ -94,9 +90,6 @@ const props = withDefaults(
|
|||||||
|
|
||||||
const emit = defineEmits<{(e: 'update:modelValue', value: string | null): void}>()
|
const emit = defineEmits<{(e: 'update:modelValue', value: string | null): void}>()
|
||||||
|
|
||||||
const generatedId = useId()
|
|
||||||
const timeInputId = computed(() => `${props.id || `malio-datetime-${generatedId}`}-time`)
|
|
||||||
|
|
||||||
// pendingTime : heure réglée avant qu'un jour ne soit choisi (sinon on ne peut pas émettre).
|
// pendingTime : heure réglée avant qu'un jour ne soit choisi (sinon on ne peut pas émettre).
|
||||||
const pendingTime = ref('')
|
const pendingTime = ref('')
|
||||||
|
|
||||||
@@ -110,9 +103,7 @@ function onSelectDay(iso: string) {
|
|||||||
emit('update:modelValue', composeDateTime(iso, time))
|
emit('update:modelValue', composeDateTime(iso, time))
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTimeInput(e: Event) {
|
function onTimeChange(value: string) {
|
||||||
const value = (e.target as HTMLInputElement).value
|
|
||||||
if (!value) return
|
|
||||||
if (datePart.value) {
|
if (datePart.value) {
|
||||||
emit('update:modelValue', composeDateTime(datePart.value, value))
|
emit('update:modelValue', composeDateTime(datePart.value, value))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user