From 8223cf89c0096d8ee0acfe1d409c7a00ad010d94 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Tue, 6 Feb 2024 12:26:29 -0800 Subject: [PATCH 1/5] [setup] Add new `minInterval` prop --- src/components/date_picker/auto_refresh/auto_refresh.tsx | 4 ++++ .../date_picker/auto_refresh/refresh_interval.tsx | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/date_picker/auto_refresh/auto_refresh.tsx b/src/components/date_picker/auto_refresh/auto_refresh.tsx index e4e7f89b67a..d8b08dd5366 100644 --- a/src/components/date_picker/auto_refresh/auto_refresh.tsx +++ b/src/components/date_picker/auto_refresh/auto_refresh.tsx @@ -40,6 +40,7 @@ export const EuiAutoRefresh: FunctionComponent = ({ isDisabled, isPaused = true, refreshInterval = 1000, + minInterval = 0, readOnly = true, ...rest }) => { @@ -90,6 +91,7 @@ export const EuiAutoRefresh: FunctionComponent = ({ onRefreshChange={onRefreshChange} isPaused={isPaused} refreshInterval={refreshInterval} + minInterval={minInterval} intervalUnits={intervalUnits} /> @@ -115,6 +117,7 @@ export const EuiAutoRefreshButton: FunctionComponent< isDisabled, isPaused = true, refreshInterval = 1000, + minInterval = 0, shortHand = false, size = 's', color = 'text', @@ -168,6 +171,7 @@ export const EuiAutoRefreshButton: FunctionComponent< onRefreshChange={onRefreshChange} isPaused={isPaused} refreshInterval={refreshInterval} + minInterval={minInterval} intervalUnits={intervalUnits} /> diff --git a/src/components/date_picker/auto_refresh/refresh_interval.tsx b/src/components/date_picker/auto_refresh/refresh_interval.tsx index 5c86a7ef997..8686a740b35 100644 --- a/src/components/date_picker/auto_refresh/refresh_interval.tsx +++ b/src/components/date_picker/auto_refresh/refresh_interval.tsx @@ -77,9 +77,9 @@ export type EuiRefreshIntervalProps = { */ refreshInterval?: Milliseconds; /** - * Passes back the updated state of `isPaused` `refreshInterval`, and `intervalUnits`. + * Allows specifying a minimum interval in milliseconds */ - onRefreshChange: ApplyRefreshInterval; + minInterval?: Milliseconds; /** * By default, refresh interval units will be rounded up to next largest unit of time * (for example, 90 seconds will become 2m). @@ -87,6 +87,10 @@ export type EuiRefreshIntervalProps = { * If you do not want this behavior, you can manually control the rendered unit via this prop. */ intervalUnits?: RefreshUnitsOptions; + /** + * Passes back the updated state of `isPaused`, `refreshInterval`, and `intervalUnits`. + */ + onRefreshChange: ApplyRefreshInterval; }; interface EuiRefreshIntervalState { @@ -101,6 +105,7 @@ export class EuiRefreshInterval extends Component< static defaultProps = { isPaused: true, refreshInterval: 1000, + minInterval: 0, }; state: EuiRefreshIntervalState = fromMilliseconds( From 79e708c54fa4c9b05a5250def2208a235d706541 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Tue, 6 Feb 2024 12:40:05 -0800 Subject: [PATCH 2/5] Add internal logic for setting `min` property of field number --- .../auto_refresh/auto_refresh.test.tsx | 27 +++++++++++++ .../auto_refresh/refresh_interval.tsx | 38 +++++++++++++------ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/components/date_picker/auto_refresh/auto_refresh.test.tsx b/src/components/date_picker/auto_refresh/auto_refresh.test.tsx index 9edbcc3f546..5919297721f 100644 --- a/src/components/date_picker/auto_refresh/auto_refresh.test.tsx +++ b/src/components/date_picker/auto_refresh/auto_refresh.test.tsx @@ -42,6 +42,33 @@ describe('EuiAutoRefresh', () => { expect(container.firstChild).toMatchSnapshot(); }); + test('minInterval renders an invalid warning on the number input', () => { + const { getByRole, getByTestSubject } = render( + {}} + /> + ); + const getNumberInput = () => + getByTestSubject('superDatePickerRefreshIntervalInput'); + const getUnitSelect = () => + getByTestSubject('superDatePickerRefreshIntervalUnitsSelect'); + + fireEvent.click(getByRole('button')); + expect(getNumberInput()).toBeInvalid(); + + fireEvent.change(getUnitSelect(), { target: { value: 'm' } }); + expect(getNumberInput()).toBeValid(); + + fireEvent.change(getUnitSelect(), { target: { value: 's' } }); + expect(getNumberInput()).toBeInvalid(); + + fireEvent.change(getNumberInput(), { target: { value: 5 } }); + expect(getNumberInput()).toBeValid(); + }); + test('intervalUnits forces rendering in the provided units', () => { const { getByLabelText, getByRole, getByTestSubject } = render( { const round = (value: number) => parseFloat(value.toFixed(2)); if (unit === 'h' || (!unit && milliseconds > MILLISECONDS_IN_HOUR)) { return { @@ -53,9 +53,9 @@ function fromMilliseconds( units: 's', value: round(milliseconds / MILLISECONDS_IN_SECOND), }; -} +}; -function toMilliseconds(units: RefreshUnitsOptions, value: Milliseconds) { +const toMilliseconds = (units: RefreshUnitsOptions, value: Milliseconds) => { switch (units) { case 'h': return Math.round(value * MILLISECONDS_IN_HOUR); @@ -65,7 +65,16 @@ function toMilliseconds(units: RefreshUnitsOptions, value: Milliseconds) { default: return Math.round(value * MILLISECONDS_IN_SECOND); } -} +}; + +const getMinInterval = ( + minInterval?: Milliseconds, + unit?: RefreshUnitsOptions +): number => { + if (!minInterval) return 0; + const { value } = fromMilliseconds(minInterval, unit); + return Math.floor(value || 0); +}; export type EuiRefreshIntervalProps = { /** @@ -96,6 +105,7 @@ export type EuiRefreshIntervalProps = { interface EuiRefreshIntervalState { value: number | ''; units: RefreshUnitsOptions; + min?: Milliseconds; } export class EuiRefreshInterval extends Component< @@ -108,10 +118,13 @@ export class EuiRefreshInterval extends Component< minInterval: 0, }; - state: EuiRefreshIntervalState = fromMilliseconds( - this.props.refreshInterval || 0, - this.props.intervalUnits - ); + state: EuiRefreshIntervalState = { + ...fromMilliseconds( + this.props.refreshInterval || 0, + this.props.intervalUnits + ), + min: getMinInterval(this.props.minInterval, this.props.intervalUnits), + }; generateId = htmlIdGenerator(); legendId = this.generateId(); @@ -128,9 +141,11 @@ export class EuiRefreshInterval extends Component< }; onUnitsChange: ChangeEventHandler = (event) => { + const units = event.target.value as RefreshUnitsOptions; this.setState( { - units: event.target.value as RefreshUnitsOptions, + units, + min: getMinInterval(this.props.minInterval, units), }, this.applyRefreshInterval ); @@ -226,7 +241,7 @@ export class EuiRefreshInterval extends Component< render() { const { isPaused } = this.props; - const { value, units } = this.state; + const { value, units, min } = this.state; return ( @@ -260,6 +275,7 @@ export class EuiRefreshInterval extends Component< compressed fullWidth value={value} + min={min} onChange={this.onValueChange} onKeyDown={this.handleKeyDown} isInvalid={!isPaused && (value === '' || value <= 0)} From bfc4f19d9d5404222248e67be9a294a4dfc0a320 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Tue, 6 Feb 2024 12:40:43 -0800 Subject: [PATCH 3/5] Update `onRefreshChange` callback to respect min interval --- .../auto_refresh/auto_refresh.test.tsx | 18 +++++++++++++++++- .../auto_refresh/refresh_interval.tsx | 7 +++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/components/date_picker/auto_refresh/auto_refresh.test.tsx b/src/components/date_picker/auto_refresh/auto_refresh.test.tsx index 5919297721f..fe6a4ff158f 100644 --- a/src/components/date_picker/auto_refresh/auto_refresh.test.tsx +++ b/src/components/date_picker/auto_refresh/auto_refresh.test.tsx @@ -43,12 +43,13 @@ describe('EuiAutoRefresh', () => { }); test('minInterval renders an invalid warning on the number input', () => { + const onRefreshChange = jest.fn(); const { getByRole, getByTestSubject } = render( {}} + onRefreshChange={onRefreshChange} /> ); const getNumberInput = () => @@ -60,12 +61,27 @@ describe('EuiAutoRefresh', () => { expect(getNumberInput()).toBeInvalid(); fireEvent.change(getUnitSelect(), { target: { value: 'm' } }); + expect(onRefreshChange).toHaveBeenLastCalledWith({ + refreshInterval: 60000, + intervalUnits: 'm', + isPaused: false, + }); expect(getNumberInput()).toBeValid(); fireEvent.change(getUnitSelect(), { target: { value: 's' } }); + expect(onRefreshChange).toHaveBeenLastCalledWith({ + refreshInterval: 3000, // Should pass back the minimum instead of the current 1000 value + intervalUnits: 's', + isPaused: false, + }); expect(getNumberInput()).toBeInvalid(); fireEvent.change(getNumberInput(), { target: { value: 5 } }); + expect(onRefreshChange).toHaveBeenLastCalledWith({ + refreshInterval: 5000, + intervalUnits: 's', + isPaused: false, + }); expect(getNumberInput()).toBeValid(); }); diff --git a/src/components/date_picker/auto_refresh/refresh_interval.tsx b/src/components/date_picker/auto_refresh/refresh_interval.tsx index 483982fda61..9de28b0775e 100644 --- a/src/components/date_picker/auto_refresh/refresh_interval.tsx +++ b/src/components/date_picker/auto_refresh/refresh_interval.tsx @@ -171,7 +171,7 @@ export class EuiRefreshInterval extends Component< }; applyRefreshInterval = () => { - const { onRefreshChange, isPaused } = this.props; + const { onRefreshChange, isPaused, minInterval } = this.props; const { units, value } = this.state; if (value === '') { return; @@ -180,7 +180,10 @@ export class EuiRefreshInterval extends Component< return; } - const refreshInterval = toMilliseconds(units, value); + const refreshInterval = Math.max( + toMilliseconds(units, value), + minInterval || 0 + ); onRefreshChange({ refreshInterval, From 24b8713410a88c6b994b55ce28dbc0ce39cf3acf Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Tue, 6 Feb 2024 12:45:29 -0800 Subject: [PATCH 4/5] Update `EuiSuperDatePicker` to pass down min interval prop to underlying component --- .../__snapshots__/quick_select_popover.test.tsx.snap | 1 + .../date_picker/super_date_picker/super_date_picker.tsx | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.tsx.snap b/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.tsx.snap index c1be9589f98..54dbcc3c5cb 100644 --- a/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.tsx.snap +++ b/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.tsx.snap @@ -350,6 +350,7 @@ exports[`EuiQuickSelectPanels customQuickSelectPanels should render custom panel class="euiFieldNumber euiFieldNumber--fullWidth euiFieldNumber--compressed" data-test-subj="superDatePickerRefreshIntervalInput" disabled="" + min="0" step="any" type="number" value="0" diff --git a/src/components/date_picker/super_date_picker/super_date_picker.tsx b/src/components/date_picker/super_date_picker/super_date_picker.tsx index ab94d756605..d7db318dd59 100644 --- a/src/components/date_picker/super_date_picker/super_date_picker.tsx +++ b/src/components/date_picker/super_date_picker/super_date_picker.tsx @@ -148,6 +148,11 @@ export type EuiSuperDatePickerProps = CommonProps & { * @default 1000 */ refreshInterval?: Milliseconds; + /** + * Minimum refresh interval in milliseconds + * @default 0 + */ + refreshMinInterval?: Milliseconds; /** * By default, refresh interval units will be rounded up to next largest unit of time * (for example, 90 seconds will become 2m). @@ -497,6 +502,7 @@ export class EuiSuperDatePickerInternal extends Component< timeOptions, dateFormat, refreshInterval, + refreshMinInterval, refreshIntervalUnits, isPaused, isDisabled, @@ -511,6 +517,7 @@ export class EuiSuperDatePickerInternal extends Component< const autoRefreshAppend: EuiFormControlLayoutProps['append'] = !isPaused ? ( Date: Tue, 6 Feb 2024 12:59:57 -0800 Subject: [PATCH 5/5] changelog --- changelogs/upcoming/7516.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/upcoming/7516.md diff --git a/changelogs/upcoming/7516.md b/changelogs/upcoming/7516.md new file mode 100644 index 00000000000..f05e82de4e9 --- /dev/null +++ b/changelogs/upcoming/7516.md @@ -0,0 +1,2 @@ +- Updated `EuiSuperDatePicker` with a new `refreshMinInterval` prop, which accepts a minimum number in milliseconds +- Updated `EuiAutoRefresh` and `EuiRefreshInterval` with a new `minInterval` prop, which accepts a minimum number in milliseconds