Skip to content

Commit

Permalink
fix(datepicker): input event
Browse files Browse the repository at this point in the history
  • Loading branch information
segunadebayo committed Sep 13, 2024
1 parent a5e3cae commit a73bd8f
Show file tree
Hide file tree
Showing 17 changed files with 370 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/long-cougars-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@zag-js/date-picker": patch
---

Fix issue in Vue.js where input value could not be changed by typing.
2 changes: 1 addition & 1 deletion .xstate/date-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const fetchMachine = createMachine({
actions: ["clearDateValue", "clearFocusedDate", "focusFirstInputElement"]
},
"INPUT.CHANGE": {
actions: ["focusParsedDate"]
actions: ["setInputValue", "focusParsedDate"]
},
"INPUT.ENTER": {
actions: ["focusParsedDate", "selectFocusedDate"]
Expand Down
16 changes: 16 additions & 0 deletions e2e/date-picker.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ test.describe("datepicker [single]", () => {
await I.dontSeeContent()
})

test("keyboard selection", async () => {
await I.type("02/28/2024")
await I.pressKey("Enter")
await I.seeInputHasValue("02/28/2024")
})

test("keyboard selection + re-selection", async () => {
await I.type("02/28/2024")
await I.pressKey("Enter")
await I.seeInputHasValue("02/28/2024")

await I.pressKey("Backspace", 5) // becomes 02/28
await I.pressKey("Enter")
await I.seeInputHasValue("02/28/2024")
})

// test("click trigger + focus input + selection, set value in input", async () => {
// await I.clickTrigger()
// await I.seeContent()
Expand Down
4 changes: 4 additions & 0 deletions e2e/models/datepicker.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ export class DatePickerModel extends Model {
return this.trigger.click()
}

type(value: string, index = 0) {
return this.getInput(index).pressSequentially(value)
}

focusInput(index = 0) {
return this.getInput(index).focus()
}
Expand Down
7 changes: 3 additions & 4 deletions examples/next-ts/pages/date-picker-multi.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as datePicker from "@zag-js/date-picker"
import { getYearsRange } from "@zag-js/date-utils"
import { normalizeProps, useMachine } from "@zag-js/react"
import { datePickerControls } from "@zag-js/shared"
import { useId } from "react"
Expand Down Expand Up @@ -53,9 +52,9 @@ export default function Page() {
</select>

<select {...api.getYearSelectProps()}>
{getYearsRange({ from: 1_000, to: 4_000 }).map((year, i) => (
<option key={i} value={year}>
{year}
{api.getYears().map((year, i) => (
<option key={i} value={year.value}>
{year.label}
</option>
))}
</select>
Expand Down
7 changes: 3 additions & 4 deletions examples/next-ts/pages/date-picker-range.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as datePicker from "@zag-js/date-picker"
import { getYearsRange } from "@zag-js/date-utils"
import { normalizeProps, useMachine } from "@zag-js/react"
import { datePickerControls } from "@zag-js/shared"
import { useId } from "react"
Expand Down Expand Up @@ -57,9 +56,9 @@ export default function Page() {
</select>

<select {...api.getYearSelectProps()}>
{getYearsRange({ from: 1_000, to: 4_000 }).map((year, i) => (
<option key={i} value={year}>
{year}
{api.getYears().map((year, i) => (
<option key={i} value={year.value}>
{year.label}
</option>
))}
</select>
Expand Down
7 changes: 3 additions & 4 deletions examples/next-ts/pages/date-picker.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as datePicker from "@zag-js/date-picker"
import { getYearsRange } from "@zag-js/date-utils"
import { normalizeProps, useMachine } from "@zag-js/react"
import { datePickerControls } from "@zag-js/shared"
import { useId } from "react"
Expand Down Expand Up @@ -53,9 +52,9 @@ export default function Page() {
</select>

<select {...api.getYearSelectProps()}>
{getYearsRange({ from: 1_000, to: 4_000 }).map((year, i) => (
<option key={i} value={year}>
{year}
{api.getYears().map((year, i) => (
<option key={i} value={year.value}>
{year.label}
</option>
))}
</select>
Expand Down
129 changes: 129 additions & 0 deletions examples/nuxt-ts/pages/date-picker-range.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<script setup lang="ts">
import * as datePicker from "@zag-js/date-picker"
import { datePickerControls } from "@zag-js/shared"
import { normalizeProps, useMachine } from "@zag-js/vue"
import { computed } from "vue"
const controls = useControls(datePickerControls)
const [state, send] = useMachine(
datePicker.machine({
id: "1",
name: "date[]",
locale: "en",
numOfMonths: 2,
selectionMode: "range",
}),
{
context: controls.context,
},
)
const api = computed(() => datePicker.connect(state.value, send, normalizeProps))
const offset = computed(() => api.value.getOffset({ months: 1 }))
</script>

<template>
<main class="date-picker">
<div>
<button>Outside Element</button>
</div>
<p>{{ `Visible range: ${api.visibleRangeText.formatted}` }}</p>

<output class="date-output">
<div>Selected: {{ api.valueAsString.join(", ") ?? "-" }}</div>
<div>Focused: {{ api.focusedValueAsString }}</div>
</output>

<div v-bind="api.getControlProps()">
<input v-bind="api.getInputProps({ index: 0 })" />
<input v-bind="api.getInputProps({ index: 1 })" />
<button v-bind="api.getClearTriggerProps()"></button>
<button v-bind="api.getTriggerProps()">🗓</button>
</div>

<div v-bind="api.getPositionerProps()">
<div v-bind="api.getContentProps()">
<div style="margin-bottom: 20px">
<select v-bind="api.getMonthSelectProps()">
<option v-for="(month, i) in api.getMonths()" :key="i" :value="i + 1">
{{ month.label }}
</option>
</select>

<select v-bind="api.getYearSelectProps()">
<option v-for="(year, i) in api.getYears()" :key="i" :value="year.value">
{{ year.label }}
</option>
</select>
</div>

<div>
<div v-bind="api.getViewControlProps({ view: 'year' })">
<button v-bind="api.getPrevTriggerProps()">Prev</button>
<span> {{ api.visibleRangeText.start }} - {{ api.visibleRangeText.end }} </span>
<button v-bind="api.getNextTriggerProps()">Next</button>
</div>

<div style="display: flex; gap: 24px">
<table v-bind="api.getTableProps()">
<thead v-bind="api.getTableHeaderProps()">
<tr v-bind="api.getTableRowProps()">
<th v-for="(day, i) in api.weekDays" :key="i" scope="col" :aria-label="day.long">
{{ day.narrow }}
</th>
</tr>
</thead>
<tbody v-bind="api.getTableBodyProps()">
<tr v-for="(week, i) in api.weeks" :key="i" v-bind="api.getTableRowProps()">
<td v-for="(value, i) in week" :key="i" v-bind="api.getDayTableCellProps({ value })">
<div v-bind="api.getDayTableCellTriggerProps({ value })">{{ value.day }}</div>
</td>
</tr>
</tbody>
</table>

<table v-bind="api.getTableProps()">
<thead v-bind="api.getTableHeaderProps()">
<tr v-bind="api.getTableRowProps()">
<th v-for="(day, i) in api.weekDays" :key="i" scope="col" :aria-label="day.long">
{{ day.narrow }}
</th>
</tr>
</thead>
<tbody v-bind="api.getTableBodyProps()">
<tr v-for="(week, i) in offset.weeks" :key="i" v-bind="api.getTableRowProps()">
<td
v-for="(value, i) in week"
:key="i"
v-bind="api.getDayTableCellProps({ value, visibleRange: offset.visibleRange })"
>
<div v-bind="api.getDayTableCellTriggerProps({ value, visibleRange: offset.visibleRange })">
{{ value.day }}
</div>
</td>
</tr>
</tbody>
</table>

<div style="min-width: 80px; display: flex; flex-direction: column; gap: 4px">
<b>Presets</b>
<button v-bind="api.getPresetTriggerProps({ value: 'last3Days' })">Last 3 Days</button>
<button v-bind="api.getPresetTriggerProps({ value: 'last7Days' })">Last 7 Days</button>
<button v-bind="api.getPresetTriggerProps({ value: 'last14Days' })">Last 14 Days</button>
<button v-bind="api.getPresetTriggerProps({ value: 'last30Days' })">Last 30 Days</button>
<button v-bind="api.getPresetTriggerProps({ value: 'last90Days' })">Last 90 Days</button>
</div>
</div>
</div>
</div>
</div>
</main>

<Toolbar viz>
<StateVisualizer :state="state" :omit="['weeks']" />
<template #controls>
<Controls :control="controls" />
</template>
</Toolbar>
</template>
146 changes: 146 additions & 0 deletions examples/nuxt-ts/pages/date-picker.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<script setup lang="ts">
import * as datePicker from "@zag-js/date-picker"
import { datePickerControls } from "@zag-js/shared"
import { normalizeProps, useMachine } from "@zag-js/vue"
import { computed } from "vue"
const controls = useControls(datePickerControls)
const [state, send] = useMachine(
datePicker.machine({
id: "1",
locale: "en",
selectionMode: "single",
}),
{
context: controls.context,
},
)
const api = computed(() => datePicker.connect(state.value, send, normalizeProps))
</script>

<template>
<main class="date-picker">
<div>
<button>Outside Element</button>
</div>
<p>{{ `Visible range: ${api.visibleRangeText.formatted}` }}</p>

<output class="date-output">
<div>Selected: {{ api.valueAsString ?? "-" }}</div>
<div>Focused: {{ api.focusedValueAsString }}</div>
</output>

<div v-bind="api.getControlProps()">
<input v-bind="api.getInputProps()" />
<input v-bind="api.getInputProps()" />
<button v-bind="api.getClearTriggerProps()"></button>
<button v-bind="api.getTriggerProps()">🗓</button>
</div>

<div v-bind="api.getPositionerProps()">
<div v-bind="api.getContentProps()">
<div style="margin-bottom: 20px">
<select v-bind="api.getMonthSelectProps()">
<option v-for="(month, i) in api.getMonths()" :key="i" :value="month.value">
{{ month.label }}
</option>
</select>

<select v-bind="api.getYearSelectProps()">
<option v-for="(year, i) in api.getYears()" :key="i" :value="year.value">
{{ year.label }}
</option>
</select>
</div>

<div v-if="api.view === 'day'">
<div v-bind="api.getViewControlProps({ view: 'year' })">
<button v-bind="api.getPrevTriggerProps()">Prev</button>
<button v-bind="api.getViewTriggerProps()">{{ api.visibleRangeText.start }}</button>
<button v-bind="api.getNextTriggerProps()">Next</button>
</div>

<table v-bind="api.getTableProps({ view: 'day' })">
<thead v-bind="api.getTableHeaderProps({ view: 'day' })">
<tr v-bind="api.getTableRowProps({ view: 'day' })">
<th v-for="(day, i) in api.weekDays" :key="i" scope="col" :aria-label="day.long">
{{ day.narrow }}
</th>
</tr>
</thead>
<tbody v-bind="api.getTableBodyProps({ view: 'day' })">
<tr v-for="(week, i) in api.weeks" :key="i" v-bind="api.getTableRowProps({ view: 'day' })">
<td v-for="(value, i) in week" :key="i" v-bind="api.getDayTableCellProps({ value })">
<div v-bind="api.getDayTableCellTriggerProps({ value })">{{ value.day }}</div>
</td>
</tr>
</tbody>
</table>
</div>

<div style="display: flex; gap: 40px">
<div v-if="api.view === 'month'" style="width: 100%">
<div v-bind="api.getViewControlProps({ view: 'month' })">
<button v-bind="api.getPrevTriggerProps({ view: 'month' })">Prev</button>
<button v-bind="api.getViewTriggerProps({ view: 'month' })">{{ api.visibleRange.start.year }}</button>
<button v-bind="api.getNextTriggerProps({ view: 'month' })">Next</button>
</div>

<table v-bind="api.getTableProps({ view: 'month', columns: 4 })">
<tbody v-bind="api.getTableBodyProps()">
<tr
v-for="(months, row) in api.getMonthsGrid({ columns: 4, format: 'short' })"
:key="row"
v-bind="api.getTableRowProps()"
>
<td
v-for="(month, index) in months"
:key="index"
v-bind="api.getMonthTableCellProps({ ...month, columns: 4 })"
>
<div v-bind="api.getMonthTableCellTriggerProps({ ...month, columns: 4 })">{{ month.label }}</div>
</td>
</tr>
</tbody>
</table>
</div>

<div v-if="api.view === 'year'" style="width: 100%">
<div v-bind="api.getViewControlProps({ view: 'year' })">
<button v-bind="api.getPrevTriggerProps({ view: 'year' })">Prev</button>
<span> {{ api.getDecade().start }} - {{ api.getDecade().end }} </span>
<button v-bind="api.getNextTriggerProps({ view: 'year' })">Next</button>
</div>

<table v-bind="api.getTableProps({ view: 'year', columns: 4 })">
<tbody v-bind="api.getTableBodyProps()">
<tr
v-for="(years, row) in api.getYearsGrid({ columns: 4 })"
:key="row"
v-bind="api.getTableRowProps({ view: 'year' })"
>
<td
v-for="(year, index) in years"
:key="index"
v-bind="api.getYearTableCellProps({ ...year, columns: 4 })"
>
<div v-bind="api.getYearTableCellTriggerProps({ ...year, columns: 4 })">{{ year.label }}</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>

<Toolbar viz>
<StateVisualizer :state="state" :omit="['weeks']" />
<template #controls>
<Controls :control="controls" />
</template>
</Toolbar>
</template>
Loading

0 comments on commit a73bd8f

Please sign in to comment.