Skip to content

Commit

Permalink
[EuiSuperDatePicker][EuiAutoRefresh][EuiRefreshInterval] Allow settin…
Browse files Browse the repository at this point in the history
…g a minimum interval in milliseconds (#7516)
  • Loading branch information
cee-chen authored Feb 9, 2024
1 parent 46d5138 commit 890f762
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 15 deletions.
2 changes: 2 additions & 0 deletions changelogs/upcoming/7516.md
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions src/components/date_picker/auto_refresh/auto_refresh.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,49 @@ describe('EuiAutoRefresh', () => {
expect(container.firstChild).toMatchSnapshot();
});

test('minInterval renders an invalid warning on the number input', () => {
const onRefreshChange = jest.fn();
const { getByRole, getByTestSubject } = render(
<EuiAutoRefresh
isPaused={false}
refreshInterval={1000}
minInterval={3000}
onRefreshChange={onRefreshChange}
/>
);
const getNumberInput = () =>
getByTestSubject('superDatePickerRefreshIntervalInput');
const getUnitSelect = () =>
getByTestSubject('superDatePickerRefreshIntervalUnitsSelect');

fireEvent.click(getByRole('button'));
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();
});

test('intervalUnits forces rendering in the provided units', () => {
const { getByLabelText, getByRole, getByTestSubject } = render(
<EuiAutoRefresh
Expand Down
4 changes: 4 additions & 0 deletions src/components/date_picker/auto_refresh/auto_refresh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const EuiAutoRefresh: FunctionComponent<EuiAutoRefreshProps> = ({
isDisabled,
isPaused = true,
refreshInterval = 1000,
minInterval = 0,
readOnly = true,
...rest
}) => {
Expand Down Expand Up @@ -90,6 +91,7 @@ export const EuiAutoRefresh: FunctionComponent<EuiAutoRefreshProps> = ({
onRefreshChange={onRefreshChange}
isPaused={isPaused}
refreshInterval={refreshInterval}
minInterval={minInterval}
intervalUnits={intervalUnits}
/>
</EuiInputPopover>
Expand All @@ -115,6 +117,7 @@ export const EuiAutoRefreshButton: FunctionComponent<
isDisabled,
isPaused = true,
refreshInterval = 1000,
minInterval = 0,
shortHand = false,
size = 's',
color = 'text',
Expand Down Expand Up @@ -168,6 +171,7 @@ export const EuiAutoRefreshButton: FunctionComponent<
onRefreshChange={onRefreshChange}
isPaused={isPaused}
refreshInterval={refreshInterval}
minInterval={minInterval}
intervalUnits={intervalUnits}
/>
</EuiPopover>
Expand Down
54 changes: 39 additions & 15 deletions src/components/date_picker/auto_refresh/refresh_interval.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const MILLISECONDS_IN_SECOND = 1000;
const MILLISECONDS_IN_MINUTE = MILLISECONDS_IN_SECOND * 60;
const MILLISECONDS_IN_HOUR = MILLISECONDS_IN_MINUTE * 60;

function fromMilliseconds(
const fromMilliseconds = (
milliseconds: Milliseconds,
unit?: RefreshUnitsOptions
): EuiRefreshIntervalState {
): EuiRefreshIntervalState => {
const round = (value: number) => parseFloat(value.toFixed(2));
if (unit === 'h' || (!unit && milliseconds > MILLISECONDS_IN_HOUR)) {
return {
Expand All @@ -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);
Expand All @@ -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 = {
/**
Expand All @@ -77,21 +86,26 @@ 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).
*
* 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 {
value: number | '';
units: RefreshUnitsOptions;
min?: Milliseconds;
}

export class EuiRefreshInterval extends Component<
Expand All @@ -101,12 +115,16 @@ export class EuiRefreshInterval extends Component<
static defaultProps = {
isPaused: true,
refreshInterval: 1000,
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();
Expand All @@ -123,9 +141,11 @@ export class EuiRefreshInterval extends Component<
};

onUnitsChange: ChangeEventHandler<HTMLSelectElement> = (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
);
Expand All @@ -151,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;
Expand All @@ -160,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,
Expand Down Expand Up @@ -221,7 +244,7 @@ export class EuiRefreshInterval extends Component<

render() {
const { isPaused } = this.props;
const { value, units } = this.state;
const { value, units, min } = this.state;

return (
<RenderI18nTimeOptions>
Expand Down Expand Up @@ -255,6 +278,7 @@ export class EuiRefreshInterval extends Component<
compressed
fullWidth
value={value}
min={min}
onChange={this.onValueChange}
onKeyDown={this.handleKeyDown}
isInvalid={!isPaused && (value === '' || value <= 0)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -497,6 +502,7 @@ export class EuiSuperDatePickerInternal extends Component<
timeOptions,
dateFormat,
refreshInterval,
refreshMinInterval,
refreshIntervalUnits,
isPaused,
isDisabled,
Expand All @@ -511,6 +517,7 @@ export class EuiSuperDatePickerInternal extends Component<
const autoRefreshAppend: EuiFormControlLayoutProps['append'] = !isPaused ? (
<EuiAutoRefreshButton
refreshInterval={refreshInterval}
minInterval={refreshMinInterval}
intervalUnits={refreshIntervalUnits}
isDisabled={!!isDisabled}
isPaused={isPaused}
Expand Down Expand Up @@ -671,6 +678,7 @@ export class EuiSuperDatePickerInternal extends Component<
isPaused,
onRefreshChange,
refreshInterval,
refreshMinInterval,
refreshIntervalUnits,
showUpdateButton,
'data-test-subj': dataTestSubj,
Expand Down Expand Up @@ -700,6 +708,7 @@ export class EuiSuperDatePickerInternal extends Component<
<EuiAutoRefresh
isPaused={isPaused}
refreshInterval={refreshInterval}
minInterval={refreshMinInterval}
intervalUnits={refreshIntervalUnits}
onRefreshChange={this.onRefreshChange}
fullWidth={width === 'full'}
Expand Down

0 comments on commit 890f762

Please sign in to comment.