diff --git a/.changeset/polite-apes-create.md b/.changeset/polite-apes-create.md new file mode 100644 index 0000000000..942f6b90c0 --- /dev/null +++ b/.changeset/polite-apes-create.md @@ -0,0 +1,28 @@ +--- +'@commercetools-uikit/date-range-input': minor +'@commercetools-uikit/date-time-input': minor +'@commercetools-uikit/date-input': minor +'@commercetools-uikit/calendar-utils': minor +--- + +feat: add `appearance` prop with 'filter' option to date input components + +To use the date filters, there are some visual modifications that need to happen in the different date inputs to support the designs and ux of the filters pattern. Most of these changes are dependent on new props to set these options when the component is used in a filter component. + +Add support for `appearance: 'filter'` to DateInput, DateTimeInput, and DateRangeInput components. When set to 'filter', the components: + +- Remove borders and box shadows for a clean, inline appearance +- Keep the calendar always open (when not disabled or read-only) +- Maintain transparent backgrounds to blend seamlessly with filter UIs + +This follows the same design pattern established in select input components and enables date inputs to be used effectively within filter components and search interfaces. + +**New Props:** +- `appearance?: 'default' | 'filter'` - Controls the visual styling of the date input + +**Examples:** +```jsx + + + +``` diff --git a/packages/calendar-utils/src/calendar-body/calendar-body.styles.ts b/packages/calendar-utils/src/calendar-body/calendar-body.styles.ts index 8442341d03..7e05002a94 100644 --- a/packages/calendar-utils/src/calendar-body/calendar-body.styles.ts +++ b/packages/calendar-utils/src/calendar-body/calendar-body.styles.ts @@ -27,10 +27,13 @@ const getClearSectionStyles = () => { }; type TState = { - isFocused: boolean; + isFocused?: boolean; }; const getIconBorderColor = (props: TCalendarBody, state: TState) => { + if (props.appearance === 'filter') { + return designTokens.colorTransparent; + } if (props.isDisabled) { return designTokens.borderColorForInputWhenDisabled; } @@ -89,7 +92,9 @@ const getCalendarIconContainerStyles = ( &:active, &:hover:not(:disabled)&:not(:read-only), &:focus { - border-color: ${designTokens.borderColorForInputWhenFocused}; + border-color: ${props.appearance === 'filter' + ? designTokens.colorTransparent + : designTokens.borderColorForInputWhenFocused}; } `, ]; @@ -108,7 +113,7 @@ const getInputBorderColor = (props: TCalendarBody, state: TState) => { if (props.isReadOnly) { return designTokens.borderColorForInputWhenReadonly; } - if ((props.isOpen || state.isFocused) && !props.isReadOnly) { + if (props.isOpen || state.isFocused) { return designTokens.borderColorForInputWhenFocused; } return designTokens.borderColorForInput; @@ -131,6 +136,9 @@ const getInputFontColor = (props: TCalendarBody) => { }; const getInputContainerBackgroundColor = (props: TCalendarBody) => { + if (props.appearance === 'filter') { + return designTokens.colorTransparent; + } if (props.isDisabled) { return designTokens.backgroundColorForInputWhenDisabled; } @@ -164,7 +172,9 @@ const getInputContainerStyles = (props: TCalendarBody, state: TState) => { &:hover:not(:focus) { background-color: ${!props.isDisabled && !props.isReadOnly - ? designTokens.backgroundColorForInputWhenHovered + ? props.appearance === 'filter' + ? designTokens.colorTransparent + : designTokens.backgroundColorForInputWhenHovered : null}; } &:focus { @@ -174,10 +184,13 @@ const getInputContainerStyles = (props: TCalendarBody, state: TState) => { props.isReadOnly || ((props.isOpen || state.isFocused) && !props.isReadOnly) ? '' + : props.appearance === 'filter' + ? designTokens.colorTransparent : designTokens.borderColorForInputWhenFocused}; } `, !props.isReadOnly && + props.appearance !== 'filter' && css` &:focus-within { border-color: ${designTokens.borderColorForInputWhenFocused}; @@ -189,6 +202,7 @@ const getInputContainerStyles = (props: TCalendarBody, state: TState) => { } `, (props.hasError || props.hasWarning) && + props.appearance !== 'filter' && css` box-shadow: inset 0 0 0 1px; `, diff --git a/packages/calendar-utils/src/calendar-body/calendar-body.tsx b/packages/calendar-utils/src/calendar-body/calendar-body.tsx index e76c95077d..6ea7c4a3aa 100644 --- a/packages/calendar-utils/src/calendar-body/calendar-body.tsx +++ b/packages/calendar-utils/src/calendar-body/calendar-body.tsx @@ -77,6 +77,11 @@ export type TCalendarBody = { placeholder?: string; /** @deprecated */ theme?: Theme; + /** + * Indicates the appearance of the input. + * Filter appearance removes borders and box shadows for use in filter components. + */ + appearance?: 'default' | 'filter'; }; export const CalendarBody = ({ @@ -151,42 +156,47 @@ export const CalendarBody = ({ onBlur={handleInputBlur} aria-readonly={props.isReadOnly} /> - {!disabledOrReadOnly && props.hasSelection && isClearable && ( - - )} - + {props.appearance !== 'filter' && ( + + )} ); diff --git a/packages/calendar-utils/src/calendar-menu/calendar-menu.tsx b/packages/calendar-utils/src/calendar-menu/calendar-menu.tsx index 00e3dcc9bc..b7925d2873 100644 --- a/packages/calendar-utils/src/calendar-menu/calendar-menu.tsx +++ b/packages/calendar-utils/src/calendar-menu/calendar-menu.tsx @@ -8,12 +8,23 @@ type TCalendarMenu = { hasError?: boolean; hasWarning?: boolean; footer?: ReactNode; + /** + * Indicates the appearance of the calendar menu. + * Filter appearance removes box shadows and positioning for inline display. + */ + appearance?: 'default' | 'filter'; }; export default class CalendarMenu extends Component { static displayName = 'CalendarMenu'; render() { - const { hasFooter, hasWarning, hasError, ...rest } = this.props; + const { + hasFooter, + hasWarning, + hasError, + appearance = 'default', + ...rest + } = this.props; return (
{ color: ${designTokens.colorSolid}; font-family: inherit; border: none; - box-shadow: 0 2px 5px 0px rgba(0, 0, 0, 0.15); + box-shadow: ${appearance === 'filter' + ? 'none' + : '0 2px 5px 0px rgba(0, 0, 0, 0.15)'}; border-radius: ${designTokens.borderRadiusForInput}; margin-top: ${designTokens.spacing10}; font-size: ${designTokens.fontSize30}; - position: absolute; + position: ${appearance === 'filter' ? 'inherit' : 'absolute'}; box-sizing: border-box; width: 100%; background-color: ${designTokens.colorSurface}; min-width: ${designTokens.constraint5}; - z-index: 99999; /* copied from flatpickr */ + z-index: ${appearance === 'filter' + ? 'inherit' + : '99999'}; /* copied from flatpickr */ `, !hasFooter && css` diff --git a/packages/components/filters/src/filter-menu/filter-menu.tsx b/packages/components/filters/src/filter-menu/filter-menu.tsx index 10700082f3..b4601043b4 100644 --- a/packages/components/filters/src/filter-menu/filter-menu.tsx +++ b/packages/components/filters/src/filter-menu/filter-menu.tsx @@ -100,7 +100,7 @@ export const menuStyles = css` flex-direction: column; align-items: flex-start; gap: ${designTokens.spacing30}; - width: ${designTokens.constraint6}; + width: ${designTokens.constraint7}; max-height: ${designTokens.constraint10}; padding: ${designTokens.spacing20} ${designTokens.spacing30}; background-color: ${designTokens.colorSurface}; diff --git a/packages/components/filters/src/filters.stories.tsx b/packages/components/filters/src/filters.stories.tsx index 56a27d6c3d..0a6bae70df 100644 --- a/packages/components/filters/src/filters.stories.tsx +++ b/packages/components/filters/src/filters.stories.tsx @@ -11,6 +11,10 @@ import { PrimaryColorsRadioInput, PrimaryColorsTextInput, OperatorsInput, + DateRangeFilterInput, + DateFilterWithOperator, + DateTimeFilterWithOperator, + DateOperatorsInput, } from './fixtures/inputs'; import { FILTER_GROUP_KEYS, @@ -347,3 +351,311 @@ export const BasicExample: Story = (props: TFiltersPropsWithCustomArgs) => { /> ); }; + +export const DateFiltersExample: Story = () => { + // simulate state from parent application for each date filter + // Pending values (before apply) + const [pendingDateValue, setPendingDateValue] = useState( + '' + ); + const [pendingDateTimeValue, setPendingDateTimeValue] = useState< + string | string[] + >(''); + const [pendingDateRangeValue, setPendingDateRangeValue] = useState( + [] + ); + + // Applied values (after clicking apply) + const [appliedDateValue, setAppliedDateValue] = useState< + TFiltersProps['appliedFilters'] + >([]); + const [appliedDateTimeValue, setAppliedDateTimeValue] = useState< + TFiltersProps['appliedFilters'] + >([]); + const [appliedDateRangeValue, setAppliedDateRangeValue] = useState< + TFiltersProps['appliedFilters'] + >([]); + + // Operators for each date filter + const [dateOperator, setDateOperator] = useState('is'); + const [dateTimeOperator, setDateTimeOperator] = useState('is'); + const [dateRangeOperator, setDateRangeOperator] = + useState('is between'); + + // Helper function to get applied values for a filter + const getDateAppliedValue = (): TFiltersProps['appliedFilters'] => { + if ( + dateOperator === 'is between' && + Array.isArray(pendingDateValue) && + pendingDateValue.length === 2 + ) { + return [ + { + filterKey: 'date', + values: [ + { + value: pendingDateValue.join(' - '), + label: `${pendingDateValue[0]} - ${pendingDateValue[1]}`, + }, + ], + }, + ]; + } else if (pendingDateValue && !Array.isArray(pendingDateValue)) { + return [ + { + filterKey: 'date', + values: [ + { + value: pendingDateValue, + label: pendingDateValue, + }, + ], + }, + ]; + } + return []; + }; + + const getDateTimeAppliedValue = (): TFiltersProps['appliedFilters'] => { + if ( + dateTimeOperator === 'is between' && + Array.isArray(pendingDateTimeValue) && + pendingDateTimeValue.length === 2 + ) { + return [ + { + filterKey: 'dateTime', + values: [ + { + value: pendingDateTimeValue.join(' - '), + label: `${pendingDateTimeValue[0]} - ${pendingDateTimeValue[1]}`, + }, + ], + }, + ]; + } else if (pendingDateTimeValue && !Array.isArray(pendingDateTimeValue)) { + return [ + { + filterKey: 'dateTime', + values: [ + { + value: pendingDateTimeValue, + label: new Date(pendingDateTimeValue).toLocaleString(), + }, + ], + }, + ]; + } + return []; + }; + + const getDateRangeAppliedValue = (): TFiltersProps['appliedFilters'] => { + if (pendingDateRangeValue.length === 2) { + return [ + { + filterKey: 'dateRange', + values: [ + { + value: pendingDateRangeValue.join(' - '), + label: `${pendingDateRangeValue[0]} - ${pendingDateRangeValue[1]}`, + }, + ], + }, + ]; + } + return []; + }; + + // Clear functions for pending values + const clearDateFilter = () => { + setPendingDateValue(dateOperator === 'is between' ? [] : ''); + setAppliedDateValue([]); + }; + + const clearDateTimeFilter = () => { + setPendingDateTimeValue(dateTimeOperator === 'is between' ? [] : ''); + setAppliedDateTimeValue([]); + }; + + const clearDateRangeFilter = () => { + setPendingDateRangeValue([]); + setAppliedDateRangeValue([]); + }; + + // Clear all filters + const clearAllFilters = () => { + clearDateFilter(); + clearDateTimeFilter(); + clearDateRangeFilter(); + }; + + // Handle operator changes and reset values + const handleDateOperatorChange = (newOperator: string) => { + setDateOperator(newOperator); + setPendingDateValue(newOperator === 'is between' ? [] : ''); + setAppliedDateValue([]); + }; + + const handleDateTimeOperatorChange = (newOperator: string) => { + setDateTimeOperator(newOperator); + setPendingDateTimeValue(newOperator === 'is between' ? [] : ''); + setAppliedDateTimeValue([]); + }; + + // Check if apply button should be enabled + const isDateApplyEnabled = () => { + if (dateOperator === 'is between') { + return Array.isArray(pendingDateValue) && pendingDateValue.length === 2; + } + return pendingDateValue && !Array.isArray(pendingDateValue); + }; + + const isDateTimeApplyEnabled = () => { + if (dateTimeOperator === 'is between') { + return ( + Array.isArray(pendingDateTimeValue) && pendingDateTimeValue.length === 2 + ); + } + return pendingDateTimeValue && !Array.isArray(pendingDateTimeValue); + }; + + const isDateRangeApplyEnabled = () => { + return pendingDateRangeValue.length === 2; + }; + + // generate 'appliedFilters' state based on applied values + const appliedFilters: TFiltersProps['appliedFilters'] = [ + ...appliedDateValue, + ...appliedDateTimeValue, + ...appliedDateRangeValue, + ]; + + const filters = [ + { + key: 'date', + label: 'Date', + operatorLabel: dateOperator, + groupKey: FILTER_GROUP_KEYS.dateFilters, + filterMenuConfiguration: { + renderMenuBody: () => ( + + ), + renderOperatorsInput: () => ( + + ), + renderApplyButton: () => ( + { + setAppliedDateValue(getDateAppliedValue()); + }} + isDisabled={!isDateApplyEnabled()} + label="Apply" + size="10" + /> + ), + onClearRequest: clearDateFilter, + }, + }, + { + key: 'dateTime', + label: 'Date & Time', + operatorLabel: dateTimeOperator, + groupKey: FILTER_GROUP_KEYS.dateFilters, + filterMenuConfiguration: { + renderMenuBody: () => ( + + ), + renderOperatorsInput: () => ( + + ), + renderApplyButton: () => ( + { + setAppliedDateTimeValue(getDateTimeAppliedValue()); + }} + isDisabled={!isDateTimeApplyEnabled()} + label="Apply" + size="10" + /> + ), + onClearRequest: clearDateTimeFilter, + }, + }, + { + key: 'dateRange', + label: 'Date Range', + operatorLabel: dateRangeOperator, + groupKey: FILTER_GROUP_KEYS.dateFilters, + filterMenuConfiguration: { + renderMenuBody: () => ( + + ), + renderOperatorsInput: () => ( + + ), + renderApplyButton: () => ( + { + setAppliedDateRangeValue(getDateRangeAppliedValue()); + }} + isDisabled={!isDateRangeApplyEnabled()} + label="Apply" + size="10" + /> + ), + onClearRequest: clearDateRangeFilter, + }, + }, + ]; + + return ( +
+

+ Advanced Date Filters with Operators +

+

+ This example demonstrates date filter functionality including: +
+ • Configurable operators (is, is not, is between, is before, is after) +
+ • Dynamic component switching (DateInput ↔ DateRangeInput based on + operator) +
+

+ } + filters={filters} + filterGroups={FILTER_GROUPS} + appliedFilters={appliedFilters} + onClearAllRequest={clearAllFilters} + defaultOpen={true} + /> +
+ ); +}; diff --git a/packages/components/filters/src/fixtures/constants.tsx b/packages/components/filters/src/fixtures/constants.tsx index 9c396d264e..7e419f2f3a 100644 --- a/packages/components/filters/src/fixtures/constants.tsx +++ b/packages/components/filters/src/fixtures/constants.tsx @@ -1,6 +1,7 @@ export const FILTER_GROUP_KEYS = { primaryColors: 'primaryColors', secondaryColors: 'secondaryColors', + dateFilters: 'dateFilters', }; export const FILTER_GROUPS = [ @@ -9,6 +10,10 @@ export const FILTER_GROUPS = [ key: FILTER_GROUP_KEYS.secondaryColors, label:
Secondary Colors
, }, + { + key: FILTER_GROUP_KEYS.dateFilters, + label:
Date Filters
, + }, ]; export const PRIMARY_COLOR_OPTIONS = [ @@ -47,3 +52,11 @@ export const OPERATOR_OPTIONS = [ { value: 'is', label: 'is' }, { value: 'is not', label: 'is not' }, ]; + +export const DATE_OPERATOR_OPTIONS = [ + { value: 'is', label: 'is' }, + { value: 'is not', label: 'is not' }, + { value: 'is between', label: 'is between' }, + { value: 'is before', label: 'is before' }, + { value: 'is after', label: 'is after' }, +]; diff --git a/packages/components/filters/src/fixtures/inputs.tsx b/packages/components/filters/src/fixtures/inputs.tsx index 13cfc7c6a2..4ace27eeea 100644 --- a/packages/components/filters/src/fixtures/inputs.tsx +++ b/packages/components/filters/src/fixtures/inputs.tsx @@ -4,11 +4,15 @@ import RadioInput from '@commercetools-uikit/radio-input'; import SearchTextInput from '@commercetools-uikit/search-text-input'; import SelectInput from '@commercetools-uikit/select-input'; import TextInput from '@commercetools-uikit/text-input'; +import DateInput from '@commercetools-uikit/date-input'; +import DateTimeInput from '@commercetools-uikit/date-time-input'; +import DateRangeInput from '@commercetools-uikit/date-range-input'; import { PRIMARY_COLOR_OPTIONS, SECONDARY_COLOR_OPTIONS, FRUIT_OPTIONS, OPERATOR_OPTIONS, + DATE_OPERATOR_OPTIONS, } from './constants'; type TFiltersSelectExampleProps = { @@ -21,6 +25,17 @@ type TFiltersInputExampleProps = { onChange: Function; }; +type TDateFilterWithOperatorProps = { + value: string | string[]; + onChange: Function; + operator: string; +}; + +type TDateRangeFilterProps = { + value: string[]; + onChange: Function; +}; + export const PrimaryColorsInput = ({ value, onChange, @@ -154,3 +169,102 @@ export const OperatorsInput = ({ }} /> ); + +export const DateRangeFilterInput = ({ + value, + onChange, +}: TDateRangeFilterProps) => ( + onChange(e.target.value)} + appearance="filter" + placeholder="Select date range" + isClearable={false} + /> +); + +export const DateFilterWithOperator = ({ + value, + onChange, + operator, +}: TDateFilterWithOperatorProps) => { + // Use DateRangeInput for "is between" operator, DateInput for others + if (operator === 'is between') { + return ( + onChange(e.target.value)} + appearance="filter" + placeholder="Select date range" + isClearable={false} + /> + ); + } + + return ( + onChange(e.target.value)} + appearance="filter" + placeholder="Select date" + /> + ); +}; + +export const DateTimeFilterWithOperator = ({ + value, + onChange, + operator, +}: TDateFilterWithOperatorProps) => { + // Use DateRangeInput for "is between" operator, DateTimeInput for others + if (operator === 'is between') { + return ( + onChange(e.target.value)} + appearance="filter" + placeholder="Select date range" + isClearable={false} + /> + ); + } + + return ( + onChange(e.target.value)} + appearance="filter" + timeZone="UTC" + placeholder="Select date and time" + /> + ); +}; + +export const DateOperatorsInput = ({ + value, + onChange, +}: TFiltersInputExampleProps) => ( + { + onChange(event.target.value as string); + }} + /> +); diff --git a/packages/components/inputs/date-input/src/date-input.stories.tsx b/packages/components/inputs/date-input/src/date-input.stories.tsx index eb3eb9be0a..b20f027674 100644 --- a/packages/components/inputs/date-input/src/date-input.stories.tsx +++ b/packages/components/inputs/date-input/src/date-input.stories.tsx @@ -5,6 +5,12 @@ import { useEffect, useState } from 'react'; const meta: Meta = { title: 'Form/Inputs/DateInput', component: DateInput, + argTypes: { + appearance: { + control: { type: 'select' }, + options: ['default', 'filter'], + }, + }, decorators: [ (Story) => (
@@ -43,5 +49,33 @@ export const BasicExample: Story = { id: 'date-input', horizontalConstraint: 7, value: '', + appearance: 'default', + }, +}; + +export const FilterAppearance: Story = { + render: (args) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [value, setValue] = useState(args.value); + // eslint-disable-next-line react-hooks/rules-of-hooks + useEffect(() => { + setValue(args.value || ''); + }, [args.value]); + + return ( +
+ setValue(e.target.value || '')} + /> +
+ ); + }, + args: { + id: 'date-input-filter', + horizontalConstraint: 7, + value: '', + appearance: 'filter', }, }; diff --git a/packages/components/inputs/date-input/src/date-input.tsx b/packages/components/inputs/date-input/src/date-input.tsx index fb431efd66..2f72c96861 100644 --- a/packages/components/inputs/date-input/src/date-input.tsx +++ b/packages/components/inputs/date-input/src/date-input.tsx @@ -135,6 +135,11 @@ export type TDateInput = { * A maximum selectable date. Must either be an empty string or a date formatted as "YYYY-MM-DD". */ maxValue?: string; + /** + * Indicates the appearance of the input. + * Filter appearance removes borders and box shadows, and calendar is always open. + */ + appearance?: 'default' | 'filter'; }; const DateInput = (props: TDateInput) => { @@ -145,6 +150,7 @@ const DateInput = (props: TDateInput) => { number | null | undefined >(props.value === '' ? null : getDateInMonth(props.value) - 1); const inputRef = useRef(null); + const appearance = props.appearance || 'default'; if (!props.isReadOnly) { warning( @@ -277,6 +283,7 @@ const DateInput = (props: TDateInput) => {
{ hasError={props.hasError} hasWarning={props.hasWarning} /> - {isOpen && !props.isDisabled && !props.isReadOnly && ( + {((isOpen && !props.isDisabled && !props.isReadOnly) || + (appearance === 'filter' && + !props.isDisabled && + !props.isReadOnly)) && ( ( placeholder="Select something" /> + + {}} + isCondensed={true} + horizontalConstraint={7} + placeholder="Select something" + appearance="filter" + /> + ); diff --git a/packages/components/inputs/date-range-input/src/date-range-input.stories.tsx b/packages/components/inputs/date-range-input/src/date-range-input.stories.tsx index 00f103eba1..c1c4373456 100644 --- a/packages/components/inputs/date-range-input/src/date-range-input.stories.tsx +++ b/packages/components/inputs/date-range-input/src/date-range-input.stories.tsx @@ -6,6 +6,12 @@ import { useState } from 'react'; const meta: Meta = { title: 'Form/Inputs/DateRangeInput', component: DateRangeInputProxy, + argTypes: { + appearance: { + control: { type: 'select' }, + options: ['default', 'filter'], + }, + }, }; export default meta; @@ -32,4 +38,27 @@ export const BasicExample: Story = (args: TDateRangeInputProps) => { BasicExample.args = { horizontalConstraint: 10, isClearable: true, + appearance: 'default', +}; + +export const FilterAppearance: Story = (args: TDateRangeInputProps) => { + const [value, setValue] = useState([ + '2024-11-13', + '2024-11-16', + ]); + return ( +
+ setValue(e.target.value as DateRangeArray)} + value={value} + /> +
+ ); +}; + +FilterAppearance.args = { + horizontalConstraint: 10, + isClearable: true, + appearance: 'filter', }; diff --git a/packages/components/inputs/date-range-input/src/date-range-input.tsx b/packages/components/inputs/date-range-input/src/date-range-input.tsx index b5ea71299a..1469319fc3 100644 --- a/packages/components/inputs/date-range-input/src/date-range-input.tsx +++ b/packages/components/inputs/date-range-input/src/date-range-input.tsx @@ -192,6 +192,11 @@ export type TDateRangeInputProps = { * Indicates the input field has warning */ hasWarning?: boolean; + /** + * Indicates the appearance of the input. + * Filter appearance removes borders and box shadows, and calendar is always open. + */ + appearance?: 'default' | 'filter'; } & WrappedComponentProps; type TDateRangeInputState = { @@ -285,6 +290,8 @@ class DateRangeInput extends Component< }); }; render() { + const appearance = this.props.appearance || 'default'; + return ( - {isOpen && !this.props.isDisabled && ( + {((isOpen && !this.props.isDisabled) || + (appearance === 'filter' && + !this.props.isDisabled && + !this.props.isReadOnly)) && ( = { 'Europe/Amsterdam', ], }, + appearance: { + control: { type: 'select' }, + options: ['default', 'filter'], + }, }, }; export default meta; @@ -40,4 +44,25 @@ export const BasicExample: Story = (args: TDateTimeInputProps) => { BasicExample.args = { timeZone: 'UTC', horizontalConstraint: 8, + appearance: 'default', +}; + +export const FilterAppearance: Story = (args: TDateTimeInputProps) => { + const [value, setValue] = useState(''); + + return ( +
+ setValue(e.target.value || '')} + value={value} + /> +
+ ); +}; + +FilterAppearance.args = { + timeZone: 'UTC', + horizontalConstraint: 8, + appearance: 'filter', }; diff --git a/packages/components/inputs/date-time-input/src/date-time-input.tsx b/packages/components/inputs/date-time-input/src/date-time-input.tsx index d7f1eb5741..c3e1854021 100644 --- a/packages/components/inputs/date-time-input/src/date-time-input.tsx +++ b/packages/components/inputs/date-time-input/src/date-time-input.tsx @@ -180,9 +180,14 @@ export type TDateTimeInputProps = { hasWarning?: boolean; /** * The time that will be used by default when a user selects a calendar day. - * It must follow the “HH:mm” pattern (eg: 04:30, 13:25, 23:59) + * It must follow the "HH:mm" pattern (eg: 04:30, 13:25, 23:59) */ defaultDaySelectionTime?: string; + /** + * Indicates the appearance of the input. + * Filter appearance removes borders and box shadows, and calendar is always open. + */ + appearance?: 'default' | 'filter'; } & WrappedComponentProps; type TDateTimeInputState = { @@ -281,6 +286,8 @@ class DateTimeInput extends Component< ); } + const appearance = this.props.appearance || 'default'; + return ( - {isOpen && !this.props.isDisabled && ( + {((isOpen && !this.props.isDisabled) || + (appearance === 'filter' && !this.props.isDisabled)) && ( ( horizontalConstraint={7} /> + + {}} + horizontalConstraint={7} + appearance="filter" + /> + );