diff --git a/packages/twenty-front/src/modules/object-record/record-field/components/FormFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/components/FormFieldInput.tsx index 8a11b321550e..38084a4d26ae 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/components/FormFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/components/FormFieldInput.tsx @@ -3,6 +3,7 @@ import { FormBooleanFieldInput } from '@/object-record/record-field/form-types/c import { FormEmailsFieldInput } from '@/object-record/record-field/form-types/components/FormEmailsFieldInput'; import { FormFullNameFieldInput } from '@/object-record/record-field/form-types/components/FormFullNameFieldInput'; import { FormLinksFieldInput } from '@/object-record/record-field/form-types/components/FormLinksFieldInput'; +import { FormDateFieldInput } from '@/object-record/record-field/form-types/components/FormDateFieldInput'; import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput'; import { FormSelectFieldInput } from '@/object-record/record-field/form-types/components/FormSelectFieldInput'; import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput'; @@ -20,6 +21,7 @@ import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFiel import { isFieldEmails } from '@/object-record/record-field/types/guards/isFieldEmails'; import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName'; import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks'; +import { isFieldDate } from '@/object-record/record-field/types/guards/isFieldDate'; import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText'; @@ -98,5 +100,12 @@ export const FormFieldInput = ({ onPersist={onPersist} VariablePicker={VariablePicker} /> + ) : isFieldDate(field) ? ( + ) : null; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormDateFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormDateFieldInput.tsx new file mode 100644 index 000000000000..751741c6d85d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormDateFieldInput.tsx @@ -0,0 +1,374 @@ +import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer'; +import { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer'; +import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer'; +import { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; +import { StyledCalendarContainer } from '@/ui/field/input/components/DateInput'; +import { InputLabel } from '@/ui/input/components/InputLabel'; +import { + InternalDatePicker, + MONTH_AND_YEAR_DROPDOWN_ID, + MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID, + MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID, +} from '@/ui/input/components/internal/date/components/InternalDatePicker'; +import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate'; +import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate'; +import { parseDateToString } from '@/ui/input/components/internal/date/utils/parseDateToString'; +import { parseStringToDate } from '@/ui/input/components/internal/date/utils/parseStringToDate'; +import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; +import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { UserContext } from '@/users/contexts/UserContext'; +import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString'; +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; +import { + ChangeEvent, + KeyboardEvent, + useContext, + useId, + useRef, + useState, +} from 'react'; +import { isDefined, Nullable, TEXT_INPUT_STYLE } from 'twenty-ui'; + +const StyledInputContainer = styled(FormFieldInputInputContainer)` + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 1fr 0px; + overflow: visible; + position: relative; +`; + +const StyledDateInputAbsoluteContainer = styled.div` + position: absolute; +`; + +const StyledDateInput = styled.input<{ hasError?: boolean }>` + ${TEXT_INPUT_STYLE} + + ${({ hasError, theme }) => + hasError && + css` + color: ${theme.color.red}; + `}; +`; + +const StyledDateInputContainer = styled.div` + position: relative; + z-index: 1; +`; + +type DraftValue = + | { + type: 'static'; + value: string | null; + mode: 'view' | 'edit'; + } + | { + type: 'variable'; + value: string; + }; + +type FormDateFieldInputProps = { + label?: string; + defaultValue: string | undefined; + onPersist: (value: string | null) => void; + VariablePicker?: VariablePickerComponent; +}; + +export const FormDateFieldInput = ({ + label, + defaultValue, + onPersist, + VariablePicker, +}: FormDateFieldInputProps) => { + const { timeZone } = useContext(UserContext); + + const inputId = useId(); + + const [draftValue, setDraftValue] = useState( + isStandaloneVariableString(defaultValue) + ? { + type: 'variable', + value: defaultValue, + } + : { + type: 'static', + value: defaultValue ?? null, + mode: 'view', + }, + ); + + const draftValueAsDate = isDefined(draftValue.value) + ? new Date(draftValue.value) + : null; + + const [pickerDate, setPickerDate] = + useState>(draftValueAsDate); + + const datePickerWrapperRef = useRef(null); + + const [inputDateTime, setInputDateTime] = useState( + isDefined(draftValueAsDate) && !isStandaloneVariableString(defaultValue) + ? parseDateToString({ + date: draftValueAsDate, + isDateTimeInput: false, + userTimezone: timeZone, + }) + : '', + ); + + const persistDate = (newDate: Nullable) => { + if (!isDefined(newDate)) { + onPersist(null); + } else { + const newDateISO = newDate.toISOString(); + + onPersist(newDateISO); + } + }; + + const { closeDropdown } = useDropdown(MONTH_AND_YEAR_DROPDOWN_ID); + const { closeDropdown: closeDropdownMonthSelect } = useDropdown( + MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID, + ); + const { closeDropdown: closeDropdownYearSelect } = useDropdown( + MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID, + ); + + const displayDatePicker = + draftValue.type === 'static' && draftValue.mode === 'edit'; + + useListenClickOutside({ + refs: [datePickerWrapperRef], + listenerId: 'FormDateFieldInput', + callback: (event) => { + event.stopImmediatePropagation(); + + closeDropdownYearSelect(); + closeDropdownMonthSelect(); + closeDropdown(); + handlePickerClickOutside(); + }, + enabled: displayDatePicker, + }); + + const handlePickerChange = (newDate: Nullable) => { + setDraftValue({ + type: 'static', + mode: 'edit', + value: newDate?.toDateString() ?? null, + }); + + setInputDateTime( + isDefined(newDate) + ? parseDateToString({ + date: newDate, + isDateTimeInput: false, + userTimezone: timeZone, + }) + : '', + ); + + setPickerDate(newDate); + + persistDate(newDate); + }; + + const handlePickerEnter = () => {}; + + const handlePickerEscape = () => { + // FIXME: Escape key is not handled properly by the underlying DateInput component. We need to solve that. + + setDraftValue({ + type: 'static', + value: draftValue.value, + mode: 'view', + }); + }; + + const handlePickerClickOutside = () => { + setDraftValue({ + type: 'static', + value: draftValue.value, + mode: 'view', + }); + }; + + const handlePickerClear = () => { + setDraftValue({ + type: 'static', + value: null, + mode: 'view', + }); + + setPickerDate(null); + + setInputDateTime(''); + + persistDate(null); + }; + + const handlePickerMouseSelect = (newDate: Nullable) => { + setDraftValue({ + type: 'static', + value: newDate?.toDateString() ?? null, + mode: 'view', + }); + + setPickerDate(newDate); + + setInputDateTime( + isDefined(newDate) + ? parseDateToString({ + date: newDate, + isDateTimeInput: false, + userTimezone: timeZone, + }) + : '', + ); + + persistDate(newDate); + }; + + const handleInputFocus = () => { + setDraftValue({ + type: 'static', + mode: 'edit', + value: draftValue.value, + }); + }; + + const handleInputChange = (event: ChangeEvent) => { + setInputDateTime(event.target.value); + }; + + const handleInputKeydown = (event: KeyboardEvent) => { + if (event.key !== 'Enter') { + return; + } + + const inputDateTimeTrimmed = inputDateTime.trim(); + + if (inputDateTimeTrimmed === '') { + handlePickerClear(); + + return; + } + + const parsedInputDateTime = parseStringToDate({ + dateAsString: inputDateTimeTrimmed, + isDateTimeInput: false, + userTimezone: timeZone, + }); + + if (!isDefined(parsedInputDateTime)) { + return; + } + + let validatedDate = parsedInputDateTime; + if (parsedInputDateTime < MIN_DATE) { + validatedDate = MIN_DATE; + } else if (parsedInputDateTime > MAX_DATE) { + validatedDate = MAX_DATE; + } + + setDraftValue({ + type: 'static', + value: validatedDate.toDateString(), + mode: 'edit', + }); + + setPickerDate(validatedDate); + + setInputDateTime( + parseDateToString({ + date: validatedDate, + isDateTimeInput: false, + userTimezone: timeZone, + }), + ); + + persistDate(validatedDate); + }; + + const handleVariableTagInsert = (variableName: string) => { + setDraftValue({ + type: 'variable', + value: variableName, + }); + + setInputDateTime(''); + + onPersist(variableName); + }; + + const handleUnlinkVariable = () => { + setDraftValue({ + type: 'static', + value: null, + mode: 'view', + }); + + setPickerDate(null); + + onPersist(null); + }; + + return ( + + {label ? {label} : null} + + + + {draftValue.type === 'static' ? ( + <> + + + {draftValue.mode === 'edit' ? ( + + + + + + + + ) : null} + + ) : ( + + )} + + + {VariablePicker ? ( + + ) : null} + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormDateFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormDateFieldInput.stories.tsx new file mode 100644 index 000000000000..39a313fb809c --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormDateFieldInput.stories.tsx @@ -0,0 +1,370 @@ +import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate'; +import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate'; +import { parseDateToString } from '@/ui/input/components/internal/date/utils/parseDateToString'; +import { expect } from '@storybook/jest'; +import { Meta, StoryObj } from '@storybook/react'; +import { + fn, + userEvent, + waitFor, + waitForElementToBeRemoved, + within, +} from '@storybook/test'; +import { DateTime } from 'luxon'; +import { FormDateFieldInput } from '../FormDateFieldInput'; + +const meta: Meta = { + title: 'UI/Data/Field/Form/Input/FormDateFieldInput', + component: FormDateFieldInput, + args: {}, + argTypes: {}, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + label: 'Created At', + defaultValue: '2024-12-09T13:20:19.631Z', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + await canvas.findByText('Created At'); + await canvas.findByDisplayValue('12/09/2024'); + }, +}; + +export const WithDefaultEmptyValue: Story = { + args: { + label: 'Created At', + defaultValue: undefined, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + await canvas.findByText('Created At'); + await canvas.findByDisplayValue(''); + await canvas.findByPlaceholderText('mm/dd/yyyy'); + }, +}; + +export const SetsDateWithInput: Story = { + args: { + label: 'Created At', + defaultValue: undefined, + onPersist: fn(), + }, + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + + const input = await canvas.findByPlaceholderText('mm/dd/yyyy'); + + await userEvent.click(input); + + const dialog = await canvas.findByRole('dialog'); + expect(dialog).toBeVisible(); + + await userEvent.type(input, '12/08/2024{enter}'); + + await waitFor(() => { + expect(args.onPersist).toHaveBeenCalledWith('2024-12-08T00:00:00.000Z'); + }); + + expect(dialog).toBeVisible(); + }, +}; + +export const SetsDateWithDatePicker: Story = { + args: { + label: 'Created At', + defaultValue: undefined, + onPersist: fn(), + }, + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + + const input = await canvas.findByPlaceholderText('mm/dd/yyyy'); + expect(input).toBeVisible(); + + await userEvent.click(input); + + const datePicker = await canvas.findByRole('dialog'); + expect(datePicker).toBeVisible(); + + const dayToChoose = await within(datePicker).findByRole('option', { + name: 'Choose Saturday, December 7th, 2024', + }); + + await Promise.all([ + userEvent.click(dayToChoose), + + waitForElementToBeRemoved(datePicker), + waitFor(() => { + expect(args.onPersist).toHaveBeenCalledWith( + expect.stringMatching(/^2024-12-07/), + ); + }), + waitFor(() => { + expect(canvas.getByDisplayValue('12/07/2024')).toBeVisible(); + }), + ]); + }, +}; + +export const ResetsDateByClickingButton: Story = { + args: { + label: 'Created At', + defaultValue: '2024-12-09T13:20:19.631Z', + onPersist: fn(), + }, + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + + const input = await canvas.findByPlaceholderText('mm/dd/yyyy'); + expect(input).toBeVisible(); + + await userEvent.click(input); + + const datePicker = await canvas.findByRole('dialog'); + expect(datePicker).toBeVisible(); + + const clearButton = await canvas.findByText('Clear'); + + await Promise.all([ + userEvent.click(clearButton), + + waitForElementToBeRemoved(datePicker), + waitFor(() => { + expect(args.onPersist).toHaveBeenCalledWith(null); + }), + waitFor(() => { + expect(input).toHaveDisplayValue(''); + }), + ]); + }, +}; + +export const ResetsDateByErasingInputContent: Story = { + args: { + label: 'Created At', + defaultValue: '2024-12-09T13:20:19.631Z', + onPersist: fn(), + }, + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + + const input = await canvas.findByPlaceholderText('mm/dd/yyyy'); + expect(input).toBeVisible(); + + expect(input).toHaveDisplayValue('12/09/2024'); + + await userEvent.clear(input); + + await Promise.all([ + userEvent.type(input, '{Enter}'), + + waitForElementToBeRemoved(() => canvas.queryByRole('dialog')), + waitFor(() => { + expect(args.onPersist).toHaveBeenCalledWith(null); + }), + waitFor(() => { + expect(input).toHaveDisplayValue(''); + }), + ]); + }, +}; + +export const DefaultsToMinValueWhenTypingReallyOldDate: Story = { + args: { + label: 'Created At', + defaultValue: undefined, + onPersist: fn(), + }, + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + + const input = await canvas.findByPlaceholderText('mm/dd/yyyy'); + expect(input).toBeVisible(); + + await userEvent.click(input); + + const datePicker = await canvas.findByRole('dialog'); + expect(datePicker).toBeVisible(); + + await Promise.all([ + userEvent.type(input, '02/02/1500{Enter}'), + + waitFor(() => { + expect(args.onPersist).toHaveBeenCalledWith(MIN_DATE.toISOString()); + }), + waitFor(() => { + expect(input).toHaveDisplayValue( + parseDateToString({ + date: MIN_DATE, + isDateTimeInput: false, + userTimezone: undefined, + }), + ); + }), + waitFor(() => { + const expectedDate = DateTime.fromJSDate(MIN_DATE) + .toLocal() + .set({ + day: MIN_DATE.getUTCDate(), + month: MIN_DATE.getUTCMonth() + 1, + year: MIN_DATE.getUTCFullYear(), + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + }); + + const selectedDay = within(datePicker).getByRole('option', { + selected: true, + name: (accessibleName) => { + // The name looks like "Choose Sunday, December 31st, 1899" + return accessibleName.includes(expectedDate.toFormat('yyyy')); + }, + }); + expect(selectedDay).toBeVisible(); + }), + ]); + }, +}; + +export const DefaultsToMaxValueWhenTypingReallyFarDate: Story = { + args: { + label: 'Created At', + defaultValue: undefined, + onPersist: fn(), + }, + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + + const input = await canvas.findByPlaceholderText('mm/dd/yyyy'); + expect(input).toBeVisible(); + + await userEvent.click(input); + + const datePicker = await canvas.findByRole('dialog'); + expect(datePicker).toBeVisible(); + + await Promise.all([ + userEvent.type(input, '02/02/2500{Enter}'), + + waitFor(() => { + expect(args.onPersist).toHaveBeenCalledWith(MAX_DATE.toISOString()); + }), + waitFor(() => { + expect(input).toHaveDisplayValue( + parseDateToString({ + date: MAX_DATE, + isDateTimeInput: false, + userTimezone: undefined, + }), + ); + }), + waitFor(() => { + const expectedDate = DateTime.fromJSDate(MAX_DATE) + .toLocal() + .set({ + day: MAX_DATE.getUTCDate(), + month: MAX_DATE.getUTCMonth() + 1, + year: MAX_DATE.getUTCFullYear(), + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + }); + + const selectedDay = within(datePicker).getByRole('option', { + selected: true, + name: (accessibleName) => { + // The name looks like "Choose Thursday, December 30th, 2100" + return accessibleName.includes(expectedDate.toFormat('yyyy')); + }, + }); + expect(selectedDay).toBeVisible(); + }), + ]); + }, +}; + +export const SwitchesToStandaloneVariable: Story = { + args: { + label: 'Created At', + defaultValue: undefined, + onPersist: fn(), + VariablePicker: ({ onVariableSelect }) => { + return ( + + ); + }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const addVariableButton = await canvas.findByText('Add variable'); + await userEvent.click(addVariableButton); + + const variableTag = await canvas.findByText('test'); + expect(variableTag).toBeVisible(); + + const removeVariableButton = canvas.getByTestId(/^remove-icon/); + + await Promise.all([ + userEvent.click(removeVariableButton), + + waitForElementToBeRemoved(variableTag), + waitFor(() => { + const input = canvas.getByPlaceholderText('mm/dd/yyyy'); + expect(input).toBeVisible(); + }), + ]); + }, +}; + +export const ClickingOutsideDoesNotResetInputState: Story = { + args: { + label: 'Created At', + defaultValue: '2024-12-09T13:20:19.631Z', + onPersist: fn(), + }, + play: async ({ canvasElement, args }) => { + const defaultValueAsDisplayString = parseDateToString({ + date: new Date(args.defaultValue!), + isDateTimeInput: false, + userTimezone: undefined, + }); + + const canvas = within(canvasElement); + + const input = await canvas.findByPlaceholderText('mm/dd/yyyy'); + expect(input).toBeVisible(); + expect(input).toHaveDisplayValue(defaultValueAsDisplayString); + + await userEvent.type(input, '{Backspace}{Backspace}'); + + const datePicker = await canvas.findByRole('dialog'); + expect(datePicker).toBeVisible(); + + await Promise.all([ + userEvent.click(canvasElement), + + waitForElementToBeRemoved(datePicker), + ]); + + expect(args.onPersist).not.toHaveBeenCalled(); + + expect(input).toHaveDisplayValue(defaultValueAsDisplayString.slice(0, -2)); + }, +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/DateFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/DateFieldInput.tsx index 0e67f4baa436..74ad1528189e 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/DateFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/DateFieldInput.tsx @@ -4,8 +4,8 @@ import { useDateField } from '@/object-record/record-field/meta-types/hooks/useD import { DateInput } from '@/ui/field/input/components/DateInput'; import { isDefined } from '~/utils/isDefined'; -import { usePersistField } from '../../../hooks/usePersistField'; import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput'; +import { usePersistField } from '../../../hooks/usePersistField'; type FieldInputEvent = (persist: () => void) => void; diff --git a/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx index 858436a9744e..5d587ee303b5 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx @@ -1,5 +1,4 @@ import styled from '@emotion/styled'; -import { useRef, useState } from 'react'; import { Nullable } from 'twenty-ui'; import { @@ -10,8 +9,9 @@ import { } from '@/ui/input/components/internal/date/components/InternalDatePicker'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { useRef, useState } from 'react'; -const StyledCalendarContainer = styled.div` +export const StyledCalendarContainer = styled.div` background: ${({ theme }) => theme.background.transparent.secondary}; backdrop-filter: ${({ theme }) => theme.blur.medium}; border: 1px solid ${({ theme }) => theme.border.color.light}; @@ -32,6 +32,7 @@ export type DateInputProps = { isDateTimeInput?: boolean; onClear?: () => void; onSubmit?: (newDate: Nullable) => void; + hideHeaderInput?: boolean; }; export const DateInput = ({ @@ -44,6 +45,7 @@ export const DateInput = ({ isDateTimeInput, onClear, onSubmit, + hideHeaderInput, }: DateInputProps) => { const [internalValue, setInternalValue] = useState(value); @@ -97,6 +99,7 @@ export const DateInput = ({ onEnter={onEnter} onEscape={onEscape} onClear={handleClear} + hideHeaderInput={hideHeaderInput} /> diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/AbsoluteDatePickerHeader.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/AbsoluteDatePickerHeader.tsx index 6ffeb3fc3da6..1c0e9fcc3484 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/AbsoluteDatePickerHeader.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/AbsoluteDatePickerHeader.tsx @@ -38,6 +38,7 @@ type AbsoluteDatePickerHeaderProps = { nextMonthButtonDisabled: boolean; isDateTimeInput?: boolean; timeZone: string; + hideInput?: boolean; }; export const AbsoluteDatePickerHeader = ({ @@ -51,6 +52,7 @@ export const AbsoluteDatePickerHeader = ({ nextMonthButtonDisabled, isDateTimeInput, timeZone, + hideInput = false, }: AbsoluteDatePickerHeaderProps) => { const endOfDayDateTimeInLocalTimezone = DateTime.now().set({ day: date.getDate(), @@ -66,12 +68,15 @@ export const AbsoluteDatePickerHeader = ({ return ( <> - + {!hideInput && ( + + )} +