From bd4e17977f33a7dfe1850e288f209a23d54614a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Mon, 11 Nov 2024 11:02:13 +0100 Subject: [PATCH 01/19] feat(DatePicker): convert to camelCase properties (and deprecate snake_case props) --- .../uilib/components/date-picker/events.mdx | 4 + .../components/date-picker/properties.mdx | 4 + .../src/components/date-picker/DatePicker.tsx | 526 +++++++++--------- .../date-picker/DatePickerContext.ts | 4 +- .../components/date-picker/DatePickerDocs.ts | 249 +++++++++ .../date-picker/DatePickerProvider.tsx | 4 +- .../date-picker/__tests__/DatePicker.test.tsx | 8 +- packages/dnb-eufemia/src/shared/Context.tsx | 4 +- 8 files changed, 532 insertions(+), 271 deletions(-) create mode 100644 packages/dnb-eufemia/src/components/date-picker/DatePickerDocs.ts diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/date-picker/events.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/date-picker/events.mdx index c920f51e946..d7c69066fdc 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/date-picker/events.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/date-picker/events.mdx @@ -3,9 +3,13 @@ showTabs: true --- import { DatePickerDateFnsRangeIsWeekend } from 'Docs/uilib/components/date-picker/Examples' +import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' +import { DatePickerEvents } from '@dnb/eufemia/src/components/date-picker/DatePickerDocs' ## Events + + | Events | Description | | ---------------- | ----------------------------------------------------------------------------------------------------------------------- | | `on_change` | _(optional)_ will be called on a date change event. Returns an `object`. See Returned Object below. | 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 a5812a1694c..8604fefa9a0 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 @@ -7,9 +7,13 @@ import { DatePickerDateFns, DatePickerDateFnsRange, } from 'Docs/uilib/components/date-picker/Examples' +import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' +import { DatePickerProperties } from '@dnb/eufemia/src/components/date-picker/DatePickerDocs' ## Properties + + | Properties | Description | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `date` | _(optional)_ defines the pre-filled date by either a JavaScript DateInstance or (ISO 8601) like `date="2019-05-05"`. | diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePicker.tsx b/packages/dnb-eufemia/src/components/date-picker/DatePicker.tsx index 36f30909126..6661fb59bb1 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePicker.tsx +++ b/packages/dnb-eufemia/src/components/date-picker/DatePicker.tsx @@ -55,6 +55,10 @@ import { CalendarDay, DatePickerCalendarProps } from './DatePickerCalendar' import { DatePickerContextValues, DateType } from './DatePickerContext' import { DatePickerDates } from './hooks/useDates' import { useTranslation } from '../../shared' +import { + ToCamelCasePartial, + convertCamelCaseProps, +} from '../../shared/helpers/withCamelCaseProps' export type DatePickerEventAttributes = { day?: string @@ -76,260 +80,262 @@ export type DisplayPickerEvent = ( event?: React.MouseEvent } -export type DatePickerProps = Omit< - React.HTMLProps, - 'ref' | 'children' | 'label' | 'size' | 'onBlur' | 'onFocus' | 'start' -> & - SpacingProps & { - /** - * Defines the pre-filled date by either a JavaScript DateInstance or (ISO 8601) like `date="2019-05-05"`. - */ - date?: DateType - /** - * To set the pre-filled starting date. Is used if `range={true}` is set to `true`. Defaults to `null`, showing the `mask_placeholder`. - */ - start_date?: DateType - /** - * To set the pre-filled ending date. Is used if `range={true}` is set to `true`. Defaults to `null`, showing the `mask_placeholder`. - */ - end_date?: DateType - /** - * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`. - */ - month?: DateType - /** - * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`. - */ - start_month?: DateType - /** - * To display what month should be shown in the second calendar by default. Defaults to the `date` respective `start_date`. - */ - end_month?: DateType - /** - * To limit a date range to a minimum `start_date`. Defaults to `null`. - */ - min_date?: DateType - /** - * To limit a date range to a maximum `end_date`. Defaults to `null`. - */ - max_date?: DateType - /** - * 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`. - */ - correct_invalid_date?: boolean - /** - * To define the order of the masked placeholder input fields. Defaults to `dd/mm/yyyy` - */ - mask_order?: string - /** - * To display the placeholder on input. Defaults to `dd/mm/åååå`. - */ - mask_placeholder?: string - /** - * Defines how the prop dates (`date`, `start_date` and `end_date`) should be parsed, e.g. `yyyy/MM/dd`. Defaults to `yyyy-MM-dd`. - */ - date_format?: string - /** - * Defines how the returned date, as a string, should be formatted as. Defaults to `yyyy-MM-dd`. - */ - return_format?: string - /** - * If set to `true`, the navigation will be hidden. Defaults to `false`. - */ - hide_navigation?: boolean - hide_navigation_buttons?: boolean - /** - * If set to `true`, the week days will be hidden. Defaults to `false`. - */ - hide_days?: boolean - /** - * Use `true` to only show the defined month. Disables the month navigation possibility. Defaults to `false`. - */ - only_month?: boolean - /** - * Use `true` to only show the last week in the current month if it needs to be shown. The result is that there will mainly be shows five (5) weeks (rows) instead of six (6). Defaults to `false`. - */ - hide_last_week?: boolean - /** - * Once the date picker gets opened, there is a focus handling to ensure good accessibility. can be disabled with property. Defaults to `false`. - */ - disable_autofocus?: boolean - enable_keyboard_nav?: boolean - /** - * If the input fields with the mask should be visible. Defaults to `false`. - */ - show_input?: boolean - /** - * If set to `true`, a submit button will be shown. You can change the default text by using `submit_button_text="Ok"`. Defaults to `false`. If the `range` prop is `true`, then the submit button is shown. - */ - show_submit_button?: boolean - /** - * If set to `true`, a cancel button will be shown. You can change the default text by using `cancel_button_text="Avbryt"` Defaults to `false`. If the `range` prop is `true`, then the cancel button is shown. - */ - show_cancel_button?: boolean - /** - * If set to `true`, a reset button will be shown. You can change the default text by using `reset_button_text="Tilbakestill"` Defaults to `false`. - */ - show_reset_button?: boolean - submit_button_text?: string - cancel_button_text?: string - reset_button_text?: string - reset_date?: boolean - /** - * To define the first day of the week. Defaults to `monday`. - */ - first_day?: string - /** - * @deprecated set locale with `Provider` instead. - */ - locale?: Locale - /** - * If the date picker should support a range of two dates (starting and ending date). Defaults to `false`. - */ - range?: boolean - /** - * Link both calendars, once to the user is navigating between months. Only meant to use if the range is set to `true`. Defaults to `false`. - */ - link?: boolean - /** - * Sync input values with the calendars views. Once the input values get changed, the calendar changes its views in sync. Defaults to `true`. - */ - sync?: boolean - /** - * A prepending label in sync with the date input field. - */ - label?: React.ReactNode - /** - * Use `label_direction="vertical"` to change the label layout direction. Defaults to `horizontal`. - */ - label_direction?: 'vertical' | 'horizontal' - /** - * Use `true` to make the label only readable by screen readers. - */ - label_sr_only?: boolean - /** - * Gives you the possibility to use a plain/vanilla `` HTML element by defining it as a string `input_element="input"`, a React element, or a render function `input_element={(internalProps) => ()}`. Can also be used in circumstances where the `react-text-mask` not should be used, e.g. in testing environments. Defaults to custom masked input. - */ - input_element?: InputInputElement - /** - * Gives you the possibility to inject a React element showing up over the footer. Use it to customize `shortcuts`. - */ - addon_element?: React.ReactNode - /** - * Gives you the possibility to set predefined dates and date ranges so the user can select these by one click. Define either a JSON or an object with the defined shortcuts. More info is below. - */ - shortcuts?: DatePickerAddonProps['shortcuts'] - disabled?: boolean - /** - * If set to `true`, then the date-picker input field will be 100% in `width`. - */ - stretch?: boolean - /** - * If set to `true`, an overlaying skeleton with animation will be shown. - */ - skeleton?: SkeletonShow - /** - * 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`. - */ - size?: InputSize - /** - * Text with a status message. The style defaults to an error message. You can use `true` to only get the status color, without a message. - */ - status?: FormStatusText - /** - * Defines the state of the status. Currently, there are two statuses `[error, info]`. Defaults to `error`. - */ - status_state?: FormStatusState - /** - * Use an object to define additional FormStatus properties. - */ - status_props?: FormStatusProps - status_no_animation?: boolean - /** - * The configuration used for the target GlobalStatus. - */ - globalStatus?: GlobalStatusConfigObject - /** - * Text describing the content of the DatePicker more than the label. You can also send in a React component, so it gets wrapped inside the DatePicker component. - */ - suffix?: React.ReactNode - /** - * To open the date-picker by default. Defaults to `false`. - */ - opened?: boolean - /** - * Provide a short Tooltip content that shows up on the picker button. - */ - tooltip?: React.ReactNode - tabIndex?: number - prevent_close?: boolean - no_animation?: boolean - direction?: 'auto' | 'top' | 'bottom' - /** - * Use `right` to change the calendar alignment direction. Defaults to `left`. - */ - align_picker?: 'auto' | 'left' | 'right' - className?: string - /** - * Will be called right before every new calendar view gets rendered. See the example above. - */ - on_days_render?: ( - days: Array, - nr?: DatePickerCalendarProps['nr'] - ) => void - /** - * Will be called on a date change event. Returns an `object`. See Returned Object below. - */ - on_change?: ( - event: DatePickerEvent> - ) => void - /** - * Will be called on every input and date picker interaction. Returns an `object`. See Returned Object below. - */ - on_type?: ( - event: DatePickerEvent> - ) => void - /** - * Will be called once date-picker is visible. - */ - on_show?: (event: DatePickerEvent) => void - /** - * Will be called once date-picker is hidden. - */ - on_hide?: (event: DatePickerEvent) => void - /** - * Will be called once a user presses the submit button. - */ - on_submit?: ( - event: DatePickerEvent> - ) => void - /** - * Will be called once a user presses the cancel button. - */ - on_cancel?: ( - event: DatePickerEvent> - ) => void - /** - * Will be called once a user presses the reset button. - */ - on_reset?: ( - event: DatePickerEvent> - ) => void - /** - * Will be called once the input gets focus. - */ - onFocus?: ( - event: DatePickerEvent> - ) => void - /** - * Will be called once the input lose focus. - */ - onBlur?: ( - event: DatePickerEvent> - ) => void - } +export type DatePickerProps = { + /** + * Defines the pre-filled date by either a JavaScript DateInstance or (ISO 8601) like `date="2019-05-05"`. + */ + date?: DateType + /** + * To set the pre-filled starting date. Is used if `range={true}` is set to `true`. Defaults to `null`, showing the `mask_placeholder`. + */ + start_date?: DateType + /** + * To set the pre-filled ending date. Is used if `range={true}` is set to `true`. Defaults to `null`, showing the `mask_placeholder`. + */ + end_date?: DateType + /** + * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`. + */ + month?: DateType + /** + * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`. + */ + start_month?: DateType + /** + * To display what month should be shown in the second calendar by default. Defaults to the `date` respective `start_date`. + */ + end_month?: DateType + /** + * To limit a date range to a minimum `start_date`. Defaults to `null`. + */ + min_date?: DateType + /** + * To limit a date range to a maximum `end_date`. Defaults to `null`. + */ + max_date?: DateType + /** + * 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`. + */ + correct_invalid_date?: boolean + /** + * To define the order of the masked placeholder input fields. Defaults to `dd/mm/yyyy` + */ + mask_order?: string + /** + * To display the placeholder on input. Defaults to `dd/mm/åååå`. + */ + mask_placeholder?: string + /** + * Defines how the prop dates (`date`, `start_date` and `end_date`) should be parsed, e.g. `yyyy/MM/dd`. Defaults to `yyyy-MM-dd`. + */ + date_format?: string + /** + * Defines how the returned date, as a string, should be formatted as. Defaults to `yyyy-MM-dd`. + */ + return_format?: string + /** + * If set to `true`, the navigation will be hidden. Defaults to `false`. + */ + hide_navigation?: boolean + hide_navigation_buttons?: boolean + /** + * If set to `true`, the week days will be hidden. Defaults to `false`. + */ + hide_days?: boolean + /** + * Use `true` to only show the defined month. Disables the month navigation possibility. Defaults to `false`. + */ + only_month?: boolean + /** + * Use `true` to only show the last week in the current month if it needs to be shown. The result is that there will mainly be shows five (5) weeks (rows) instead of six (6). Defaults to `false`. + */ + hide_last_week?: boolean + /** + * Once the date picker gets opened, there is a focus handling to ensure good accessibility. can be disabled with property. Defaults to `false`. + */ + disable_autofocus?: boolean + enable_keyboard_nav?: boolean + /** + * If the input fields with the mask should be visible. Defaults to `false`. + */ + showInput?: boolean + /** + * If set to `true`, a submit button will be shown. You can change the default text by using `submit_button_text="Ok"`. Defaults to `false`. If the `range` prop is `true`, then the submit button is shown. + */ + show_submit_button?: boolean + /** + * If set to `true`, a cancel button will be shown. You can change the default text by using `cancel_button_text="Avbryt"` Defaults to `false`. If the `range` prop is `true`, then the cancel button is shown. + */ + show_cancel_button?: boolean + /** + * If set to `true`, a reset button will be shown. You can change the default text by using `reset_button_text="Tilbakestill"` Defaults to `false`. + */ + show_reset_button?: boolean + submit_button_text?: string + cancel_button_text?: string + reset_button_text?: string + reset_date?: boolean + /** + * To define the first day of the week. Defaults to `monday`. + */ + first_day?: string + /** + * @deprecated set locale with `Provider` instead. + */ + locale?: Locale + /** + * If the date picker should support a range of two dates (starting and ending date). Defaults to `false`. + */ + range?: boolean + /** + * Link both calendars, once to the user is navigating between months. Only meant to use if the range is set to `true`. Defaults to `false`. + */ + link?: boolean + /** + * Sync input values with the calendars views. Once the input values get changed, the calendar changes its views in sync. Defaults to `true`. + */ + sync?: boolean + /** + * A prepending label in sync with the date input field. + */ + label?: React.ReactNode + /** + * Use `label_direction="vertical"` to change the label layout direction. Defaults to `horizontal`. + */ + label_direction?: 'vertical' | 'horizontal' + /** + * Use `true` to make the label only readable by screen readers. + */ + label_sr_only?: boolean + /** + * Gives you the possibility to use a plain/vanilla `` HTML element by defining it as a string `input_element="input"`, a React element, or a render function `input_element={(internalProps) => ()}`. Can also be used in circumstances where the `react-text-mask` not should be used, e.g. in testing environments. Defaults to custom masked input. + */ + input_element?: InputInputElement + /** + * Gives you the possibility to inject a React element showing up over the footer. Use it to customize `shortcuts`. + */ + addon_element?: React.ReactNode + /** + * Gives you the possibility to set predefined dates and date ranges so the user can select these by one click. Define either a JSON or an object with the defined shortcuts. More info is below. + */ + shortcuts?: DatePickerAddonProps['shortcuts'] + disabled?: boolean + /** + * If set to `true`, then the date-picker input field will be 100% in `width`. + */ + stretch?: boolean + /** + * If set to `true`, an overlaying skeleton with animation will be shown. + */ + skeleton?: SkeletonShow + /** + * 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`. + */ + size?: InputSize + /** + * Text with a status message. The style defaults to an error message. You can use `true` to only get the status color, without a message. + */ + status?: FormStatusText + /** + * Defines the state of the status. Currently, there are two statuses `[error, info]`. Defaults to `error`. + */ + status_state?: FormStatusState + /** + * Use an object to define additional FormStatus properties. + */ + status_props?: FormStatusProps + status_no_animation?: boolean + /** + * The configuration used for the target GlobalStatus. + */ + globalStatus?: GlobalStatusConfigObject + /** + * Text describing the content of the DatePicker more than the label. You can also send in a React component, so it gets wrapped inside the DatePicker component. + */ + suffix?: React.ReactNode + /** + * To open the date-picker by default. Defaults to `false`. + */ + opened?: boolean + /** + * Provide a short Tooltip content that shows up on the picker button. + */ + tooltip?: React.ReactNode + tabIndex?: number + prevent_close?: boolean + no_animation?: boolean + direction?: 'auto' | 'top' | 'bottom' + /** + * Use `right` to change the calendar alignment direction. Defaults to `left`. + */ + align_picker?: 'auto' | 'left' | 'right' + className?: string + /** + * Will be called right before every new calendar view gets rendered. See the example above. + */ + on_days_render?: ( + days: Array, + nr?: DatePickerCalendarProps['nr'] + ) => void + /** + * Will be called on a date change event. Returns an `object`. See Returned Object below. + */ + on_change?: ( + event: DatePickerEvent> + ) => void + /** + * Will be called on every input and date picker interaction. Returns an `object`. See Returned Object below. + */ + on_type?: ( + event: DatePickerEvent> + ) => void + /** + * Will be called once date-picker is visible. + */ + on_show?: (event: DatePickerEvent) => void + /** + * Will be called once date-picker is hidden. + */ + on_hide?: (event: DatePickerEvent) => void + /** + * Will be called once a user presses the submit button. + */ + on_submit?: ( + event: DatePickerEvent> + ) => void + /** + * Will be called once a user presses the cancel button. + */ + on_cancel?: ( + event: DatePickerEvent> + ) => void + /** + * Will be called once a user presses the reset button. + */ + on_reset?: ( + event: DatePickerEvent> + ) => void + /** + * Will be called once the input gets focus. + */ + onFocus?: (event: DatePickerEvent>) => void + /** + * Will be called once the input lose focus. + */ + onBlur?: (event: DatePickerEvent>) => void +} -const defaultProps: DatePickerProps = { +export type DatePickerAllProps = DatePickerProps & + Omit< + React.HTMLProps, + 'ref' | 'children' | 'label' | 'size' | 'onBlur' | 'onFocus' | 'start' + > & + SpacingProps +// & +// // @deprecated +// ToCamelCasePartial + +const defaultProps: DatePickerAllProps = { mask_order: 'dd/mm/yyyy', mask_placeholder: 'dd/mm/åååå', // have to be same setup as "mask" - but can be like date_format: 'yyyy-MM-dd', // in v1 of date-fns we were more flexible in terms of the format @@ -341,7 +347,7 @@ const defaultProps: DatePickerProps = { hide_last_week: false, disable_autofocus: false, enable_keyboard_nav: false, - show_input: false, + showInput: false, submit_button_text: 'Ok', cancel_button_text: 'Avbryt', reset_button_text: 'Tilbakestill', @@ -356,7 +362,7 @@ const defaultProps: DatePickerProps = { direction: 'auto', } -function DatePicker(externalProps: DatePickerProps) { +function DatePicker(externalProps: DatePickerAllProps) { const props = { ...defaultProps, ...externalProps } const { @@ -367,7 +373,7 @@ function DatePicker(externalProps: DatePickerProps) { on_cancel, on_reset, no_animation, - show_input, + showInput, align_picker, show_submit_button, show_cancel_button, @@ -376,15 +382,13 @@ function DatePicker(externalProps: DatePickerProps) { hide_navigation, opened: propsOpened, end_date, - } = props + } = convertCamelCaseProps(props) const [opened, setOpened] = useState(propsOpened) const [hidden, setHidden] = useState(!opened) const [startDate, setStartDate] = useState() const [endDate, setEndDate] = useState() - const showInput = props.show_input - const context = useContext(Context) const blurDelay = 201 // some ms more than "dropdownSlideDown 200ms" const id = props.id || makeUniqueId() @@ -460,7 +464,7 @@ function DatePicker(externalProps: DatePickerProps) { const setTrianglePosition = useCallback(() => { const triangleWidth = 16 - if (show_input && triangleRef.current && innerRef.current) { + if (showInput && triangleRef.current && innerRef.current) { try { const shellWidth = innerRef.current .querySelector('.dnb-input__shell') @@ -481,7 +485,7 @@ function DatePicker(externalProps: DatePickerProps) { warn(e) } } - }, [show_input, align_picker]) + }, [showInput, align_picker]) const showPicker = useCallback( (event?: DisplayPickerEvent) => { diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePickerContext.ts b/packages/dnb-eufemia/src/components/date-picker/DatePickerContext.ts index 62285d0a1ec..71b58afaede 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePickerContext.ts +++ b/packages/dnb-eufemia/src/components/date-picker/DatePickerContext.ts @@ -4,7 +4,7 @@ */ import React from 'react' -import { DatePickerProps, DisplayPickerEvent } from './DatePicker' +import { DatePickerAllProps, DisplayPickerEvent } from './DatePicker' import { ContextProps } from '../../shared/Context' import { DatePickerChangeEvent, @@ -19,7 +19,7 @@ export type DateType = Date | string export type DatePickerContextValues = ContextProps & DatePickerDates & { - props: DatePickerProps + props: DatePickerAllProps translation: ContextProps['translation'] views: Array hasHadValidDate: boolean diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePickerDocs.ts b/packages/dnb-eufemia/src/components/date-picker/DatePickerDocs.ts new file mode 100644 index 00000000000..0329f7c5f9f --- /dev/null +++ b/packages/dnb-eufemia/src/components/date-picker/DatePickerDocs.ts @@ -0,0 +1,249 @@ +import { PropertiesTableProps } from '../../shared/types' + +export const DatePickerProperties: PropertiesTableProps = { + help: { + doc: 'Provide a help button. Object consisting of `title` and `content`.', + type: 'object', + status: 'optional', + }, + range: { + doc: + 'Defines if the Date field should support a value of two dates (starting and ending date). ' + + 'The `value` needs to be a string containing two dates, separated by a pipe character (`|`) (`01-09-2024|30-09-2024`) when this is set to `true`. ' + + 'Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + month: { + doc: 'To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`.', + type: 'string', + status: 'optional', + }, + startMonth: { + doc: 'To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`.', + type: 'string', + status: 'optional', + }, + endMonth: { + doc: 'To display what month should be shown in the second calendar by default. Defaults to the `date` respective `start_date`.', + type: 'string', + status: 'optional', + }, + minDate: { + doc: 'To limit a date range to a minimum `start_date`. Defaults to `null`.', + type: 'string', + status: 'optional', + }, + maxDate: { + doc: 'To limit a date range to a maximum `end_date`. Defaults to `null`.', + type: 'string', + status: 'optional', + }, + dateFormat: { + doc: 'Defines how the property dates (`date`, `start_date` and `end_date`) should be parsed, e.g. `yyyy/MM/dd`. Defaults to `yyyy-MM-dd`.', + type: 'string', + status: 'optional', + }, + returnFormat: { + doc: 'Defines how the returned date, as a string, should be formatted as. Defaults to `yyyy-MM-dd`.', + type: 'string', + status: 'optional', + }, + showInput: { + doc: 'If the input fields with the mask should be visible. Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + maskOrder: { + doc: 'To define the order of the masked placeholder input fields. Defaults to `dd/mm/yyyy`.', + type: 'string', + status: 'optional', + }, + opened: { + doc: 'To open the date-picker by default. Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + maskPlaceholder: { + doc: 'To display the placeholder on input. Defaults to `dd/mm/åååå`.', + type: 'string', + status: 'optional', + }, + hideNavigation: { + doc: 'If set to `true`, the navigation will be hidden. Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + hideDays: { + doc: 'If set to `true`, the week days will be hidden. Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + showSubmitButton: { + doc: 'If set to `true`, a submit button will be shown. You can change the default text by using `submit_button_text="Ok"`. Defaults to `false`. If the `range` property is `true`, then the submit button is shown.', + type: 'boolean', + status: 'optional', + }, + showCancelButton: { + doc: 'If set to `true`, a cancel button will be shown. You can change the default text by using `cancel_button_text="Avbryt"`. If the `range` property is `true`, then the cancel button is shown. Defaults to `false`', + type: 'boolean', + status: 'optional', + }, + showResetButton: { + doc: 'If set to `true`, a reset button will be shown. You can change the default text by using `reset_button_text="Tilbakestill"`. Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + link: { + doc: 'Link both calendars, once to the user is navigating between months. Only meant to use if the range is set to `true`. Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + sync: { + doc: 'Sync input values with the calendars views. Once the input values get changed, the calendar changes its views in sync. Defaults to `true`.', + type: 'boolean', + status: 'optional', + }, + firstDay: { + doc: 'To define the first day of the week. Defaults to `monday`.', + type: 'string', + status: 'optional', + }, + alignPicker: { + doc: 'Use `right` to change the calendar alignment direction. Defaults to `left`.', + type: 'string', + status: 'optional', + }, + onlyMonth: { + doc: 'Use `true` to only show the defined month. Disables the month navigation possibility. Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + hideLastWeek: { + doc: 'Use `true` to only show the last week in the current month if it needs to be shown. The result is that there will mainly be shows five (5) weeks (rows) instead of six (6). Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, + shortcuts: { + doc: 'Gives you the possibility to set predefined dates and date ranges so the user can select these by one click. Define either a JSON or an object with the defined shortcuts. More info is below.', + type: 'object', + status: 'optional', + }, + addonElement: { + doc: 'Gives you the possibility to inject a React element showing up over the footer. Use it to customize `shortcuts`.', + type: 'object', + status: 'optional', + }, + disableAutofocus: { + doc: '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`.', + type: 'boolean', + status: 'optional', + }, + correctInvalidDate: { + doc: '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`.', + type: 'boolean', + status: 'optional', + }, + + // TODO: put in "correct" order + status: { + doc: 'Text with a status message. The style defaults to an error message. You can use `true` to only get the status color, without a message.', + type: 'string | boolean', + status: 'optional', + }, + statusState: { + doc: 'Defines the state of the status. Currently, there are two statuses `[error, info]`. Defaults to `error`.', + type: 'string', + status: 'optional', + }, + statusProps: { + doc: 'Use an object to define additional FormStatus properties.', + type: 'object', + status: 'optional', + }, + suffix: { + doc: 'Text describing the content of the DatePicker more than the label. You can also send in a React component, so it gets wrapped inside the DatePicker component.', + type: 'string | React.ReactNode', + status: 'optional', + }, + labelSrOnly: { + doc: 'Use `true` to make the label only readable by screen readers.', + type: 'boolean', + status: 'optional', + }, + inputElement: { + doc: 'Gives you the possibility to use a plain/vanilla `` HTML element by defining it as a string `input_element="input"`, a React element, or a render function `input_element={(internalProps) => ()}`. Can also be used in circumstances where the `react-text-mask` should not be used, e.g. in testing environments. Defaults to custom masked input.', + type: 'string | React.ReactNode | (internalProps: any) => React.ReactNode', + status: 'optional', + }, + tooltip: { + doc: 'Provide a short Tooltip content that shows up on the picker button.', + type: 'string', + status: 'optional', + }, + skeleton: { + doc: 'If set to `true`, an overlaying skeleton with animation will be shown.', + type: 'boolean', + status: 'optional', + }, + size: { + doc: '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`.', + type: 'string', + status: 'optional', + }, + '[Space](/uilib/layout/space/properties)': { + doc: 'Spacing properties like `top` or `bottom` are supported.', + type: ['string', 'object'], + status: 'optional', + }, + + // TODO: remove this + // | `status` | _(optional)_ text with a status message. The style defaults to an error message. You can use `true` to only get the status color, without a message. | + // | `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. | + // | `suffix` | _(optional)_ text describing the content of the DatePicker more than the label. You can also send in a React component, so it gets wrapped inside the DatePicker component. | + // | `label_sr_only` | _(optional)_ use `true` to make the label only readable by screen readers. | + // | `input_element` | _(optional)_ gives you the possibility to use a plain/vanilla `` HTML element by defining it as a string `input_element="input"`, a React element, or a render function `input_element={(internalProps) => ()}`. Can also be used in circumstances where the `react-text-mask` should not be used, e.g. in testing environments. Defaults to custom masked input. | + // | `tooltip` | _(optional)_ Provide a short Tooltip content that shows up on the picker button. | + // | `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`. | + // | [Space](/uilib/layout/space/properties) | _(optional)_ spacing properties like `top` or `bottom` are supported. | +} + +export const DatePickerEvents: PropertiesTableProps = { + onType: { + doc: 'Will be called on every input and date picker interaction. Returns an `object`. See Returned Object below.', + type: 'function', + status: 'optional', + }, + onSubmit: { + doc: 'Will be called once a user presses the submit button.', + type: 'function', + status: 'optional', + }, + onCancel: { + doc: 'Will be called once a user presses the cancel button.', + type: 'function', + status: 'optional', + }, + onReset: { + doc: 'Will be called once a user presses the reset button.', + type: 'function', + status: 'optional', + }, + onShow: { + doc: 'Will be called once date-picker is visible.', + type: 'function', + status: 'optional', + }, + onHide: { + doc: 'Will be called once date-picker is hidden.', + type: 'function', + status: 'optional', + }, + onDaysRender: { + doc: 'Will be called right before every new calendar view gets rendered. See the example above.', + type: 'function', + status: 'optional', + }, +} diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePickerProvider.tsx b/packages/dnb-eufemia/src/components/date-picker/DatePickerProvider.tsx index d1ce97072e0..78783742567 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePickerProvider.tsx +++ b/packages/dnb-eufemia/src/components/date-picker/DatePickerProvider.tsx @@ -6,7 +6,7 @@ import React, { useCallback, useContext } from 'react' import type { DatePickerEventAttributes, - DatePickerProps, + DatePickerAllProps, } from './DatePicker' import isValid from 'date-fns/isValid' @@ -24,7 +24,7 @@ import useLastEventCallCache, { LastEventCallCache, } from './hooks/useLastEventCallCache' -type DatePickerProviderProps = DatePickerProps & { +type DatePickerProviderProps = DatePickerAllProps & { setReturnObject: ( func: DatePickerContextValues['getReturnObject'] ) => DatePickerContextValues['getReturnObject'] 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 ada5846f24f..ab59863db50 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 @@ -6,7 +6,7 @@ import React from 'react' import { axeComponent, loadScss, wait } from '../../../core/jest/jestSetup' import userEvent from '@testing-library/user-event' -import DatePicker, { DatePickerProps } from '../DatePicker' +import DatePicker, { DatePickerAllProps } from '../DatePicker' jest.setTimeout(30e3) @@ -28,14 +28,14 @@ import { Provider } from '../../../shared' describe('DatePicker component', () => { it('renders with props as an object', () => { - const props: DatePickerProps = {} + const props: DatePickerAllProps = {} render() expect(document.querySelector('input')).toBeInTheDocument() }) // for the integration tests - const defaultProps: DatePickerProps = { + const defaultProps: DatePickerAllProps = { no_animation: true, range: true, show_input: true, @@ -1946,7 +1946,7 @@ describe('DatePicker component', () => { }) it('renders correct placeholder when setting locale', () => { - const props: DatePickerProps = {} + const props: DatePickerAllProps = {} render( diff --git a/packages/dnb-eufemia/src/shared/Context.tsx b/packages/dnb-eufemia/src/shared/Context.tsx index 3a86c930998..3e69ecad81b 100644 --- a/packages/dnb-eufemia/src/shared/Context.tsx +++ b/packages/dnb-eufemia/src/shared/Context.tsx @@ -56,7 +56,7 @@ import type { FormElementProps } from './helpers/filterValidProps' import type { ThemeProps } from './Theme' import type { FormsTranslation } from '../extensions/forms/hooks/useTranslation' import type { DeepPartial } from './types' -import { DatePickerProps } from '../components/DatePicker' +import { DatePickerAllProps } from '../components/DatePicker' export type ContextComponents = { Button?: Partial @@ -94,7 +94,7 @@ export type ContextComponents = { Logo?: Partial Icon?: Partial IconPrimary?: Partial - DatePicker?: Partial + DatePicker?: Partial ListFormat?: Partial Switch?: Partial From 8483504ec7c196f4dbe33930d8d94eaef1da2582 Mon Sep 17 00:00:00 2001 From: Joakim Bjerknes Date: Tue, 12 Nov 2024 19:05:28 +0100 Subject: [PATCH 02/19] convert snake_case to camelCase --- .../src/components/date-picker/DatePicker.tsx | 400 ++++++++++-------- .../date-picker/DatePickerAddon.tsx | 1 + .../components/date-picker/DatePickerCalc.ts | 10 +- .../date-picker/DatePickerCalendar.tsx | 20 +- .../components/date-picker/DatePickerDocs.ts | 22 +- .../date-picker/DatePickerFooter.tsx | 40 +- .../date-picker/DatePickerInput.tsx | 50 +-- .../date-picker/DatePickerProvider.tsx | 55 +-- .../date-picker/__tests__/DatePicker.test.tsx | 2 +- .../components/date-picker/hooks/useDates.ts | 14 +- 10 files changed, 325 insertions(+), 289 deletions(-) diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePicker.tsx b/packages/dnb-eufemia/src/components/date-picker/DatePicker.tsx index 6661fb59bb1..2a082c4355d 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePicker.tsx +++ b/packages/dnb-eufemia/src/components/date-picker/DatePicker.tsx @@ -59,6 +59,15 @@ import { ToCamelCasePartial, convertCamelCaseProps, } from '../../shared/helpers/withCamelCaseProps' +import { + ToSnakeCasePartial, + convertSnakeCaseProps, +} from '../../shared/helpers/withSnakeCaseProps' +import { + TranslationsEnGB, + TranslationsEnUS, + TranslationsNbNO, +} from '../../shared/locales' export type DatePickerEventAttributes = { day?: string @@ -86,99 +95,99 @@ export type DatePickerProps = { */ date?: DateType /** - * To set the pre-filled starting date. Is used if `range={true}` is set to `true`. Defaults to `null`, showing the `mask_placeholder`. + * To set the pre-filled starting date. Is used if `range={true}` is set to `true`. Defaults to `null`, showing the `maskPlaceholder`. */ - start_date?: DateType + startDate?: DateType /** - * To set the pre-filled ending date. Is used if `range={true}` is set to `true`. Defaults to `null`, showing the `mask_placeholder`. + * To set the pre-filled ending date. Is used if `range={true}` is set to `true`. Defaults to `null`, showing the `maskPlaceholder`. */ - end_date?: DateType + endDate?: DateType /** - * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`. + * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `startDate`. */ month?: DateType /** - * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`. + * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `startDate`. */ - start_month?: DateType + startMonth?: DateType /** - * To display what month should be shown in the second calendar by default. Defaults to the `date` respective `start_date`. + * To display what month should be shown in the second calendar by default. Defaults to the `date` respective `startDate`. */ - end_month?: DateType + endMonth?: DateType /** - * To limit a date range to a minimum `start_date`. Defaults to `null`. + * To limit a date range to a minimum `startDate`. Defaults to `null`. */ - min_date?: DateType + minDate?: DateType /** - * To limit a date range to a maximum `end_date`. Defaults to `null`. + * To limit a date range to a maximum `endDate`. Defaults to `null`. */ - max_date?: DateType + maxDate?: DateType /** - * 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`. + * Corrects the input date value to be the same as either `minDate` or `maxDate`, when the user types in a date that is either before or after one of these. Defaults to `false`. */ - correct_invalid_date?: boolean + correctInvalidDate?: boolean /** * To define the order of the masked placeholder input fields. Defaults to `dd/mm/yyyy` */ - mask_order?: string + maskOrder?: string /** * To display the placeholder on input. Defaults to `dd/mm/åååå`. */ - mask_placeholder?: string + maskPlaceholder?: string /** - * Defines how the prop dates (`date`, `start_date` and `end_date`) should be parsed, e.g. `yyyy/MM/dd`. Defaults to `yyyy-MM-dd`. + * Defines how the prop dates (`date`, `startDate` and `endDate`) should be parsed, e.g. `yyyy/MM/dd`. Defaults to `yyyy-MM-dd`. */ - date_format?: string + dateFormat?: string /** * Defines how the returned date, as a string, should be formatted as. Defaults to `yyyy-MM-dd`. */ - return_format?: string + returnFormat?: string /** * If set to `true`, the navigation will be hidden. Defaults to `false`. */ - hide_navigation?: boolean - hide_navigation_buttons?: boolean + hideNavigation?: boolean + hideNavigationButtons?: boolean /** * If set to `true`, the week days will be hidden. Defaults to `false`. */ - hide_days?: boolean + hideDays?: boolean /** * Use `true` to only show the defined month. Disables the month navigation possibility. Defaults to `false`. */ - only_month?: boolean + onlyMonth?: boolean /** * Use `true` to only show the last week in the current month if it needs to be shown. The result is that there will mainly be shows five (5) weeks (rows) instead of six (6). Defaults to `false`. */ - hide_last_week?: boolean + hideLastWeek?: boolean /** * Once the date picker gets opened, there is a focus handling to ensure good accessibility. can be disabled with property. Defaults to `false`. */ - disable_autofocus?: boolean - enable_keyboard_nav?: boolean + disableAutofocus?: boolean + enableKeyboardNav?: boolean /** * If the input fields with the mask should be visible. Defaults to `false`. */ showInput?: boolean /** - * If set to `true`, a submit button will be shown. You can change the default text by using `submit_button_text="Ok"`. Defaults to `false`. If the `range` prop is `true`, then the submit button is shown. + * If set to `true`, a submit button will be shown. You can change the default text by using `submitButtonText="Ok"`. Defaults to `false`. If the `range` prop is `true`, then the submit button is shown. */ - show_submit_button?: boolean + showSubmitButton?: boolean /** - * If set to `true`, a cancel button will be shown. You can change the default text by using `cancel_button_text="Avbryt"` Defaults to `false`. If the `range` prop is `true`, then the cancel button is shown. + * If set to `true`, a cancel button will be shown. You can change the default text by using `cancelButtonText="Avbryt"` Defaults to `false`. If the `range` prop is `true`, then the cancel button is shown. */ - show_cancel_button?: boolean + showCancelButton?: boolean /** - * If set to `true`, a reset button will be shown. You can change the default text by using `reset_button_text="Tilbakestill"` Defaults to `false`. + * If set to `true`, a reset button will be shown. You can change the default text by using `resetButtonText="Tilbakestill"` Defaults to `false`. */ - show_reset_button?: boolean - submit_button_text?: string - cancel_button_text?: string - reset_button_text?: string - reset_date?: boolean + showResetButton?: boolean + submitButtonText?: string + cancelButtonText?: string + resetButtonText?: string + resetDate?: boolean /** * To define the first day of the week. Defaults to `monday`. */ - first_day?: string + firstDay?: string /** * @deprecated set locale with `Provider` instead. */ @@ -200,21 +209,21 @@ export type DatePickerProps = { */ label?: React.ReactNode /** - * Use `label_direction="vertical"` to change the label layout direction. Defaults to `horizontal`. + * Use `labelDirection="vertical"` to change the label layout direction. Defaults to `horizontal`. */ - label_direction?: 'vertical' | 'horizontal' + labelDirection?: 'vertical' | 'horizontal' /** * Use `true` to make the label only readable by screen readers. */ - label_sr_only?: boolean + labelSrOnly?: boolean /** - * Gives you the possibility to use a plain/vanilla `` HTML element by defining it as a string `input_element="input"`, a React element, or a render function `input_element={(internalProps) => ()}`. Can also be used in circumstances where the `react-text-mask` not should be used, e.g. in testing environments. Defaults to custom masked input. + * Gives you the possibility to use a plain/vanilla `` HTML element by defining it as a string `inputElement="input"`, a React element, or a render function `inputElement={(internalProps) => ()}`. Can also be used in circumstances where the `react-text-mask` not should be used, e.g. in testing environments. Defaults to custom masked input. */ - input_element?: InputInputElement + inputElement?: InputInputElement /** * Gives you the possibility to inject a React element showing up over the footer. Use it to customize `shortcuts`. */ - addon_element?: React.ReactNode + addonElement?: React.ReactNode /** * Gives you the possibility to set predefined dates and date ranges so the user can select these by one click. Define either a JSON or an object with the defined shortcuts. More info is below. */ @@ -239,12 +248,12 @@ export type DatePickerProps = { /** * Defines the state of the status. Currently, there are two statuses `[error, info]`. Defaults to `error`. */ - status_state?: FormStatusState + statusState?: FormStatusState /** * Use an object to define additional FormStatus properties. */ - status_props?: FormStatusProps - status_no_animation?: boolean + statusProps?: FormStatusProps + statusNoAnimation?: boolean /** * The configuration used for the target GlobalStatus. */ @@ -262,57 +271,57 @@ export type DatePickerProps = { */ tooltip?: React.ReactNode tabIndex?: number - prevent_close?: boolean - no_animation?: boolean + preventClose?: boolean + noAnimation?: boolean direction?: 'auto' | 'top' | 'bottom' /** * Use `right` to change the calendar alignment direction. Defaults to `left`. */ - align_picker?: 'auto' | 'left' | 'right' + alignPicker?: 'auto' | 'left' | 'right' className?: string /** * Will be called right before every new calendar view gets rendered. See the example above. */ - on_days_render?: ( + onDaysRender?: ( days: Array, nr?: DatePickerCalendarProps['nr'] ) => void /** * Will be called on a date change event. Returns an `object`. See Returned Object below. */ - on_change?: ( + onChange?: ( event: DatePickerEvent> ) => void /** * Will be called on every input and date picker interaction. Returns an `object`. See Returned Object below. */ - on_type?: ( + onType?: ( event: DatePickerEvent> ) => void /** * Will be called once date-picker is visible. */ - on_show?: (event: DatePickerEvent) => void + onShow?: (event: DatePickerEvent) => void /** * Will be called once date-picker is hidden. */ - on_hide?: (event: DatePickerEvent) => void + onHide?: (event: DatePickerEvent) => void /** * Will be called once a user presses the submit button. */ - on_submit?: ( + onSubmit?: ( event: DatePickerEvent> ) => void /** * Will be called once a user presses the cancel button. */ - on_cancel?: ( + onCancel?: ( event: DatePickerEvent> ) => void /** * Will be called once a user presses the reset button. */ - on_reset?: ( + onReset?: ( event: DatePickerEvent> ) => void /** @@ -329,36 +338,40 @@ export type DatePickerAllProps = DatePickerProps & Omit< React.HTMLProps, 'ref' | 'children' | 'label' | 'size' | 'onBlur' | 'onFocus' | 'start' - > & - SpacingProps + > // & // // @deprecated // ToCamelCasePartial -const defaultProps: DatePickerAllProps = { - mask_order: 'dd/mm/yyyy', - mask_placeholder: 'dd/mm/åååå', // have to be same setup as "mask" - but can be like - date_format: 'yyyy-MM-dd', // in v1 of date-fns we were more flexible in terms of the format - return_format: 'yyyy-MM-dd', // used in date-fns v1 - hide_navigation: false, - hide_navigation_buttons: false, - hide_days: false, - only_month: false, - hide_last_week: false, - disable_autofocus: false, - enable_keyboard_nav: false, +// Added to prevent type errors when destructuring the translations from props +type DatePickerTranslations = Partial & + Partial & + Partial + +const defaultProps: DatePickerProps = { + maskOrder: 'dd/mm/yyyy', + maskPlaceholder: 'dd/mm/åååå', // have to be same setup as "mask" - but can be like + dateFormat: 'yyyy-MM-dd', // in v1 of date-fns we were more flexible in terms of the format + returnFormat: 'yyyy-MM-dd', // used in date-fns v1 + hideNavigation: false, + hideNavigationButtons: false, + hideDays: false, + onlyMonth: false, + hideLastWeek: false, + disableAutofocus: false, + enableKeyboardNav: false, showInput: false, - submit_button_text: 'Ok', - cancel_button_text: 'Avbryt', - reset_button_text: 'Tilbakestill', - reset_date: true, - first_day: 'monday', + submitButtonText: 'Ok', + cancelButtonText: 'Avbryt', + resetButtonText: 'Tilbakestill', + resetDate: true, + firstDay: 'monday', range: false, link: false, sync: true, - status_state: 'error', + statusState: 'error', opened: false, - no_animation: false, + noAnimation: false, direction: 'auto', } @@ -366,28 +379,29 @@ function DatePicker(externalProps: DatePickerAllProps) { const props = { ...defaultProps, ...externalProps } const { - prevent_close, - on_hide, - on_show, - on_submit, - on_cancel, - on_reset, - no_animation, + preventClose, + onHide, + onShow, + onSubmit, + onCancel, + onReset, + noAnimation, showInput, - align_picker, - show_submit_button, - show_cancel_button, + alignPicker, + showSubmitButton, + showCancelButton, range, - hide_days, - hide_navigation, - opened: propsOpened, - end_date, - } = convertCamelCaseProps(props) + hideDays, + hideNavigation, + opened: openedProp, + endDate: endDateProp, + } = convertSnakeCaseProps(props) - const [opened, setOpened] = useState(propsOpened) + const [opened, setOpened] = useState(openedProp) const [hidden, setHidden] = useState(!opened) - const [startDate, setStartDate] = useState() - const [endDate, setEndDate] = useState() + const [dates, setDates] = useState< + Pick + >({}) const context = useContext(Context) const blurDelay = 201 // some ms more than "dropdownSlideDown 200ms" @@ -403,9 +417,9 @@ function DatePicker(externalProps: DatePickerAllProps) { const translation = useTranslation().DatePicker - if (end_date && !range) { + if (endDateProp && !range) { warn( - `The DatePicker got a "end_date". You have to set range={true} as well!.` + `The DatePicker got a "endDate". You have to set range={true} as well!.` ) } @@ -417,7 +431,7 @@ function DatePicker(externalProps: DatePickerAllProps) { const hidePicker = useCallback( (args?: DisplayPickerEvent) => { - if (prevent_close) { + if (preventClose) { return // stop here } @@ -428,7 +442,7 @@ function DatePicker(externalProps: DatePickerAllProps) { setOpened(false) // Double check and compare return - on_hide?.({ + onHide?.({ ...getReturnObject.current(args), }) @@ -445,12 +459,12 @@ function DatePicker(externalProps: DatePickerAllProps) { } } }, - no_animation ? 1 : blurDelay + noAnimation ? 1 : blurDelay ) // wait until animation is over removeOutsideClickHandler() }, - [no_animation, prevent_close, on_hide, removeOutsideClickHandler] + [noAnimation, preventClose, onHide, removeOutsideClickHandler] ) const setOutsideClickHandler = useCallback(() => { @@ -474,7 +488,7 @@ function DatePicker(externalProps: DatePickerAllProps) { .querySelector('.dnb-input__submit-button__button') .getBoundingClientRect().width - if (align_picker === 'right') { + if (alignPicker === 'right') { const distance = buttonWidth / 2 - triangleWidth / 2 triangleRef.current.style.marginRight = `${distance / 16}rem` } else { @@ -485,7 +499,7 @@ function DatePicker(externalProps: DatePickerAllProps) { warn(e) } } - }, [showInput, align_picker]) + }, [showInput, alignPicker]) const showPicker = useCallback( (event?: DisplayPickerEvent) => { @@ -496,16 +510,17 @@ function DatePicker(externalProps: DatePickerAllProps) { setOpened(true) setHidden(false) - on_show?.({ ...getReturnObject.current(event) }) + onShow?.({ ...getReturnObject.current(event) }) setTrianglePosition() setOutsideClickHandler() }, - [setTrianglePosition, setOutsideClickHandler, on_show] + [setTrianglePosition, setOutsideClickHandler, onShow] ) + // React to opened prop changes useEffect(() => { - if (propsOpened) { + if (openedProp) { showPicker() } @@ -513,7 +528,7 @@ function DatePicker(externalProps: DatePickerAllProps) { clearTimeout(hideTimeout.current) removeOutsideClickHandler() } - }, [propsOpened, removeOutsideClickHandler, showPicker]) + }, [openedProp, removeOutsideClickHandler, showPicker]) const onPickerChange = useCallback( ({ @@ -523,24 +538,23 @@ function DatePicker(externalProps: DatePickerAllProps) { | React.MouseEvent | React.KeyboardEvent >) => { - if (shouldHidePicker && !show_submit_button && !show_cancel_button) { + if (shouldHidePicker && !showSubmitButton && !showCancelButton) { hidePicker() } - setStartDate(args.startDate) - setEndDate(args.endDate) + setDates({ startDate: args.startDate, endDate: args.endDate }) }, - [hidePicker, show_submit_button, show_cancel_button] + [hidePicker, showSubmitButton, showCancelButton] ) const onSubmitHandler = useCallback( (event: React.MouseEvent) => { hidePicker(event) - on_submit?.({ + onSubmit?.({ ...getReturnObject.current({ event }), }) }, - [hidePicker, on_submit] + [hidePicker, onSubmit] ) const onCancelHandler = useCallback( @@ -548,9 +562,9 @@ function DatePicker(externalProps: DatePickerAllProps) { event: DatePickerChangeEvent> ) => { hidePicker() - on_cancel?.({ ...getReturnObject.current(event) }) + onCancel?.({ ...getReturnObject.current(event) }) }, - [hidePicker, on_cancel] + [hidePicker, onCancel] ) const onResetHandler = useCallback( @@ -558,9 +572,9 @@ function DatePicker(externalProps: DatePickerAllProps) { event: DatePickerChangeEvent> ) => { hidePicker() - on_reset?.({ ...getReturnObject.current(event) }) + onReset?.({ ...getReturnObject.current(event) }) }, - [hidePicker, on_reset] + [hidePicker, onReset] ) const togglePicker = useCallback( @@ -571,7 +585,8 @@ function DatePicker(externalProps: DatePickerAllProps) { ) const formatSelectedDateTitle = useCallback(() => { - const { selected_date, start, end } = translation + const { selectedDate, start, end } = translation + const { startDate, endDate } = dates let currentDate = startDate ? format(startDate, 'PPPP') : null @@ -582,8 +597,8 @@ function DatePicker(externalProps: DatePickerAllProps) { )}` } - return currentDate ? selected_date.replace(/%s/, currentDate) : '' - }, [range, translation, startDate, endDate]) + return currentDate ? selectedDate.replace(/%s/, currentDate) : '' + }, [range, translation, dates]) // use only the props from context, who are available here anyway const extendedProps = extendPropsWithContext( @@ -599,35 +614,35 @@ function DatePicker(externalProps: DatePickerAllProps) { const { label, title, - label_direction, - label_sr_only, - only_month, - hide_last_week, - disable_autofocus, - hide_navigation_buttons, - first_day, - reset_date, + labelDirection, + labelSrOnly, + onlyMonth, + hideLastWeek, + disableAutofocus, + hideNavigationButtons, + firstDay, + resetDate, link, sync, - input_element, - addon_element, + inputElement, + addonElement, shortcuts, disabled, stretch, skeleton, size, status, - status_state, - status_props, - status_no_animation, + statusState, + statusProps, + statusNoAnimation, globalStatus, suffix, - mask_order, - mask_placeholder, - submit_button_text, - cancel_button_text, - reset_button_text, - show_reset_button, + maskOrder, + maskPlaceholder, + submitButtonText, + cancelButtonText, + resetButtonText, + showResetButton, className, tooltip, ...restProps @@ -641,30 +656,50 @@ function DatePicker(externalProps: DatePickerAllProps) { id, month, date, - start_date, - end_date, - min_date, - max_date, - enable_keyboard_nav, - hide_navigation, - return_format, - date_format, - hide_days, - correct_invalid_date, + startDate, + endDate, + minDate, + maxDate, + enableKeyboardNav, + hideNavigation, + returnFormat, + dateFormat, + hideDays, + correctInvalidDate, opened, direction, range, + showInput, + noAnimation, + onDaysRender, + onShow, + onType, + onHide, + showSubmitButton, + showCancelButton, + // These translations needs to be filtered out here, since the validateDOMAttributes + // is unable to filter out the translation props after they have been camelCased + selectedDate, + selectedMonth, + selectedYear, + nextMonth, + nextYear, + openPickerText, + placeholderCharacters, + prevMonth, + prevYear, + ...rest - } = restProps + } = restProps as DatePickerAllProps & DatePickerTranslations attributes = rest } - const shouldHideDays = only_month ? true : hide_days - const shouldHideNavigation = only_month - ? hide_navigation_buttons + const shouldHideDays = onlyMonth ? true : hideDays + const shouldHideNavigation = onlyMonth + ? hideNavigationButtons ? false : true - : hide_navigation + : hideNavigation const showStatus = getStatusState(status) @@ -690,17 +725,14 @@ function DatePicker(externalProps: DatePickerAllProps) { const mainParams = { className: classnames( 'dnb-date-picker', - status && `dnb-date-picker__status--${status_state}`, - label_direction && `dnb-date-picker--${label_direction}`, + status && `dnb-date-picker__status--${statusState}`, + labelDirection && `dnb-date-picker--${labelDirection}`, opened && 'dnb-date-picker--opened', hidden && 'dnb-date-picker--hidden', showInput && 'dnb-date-picker--show-input', - (range || - show_submit_button || - show_cancel_button || - show_reset_button) && + (range || showSubmitButton || showCancelButton || showResetButton) && 'dnb-date-picker--show-footer', - align_picker && `dnb-date-picker--${align_picker}`, + alignPicker && `dnb-date-picker--${alignPicker}`, stretch && `dnb-date-picker--stretch`, 'dnb-form-component', size && `dnb-date-picker--${size}`, @@ -730,8 +762,8 @@ function DatePicker(externalProps: DatePickerAllProps) { id={id + '-label'} forId={id} text={label} - label_direction={label_direction} - srOnly={label_sr_only} + labelDirection={labelDirection} + srOnly={labelSrOnly} disabled={disabled} skeleton={skeleton} /> @@ -752,10 +784,10 @@ function DatePicker(externalProps: DatePickerAllProps) { text_id={id + '-status'} // used for "aria-describedby" width_selector={id + '-shell'} text={status} - state={status_state} - no_animation={status_no_animation} + state={statusState} + no_animation={statusNoAnimation} skeleton={skeleton} - {...status_props} + {...statusProps} /> @@ -766,22 +798,22 @@ function DatePicker(externalProps: DatePickerAllProps) { disabled={disabled} stretch={stretch} skeleton={skeleton} - maskOrder={mask_order} - maskPlaceholder={mask_placeholder} + maskOrder={maskOrder} + maskPlaceholder={maskPlaceholder} isRange={range} showInput={showInput} selectedDateTitle={selectedDateTitle} - input_element={input_element} + inputElement={inputElement} opened={opened} hidden={hidden} size={size} status={status ? 'error' : null} - status_state={status_state} + statusState={statusState} lang={context.locale} {...attributes} submitAttributes={remainingSubmitProps} onSubmit={togglePicker} - {...status_props} + {...statusProps} /> - {(addon_element || shortcuts) && ( + {(addonElement || shortcuts) && ( )} @@ -821,9 +853,9 @@ function DatePicker(externalProps: DatePickerAllProps) { onSubmit={onSubmitHandler} onCancel={onCancelHandler} onReset={onResetHandler} - submitButtonText={submit_button_text} - cancelButtonText={cancel_button_text} - resetButtonText={reset_button_text} + submitButtonText={submitButtonText} + cancelButtonText={cancelButtonText} + resetButtonText={resetButtonText} /> )} diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePickerAddon.tsx b/packages/dnb-eufemia/src/components/date-picker/DatePickerAddon.tsx index 3d6b7c79dbb..30fb234bc0f 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePickerAddon.tsx +++ b/packages/dnb-eufemia/src/components/date-picker/DatePickerAddon.tsx @@ -8,6 +8,7 @@ import { convertStringToDate } from './DatePickerCalc' import Button from '../button/Button' import DatePickerContext from './DatePickerContext' +// TODO: convert shortucts properties to camelCase, constitutes a breaking change export type DatePickerShortcut = { title?: string date?: string | Date | ((...args: unknown[]) => Date) diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePickerCalc.ts b/packages/dnb-eufemia/src/components/date-picker/DatePickerCalc.ts index 0b8481e831a..a9ad4a1426b 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePickerCalc.ts +++ b/packages/dnb-eufemia/src/components/date-picker/DatePickerCalc.ts @@ -239,7 +239,7 @@ export function correctV1Format(date: string) { // TODO: Remove this in next major version if (/YYYY/.test(date) && /DD/.test(date)) { warn( - 'You are using "YYYY-MM-DD" as the date_format or return_format? Please use "yyyy-MM-dd" instead!' + 'You are using "YYYY-MM-DD" as the dateFormat or returnFormat? Please use "yyyy-MM-dd" instead!' ) date = date.replace(/DD/, 'dd').replace(/YYYY/, 'yyyy') } @@ -249,7 +249,7 @@ export function correctV1Format(date: string) { export function convertStringToDate( date: string | Date, - { date_format = null }: { date_format?: string | null } = {} + { dateFormat = null }: { dateFormat?: string | null } = {} ): Date { if (!date) { return null @@ -259,9 +259,9 @@ export function convertStringToDate( dateObject = typeof date === 'string' ? parseISO(date) : toDate(date) // check one more time if we can generate a valid date - if (typeof date === 'string' && date_format && !isValid(dateObject)) { - date_format = correctV1Format(date_format) - dateObject = parse(date, date_format, new Date()) + if (typeof date === 'string' && dateFormat && !isValid(dateObject)) { + dateFormat = correctV1Format(dateFormat) + dateObject = parse(date, dateFormat, new Date()) } // rather return null than an invalid date diff --git a/packages/dnb-eufemia/src/components/date-picker/DatePickerCalendar.tsx b/packages/dnb-eufemia/src/components/date-picker/DatePickerCalendar.tsx index f6227d483f5..2d262c77c00 100644 --- a/packages/dnb-eufemia/src/components/date-picker/DatePickerCalendar.tsx +++ b/packages/dnb-eufemia/src/components/date-picker/DatePickerCalendar.tsx @@ -86,7 +86,7 @@ export type DatePickerCalendarProps = Omit< id?: string nr?: number /** - * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `start_date`. + * To display what month should be shown in the first calendar by default. Defaults to the `date` respective `startDate`. */ month?: Date prevBtn?: boolean @@ -169,9 +169,9 @@ function DatePickerCalendar(restOfProps: DatePickerCalendarProps) { startMonth, endMonth, translation: { - DatePicker: { selected_month }, + DatePicker: { selectedMonth }, }, - props: { on_days_render }, + props: { onDaysRender }, } = useContext(DatePickerContext) const { @@ -251,8 +251,8 @@ function DatePickerCalendar(restOfProps: DatePickerCalendarProps) { }) ) - if (on_days_render) { - const changedDays = on_days_render(daysFromCalendar, nr) + if (onDaysRender) { + const changedDays = onDaysRender(daysFromCalendar, nr) if (Array.isArray(changedDays)) { daysFromCalendar = changedDays } @@ -271,7 +271,7 @@ function DatePickerCalendar(restOfProps: DatePickerCalendarProps) { maxDate, minDate, nr, - on_days_render, + onDaysRender, onlyMonth, startDate, ] @@ -293,7 +293,7 @@ function DatePickerCalendar(restOfProps: DatePickerCalendarProps) { const findValid = useCallback( (date: Date, keyCode: string) => { - if (!on_days_render) { + if (!onDaysRender) { return date } @@ -330,7 +330,7 @@ function DatePickerCalendar(restOfProps: DatePickerCalendarProps) { return date }, - [on_days_render, getDays, keyNavCalc] + [onDaysRender, getDays, keyNavCalc] ) const hasReachedEnd = useCallback( @@ -539,7 +539,7 @@ function DatePickerCalendar(restOfProps: DatePickerCalendarProps) {