Skip to content

Commit

Permalink
feat(Calendar/RangeCalendar): pagination function props (#972)
Browse files Browse the repository at this point in the history
* feat(Calendar): implement pagination function props

* feat(RangeCalendar): implement pagination function props

* chore: deprecate step prop

* chore: update docs

* chore(Calendar): update tests

* chore(Calendar): update Chromatic story

* chore(RangeCalendar): add variant to Chromatic story

* docs(Calendar): update year increment examples

* chore: rerun docs:gen

* fix(Calendar): fix story setup

* fix(useCalendar): preserve placeholder via increment button overwrite

* fix(useCalendar): adjust set on handlePrevDisabled function

---------

Co-authored-by: zernonia <[email protected]>
  • Loading branch information
epr3 and zernonia authored Jun 8, 2024
1 parent db43c23 commit 6f66b95
Show file tree
Hide file tree
Showing 24 changed files with 347 additions and 44 deletions.
11 changes: 9 additions & 2 deletions docs/components/demo/CalendarYearIncrement/css/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@
import { Icon } from '@iconify/vue'
import { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNext, CalendarPrev, CalendarRoot, type CalendarRootProps } from 'radix-vue'
import './styles.css'
import type { DateValue } from '@internationalized/date';
const isDateUnavailable: CalendarRootProps['isDateUnavailable'] = (date) => {
return date.day === 17 || date.day === 18
}
const pagingFunc = (date: DateValue, sign: -1 | 1) => {
if (sign === -1)
return date.subtract({ years: 1})
return date.add({ years: 1})
}
</script>

<template>
Expand All @@ -18,7 +25,7 @@ const isDateUnavailable: CalendarRootProps['isDateUnavailable'] = (date) => {
<CalendarHeader class="CalendarHeader">
<CalendarPrev
class="CalendarNavButton"
step="year"
:prev-page="(date: DateValue) => pagingFunc(date, -1)"
>
<Icon icon="radix-icons:double-arrow-left" class="Icon" />
</CalendarPrev>
Expand All @@ -36,7 +43,7 @@ const isDateUnavailable: CalendarRootProps['isDateUnavailable'] = (date) => {

<CalendarNext
class="CalendarNavButton"
step="year"
:next-page="(date: DateValue) => pagingFunc(date, 1)"
>
<Icon icon="radix-icons:double-arrow-right" class="Icon" />
</CalendarNext>
Expand Down
11 changes: 9 additions & 2 deletions docs/components/demo/CalendarYearIncrement/tailwind/index.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import type { DateValue } from '@internationalized/date';
import { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNext, CalendarPrev, CalendarRoot, type CalendarRootProps } from 'radix-vue'
const isDateUnavailable: CalendarRootProps['isDateUnavailable'] = (date) => {
return date.day === 17 || date.day === 18
}
const pagingFunc = (date: DateValue, sign: -1 | 1) => {
if (sign === -1)
return date.subtract({ years: 1})
return date.add({ years: 1})
}
</script>

<template>
Expand All @@ -17,7 +24,7 @@ const isDateUnavailable: CalendarRootProps['isDateUnavailable'] = (date) => {
<CalendarHeader class="flex items-center justify-between">
<CalendarPrev
class="inline-flex items-center cursor-pointer text-black justify-center rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
step="year"
:prev-page="(date: DateValue) => pagingFunc(date, -1)"
>
<Icon icon="radix-icons:double-arrow-left" class="w-6 h-6" />
</CalendarPrev>
Expand All @@ -36,7 +43,7 @@ const isDateUnavailable: CalendarRootProps['isDateUnavailable'] = (date) => {

<CalendarNext
class="inline-flex items-center cursor-pointer justify-center text-black rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
step="year"
:next-page="(date: DateValue) => pagingFunc(date, 1)"
>
<Icon icon="radix-icons:double-arrow-right" class="w-6 h-6" />
</CalendarNext>
Expand Down
6 changes: 6 additions & 0 deletions docs/content/meta/CalendarNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
'type': 'boolean',
'required': false
},
{
'name': 'nextPage',
'description': '<p>The function to be used for the next page. Overwrites the <code>nextPage</code> function set on the <code>CalendarRoot</code>.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'step',
'description': '<p>The calendar unit to go forward</p>\n',
Expand Down
6 changes: 6 additions & 0 deletions docs/content/meta/CalendarPrev.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
'type': 'boolean',
'required': false
},
{
'name': 'prevPage',
'description': '<p>The function to be used for the prev page. Overwrites the <code>prevPage</code> function set on the <code>CalendarRoot</code>.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'step',
'description': '<p>The calendar unit to go back</p>\n',
Expand Down
12 changes: 12 additions & 0 deletions docs/content/meta/CalendarRoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@
'required': false,
'default': 'false'
},
{
'name': 'nextPage',
'description': '<p>A function that returns the next page of the calendar. It receives the current placeholder as an argument inside the component.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'numberOfMonths',
'description': '<p>The number of months to display at once</p>\n',
Expand Down Expand Up @@ -130,6 +136,12 @@
'required': false,
'default': 'false'
},
{
'name': 'prevPage',
'description': '<p>A function that returns the previous page of the calendar. It receives the current placeholder as an argument inside the component.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'readonly',
'description': '<p>Whether or not the calendar is readonly</p>\n',
Expand Down
6 changes: 6 additions & 0 deletions docs/content/meta/DatePickerNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
'type': 'boolean',
'required': false
},
{
'name': 'nextPage',
'description': '<p>The function to be used for the next page. Overwrites the <code>nextPage</code> function set on the <code>CalendarRoot</code>.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'step',
'description': '<p>The calendar unit to go forward</p>\n',
Expand Down
6 changes: 6 additions & 0 deletions docs/content/meta/DatePickerPrev.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
'type': 'boolean',
'required': false
},
{
'name': 'prevPage',
'description': '<p>The function to be used for the prev page. Overwrites the <code>prevPage</code> function set on the <code>CalendarRoot</code>.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'step',
'description': '<p>The calendar unit to go back</p>\n',
Expand Down
6 changes: 6 additions & 0 deletions docs/content/meta/DateRangePickerNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
'type': 'boolean',
'required': false
},
{
'name': 'nextPage',
'description': '<p>The function to be used for the next page. Overwrites the <code>nextPage</code> function set on the <code>RangeCalendarRoot</code>.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'step',
'description': '<p>The calendar unit to go forward</p>\n',
Expand Down
6 changes: 6 additions & 0 deletions docs/content/meta/DateRangePickerPrev.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
'type': 'boolean',
'required': false
},
{
'name': 'prevPage',
'description': '<p>The function to be used for the prev page. Overwrites the <code>prevPage</code> function set on the <code>RangeCalendarRoot</code>.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'step',
'description': '<p>The calendar unit to go forward</p>\n',
Expand Down
6 changes: 6 additions & 0 deletions docs/content/meta/RangeCalendarNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
'type': 'boolean',
'required': false
},
{
'name': 'nextPage',
'description': '<p>The function to be used for the next page. Overwrites the <code>nextPage</code> function set on the <code>RangeCalendarRoot</code>.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'step',
'description': '<p>The calendar unit to go forward</p>\n',
Expand Down
6 changes: 6 additions & 0 deletions docs/content/meta/RangeCalendarPrev.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
'type': 'boolean',
'required': false
},
{
'name': 'prevPage',
'description': '<p>The function to be used for the prev page. Overwrites the <code>prevPage</code> function set on the <code>RangeCalendarRoot</code>.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'step',
'description': '<p>The calendar unit to go forward</p>\n',
Expand Down
12 changes: 12 additions & 0 deletions docs/content/meta/RangeCalendarRoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@
'type': 'DateRange',
'required': false
},
{
'name': 'nextPage',
'description': '<p>A function that returns the next page of the calendar. It receives the current placeholder as an argument inside the component.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'numberOfMonths',
'description': '<p>The number of months to display at once</p>\n',
Expand Down Expand Up @@ -124,6 +130,12 @@
'required': false,
'default': 'false'
},
{
'name': 'prevPage',
'description': '<p>A function that returns the previous page of the calendar. It receives the current placeholder as an argument inside the component.</p>\n',
'type': '((placeholder: DateValue) => DateValue)',
'required': false
},
{
'name': 'readonly',
'description': '<p>Whether or not the calendar is readonly</p>\n',
Expand Down
44 changes: 44 additions & 0 deletions packages/radix-vue/src/Calendar/Calendar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,50 @@ describe('calendar', async () => {
expect(heading).toHaveTextContent('January 1981')
})

it('navigates 10 years into the past when setting the `prevPage` function to subtract 10 years', async () => {
const { getByTestId, user } = setup({ calendarProps: { modelValue: calendarDate, prevPage: (date: DateValue) => date.subtract({ years: 10 }) } })

const heading = getByTestId('heading')
const prevBtn = getByTestId('prev-button')

await user.click(prevBtn)

expect(heading).toHaveTextContent('January 1970')
})

it('overwrites the `nextPage` function from `CalendarRoot`', async () => {
const { getByTestId, user } = setup({ calendarProps: { modelValue: calendarDate, nextPage: (date: DateValue) => date.add({ years: 10 }) } })

const heading = getByTestId('heading')
const nextBtn = getByTestId('next-year-button')

await user.click(nextBtn)

expect(heading).toHaveTextContent('January 1981')
})

it('overwrites the `prevPage` function from `CalendarRoot`', async () => {
const { getByTestId, user } = setup({ calendarProps: { modelValue: calendarDate, prevPage: (date: DateValue) => date.subtract({ years: 10 }) } })

const heading = getByTestId('heading')
const prevBtn = getByTestId('prev-year-button')

await user.click(prevBtn)

expect(heading).toHaveTextContent('January 1979')
})

it('navigates 10 years into the future when setting the `nextPage` function to add 10 years', async () => {
const { getByTestId, user } = setup({ calendarProps: { modelValue: calendarDate, nextPage: (date: DateValue) => date.add({ years: 10 }) } })

const heading = getByTestId('heading')
const nextBtn = getByTestId('next-button')

await user.click(nextBtn)

expect(heading).toHaveTextContent('January 1990')
})

it('navigates the months backwards using the prev button', async () => {
const { getByTestId, user } = setup({ calendarProps: { modelValue: calendarDate } })

Expand Down
15 changes: 10 additions & 5 deletions packages/radix-vue/src/Calendar/CalendarNext.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<script lang="ts">
import type { PrimitiveProps } from '@/Primitive'
import type { CalendarIncrement } from '@/shared/date'
import type { DateValue } from '@internationalized/date'
export interface CalendarNextProps extends PrimitiveProps {
/** The calendar unit to go forward */
/** The calendar unit to go forward
* @deprecated Use `nextPage` instead
*/
step?: CalendarIncrement
/** The function to be used for the next page. Overwrites the `nextPage` function set on the `CalendarRoot`. */
nextPage?: (placeholder: DateValue) => DateValue
}
</script>

Expand All @@ -23,10 +28,10 @@ const rootContext = injectCalendarRootContext()
:as-child="props.asChild"
aria-label="Next page"
:type="as === 'button' ? 'button' : undefined"
:aria-disabled="rootContext.isNextButtonDisabled(props.step) || undefined"
:data-disabled="rootContext.isNextButtonDisabled(props.step) || undefined"
:disabled="rootContext.isNextButtonDisabled(props.step)"
@click="rootContext.nextPage(props.step)"
:aria-disabled="rootContext.isNextButtonDisabled(props.step, props.nextPage) || undefined"
:data-disabled="rootContext.isNextButtonDisabled(props.step, props.nextPage) || undefined"
:disabled="rootContext.isNextButtonDisabled(props.step, props.nextPage)"
@click="rootContext.nextPage(props.step, props.nextPage)"
>
<slot>Next page</slot>
</Primitive>
Expand Down
15 changes: 10 additions & 5 deletions packages/radix-vue/src/Calendar/CalendarPrev.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<script lang="ts">
import type { PrimitiveProps } from '@/Primitive'
import type { CalendarIncrement } from '@/shared/date'
import type { DateValue } from '@internationalized/date'
export interface CalendarPrevProps extends PrimitiveProps {
/** The calendar unit to go back */
/** The calendar unit to go back
* @deprecated Use `prevPage` instead
*/
step?: CalendarIncrement
/** The function to be used for the prev page. Overwrites the `prevPage` function set on the `CalendarRoot`. */
prevPage?: (placeholder: DateValue) => DateValue
}
</script>

Expand All @@ -23,10 +28,10 @@ const rootContext = injectCalendarRootContext()
:as="props.as"
:as-child="props.asChild"
:type="as === 'button' ? 'button' : undefined"
:aria-disabled="rootContext.isPrevButtonDisabled(props.step) || undefined"
:data-disabled="rootContext.isPrevButtonDisabled(props.step) || undefined"
:disabled="rootContext.isPrevButtonDisabled(props.step)"
@click="rootContext.prevPage(props.step)"
:aria-disabled="rootContext.isPrevButtonDisabled(props.step, props.prevPage) || undefined"
:data-disabled="rootContext.isPrevButtonDisabled(props.step, props.prevPage) || undefined"
:disabled="rootContext.isPrevButtonDisabled(props.step, props.prevPage)"
@click="rootContext.prevPage(props.step, props.prevPage)"
>
<slot>Prev page</slot>
</Primitive>
Expand Down
16 changes: 12 additions & 4 deletions packages/radix-vue/src/Calendar/CalendarRoot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ type CalendarRootContext = {
isDateSelected: Matcher
isDateUnavailable?: Matcher
isOutsideVisibleView: (date: DateValue) => boolean
prevPage: (step?: CalendarIncrement) => void
nextPage: (step?: CalendarIncrement) => void
isNextButtonDisabled: (step?: CalendarIncrement) => boolean
isPrevButtonDisabled: (step?: CalendarIncrement) => boolean
prevPage: (step?: CalendarIncrement, prevPageFunc?: (date: DateValue) => DateValue) => void
nextPage: (step?: CalendarIncrement, nextPageFunc?: (date: DateValue) => DateValue) => void
isNextButtonDisabled: (step?: CalendarIncrement, nextPageFunc?: (date: DateValue) => DateValue) => boolean
isPrevButtonDisabled: (step?: CalendarIncrement, prevPageFunc?: (date: DateValue) => DateValue) => boolean
formatter: Formatter
dir: Ref<Direction>
}
Expand Down Expand Up @@ -81,6 +81,10 @@ interface BaseCalendarRootProps extends PrimitiveProps {
isDateUnavailable?: Matcher
/** The reading direction of the calendar when applicable. <br> If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. */
dir?: Direction
/** A function that returns the next page of the calendar. It receives the current placeholder as an argument inside the component. */
nextPage?: (placeholder: DateValue) => DateValue
/** A function that returns the previous page of the calendar. It receives the current placeholder as an argument inside the component. */
prevPage?: (placeholder: DateValue) => DateValue
}
export interface MultipleCalendarRootProps extends BaseCalendarRootProps {
Expand Down Expand Up @@ -169,6 +173,8 @@ const {
isDateUnavailable: propsIsDateUnavailable,
calendarLabel,
defaultValue,
nextPage: propsNextPage,
prevPage: propsPrevPage,
dir: propDir,
} = toRefs(props)
Expand Down Expand Up @@ -222,6 +228,8 @@ const {
isDateDisabled: propsIsDateDisabled.value,
isDateUnavailable: propsIsDateUnavailable.value,
calendarLabel,
nextPage: propsNextPage,
prevPage: propsPrevPage,
})
const {
Expand Down
Loading

0 comments on commit 6f66b95

Please sign in to comment.