fix: month and year dropdowns in settings logs date picker (#21529)
### Summary - Fixes #21514 and Issue 2 - **Issue 1**: when opening the calendar and choosing a month or year, those lists could appear underneath the calendar, making them impossible to see and use. (Issue #21514) - **Issue 2**: after opening the calendar icon menu, clicking the month or year controls don't work, so you couldn’t actually change the month or year. Before: <img width="355" height="434" alt="607363028-0d3a302e-9dba-4d9a-b354-ad7cbcd1fba5" src="https://github.com/user-attachments/assets/b1c357d1-a7cf-4572-8737-721cf4e2597a" /> After: https://github.com/user-attachments/assets/ffc26447-ff34-4f11-a3b4-4c329e446ec4 <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21529?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Signed-off-by: Parship Chowdhury <parshipchowdhury@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
committed by
GitHub
parent
e1c962acba
commit
c18787350f
+18
-11
@@ -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 = ({
|
||||
}
|
||||
>
|
||||
<OverlayContainer>
|
||||
<DateTimePicker
|
||||
instanceId={instanceId}
|
||||
date={dateValue}
|
||||
onChange={handlePickerChange}
|
||||
onClose={handlePickerMouseSelect}
|
||||
onEnter={handlePickerEnter}
|
||||
onEscape={handlePickerEscape}
|
||||
onClear={handlePickerClear}
|
||||
hideHeaderInput
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
<ParentClickOutsideIdContext.Provider
|
||||
value={FORM_DATE_TIME_FIELD_PICKER_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
<DateTimePicker
|
||||
instanceId={instanceId}
|
||||
date={dateValue}
|
||||
onChange={handlePickerChange}
|
||||
onClose={handlePickerMouseSelect}
|
||||
onEnter={handlePickerEnter}
|
||||
onEscape={handlePickerEscape}
|
||||
onClear={handlePickerClear}
|
||||
hideHeaderInput
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</ParentClickOutsideIdContext.Provider>
|
||||
</OverlayContainer>
|
||||
</div>
|
||||
</FloatingPortal>
|
||||
|
||||
@@ -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 = ({
|
||||
</StyledInput>
|
||||
{isOpen && (
|
||||
<FloatingPortal>
|
||||
<StyledFloatingContainer
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
data-click-outside-id={SETTINGS_DATE_PICKER_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
<OverlayContainer>
|
||||
<DateTimePicker
|
||||
instanceId={`settings-date-picker-${pickerInstanceId}`}
|
||||
date={zonedDateTime}
|
||||
onChange={handleDateTimeChange}
|
||||
onClose={handleDateTimeClose}
|
||||
onClear={handleClear}
|
||||
clearable
|
||||
/>
|
||||
<ParentClickOutsideIdContext.Provider
|
||||
value={SETTINGS_DATE_PICKER_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
<DateTimePicker
|
||||
instanceId={`settings-date-picker-${pickerInstanceId}`}
|
||||
date={zonedDateTime}
|
||||
onChange={handleDateTimeChange}
|
||||
onClose={handleDateTimeClose}
|
||||
onClear={handleClear}
|
||||
clearable
|
||||
/>
|
||||
</ParentClickOutsideIdContext.Provider>
|
||||
</OverlayContainer>
|
||||
</StyledFloatingContainer>
|
||||
</div>
|
||||
</FloatingPortal>
|
||||
)}
|
||||
</StyledInputContainer>
|
||||
|
||||
+82
@@ -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<Date | undefined>(INITIAL_DATE);
|
||||
|
||||
return (
|
||||
<SettingsDatePickerInput label="Date" value={value} onChange={setValue} />
|
||||
);
|
||||
};
|
||||
|
||||
const meta: Meta<typeof SettingsDatePickerInput> = {
|
||||
title: 'Modules/Settings/SettingsDatePickerInput',
|
||||
component: SettingsDatePickerInput,
|
||||
decorators: [ComponentDecorator],
|
||||
render: () => <SettingsDatePickerInputStory />,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof SettingsDatePickerInput>;
|
||||
|
||||
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();
|
||||
},
|
||||
};
|
||||
+2
@@ -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'};
|
||||
|
||||
Reference in New Issue
Block a user