Skip to content

Commit

Permalink
feat: custom refresh frequency (apache#24449)
Browse files Browse the repository at this point in the history
Co-authored-by: Nitesh Kumar Dubey Samsung <[email protected]>
  • Loading branch information
2 people authored and qleroy committed Apr 28, 2024
1 parent 4f0e6bf commit 9a4f0ef
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,23 @@ test('should change selected value', async () => {
expect(selectedValue.title).not.toMatch(/don't refresh/i);
});

test('should change selected value to custom value', async () => {
render(setup(editModeOnProps));
await openRefreshIntervalModal();

// Initial selected value should be "Don't refresh"
const selectedValue = screen.getByText(/don't refresh/i);
expect(selectedValue.title).toMatch(/don't refresh/i);

// Display options and select "Custom interval"
await displayOptions();
userEvent.click(screen.getByText(/Custom interval/i));

// Selected value should now be "Custom interval"
expect(selectedValue.title).toMatch(/Custom interval/i);
expect(selectedValue.title).not.toMatch(/don't refresh/i);
});

test('should save a newly-selected value', async () => {
render(setup(editModeOnProps));
await openRefreshIntervalModal();
Expand Down
200 changes: 184 additions & 16 deletions superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Select from 'src/components/Select/Select';
import { t, styled } from '@superset-ui/core';
import Alert from 'src/components/Alert';
import Button from 'src/components/Button';
import { Input } from 'src/components/Input';

import ModalTrigger, { ModalTriggerRef } from 'src/components/ModalTrigger';
import { FormLabel } from 'src/components/Form';
Expand All @@ -36,6 +37,16 @@ const RefreshWarningContainer = styled.div`
margin-top: ${({ theme }) => theme.gridUnit * 6}px;
`;

const StyledDiv = styled.div`
display: flex;
margin-top: ${({ theme }) => theme.gridUnit * 3}px;
`;

const InnerStyledDiv = styled.div`
width: 30%;
margin: auto;
`;

type RefreshIntervalModalProps = {
addSuccessToast: (msg: string) => void;
triggerNode: JSX.Element;
Expand All @@ -49,6 +60,10 @@ type RefreshIntervalModalProps = {

type RefreshIntervalModalState = {
refreshFrequency: number;
custom_hour: number;
custom_min: number;
custom_sec: number;
custom_block: boolean;
};

class RefreshIntervalModal extends React.PureComponent<
Expand All @@ -67,6 +82,10 @@ class RefreshIntervalModal extends React.PureComponent<
this.modalRef = React.createRef() as ModalTriggerRef;
this.state = {
refreshFrequency: props.refreshFrequency,
custom_hour: 0,
custom_min: 0,
custom_sec: 0,
custom_block: false,
};
this.handleFrequencyChange = this.handleFrequencyChange.bind(this);
this.onSave = this.onSave.bind(this);
Expand All @@ -91,6 +110,85 @@ class RefreshIntervalModal extends React.PureComponent<
this.setState({
refreshFrequency: value || refreshIntervalOptions[0][0],
});

this.setState({
custom_block: value === -1,
});

if (value === -1) {
this.setState({
custom_hour: 0,
custom_min: 0,
custom_sec: 0,
});
}
}

onSaveValue(value: number) {
this.props.onChange(value, this.props.editMode);
this.modalRef?.current?.close();
this.props.addSuccessToast(t('Refresh interval saved'));
}

createIntervalOptions(refreshIntervalOptions: [number, string][]) {
const refresh_options = [];

refresh_options.push({ value: -1, label: t('Custom interval') });
refresh_options.push(
...refreshIntervalOptions.map(option => ({
value: option[0],
label: t(option[1]),
})),
);

return refresh_options;
}

min_sec_options(min_or_sec: string) {
return Array.from({ length: 60 }, (_, i) => ({
value: i,
label: `${i} ${min_or_sec}`,
}));
}

refresh_custom_val(
custom_block: boolean,
custom_hour: number,
custom_min: number,
custom_sec: number,
) {
if (custom_block === true) {
// Get hour value
const hour_value = custom_hour;

// Get minutes value
const minute_value = custom_min;

// Get seconds value
const second_value = custom_sec;

if (
hour_value < 0 ||
minute_value < 0 ||
second_value < 0 ||
minute_value >= 60 ||
second_value >= 60
) {
this.props.addSuccessToast(
t(
'Put positive values and valid minute and second value less than 60',
),
);
}
// Convert given input to seconds
const value = hour_value * 60 * 60 + minute_value * 60 + second_value;
if (value === 0) {
this.props.addSuccessToast(t('Put some positive value greater than 0'));
return;
}
this.handleFrequencyChange(value);
this.onSaveValue(value);
} else this.onSave();
}

render() {
Expand All @@ -100,7 +198,13 @@ class RefreshIntervalModal extends React.PureComponent<
editMode,
refreshIntervalOptions,
} = this.props;
const { refreshFrequency = 0 } = this.state;
const {
refreshFrequency = 0,
custom_hour = 0,
custom_min = 0,
custom_sec = 0,
custom_block = false,
} = this.state;
const showRefreshWarning =
!!refreshFrequency && !!refreshWarning && refreshFrequency < refreshLimit;

Expand All @@ -111,17 +215,74 @@ class RefreshIntervalModal extends React.PureComponent<
modalTitle={t('Refresh interval')}
modalBody={
<div>
<FormLabel>{t('Refresh frequency')}</FormLabel>
<Select
ariaLabel={t('Refresh interval')}
options={refreshIntervalOptions.map(option => ({
value: option[0],
label: t(option[1]),
}))}
value={refreshFrequency}
onChange={this.handleFrequencyChange}
sortComparator={propertyComparator('value')}
/>
<div id="refresh_from_dropdown">
<FormLabel>
<b>{t('Refresh frequency')}</b>
</FormLabel>
<Select
ariaLabel={t('Refresh interval')}
options={this.createIntervalOptions(refreshIntervalOptions)}
value={refreshFrequency}
onChange={this.handleFrequencyChange}
sortComparator={propertyComparator('value')}
/>
</div>
{custom_block && (
<StyledDiv>
<InnerStyledDiv>
<FormLabel>
<b>{t('HOUR')}</b>
</FormLabel>{' '}
<br />
<Input
type="number"
min="0"
className="form-control input-sm"
placeholder={t('Type a number')}
onChange={event => {
this.setState({
custom_hour: Number(event.target.value),
});
}}
value={custom_hour}
/>
</InnerStyledDiv>
<InnerStyledDiv>
<FormLabel>
<b>{t('MINUTE')}</b>
</FormLabel>{' '}
<br />
<Select
ariaLabel={t('Minutes value')}
options={this.min_sec_options('minutes')}
value={custom_min}
onChange={(value: number) => {
this.setState({
custom_min: value,
});
}}
sortComparator={propertyComparator('value')}
/>
</InnerStyledDiv>
<InnerStyledDiv>
<FormLabel>
<b>{t('SECOND')}</b>
</FormLabel>{' '}
<br />
<Select
ariaLabel={t('Seconds value')}
options={this.min_sec_options('seconds')}
value={custom_sec}
onChange={(value: number) => {
this.setState({
custom_sec: value,
});
}}
sortComparator={propertyComparator('value')}
/>
</InnerStyledDiv>
</StyledDiv>
)}
{showRefreshWarning && (
<RefreshWarningContainer>
<Alert
Expand All @@ -140,16 +301,23 @@ class RefreshIntervalModal extends React.PureComponent<
}
modalFooter={
<>
<Button onClick={this.onCancel} buttonSize="small">
{t('Cancel')}
</Button>
<Button
buttonStyle="primary"
buttonSize="small"
onClick={this.onSave}
onClick={() =>
this.refresh_custom_val(
custom_block,
custom_hour,
custom_min,
custom_sec,
)
}
>
{editMode ? t('Save') : t('Save for this session')}
</Button>
<Button onClick={this.onCancel} buttonSize="small">
{t('Cancel')}
</Button>
</>
}
/>
Expand Down

0 comments on commit 9a4f0ef

Please sign in to comment.