diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png
index c76efb162dc..ffa99f4fdca 100644
Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png differ
diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Restricted_Range.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Restricted_Range.png
new file mode 100644
index 00000000000..585d9fdc753
Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Restricted_Range.png differ
diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png
index e1b9ecdcc06..57abbb2620a 100644
Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSuperDatePicker_EuiSuperUpdateButton_Playground.png differ
diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png
index 28a69a24121..6eb2a51e3d3 100644
Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Custom_Quick_Select_Panel.png differ
diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Restricted_Range.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Restricted_Range.png
new file mode 100644
index 00000000000..32277cccdf5
Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSuperDatePicker_EuiSuperDatePicker_Restricted_Range.png differ
diff --git a/packages/eui/changelogs/upcoming/8071.md b/packages/eui/changelogs/upcoming/8071.md
new file mode 100644
index 00000000000..0d1f53ecdbc
--- /dev/null
+++ b/packages/eui/changelogs/upcoming/8071.md
@@ -0,0 +1,2 @@
+- Added props `minDate` and `maxDate` on `EuiSuperDatePicker` to support restricting date range selections
+
diff --git a/packages/eui/src-docs/src/views/super_date_picker/super_date_picker_example.js b/packages/eui/src-docs/src/views/super_date_picker/super_date_picker_example.js
index 4fea50c1a86..c520d7ddd6b 100644
--- a/packages/eui/src-docs/src/views/super_date_picker/super_date_picker_example.js
+++ b/packages/eui/src-docs/src/views/super_date_picker/super_date_picker_example.js
@@ -41,6 +41,9 @@ const superDatePickerPatternSource = require('!!raw-loader!./super_date_picker_p
import SuperDatePickerLocale from './super_date_picker_locale';
const superDatePickerLocaleSource = require('!!raw-loader!./super_date_picker_locale');
+import SuperDatePickerRangeRestricted from './super_date_picker_range_restricted';
+const superDatePickerRangeRestrictedSource = require('!!raw-loader!./super_date_picker_range_restricted');
+
const superDatePickerSnippet = ``;
+const superDatePickerRangeRestrictedSnippet = ``;
+
export const SuperDatePickerExample = {
title: 'Super date picker',
intro: (
@@ -391,5 +403,28 @@ if (!endMoment || !endMoment.isValid()) {
snippet: superDatePickerLocaleSnippet,
demo: ,
},
+ {
+ title: 'Restricted Range',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: superDatePickerRangeRestrictedSource,
+ },
+ ],
+ text: (
+ <>
+
+ To limit the range from which users can choose a date, you can use{' '}
+ minDate and maxDate. By
+ updating the date input values for start and{' '}
+ end users get immediate feedback on what range
+ values are allowed.
+
+ >
+ ),
+ props: { EuiSuperDatePicker },
+ snippet: superDatePickerRangeRestrictedSnippet,
+ demo: ,
+ },
],
};
diff --git a/packages/eui/src-docs/src/views/super_date_picker/super_date_picker_range_restricted.tsx b/packages/eui/src-docs/src/views/super_date_picker/super_date_picker_range_restricted.tsx
new file mode 100644
index 00000000000..583dbf00fe0
--- /dev/null
+++ b/packages/eui/src-docs/src/views/super_date_picker/super_date_picker_range_restricted.tsx
@@ -0,0 +1,29 @@
+import React, { useState } from 'react';
+
+import {
+ EuiSuperDatePicker,
+ OnTimeChangeProps,
+} from '../../../../src/components';
+import moment from 'moment';
+
+export default () => {
+ const [start, setStart] = useState('now-30m');
+ const [end, setEnd] = useState('now');
+ const minDate = moment().subtract(1, 'M');
+ const maxDate = moment().add(1, 'M');
+
+ const onTimeChange = ({ start, end }: OnTimeChangeProps) => {
+ setStart(start);
+ setEnd(end);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx
index 76b4b2717e2..e81813a8f9b 100644
--- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx
+++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx
@@ -45,6 +45,8 @@ export interface EuiAbsoluteTabProps {
roundUp: boolean;
labelPrefix: string;
utcOffset?: number;
+ minDate?: Moment;
+ maxDate?: Moment;
}
export const EuiAbsoluteTab: FunctionComponent = ({
@@ -55,6 +57,8 @@ export const EuiAbsoluteTab: FunctionComponent = ({
locale,
roundUp,
utcOffset,
+ minDate,
+ maxDate,
labelPrefix,
}) => {
const styles = useEuiMemoizedStyles(euiAbsoluteTabDateFormStyles);
@@ -157,6 +161,8 @@ export const EuiAbsoluteTab: FunctionComponent = ({
timeFormat={timeFormat}
locale={locale}
utcOffset={utcOffset}
+ minDate={minDate}
+ maxDate={maxDate}
/>
);
diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx
index cf62ac294ae..4ee97b3d971 100644
--- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx
+++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx
@@ -7,7 +7,7 @@
*/
import React, { FunctionComponent } from 'react';
-import { LocaleSpecifier } from 'moment';
+import { LocaleSpecifier, Moment } from 'moment';
import { useEuiMemoizedStyles } from '../../../../services';
import { useEuiPaddingCSS } from '../../../../global_styling';
@@ -37,6 +37,8 @@ export interface EuiDatePopoverContentProps {
locale?: LocaleSpecifier;
position: 'start' | 'end';
utcOffset?: number;
+ minDate?: Moment;
+ maxDate?: Moment;
timeOptions: TimeOptions;
}
@@ -53,6 +55,8 @@ export const EuiDatePopoverContent: FunctionComponent<
position,
utcOffset,
timeOptions,
+ minDate,
+ maxDate,
}) => {
const styles = useEuiMemoizedStyles(euiDatePopoverContentStyles);
@@ -101,6 +105,8 @@ export const EuiDatePopoverContent: FunctionComponent<
roundUp={roundUp}
labelPrefix={labelPrefix}
utcOffset={utcOffset}
+ minDate={minDate}
+ maxDate={maxDate}
/>
),
'data-test-subj': 'superDatePickerAbsoluteTab',
diff --git a/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.stories.tsx b/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.stories.tsx
index 528d9baf4b3..39ada5dbf93 100644
--- a/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.stories.tsx
+++ b/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.stories.tsx
@@ -7,6 +7,7 @@
*/
import React from 'react';
+import moment from 'moment';
import type { Meta, StoryObj } from '@storybook/react';
import { expect } from '@storybook/test';
import { within } from '../../../../.storybook/test';
@@ -48,6 +49,8 @@ const meta: Meta = {
isLoading: false,
isQuickSelectOnly: false,
commonlyUsedRanges: [{ start: 'now/d', end: 'now/d', label: 'Today' }],
+ maxDate: undefined,
+ minDate: undefined,
},
};
enableFunctionToggleControls(meta, ['onTimeChange']);
@@ -89,6 +92,28 @@ export const CustomQuickSelectPanel: Story = {
},
};
+export const RestrictedRange: Story = {
+ parameters: {
+ controls: {
+ include: [
+ 'dateFormat',
+ 'start',
+ 'end',
+ 'minDate',
+ 'maxDate',
+ 'onTimeChange',
+ ],
+ },
+ loki: {
+ chromeSelector: LOKI_SELECTORS.portal,
+ },
+ },
+ args: {
+ minDate: moment('10/01/2024'),
+ maxDate: moment('11/01/2024'),
+ },
+};
+
function CustomPanel({ applyTime }: { applyTime?: ApplyTime }) {
function applyMyCustomTime() {
applyTime!({ start: 'now-30d', end: 'now+7d' });
diff --git a/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.test.tsx b/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.test.tsx
index f6d9e552a08..7c9520abf10 100644
--- a/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.test.tsx
+++ b/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.test.tsx
@@ -16,6 +16,7 @@ import {
EuiSuperDatePicker,
EuiSuperDatePickerProps,
} from './super_date_picker';
+import moment from 'moment';
const noop = () => {};
@@ -396,5 +397,83 @@ describe('EuiSuperDatePicker', () => {
expect(startButton).toHaveTextContent('300 days ago');
});
});
+
+ describe('minDate', () => {
+ const props = {
+ onTimeChange: noop,
+ end: 'now',
+ };
+
+ it('is valid when the start value is set after the minDate', () => {
+ const { container } = render(
+
+ );
+
+ const formWraper = container.querySelector(
+ '.euiFormControlLayout__childrenWrapper'
+ )!;
+
+ expect(formWraper.className).not.toContain('invalid');
+ });
+
+ it('is invalid when the start value is set before the minDate', () => {
+ const { container } = render(
+
+ );
+
+ const formWraper = container.querySelector(
+ '.euiFormControlLayout__childrenWrapper'
+ )!;
+
+ expect(formWraper.className).toContain('invalid');
+ });
+ });
+
+ describe('maxDate', () => {
+ const props = {
+ onTimeChange: noop,
+ start: '10/01/2024',
+ };
+
+ it('is valid when the end value is set before the maxDate', () => {
+ const { container } = render(
+
+ );
+
+ const formWraper = container.querySelector(
+ '.euiFormControlLayout__childrenWrapper'
+ )!;
+
+ expect(formWraper.className).not.toContain('invalid');
+ });
+
+ it('is invalid when the end date exceeds the maxDate', () => {
+ const { container } = render(
+
+ );
+
+ const formWraper = container.querySelector(
+ '.euiFormControlLayout__childrenWrapper'
+ )!;
+
+ expect(formWraper.className).toContain('invalid');
+ });
+ });
});
});
diff --git a/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.tsx b/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.tsx
index 0c804ca5f79..f63194683c5 100644
--- a/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.tsx
+++ b/packages/eui/src/components/date_picker/super_date_picker/super_date_picker.tsx
@@ -13,7 +13,7 @@ import React, {
ReactNode,
} from 'react';
import classNames from 'classnames';
-import moment, { LocaleSpecifier } from 'moment'; // eslint-disable-line import/named
+import moment, { LocaleSpecifier, Moment } from 'moment'; // eslint-disable-line import/named
import dateMath from '@elastic/datemath';
import { useEuiMemoizedStyles } from '../../../services';
@@ -174,6 +174,16 @@ export type EuiSuperDatePickerProps = CommonProps & {
*/
end?: ShortDate;
+ /**
+ * Defines min. date accepted as a selection (in moment format)
+ */
+ minDate?: moment.Moment;
+
+ /**
+ * Defines max. date accepted as a selection (in moment format)
+ */
+ maxDate?: moment.Moment;
+
/**
* Specifies the formatted used when displaying times
* @default 'HH:mm'
@@ -235,7 +245,12 @@ interface EuiSuperDatePickerState {
start: ShortDate;
}
-function isRangeInvalid(start: ShortDate, end: ShortDate) {
+function isRangeInvalid(
+ start: ShortDate,
+ end: ShortDate,
+ minDate?: Moment,
+ maxDate?: Moment
+) {
if (start === 'now' && end === 'now') {
return true;
}
@@ -250,7 +265,10 @@ function isRangeInvalid(start: ShortDate, end: ShortDate) {
!endMoment.isValid() ||
!moment(startMoment).isValid() ||
!moment(endMoment).isValid() ||
- startMoment.isAfter(endMoment);
+ startMoment.isAfter(endMoment) ||
+ endMoment.isBefore(startMoment) ||
+ (minDate != null && startMoment.isBefore(minDate)) ||
+ (maxDate != null && endMoment.isAfter(maxDate));
return isInvalid;
}
@@ -283,7 +301,12 @@ export class EuiSuperDatePickerInternal extends Component<
},
start: this.props.start,
end: this.props.end,
- isInvalid: isRangeInvalid(this.props.start, this.props.end),
+ isInvalid: isRangeInvalid(
+ this.props.start,
+ this.props.end,
+ this.props.minDate,
+ this.props.maxDate
+ ),
hasChanged: false,
showPrettyDuration: showPrettyDuration(
this.props.start,
@@ -309,7 +332,12 @@ export class EuiSuperDatePickerInternal extends Component<
},
start: nextProps.start,
end: nextProps.end,
- isInvalid: isRangeInvalid(nextProps.start, nextProps.end),
+ isInvalid: isRangeInvalid(
+ nextProps.start,
+ nextProps.end,
+ nextProps.minDate,
+ nextProps.maxDate
+ ),
hasChanged: false,
showPrettyDuration: showPrettyDuration(
nextProps.start,
@@ -323,7 +351,12 @@ export class EuiSuperDatePickerInternal extends Component<
}
setTime = ({ end, start }: DurationRange) => {
- const isInvalid = isRangeInvalid(start, end);
+ const isInvalid = isRangeInvalid(
+ start,
+ end,
+ this.props.minDate,
+ this.props.maxDate
+ );
this.setState({
start,
@@ -516,6 +549,8 @@ export class EuiSuperDatePickerInternal extends Component<
locale,
timeFormat,
utcOffset,
+ minDate,
+ maxDate,
compressed,
onFocus,
memoizedStyles: styles,
@@ -631,6 +666,8 @@ export class EuiSuperDatePickerInternal extends Component<
utcOffset={utcOffset}
timeFormat={timeFormat}
locale={locale || contextLocale}
+ minDate={minDate}
+ maxDate={maxDate}
canRoundRelativeUnits={canRoundRelativeUnits}
isOpen={this.state.isStartDatePopoverOpen}
onPopoverToggle={this.onStartDatePopoverToggle}
@@ -653,6 +690,8 @@ export class EuiSuperDatePickerInternal extends Component<
utcOffset={utcOffset}
timeFormat={timeFormat}
locale={locale || contextLocale}
+ minDate={minDate}
+ maxDate={maxDate}
canRoundRelativeUnits={canRoundRelativeUnits}
roundUp
isOpen={this.state.isEndDatePopoverOpen}