diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormDateTimeFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormDateTimeFieldInput.tsx index 4be6858601..dae7b3d5da 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormDateTimeFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormDateTimeFieldInput.tsx @@ -5,6 +5,7 @@ import { VariableChipStandalone } from '@/object-record/record-field/ui/form-typ import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent'; import { InputLabel } from '@/ui/input/components/InputLabel'; import { + DATE_TIME_PICKER_MONTH_YEAR_PANEL_DROPDOWN_ID, DateTimePicker, MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID, MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID, @@ -15,6 +16,7 @@ import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUs import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; +import { ParentClickOutsideIdContext } from '@/ui/utilities/pointer-event/contexts/ParentClickOutsideIdContext'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString'; @@ -133,6 +135,7 @@ export const FormDateTimeFieldInput = ({ enabled: displayDatePicker, excludedClickOutsideIds: [ FORM_DATE_TIME_FIELD_PICKER_CLICK_OUTSIDE_ID, + DATE_TIME_PICKER_MONTH_YEAR_PANEL_DROPDOWN_ID, MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID, MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID, ], @@ -296,17 +299,21 @@ export const FormDateTimeFieldInput = ({ } > - + + + diff --git a/packages/twenty-front/src/modules/settings/components/SettingsDatePickerInput.tsx b/packages/twenty-front/src/modules/settings/components/SettingsDatePickerInput.tsx index 3e75420adb..f6bb20b761 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsDatePickerInput.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsDatePickerInput.tsx @@ -11,12 +11,14 @@ import { } from '@floating-ui/react'; import { + DATE_TIME_PICKER_MONTH_YEAR_PANEL_DROPDOWN_ID, DateTimePicker, MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID, MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID, } from '@/ui/input/components/internal/date/components/DateTimePicker'; import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone'; import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer'; +import { ParentClickOutsideIdContext } from '@/ui/utilities/pointer-event/contexts/ParentClickOutsideIdContext'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { isDefined } from 'twenty-shared/utils'; import { IconCalendar } from 'twenty-ui/icon'; @@ -62,10 +64,6 @@ const StyledIconContainer = styled.div` display: flex; `; -const StyledFloatingContainer = styled.div` - z-index: 1000; -`; - export type SettingsDatePickerInputProps = { label?: string; instanceId?: string; @@ -107,6 +105,7 @@ export const SettingsDatePickerInput = ({ enabled: isOpen, excludedClickOutsideIds: [ SETTINGS_DATE_PICKER_CLICK_OUTSIDE_ID, + DATE_TIME_PICKER_MONTH_YEAR_PANEL_DROPDOWN_ID, MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID, MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID, ], @@ -163,22 +162,26 @@ export const SettingsDatePickerInput = ({ {isOpen && ( - - + + + - + )} diff --git a/packages/twenty-front/src/modules/settings/components/__stories__/SettingsDatePickerInput.stories.tsx b/packages/twenty-front/src/modules/settings/components/__stories__/SettingsDatePickerInput.stories.tsx new file mode 100644 index 0000000000..f50769a486 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/__stories__/SettingsDatePickerInput.stories.tsx @@ -0,0 +1,82 @@ +import { SettingsDatePickerInput } from '@/settings/components/SettingsDatePickerInput'; +import { type Meta, type StoryObj } from '@storybook/react-vite'; +import { useState } from 'react'; +import { expect, userEvent, within } from 'storybook/test'; +import { ComponentDecorator } from 'twenty-ui/testing'; + +// Midday UTC so the rendered date stays on 15 Jan 2023 across timezones +const INITIAL_DATE = new Date('2023-01-15T12:00:00Z'); + +const SettingsDatePickerInputStory = () => { + const [value, setValue] = useState(INITIAL_DATE); + + return ( + + ); +}; + +const meta: Meta = { + title: 'Modules/Settings/SettingsDatePickerInput', + component: SettingsDatePickerInput, + decorators: [ComponentDecorator], + render: () => , +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const body = within(canvasElement.ownerDocument.body); + + await userEvent.click( + await canvas.findByText(/2023/, {}, { timeout: 10000 }), + ); + + await body.findByRole( + 'button', + { name: 'Select month and year' }, + { timeout: 10000 }, + ); + }, +}; + +// Regression test for the month/year panel closing the whole picker on click. +// Opening the month select used to trigger the parent click-outside listener and +// close the date picker, so the month/year controls could never be used. +export const ChangingMonthKeepsPickerOpen: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const body = within(canvasElement.ownerDocument.body); + + await userEvent.click( + await canvas.findByText(/2023/, {}, { timeout: 10000 }), + ); + + const monthYearButton = await body.findByRole( + 'button', + { name: 'Select month and year' }, + { timeout: 10000 }, + ); + await userEvent.click(monthYearButton); + + await userEvent.click( + await body.findByText('January', {}, { timeout: 10000 }), + ); + + // The picker must stay open after interacting with the month select + expect( + body.getByRole('button', { name: 'Select month and year' }), + ).toBeInTheDocument(); + + await userEvent.click( + await body.findByText('February', {}, { timeout: 10000 }), + ); + + expect(await body.findByText('February')).toBeInTheDocument(); + expect( + body.getByRole('button', { name: 'Select month and year' }), + ).toBeInTheDocument(); + }, +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DateTimePicker.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DateTimePicker.tsx index 00f7aeebe9..cf18fe9db5 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DateTimePicker.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DateTimePicker.tsx @@ -205,6 +205,8 @@ const StyledContainer = styled.div<{ & .react-datepicker__month { margin-top: 0; + margin-left: 0; + margin-right: 0; pointer-events: ${({ calendarDisabled }) => calendarDisabled ? 'none' : 'auto'};