Web-console: add timed button (#7912)

* add refresh button

* update snapshots

* fix spacing

* add supervisors button

* modify query manager

* add icon

* update snapshots

* fix space

* stop refreshing when option selected

* small fixes

* add default value

* fix spelling mistake

* fix query-input

* rename class
This commit is contained in:
mcbrewster 2019-06-24 21:18:19 -07:00 committed by Fangjin Yang
parent 6cc8802b8e
commit cd01a48eab
15 changed files with 268 additions and 47 deletions

View File

@ -33,3 +33,5 @@ export * from './show-log/show-log';
export * from './table-column-selector/table-column-selector';
export * from './view-control-bar/view-control-bar';
export * from './clearable-input/clearable-input';
export * from './refresh-button/refresh-button';
export * from './timed-button/timed-button';

View File

@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { IconNames } from '@blueprintjs/icons';
import React from 'react';
import { LocalStorageKeys } from '../../utils';
import { TimedButton } from '../timed-button/timed-button';
export interface RefreshButtonProps extends React.Props<any> {
onRefresh: (auto: boolean) => void;
localStorageKey?: LocalStorageKeys;
}
export class RefreshButton extends React.PureComponent<RefreshButtonProps> {
constructor(props: RefreshButtonProps, context: any) {
super(props, context);
}
render() {
const { onRefresh, localStorageKey } = this.props;
const intervals = [
{ label: '5 seconds', value: 5000 },
{ label: '10 seconds', value: 10000 },
{ label: '30 seconds', value: 30000 },
{ label: '1 minute', value: 60000 },
{ label: '2 minutes', value: 120000 },
{ label: 'None', value: 0 },
];
return (
<TimedButton
defaultValue={30000}
label={'Auto refresh every:'}
intervals={intervals}
icon={IconNames.REFRESH}
text={'Refresh'}
onRefresh={onRefresh}
localStorageKey={localStorageKey}
/>
);
}
}

View File

@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.timed-button {
padding: 10px 10px 5px 10px;
}

View File

@ -0,0 +1,131 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Button, ButtonGroup, IButtonProps, Popover, Radio, RadioGroup } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import React from 'react';
import { localStorageGet, LocalStorageKeys, localStorageSet } from '../../utils';
import './timed-button.scss';
export interface Interval {
label: string;
value: number;
}
export interface TimedButtonProps extends IButtonProps {
intervals: Interval[];
onRefresh: (auto: boolean) => void;
localStorageKey?: LocalStorageKeys;
label: string;
defaultValue: number;
}
export interface TimedButtonState {
interval: number;
}
export class TimedButton extends React.PureComponent<TimedButtonProps, TimedButtonState> {
constructor(props: TimedButtonProps, context: any) {
super(props, context);
this.state = {
interval:
this.props.localStorageKey && localStorageGet(this.props.localStorageKey)
? Number(localStorageGet(this.props.localStorageKey))
: this.props.defaultValue,
};
}
private timer: any;
componentDidMount(): void {
if (this.state.interval) {
this.timer = setTimeout(() => {
this.continuousRefresh(this.state.interval);
}, this.state.interval);
}
}
componentWillUnmount(): void {
this.clearTimer();
}
clearTimer() {
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = undefined;
}
continuousRefresh = (selectedInterval: number) => {
if (selectedInterval) {
this.timer = setTimeout(() => {
this.props.onRefresh(true);
this.continuousRefresh(selectedInterval);
}, selectedInterval);
}
};
handleSelection = (e: any) => {
const selectedInterval = Number(e.currentTarget.value);
this.clearTimer();
this.setState({ interval: selectedInterval });
if (this.props.localStorageKey) {
localStorageSet(this.props.localStorageKey, String(selectedInterval));
}
this.continuousRefresh(selectedInterval);
};
render() {
const {
label,
intervals,
onRefresh,
type,
text,
icon,
defaultValue,
localStorageKey,
...other
} = this.props;
const { interval } = this.state;
return (
<ButtonGroup>
<Button {...other} text={text} icon={icon} onClick={() => onRefresh(false)} />
<Popover
content={
<RadioGroup
label={label}
className="timed-button"
onChange={this.handleSelection}
selectedValue={interval}
>
{intervals.map((interval: any) => (
<Radio label={interval.label} value={interval.value} key={interval.label} />
))}
</RadioGroup>
}
>
<Button {...other} rightIcon={IconNames.CARET_DOWN} />
</Popover>
</ButtonGroup>
);
}
}

View File

@ -44,7 +44,6 @@ export class SupervisorTableActionDialog extends React.PureComponent<
activeTab: 'status',
};
}
render(): React.ReactNode {
const { supervisorId, actions, onClose } = this.props;
const { activeTab } = this.state;

View File

@ -27,6 +27,11 @@ export const LocalStorageKeys = {
QUERY_KEY: 'druid-console-query' as 'druid-console-query',
TASKS_VIEW_PANE_SIZE: 'tasks-view-pane-size' as 'tasks-view-pane-size',
QUERY_VIEW_PANE_SIZE: 'query-view-pane-size' as 'query-view-pane-size',
TASKS_REFRESH_RATE: 'task-refresh-rate' as 'task-refresh-rate',
DATASOURCES_REFRESH_RATE: 'datasources-refresh-rate' as 'datasources-refresh-rate',
SEGMENTS_REFRESH_RATE: 'segments-refresh-rate' as 'segments-refresh-rate',
SERVERS_REFRESH_RATE: 'servers-refresh-rate' as 'servers-refresh-rate',
SUPERVISORS_REFRESH_RATE: 'supervisors-refresh-rate' as 'supervisors-refresh-rate',
};
export type LocalStorageKeys = typeof LocalStorageKeys[keyof typeof LocalStorageKeys];

View File

@ -125,6 +125,15 @@ export class QueryManager<Q, R> {
this.trigger();
}
public rerunLastQueryInBackground(auto: boolean): void {
this.nextQuery = this.lastQuery;
if (auto) {
this.runWhenIdle();
} else {
this.trigger();
}
}
public getLastQuery(): Q {
return this.lastQuery;
}

View File

@ -7,10 +7,9 @@ exports[`data source view matches snapshot 1`] = `
<ViewControlBar
label="Datasources"
>
<Blueprint3.Button
icon="refresh"
onClick={[Function]}
text="Refresh"
<RefreshButton
localStorageKey="datasources-refresh-rate"
onRefresh={[Function]}
/>
<Blueprint3.Button
icon="application"

View File

@ -31,7 +31,13 @@ import axios from 'axios';
import React from 'react';
import ReactTable, { Filter } from 'react-table';
import { ActionCell, RuleEditor, TableColumnSelector, ViewControlBar } from '../../components';
import {
ActionCell,
RefreshButton,
RuleEditor,
TableColumnSelector,
ViewControlBar,
} from '../../components';
import { ActionIcon } from '../../components/action-icon/action-icon';
import { AsyncActionDialog, CompactionDialog, RetentionDialog } from '../../dialogs';
import { AppToaster } from '../../singletons/toaster';
@ -777,10 +783,9 @@ GROUP BY 1`);
return (
<div className="data-sources-view app-view">
<ViewControlBar label="Datasources">
<Button
icon={IconNames.REFRESH}
text="Refresh"
onClick={() => this.datasourceQueryManager.rerunLastQuery()}
<RefreshButton
onRefresh={auto => this.datasourceQueryManager.rerunLastQueryInBackground(auto)}
localStorageKey={LocalStorageKeys.DATASOURCES_REFRESH_RATE}
/>
{!noSqlMode && (
<Button

View File

@ -7,10 +7,9 @@ exports[`segments-view matches snapshot 1`] = `
<ViewControlBar
label="Segments"
>
<Blueprint3.Button
icon="refresh"
onClick={[Function]}
text="Refresh"
<RefreshButton
localStorageKey="segments-refresh-rate"
onRefresh={[Function]}
/>
<Blueprint3.Button
hidden={false}

View File

@ -24,7 +24,7 @@ import React from 'react';
import ReactTable from 'react-table';
import { Filter } from 'react-table';
import { TableColumnSelector, ViewControlBar } from '../../components';
import { RefreshButton, TableColumnSelector, ViewControlBar } from '../../components';
import {
addFilter,
formatBytes,
@ -454,14 +454,13 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
return (
<div className="segments-view app-view">
<ViewControlBar label="Segments">
<Button
icon={IconNames.REFRESH}
text="Refresh"
onClick={() =>
<RefreshButton
onRefresh={auto =>
noSqlMode
? this.segmentsJsonQueryManager.rerunLastQuery()
: this.segmentsSqlQueryManager.rerunLastQuery()
? this.segmentsJsonQueryManager.rerunLastQueryInBackground(auto)
: this.segmentsSqlQueryManager.rerunLastQueryInBackground(auto)
}
localStorageKey={LocalStorageKeys.SEGMENTS_REFRESH_RATE}
/>
{!noSqlMode && (
<Button

View File

@ -30,10 +30,9 @@ exports[`servers view action servers view 1`] = `
Tier
</Blueprint3.Button>
</Blueprint3.ButtonGroup>
<Blueprint3.Button
icon="refresh"
onClick={[Function]}
text="Refresh"
<RefreshButton
localStorageKey="servers-refresh-rate"
onRefresh={[Function]}
/>
<Blueprint3.Button
icon="application"

View File

@ -24,7 +24,7 @@ import React from 'react';
import ReactTable from 'react-table';
import { Filter } from 'react-table';
import { ActionCell, TableColumnSelector, ViewControlBar } from '../../components';
import { ActionCell, RefreshButton, TableColumnSelector, ViewControlBar } from '../../components';
import { AsyncActionDialog } from '../../dialogs';
import {
addFilter,
@ -639,10 +639,9 @@ ORDER BY "rank" DESC, "server" DESC`);
Tier
</Button>
</ButtonGroup>
<Button
icon={IconNames.REFRESH}
text="Refresh"
onClick={() => this.serverQueryManager.rerunLastQuery()}
<RefreshButton
onRefresh={auto => this.serverQueryManager.rerunLastQueryInBackground(auto)}
localStorageKey={LocalStorageKeys.SERVERS_REFRESH_RATE}
/>
{!noSqlMode && (
<Button

View File

@ -20,10 +20,9 @@ exports[`tasks view matches snapshot 1`] = `
<ViewControlBar
label="Supervisors"
>
<Blueprint3.Button
icon="refresh"
onClick={[Function]}
text="Refresh"
<RefreshButton
localStorageKey="supervisors-refresh-rate"
onRefresh={[Function]}
/>
<Blueprint3.Popover
boundary="scrollParent"
@ -349,10 +348,9 @@ exports[`tasks view matches snapshot 1`] = `
Status
</Blueprint3.Button>
</Blueprint3.ButtonGroup>
<Blueprint3.Button
icon="refresh"
onClick={[Function]}
text="Refresh"
<RefreshButton
localStorageKey="task-refresh-rate"
onRefresh={[Function]}
/>
<Blueprint3.Button
icon="application"

View File

@ -34,7 +34,7 @@ import SplitterLayout from 'react-splitter-layout';
import ReactTable from 'react-table';
import { Filter } from 'react-table';
import { ActionCell, TableColumnSelector, ViewControlBar } from '../../components';
import { ActionCell, RefreshButton, TableColumnSelector, ViewControlBar } from '../../components';
import {
AsyncActionDialog,
SpecDialog,
@ -905,10 +905,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
>
<div className={'top-pane'}>
<ViewControlBar label="Supervisors">
<Button
icon={IconNames.REFRESH}
text="Refresh"
onClick={() => this.supervisorQueryManager.rerunLastQuery()}
<RefreshButton
localStorageKey={LocalStorageKeys.SUPERVISORS_REFRESH_RATE}
onRefresh={auto => this.supervisorQueryManager.rerunLastQueryInBackground(auto)}
/>
<Popover content={submitSupervisorMenu} position={Position.BOTTOM_LEFT}>
<Button icon={IconNames.PLUS} text="Submit supervisor" />
@ -952,10 +951,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
Status
</Button>
</ButtonGroup>
<Button
icon={IconNames.REFRESH}
text="Refresh"
onClick={() => this.taskQueryManager.rerunLastQuery()}
<RefreshButton
localStorageKey={LocalStorageKeys.TASKS_REFRESH_RATE}
onRefresh={auto => this.taskQueryManager.rerunLastQueryInBackground(auto)}
/>
{!noSqlMode && (
<Button