From c635a191bfcd123479c52bd11cc2dd5c318a014e Mon Sep 17 00:00:00 2001 From: Parship Chowdhury Date: Fri, 26 Jun 2026 11:21:37 +0530 Subject: [PATCH] fix: uneven spacing in date picker calendar grid (#21881) ### Summary While working on #19739, I found that in the date filter calendar dropdown, day cells and highlighted dates looked misaligned i.e. tighter on the right side. The solution is to apply a uniform margin in `DatePicker.tsx` and `DateTimePicker.tsx`. ### Before: Screenshot 2026-06-20 021856 Screenshot 2026-06-20 021932 ### After: Screenshot 2026-06-20 021813 Screenshot 2026-06-20 022013 Review in cubic --------- Signed-off-by: Parship Chowdhury Co-authored-by: Charles Bochet --- .../internal/date/components/DatePicker.tsx | 247 +--------------- .../components/DatePickerWithoutCalendar.tsx | 257 +---------------- .../date/components/DateTimePicker.tsx | 242 +--------------- .../components/StyledDatePickerContainer.tsx | 263 ++++++++++++++++++ .../__stories__/DatePicker.stories.tsx | 106 +++++++ .../DatePickerWithoutCalendar.stories.tsx | 42 +++ ...stories.tsx => DateTimePicker.stories.tsx} | 2 +- 7 files changed, 427 insertions(+), 732 deletions(-) create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/components/StyledDatePickerContainer.tsx create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DatePicker.stories.tsx create mode 100644 packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DatePickerWithoutCalendar.stories.tsx rename packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/{InternalDatePicker.stories.tsx => DateTimePicker.stories.tsx} (98%) diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DatePicker.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DatePicker.tsx index 314f2fbc50..c9159fa993 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DatePicker.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DatePicker.tsx @@ -10,6 +10,10 @@ import { CalendarStartDay } from 'twenty-shared/constants'; import { detectCalendarStartDay } from '@/localization/utils/detection/detectCalendarStartDay'; import { DatePickerHeader } from '@/ui/input/components/internal/date/components/DatePickerHeader'; import { RelativeDatePickerHeader } from '@/ui/input/components/internal/date/components/RelativeDatePickerHeader'; +import { + DATE_PICKER_CONTAINER_WIDTH, + StyledDatePickerContainer, +} from '@/ui/input/components/internal/date/components/StyledDatePickerContainer'; import { getHighlightedDates } from '@/ui/input/components/internal/date/utils/getHighlightedDates'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { t } from '@lingui/core/macro'; @@ -33,245 +37,6 @@ export const MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID = export const MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID = 'date-picker-month-and-year-dropdown-year-select'; -const DATE_PICKER_CONTAINER_WIDTH = 280; - -const StyledContainer = styled.div<{ calendarDisabled?: boolean }>` - width: ${DATE_PICKER_CONTAINER_WIDTH}px; - - & .react-datepicker { - border-color: ${themeCssVariables.border.color.light}; - background: transparent; - font-family: 'Inter'; - font-size: ${themeCssVariables.font.size.md}; - border: none; - display: block; - font-weight: ${themeCssVariables.font.weight.regular}; - } - - & .react-datepicker-popper { - position: relative !important; - inset: auto !important; - transform: none !important; - padding: 0 !important; - } - - & .react-datepicker__triangle { - display: none; - } - - & .react-datepicker__triangle::after { - display: none; - } - - & .react-datepicker__triangle::before { - display: none; - } - - & .react-datepicker-wrapper { - display: none; - } - - // Header - - & .react-datepicker__header { - background: transparent; - border: none; - padding: 0; - } - - & - .react-datepicker__input-time-container - .react-datepicker-time__input-container - .react-datepicker-time__input { - outline: none; - } - - & .react-datepicker__header__dropdown { - display: flex; - color: ${themeCssVariables.font.color.primary}; - margin-left: ${themeCssVariables.spacing[1]}; - margin-bottom: ${themeCssVariables.spacing[10]}; - } - - & .react-datepicker__month-dropdown-container, - & .react-datepicker__year-dropdown-container { - text-align: left; - border-radius: ${themeCssVariables.border.radius.sm}; - margin-left: ${themeCssVariables.spacing[1]}; - margin-right: 0; - padding: ${themeCssVariables.spacing[2]}; - padding-right: ${themeCssVariables.spacing[4]}; - background-color: ${themeCssVariables.background.tertiary}; - } - - & .react-datepicker__month-read-view--down-arrow, - & .react-datepicker__year-read-view--down-arrow { - height: 5px; - width: 5px; - border-width: 1px 1px 0 0; - border-color: ${themeCssVariables.border.color.light}; - top: 3px; - right: -6px; - } - - & .react-datepicker__year-read-view, - & .react-datepicker__month-read-view { - padding-right: ${themeCssVariables.spacing[2]}; - } - - & .react-datepicker__month-dropdown-container { - width: 80px; - } - - & .react-datepicker__year-dropdown-container { - width: 50px; - } - - & .react-datepicker__month-dropdown, - & .react-datepicker__year-dropdown { - overflow-y: scroll; - top: ${themeCssVariables.spacing[2]}; - } - & .react-datepicker__month-dropdown { - left: ${themeCssVariables.spacing[2]}; - height: 260px; - } - - & .react-datepicker__year-dropdown { - left: calc(${themeCssVariables.spacing[9]} + 80px); - width: 100px; - height: 260px; - } - - & .react-datepicker__navigation--years { - display: none; - } - - & .react-datepicker__month-option--selected, - & .react-datepicker__year-option--selected { - display: none; - } - - & .react-datepicker__year-option, - & .react-datepicker__month-option { - text-align: left; - padding: ${themeCssVariables.spacing[2]} - calc(${themeCssVariables.spacing[2]} - 2px); - width: calc(100% - ${themeCssVariables.spacing[4]}); - border-radius: ${themeCssVariables.border.radius.xs}; - color: ${themeCssVariables.font.color.secondary}; - cursor: pointer; - margin: 2px; - - &:hover { - background: ${themeCssVariables.background.transparent.light}; - } - } - - & .react-datepicker__year-option { - &:first-of-type, - &:last-of-type { - display: none; - } - } - - & .react-datepicker__current-month { - display: none; - } - - & .react-datepicker__day-name { - color: ${themeCssVariables.font.color.secondary}; - width: 34px; - height: 40px; - line-height: 40px; - } - - & .react-datepicker__month-container { - float: none; - } - - // Days - - & .react-datepicker__month { - margin-top: 0; - - pointer-events: ${({ calendarDisabled }) => - calendarDisabled ? 'none' : 'auto'}; - opacity: ${({ calendarDisabled }) => (calendarDisabled ? '0.5' : '1')}; - } - - & .react-datepicker__day { - width: 34px; - height: 34px; - line-height: 34px; - } - - & .react-datepicker__navigation--previous, - & .react-datepicker__navigation--next { - height: 34px; - border-radius: ${themeCssVariables.border.radius.sm}; - padding-top: 6px; - &:hover { - background: ${themeCssVariables.background.transparent.light}; - } - } - & .react-datepicker__navigation--previous { - right: 38px; - top: 6px; - left: auto; - - & > span { - margin-left: -6px; - } - } - - & .react-datepicker__navigation--next { - right: 6px; - top: 6px; - - & > span { - margin-left: 6px; - } - } - - & .react-datepicker__navigation-icon::before { - height: 7px; - width: 7px; - border-width: 1px 1px 0 0; - border-color: ${themeCssVariables.font.color.tertiary}; - } - - & .react-datepicker__day--keyboard-selected { - background-color: inherit; - } - - & .react-datepicker__day, - .react-datepicker__time-name { - color: ${themeCssVariables.font.color.primary}; - } - - & .react-datepicker__day--selected { - background-color: ${themeCssVariables.color.blue}; - color: ${themeCssVariables.background.primary}; - - &.react-datepicker__day:hover { - color: ${themeCssVariables.background.primary}; - } - } - - & .react-datepicker__day--outside-month { - color: ${themeCssVariables.font.color.tertiary}; - } - - & .react-datepicker__day:hover { - color: ${themeCssVariables.font.color.tertiary}; - } - - & .clearable { - border-bottom: 1px solid ${themeCssVariables.border.color.light}; - } -`; - const StyledButtonContainer = styled.div` align-items: center; border-radius: ${themeCssVariables.border.radius.sm}; @@ -468,7 +233,7 @@ export const DatePicker = ({ : null; return ( - +
)} - + ); }; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DatePickerWithoutCalendar.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DatePickerWithoutCalendar.tsx index 56ad3ba743..bc200e7a8c 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DatePickerWithoutCalendar.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/DatePickerWithoutCalendar.tsx @@ -9,6 +9,10 @@ import { CalendarStartDay } from 'twenty-shared/constants'; import { detectCalendarStartDay } from '@/localization/utils/detection/detectCalendarStartDay'; import { DatePickerHeader } from '@/ui/input/components/internal/date/components/DatePickerHeader'; +import { + DATE_PICKER_CONTAINER_WIDTH, + StyledDatePickerContainer, +} from '@/ui/input/components/internal/date/components/StyledDatePickerContainer'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import 'react-datepicker/dist/react-datepicker.css'; @@ -23,257 +27,8 @@ export const MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID = export const MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID = 'date-picker-month-and-year-dropdown-year-select'; -const DATE_PICKER_CONTAINER_WIDTH = 280; const DATE_PICKER_SKELETON_PADDING = 16; -const StyledContainer = styled.div<{ - calendarDisabled?: boolean; - hideCalendar?: boolean; -}>` - width: ${DATE_PICKER_CONTAINER_WIDTH}px; - - & .react-datepicker { - border-color: ${themeCssVariables.border.color.light}; - background: transparent; - font-family: 'Inter'; - font-size: ${themeCssVariables.font.size.md}; - border: none; - display: block; - font-weight: ${themeCssVariables.font.weight.regular}; - } - - & .react-datepicker-popper { - position: relative !important; - inset: auto !important; - transform: none !important; - padding: 0 !important; - } - - & .react-datepicker__triangle { - display: none; - } - - & .react-datepicker__triangle::after { - display: none; - } - - & .react-datepicker__triangle::before { - display: none; - } - - & .react-datepicker-wrapper { - display: none; - } - - // Header - - & .react-datepicker__header { - background: transparent; - border: none; - padding: 0; - } - - & - .react-datepicker__input-time-container - .react-datepicker-time__input-container - .react-datepicker-time__input { - outline: none; - } - - & .react-datepicker__header__dropdown { - display: flex; - color: ${themeCssVariables.font.color.primary}; - margin-left: ${themeCssVariables.spacing[1]}; - margin-bottom: ${themeCssVariables.spacing[10]}; - } - - & .react-datepicker__month-dropdown-container, - & .react-datepicker__year-dropdown-container { - text-align: left; - border-radius: ${themeCssVariables.border.radius.sm}; - margin-left: ${themeCssVariables.spacing[1]}; - margin-right: 0; - padding: ${themeCssVariables.spacing[2]}; - padding-right: ${themeCssVariables.spacing[4]}; - background-color: ${themeCssVariables.background.tertiary}; - } - - & .react-datepicker__month-read-view--down-arrow, - & .react-datepicker__year-read-view--down-arrow { - height: 5px; - width: 5px; - border-width: 1px 1px 0 0; - border-color: ${themeCssVariables.border.color.light}; - top: 3px; - right: -6px; - } - - & .react-datepicker__year-read-view, - & .react-datepicker__month-read-view { - padding-right: ${themeCssVariables.spacing[2]}; - } - - & .react-datepicker__month-dropdown-container { - width: 80px; - } - - & .react-datepicker__year-dropdown-container { - width: 50px; - } - - & .react-datepicker__month-dropdown, - & .react-datepicker__year-dropdown { - overflow-y: scroll; - top: ${themeCssVariables.spacing[2]}; - } - & .react-datepicker__month-dropdown { - left: ${themeCssVariables.spacing[2]}; - height: 260px; - } - - & .react-datepicker__year-dropdown { - left: calc(${themeCssVariables.spacing[9]} + 80px); - width: 100px; - height: 260px; - } - - & .react-datepicker__navigation--years { - display: none; - } - - & .react-datepicker__month-option--selected, - & .react-datepicker__year-option--selected { - display: none; - } - - & .react-datepicker__year-option, - & .react-datepicker__month-option { - text-align: left; - padding: ${themeCssVariables.spacing[2]} - calc(${themeCssVariables.spacing[2]} - 2px); - width: calc(100% - ${themeCssVariables.spacing[4]}); - border-radius: ${themeCssVariables.border.radius.xs}; - color: ${themeCssVariables.font.color.secondary}; - cursor: pointer; - margin: 2px; - - &:hover { - background: ${themeCssVariables.background.transparent.light}; - } - } - - & .react-datepicker__year-option { - &:first-of-type, - &:last-of-type { - display: none; - } - } - - & .react-datepicker__current-month { - display: none; - } - - & .react-datepicker__day-name { - color: ${themeCssVariables.font.color.secondary}; - width: 34px; - height: 40px; - line-height: 40px; - } - - & .react-datepicker__month-container { - float: none; - } - - // Days - - & .react-datepicker__month { - margin-top: 0; - - pointer-events: ${({ calendarDisabled }) => - calendarDisabled ? 'none' : 'auto'}; - opacity: ${({ calendarDisabled }) => (calendarDisabled ? '0.5' : '1')}; - - display: ${({ hideCalendar }) => - hideCalendar === true ? 'none' : 'visible'}; - } - - & .react-datepicker__day-names { - display: ${({ hideCalendar }) => - hideCalendar === true ? 'none' : 'visible'}; - } - - & .react-datepicker__day { - width: 34px; - height: 34px; - line-height: 34px; - } - - & .react-datepicker__navigation--previous, - & .react-datepicker__navigation--next { - height: 34px; - border-radius: ${themeCssVariables.border.radius.sm}; - padding-top: 6px; - &:hover { - background: ${themeCssVariables.background.transparent.light}; - } - } - & .react-datepicker__navigation--previous { - right: 38px; - top: 6px; - left: auto; - - & > span { - margin-left: -6px; - } - } - - & .react-datepicker__navigation--next { - right: 6px; - top: 6px; - - & > span { - margin-left: 6px; - } - } - - & .react-datepicker__navigation-icon::before { - height: 7px; - width: 7px; - border-width: 1px 1px 0 0; - border-color: ${themeCssVariables.font.color.tertiary}; - } - - & .react-datepicker__day--keyboard-selected { - background-color: inherit; - } - - & .react-datepicker__day, - .react-datepicker__time-name { - color: ${themeCssVariables.font.color.primary}; - } - - & .react-datepicker__day--selected { - background-color: ${themeCssVariables.color.blue}; - color: ${themeCssVariables.background.primary}; - - &.react-datepicker__day:hover { - color: ${themeCssVariables.background.primary}; - } - } - - & .react-datepicker__day--outside-month { - color: ${themeCssVariables.font.color.tertiary}; - } - - & .react-datepicker__day:hover { - color: ${themeCssVariables.font.color.tertiary}; - } - - & .clearable { - border-bottom: 1px solid ${themeCssVariables.border.color.light}; - } -`; - const StyledSpacer = styled.div` height: ${themeCssVariables.spacing[2]}; width: auto; @@ -399,7 +154,7 @@ export const DatePickerWithoutCalendar = ({ : null; return ( - +
-
+ ); }; 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 cf18fe9db5..6327d8aa72 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 @@ -10,6 +10,7 @@ import { DateTimePickerHeader, } from '@/ui/input/components/internal/date/components/DateTimePickerHeader'; import { RelativeDatePickerHeader } from '@/ui/input/components/internal/date/components/RelativeDatePickerHeader'; +import { StyledDatePickerContainer } from '@/ui/input/components/internal/date/components/StyledDatePickerContainer'; import { getHighlightedDates } from '@/ui/input/components/internal/date/utils/getHighlightedDates'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { styled } from '@linaria/react'; @@ -44,243 +45,6 @@ const StyledOuterWrapper = styled.div` width: 280px; `; -const StyledContainer = styled.div<{ - calendarDisabled?: boolean; -}>` - width: 280px; - - & .react-datepicker { - border-color: ${themeCssVariables.border.color.light}; - background: transparent; - font-family: 'Inter'; - font-size: ${themeCssVariables.font.size.md}; - border: none; - display: block; - font-weight: ${themeCssVariables.font.weight.regular}; - } - - & .react-datepicker-popper { - position: relative !important; - inset: auto !important; - transform: none !important; - padding: 0 !important; - } - - & .react-datepicker__triangle { - display: none; - } - - & .react-datepicker__triangle::after { - display: none; - } - - & .react-datepicker__triangle::before { - display: none; - } - - & .react-datepicker-wrapper { - display: none; - } - - // Header - - & .react-datepicker__header { - background: transparent; - border: none; - padding: 0; - } - - & - .react-datepicker__input-time-container - .react-datepicker-time__input-container - .react-datepicker-time__input { - outline: none; - } - - & .react-datepicker__header__dropdown { - display: flex; - color: ${themeCssVariables.font.color.primary}; - margin-left: ${themeCssVariables.spacing[1]}; - margin-bottom: ${themeCssVariables.spacing[10]}; - } - - & .react-datepicker__month-dropdown-container, - & .react-datepicker__year-dropdown-container { - text-align: left; - border-radius: ${themeCssVariables.border.radius.sm}; - margin-left: ${themeCssVariables.spacing[1]}; - margin-right: 0; - padding: ${themeCssVariables.spacing[2]}; - padding-right: ${themeCssVariables.spacing[4]}; - background-color: ${themeCssVariables.background.tertiary}; - } - - & .react-datepicker__month-read-view--down-arrow, - & .react-datepicker__year-read-view--down-arrow { - height: 5px; - width: 5px; - border-width: 1px 1px 0 0; - border-color: ${themeCssVariables.border.color.light}; - top: 3px; - right: -6px; - } - - & .react-datepicker__year-read-view, - & .react-datepicker__month-read-view { - padding-right: ${themeCssVariables.spacing[2]}; - } - - & .react-datepicker__month-dropdown-container { - width: 80px; - } - - & .react-datepicker__year-dropdown-container { - width: 50px; - } - - & .react-datepicker__month-dropdown, - & .react-datepicker__year-dropdown { - overflow-y: scroll; - top: ${themeCssVariables.spacing[2]}; - } - & .react-datepicker__month-dropdown { - left: ${themeCssVariables.spacing[2]}; - height: 260px; - } - - & .react-datepicker__year-dropdown { - left: calc(${themeCssVariables.spacing[9]} + 80px); - width: 100px; - height: 260px; - } - - & .react-datepicker__navigation--years { - display: none; - } - - & .react-datepicker__month-option--selected, - & .react-datepicker__year-option--selected { - display: none; - } - - & .react-datepicker__year-option, - & .react-datepicker__month-option { - text-align: left; - padding: ${themeCssVariables.spacing[2]} - calc(${themeCssVariables.spacing[2]} - 2px); - width: calc(100% - ${themeCssVariables.spacing[4]}); - border-radius: ${themeCssVariables.border.radius.xs}; - color: ${themeCssVariables.font.color.secondary}; - cursor: pointer; - margin: 2px; - - &:hover { - background: ${themeCssVariables.background.transparent.light}; - } - } - - & .react-datepicker__year-option { - &:first-of-type, - &:last-of-type { - display: none; - } - } - - & .react-datepicker__current-month { - display: none; - } - - & .react-datepicker__day-name { - color: ${themeCssVariables.font.color.secondary}; - width: 34px; - height: 40px; - line-height: 40px; - } - - & .react-datepicker__month-container { - float: none; - } - - // Days - - & .react-datepicker__month { - margin-top: 0; - margin-left: 0; - margin-right: 0; - - pointer-events: ${({ calendarDisabled }) => - calendarDisabled ? 'none' : 'auto'}; - opacity: ${({ calendarDisabled }) => (calendarDisabled ? '0.5' : '1')}; - } - - & .react-datepicker__day { - width: 34px; - height: 34px; - line-height: 34px; - } - - & .react-datepicker__navigation--previous, - & .react-datepicker__navigation--next { - height: 34px; - border-radius: ${themeCssVariables.border.radius.sm}; - padding-top: 6px; - &:hover { - background: ${themeCssVariables.background.transparent.light}; - } - } - & .react-datepicker__navigation--previous { - right: 38px; - top: 6px; - left: auto; - - & > span { - margin-left: -6px; - } - } - - & .react-datepicker__navigation--next { - right: 6px; - top: 6px; - - & > span { - margin-left: 6px; - } - } - - & .react-datepicker__navigation-icon::before { - height: 7px; - width: 7px; - border-width: 1px 1px 0 0; - border-color: ${themeCssVariables.font.color.tertiary}; - } - - & .react-datepicker__day--keyboard-selected { - background-color: inherit; - } - - & .react-datepicker__day, - .react-datepicker__time-name { - color: ${themeCssVariables.font.color.primary}; - } - - & .react-datepicker__day--selected { - background-color: ${themeCssVariables.color.blue}; - color: ${themeCssVariables.background.primary}; - - &.react-datepicker__day:hover { - color: ${themeCssVariables.background.primary}; - } - } - - & .react-datepicker__day--outside-month { - color: ${themeCssVariables.font.color.tertiary}; - } - - & .react-datepicker__day:hover { - color: ${themeCssVariables.font.color.tertiary}; - } -`; - const StyledSeparator = styled.div` border-bottom: 1px solid ${themeCssVariables.border.color.light}; width: 100%; @@ -502,7 +266,7 @@ export const DateTimePicker = ({ return ( - + @@ -582,7 +346,7 @@ export const DateTimePicker = ({ )} - + ); }; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/StyledDatePickerContainer.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/StyledDatePickerContainer.tsx new file mode 100644 index 0000000000..d237cafebb --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/StyledDatePickerContainer.tsx @@ -0,0 +1,263 @@ +import { styled } from '@linaria/react'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +export const DATE_PICKER_CONTAINER_WIDTH = 280; + +// Shared react-datepicker overrides for every internal date picker +// (DatePicker, DateTimePicker, DatePickerWithoutCalendar). Keep the calendar +// grid styling in one place so day/day-name spacing stays consistent across all +// pickers instead of drifting between per-component copies. +// - calendarDisabled: dims and freezes the grid (used for relative date mode) +// - hideCalendar: hides the day grid, keeping only the month/year header +export const StyledDatePickerContainer = styled.div<{ + calendarDisabled?: boolean; + hideCalendar?: boolean; +}>` + width: ${DATE_PICKER_CONTAINER_WIDTH}px; + + & .react-datepicker { + border-color: ${themeCssVariables.border.color.light}; + background: transparent; + font-family: 'Inter'; + font-size: ${themeCssVariables.font.size.md}; + border: none; + display: block; + font-weight: ${themeCssVariables.font.weight.regular}; + } + + & .react-datepicker-popper { + position: relative !important; + inset: auto !important; + transform: none !important; + padding: 0 !important; + } + + & .react-datepicker__triangle { + display: none; + } + + & .react-datepicker__triangle::after { + display: none; + } + + & .react-datepicker__triangle::before { + display: none; + } + + & .react-datepicker-wrapper { + display: none; + } + + // Header + + & .react-datepicker__header { + background: transparent; + border: none; + padding: 0; + } + + & + .react-datepicker__input-time-container + .react-datepicker-time__input-container + .react-datepicker-time__input { + outline: none; + } + + & .react-datepicker__header__dropdown { + display: flex; + color: ${themeCssVariables.font.color.primary}; + margin-left: ${themeCssVariables.spacing[1]}; + margin-bottom: ${themeCssVariables.spacing[10]}; + } + + & .react-datepicker__month-dropdown-container, + & .react-datepicker__year-dropdown-container { + text-align: left; + border-radius: ${themeCssVariables.border.radius.sm}; + margin-left: ${themeCssVariables.spacing[1]}; + margin-right: 0; + padding: ${themeCssVariables.spacing[2]}; + padding-right: ${themeCssVariables.spacing[4]}; + background-color: ${themeCssVariables.background.tertiary}; + } + + & .react-datepicker__month-read-view--down-arrow, + & .react-datepicker__year-read-view--down-arrow { + height: 5px; + width: 5px; + border-width: 1px 1px 0 0; + border-color: ${themeCssVariables.border.color.light}; + top: 3px; + right: -6px; + } + + & .react-datepicker__year-read-view, + & .react-datepicker__month-read-view { + padding-right: ${themeCssVariables.spacing[2]}; + } + + & .react-datepicker__month-dropdown-container { + width: 80px; + } + + & .react-datepicker__year-dropdown-container { + width: 50px; + } + + & .react-datepicker__month-dropdown, + & .react-datepicker__year-dropdown { + overflow-y: scroll; + top: ${themeCssVariables.spacing[2]}; + } + & .react-datepicker__month-dropdown { + left: ${themeCssVariables.spacing[2]}; + height: 260px; + } + + & .react-datepicker__year-dropdown { + left: calc(${themeCssVariables.spacing[9]} + 80px); + width: 100px; + height: 260px; + } + + & .react-datepicker__navigation--years { + display: none; + } + + & .react-datepicker__month-option--selected, + & .react-datepicker__year-option--selected { + display: none; + } + + & .react-datepicker__year-option, + & .react-datepicker__month-option { + text-align: left; + padding: ${themeCssVariables.spacing[2]} + calc(${themeCssVariables.spacing[2]} - 2px); + width: calc(100% - ${themeCssVariables.spacing[4]}); + border-radius: ${themeCssVariables.border.radius.xs}; + color: ${themeCssVariables.font.color.secondary}; + cursor: pointer; + margin: 2px; + + &:hover { + background: ${themeCssVariables.background.transparent.light}; + } + } + + & .react-datepicker__year-option { + &:first-of-type, + &:last-of-type { + display: none; + } + } + + & .react-datepicker__current-month { + display: none; + } + + & .react-datepicker__day-name { + color: ${themeCssVariables.font.color.secondary}; + width: 34px; + height: 40px; + line-height: 40px; + // Uniform 2px margin keeps the header row aligned with the day grid below; + // react-datepicker's default (0.166rem) leaves the grid tighter on the right. + margin: 2px; + } + + & .react-datepicker__month-container { + float: none; + } + + // Days + + & .react-datepicker__month { + margin: 0; + + pointer-events: ${({ calendarDisabled }) => + calendarDisabled ? 'none' : 'auto'}; + opacity: ${({ calendarDisabled }) => (calendarDisabled ? '0.5' : '1')}; + + display: ${({ hideCalendar }) => + hideCalendar === true ? 'none' : 'visible'}; + } + + & .react-datepicker__day-names { + display: ${({ hideCalendar }) => + hideCalendar === true ? 'none' : 'visible'}; + } + + & .react-datepicker__day { + width: 34px; + height: 34px; + line-height: 34px; + // Matches the day-name margin so each column lines up symmetrically. + margin: 2px; + } + + & .react-datepicker__navigation--previous, + & .react-datepicker__navigation--next { + height: 34px; + border-radius: ${themeCssVariables.border.radius.sm}; + padding-top: 6px; + &:hover { + background: ${themeCssVariables.background.transparent.light}; + } + } + & .react-datepicker__navigation--previous { + right: 38px; + top: 6px; + left: auto; + + & > span { + margin-left: -6px; + } + } + + & .react-datepicker__navigation--next { + right: 6px; + top: 6px; + + & > span { + margin-left: 6px; + } + } + + & .react-datepicker__navigation-icon::before { + height: 7px; + width: 7px; + border-width: 1px 1px 0 0; + border-color: ${themeCssVariables.font.color.tertiary}; + } + + & .react-datepicker__day--keyboard-selected { + background-color: inherit; + } + + & .react-datepicker__day, + .react-datepicker__time-name { + color: ${themeCssVariables.font.color.primary}; + } + + & .react-datepicker__day--selected { + background-color: ${themeCssVariables.color.blue}; + color: ${themeCssVariables.background.primary}; + + &.react-datepicker__day:hover { + color: ${themeCssVariables.background.primary}; + } + } + + & .react-datepicker__day--outside-month { + color: ${themeCssVariables.font.color.tertiary}; + } + + & .react-datepicker__day:hover { + color: ${themeCssVariables.font.color.tertiary}; + } + + & .clearable { + border-bottom: 1px solid ${themeCssVariables.border.color.light}; + } +`; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DatePicker.stories.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DatePicker.stories.tsx new file mode 100644 index 0000000000..535a2d4833 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DatePicker.stories.tsx @@ -0,0 +1,106 @@ +import { DatePicker } from '@/ui/input/components/internal/date/components/DatePicker'; +import { type Meta, type StoryObj } from '@storybook/react-vite'; +import { useState } from 'react'; +import { expect, userEvent, within } from 'storybook/test'; +import { type RelativeDateFilter } from 'twenty-shared/utils'; +import { ComponentDecorator } from 'twenty-ui/testing'; + +const INITIAL_PLAIN_DATE = '2023-01-01'; + +const RELATIVE_DATE: RelativeDateFilter & { start: string; end: string } = { + direction: 'PAST', + amount: 7, + unit: 'DAY', + start: '2022-12-25', + end: '2023-01-01', +}; + +const DatePickerStory = ({ + isRelative, + clearable, +}: { + isRelative?: boolean; + clearable?: boolean; +}) => { + const [plainDateString, setPlainDateString] = useState( + INITIAL_PLAIN_DATE, + ); + + return ( + + ); +}; + +const meta: Meta = { + title: 'UI/Input/Internal/Date/DatePicker', + component: DatePicker, + decorators: [ComponentDecorator], + render: () => , +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + // Lazy-loaded react-datepicker renders the day grid; wait for a day cell. + await canvas.findByText('15', {}, { timeout: 10000 }); + }, +}; + +export const NotClearable: Story = { + render: () => , + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await canvas.findByText('15', {}, { timeout: 10000 }); + expect(canvas.queryByText('Clear')).not.toBeInTheDocument(); + }, +}; + +export const Relative: Story = { + render: () => , + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await canvas.findByText('15', {}, { timeout: 10000 }); + }, +}; + +export const WithOpenMonthSelect: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const body = within(canvasElement.ownerDocument.body); + + const monthSelect = await canvas.findByText( + 'January', + {}, + { timeout: 10000 }, + ); + await userEvent.click(monthSelect); + + for (const monthLabel of ['February', 'June', 'December']) { + expect(await body.findByText(monthLabel)).toBeInTheDocument(); + } + }, +}; + +export const WithOpenYearSelect: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const body = within(canvasElement.ownerDocument.body); + + const yearSelect = await canvas.findByText('2023', {}, { timeout: 10000 }); + await userEvent.click(yearSelect); + + for (const yearLabel of ['2022', '2024']) { + expect(await body.findByText(yearLabel)).toBeInTheDocument(); + } + }, +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DatePickerWithoutCalendar.stories.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DatePickerWithoutCalendar.stories.tsx new file mode 100644 index 0000000000..cfa319332f --- /dev/null +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DatePickerWithoutCalendar.stories.tsx @@ -0,0 +1,42 @@ +import { DatePickerWithoutCalendar } from '@/ui/input/components/internal/date/components/DatePickerWithoutCalendar'; +import { type Meta, type StoryObj } from '@storybook/react-vite'; +import { useState } from 'react'; +import { expect, within } from 'storybook/test'; +import { ComponentDecorator } from 'twenty-ui/testing'; + +const INITIAL_PLAIN_DATE = '2023-01-01'; + +const DatePickerWithoutCalendarStory = () => { + const [date, setDate] = useState(INITIAL_PLAIN_DATE); + + return ( + + ); +}; + +const meta: Meta = { + title: 'UI/Input/Internal/Date/DatePickerWithoutCalendar', + component: DatePickerWithoutCalendar, + decorators: [ComponentDecorator], + render: () => , +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + // Calendar grid is hidden; only the month/year header should be present. + const monthSelect = await canvas.findByText( + 'January', + {}, + { timeout: 10000 }, + ); + expect(monthSelect).toBeInTheDocument(); + }, +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/InternalDatePicker.stories.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DateTimePicker.stories.tsx similarity index 98% rename from packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/InternalDatePicker.stories.tsx rename to packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DateTimePicker.stories.tsx index 287554d205..5c3cc51e14 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/InternalDatePicker.stories.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/__stories__/DateTimePicker.stories.tsx @@ -22,7 +22,7 @@ const DateTimePickerStory = () => { }; const meta: Meta = { - title: 'UI/Input/Internal/InternalDatePicker', + title: 'UI/Input/Internal/Date/DateTimePicker', component: DateTimePicker, decorators: [ComponentDecorator], render: () => ,