From 0912bb7497509dca909301ef3b8951574fdef654 Mon Sep 17 00:00:00 2001 From: Joakim Bjerknes Date: Mon, 22 Jan 2024 15:56:26 +0100 Subject: [PATCH] chore(DatePicker): document correct_invalid_date prop, and make sure functionality fires when min_date or max_date is set (#3240) --- .../components/date-picker/properties.mdx | 1 + .../components/date-picker/DatePicker.d.ts | 4 + .../date-picker/DatePickerProvider.js | 15 +- .../date-picker/__tests__/DatePicker.test.tsx | 137 ++++++++++++++++++ 4 files changed, 155 insertions(+), 2 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/date-picker/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/date-picker/properties.mdx index f230dec65dd..f1581853301 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/date-picker/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/date-picker/properties.mdx @@ -50,6 +50,7 @@ import { | `status_state` | _(optional)_ defines the state of the status. Currently, there are two statuses `[error, info]`. Defaults to `error`. | | `status_props` | _(optional)_ use an object to define additional FormStatus properties. | | `disable_autofocus` | _(optional)_ once the date picker gets opened, there is a focus handling to ensure good accessibility. This can be disabled with this property. Defaults to `false`. | +| `correct_invalid_date` | _(optional)_ corrects the input date value to be the same as either `min_date` or `max_date`, when the user types in a date that is either before or after one of these. Defaults to `false`. | | `globalStatus` | _(optional)_ the [configuration](/uilib/components/global-status/properties/#configuration-object) used for the target [GlobalStatus](/uilib/components/global-status). | | `skeleton` | _(optional)_ if set to `true`, an overlaying skeleton with animation will be shown. | | `size` | _(optional)_ the sizes you can choose is `small` (1.5rem), `default` (2rem), `medium` (2.5rem) and `large` (3rem) are supported component sizes. Defaults to `default` / `null`. | diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePicker.d.ts b/packages/dnb-eufemia/src/components/date-picker/DatePicker.d.ts index bc512b67437..5c76fa289a4 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePicker.d.ts +++ b/packages/dnb-eufemia/src/components/date-picker/DatePicker.d.ts @@ -70,6 +70,10 @@ export interface DatePickerProps * To limit a date range to a maximum `end_date`. Defaults to `null`. */ max_date?: DatePickerMaxDate; + /** + * Corrects the date input value if the user tries to set a date that is before or after `min_date` or `max_date`. + * Defaults to `false`. + */ correct_invalid_date?: boolean; /** * To define the order of the masked placeholder input fields. Defaults to `dd/mm/yyyy` diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePickerProvider.js b/packages/dnb-eufemia/src/components/date-picker/DatePickerProvider.js index ec6cee5d55d..69909ebe723 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePickerProvider.js +++ b/packages/dnb-eufemia/src/components/date-picker/DatePickerProvider.js @@ -141,12 +141,23 @@ export default class DatePickerProvider extends React.PureComponent { * Because now we do not any more relay on auto "correction", * but rather return "is_valid_start_date=false" */ - if (isTrue(props.correct_invalid_date)) { + if ( + isTrue(props.correct_invalid_date) || + ((typeof props.min_date !== 'undefined' || + typeof props.max_date !== 'undefined') && + props.correct_invalid_date !== false) + ) { if (isDisabled(state.startDate, state.minDate, state.maxDate)) { state.startDate = state.minDate } if (isDisabled(state.endDate, state.minDate, state.maxDate)) { - state.endDate = state.maxDate + // state.endDate is only used by the input if range is set to true. + // this is done to make max_date correction work if the input is not a range and only max_date is defined. + if (!props.range && !props.min_date) { + state.startDate = state.maxDate + } else { + state.endDate = state.maxDate + } } } } diff --git a/packages/dnb-eufemia/src/components/date-picker/__tests__/DatePicker.test.tsx b/packages/dnb-eufemia/src/components/date-picker/__tests__/DatePicker.test.tsx index fcf859d0c92..f4c468db39d 100644 --- a/packages/dnb-eufemia/src/components/date-picker/__tests__/DatePicker.test.tsx +++ b/packages/dnb-eufemia/src/components/date-picker/__tests__/DatePicker.test.tsx @@ -629,6 +629,143 @@ describe('DatePicker component', () => { expect(on_change.mock.calls[1][0].is_valid_start_date).toBe(true) }) + it('has to auto-correct invalid min dates when min_date is given', async () => { + render() + const day = document.querySelectorAll( + 'input.dnb-date-picker__input--day' + )[0] as HTMLInputElement + const month = document.querySelectorAll( + 'input.dnb-date-picker__input--month' + )[0] as HTMLInputElement + const year = document.querySelectorAll( + 'input.dnb-date-picker__input--year' + )[0] as HTMLInputElement + + // by default we have the corrected start day + expect(day).toHaveValue('02') + + // try to type invalid day + await userEvent.type(day, '01') + expect(day).toHaveValue('02') + + // then type valid day + await userEvent.type(day, '{Backspace>2}03') + expect(day).toHaveValue('03') + + // expect default month to be 02 + expect(month).toHaveValue('02') + + // try to type invalid month + await userEvent.type(month, '01') + expect(month).toHaveValue('02') + + // then type valid month + await userEvent.type(month, '{Backspace>2}03') + expect(month).toHaveValue('03') + + // expect default year to be 2024 + expect(year).toHaveValue('2024') + + // try to type invalid year + await userEvent.type(year, '2023') + expect(year).toHaveValue('2024') + + // then type valid year + await userEvent.type(year, '{Backspace>4}2025') + expect(year).toHaveValue('2025') + }) + + it('has to auto-correct invalid max dates when max_date is given', async () => { + render() + const day = document.querySelectorAll( + 'input.dnb-date-picker__input--day' + )[0] as HTMLInputElement + const month = document.querySelectorAll( + 'input.dnb-date-picker__input--month' + )[0] as HTMLInputElement + const year = document.querySelectorAll( + 'input.dnb-date-picker__input--year' + )[0] as HTMLInputElement + + // by default we have the corrected start day + expect(day).toHaveValue('02') + + // try to type invalid day + await userEvent.type(day, '03') + expect(day).toHaveValue('02') + + // then type valid day + await userEvent.type(day, '{Backspace>2}01') + expect(day).toHaveValue('01') + + // expect default month to be 02 + expect(month).toHaveValue('02') + + // try to type invalid month + await userEvent.type(month, '03') + expect(month).toHaveValue('02') + + // then type valid month + await userEvent.type(month, '{Backspace>2}01') + expect(month).toHaveValue('01') + + // expect default year to be 2024 + expect(year).toHaveValue('2024') + + // try to type invalid year + await userEvent.type(year, '2025') + expect(year).toHaveValue('2024') + + // then type valid year + await userEvent.type(year, '{Backspace>4}2023') + expect(year).toHaveValue('2023') + }) + + it('should not auto-correct invalid min/max dates if correct_invalid_date is set to false', async () => { + render( + + ) + const day = document.querySelectorAll( + 'input.dnb-date-picker__input--day' + )[0] as HTMLInputElement + const month = document.querySelectorAll( + 'input.dnb-date-picker__input--month' + )[0] as HTMLInputElement + const year = document.querySelectorAll( + 'input.dnb-date-picker__input--year' + )[0] as HTMLInputElement + + // by default we have the corrected start day + expect(day).toHaveValue('02') + + await userEvent.type(day, '01') + expect(day).toHaveValue('01') + + await userEvent.type(day, '{Backspace>2}18') + expect(day).toHaveValue('18') + + expect(month).toHaveValue('02') + + await userEvent.type(month, '05') + expect(month).toHaveValue('05') + + await userEvent.type(month, '{Backspace>2}01') + expect(month).toHaveValue('01') + + expect(year).toHaveValue('2024') + + await userEvent.type(year, '2026') + expect(year).toHaveValue('2026') + + await userEvent.type(year, '{Backspace>4}2023') + expect(year).toHaveValue('2023') + }) + it('has a working min and max date limitation', () => { const on_type = jest.fn() const on_change = jest.fn()