[ML] Add button for refreshing job list without full page refresh. (#24084)

* Add refresh button for job list

* Show load icon while refreshing

* Move view build logic to jobsListView

* Align job action buttons in ui

* Extract RefreshJobsListButton component

* update jobStatsBar to take list as prop
This commit is contained in:
Melissa Alvarez 2018-10-17 13:39:10 +01:00 committed by GitHub
parent 688795f166
commit 36025138ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 128 additions and 114 deletions

View file

@ -18,12 +18,21 @@ import { DeleteJobModal } from '../delete_job_modal';
import { StartDatafeedModal } from '../start_datafeed_modal';
import { CreateWatchFlyout } from '../create_watch_flyout';
import { MultiJobActions } from '../multi_job_actions';
import { NewJobButton } from '../new_job_button';
import { JobStatsBar } from '../jobs_stats_bar';
import { NodeAvailableWarning } from '../node_available_warning';
import { RefreshJobsListButton } from '../refresh_jobs_list_button';
import PropTypes from 'prop-types';
import React, {
Component
} from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
} from '@elastic/eui';
const DEFAULT_REFRESH_INTERVAL_MS = 30000;
const MINIMUM_REFRESH_INTERVAL_MS = 5000;
let jobsRefreshInterval = null;
@ -33,6 +42,7 @@ export class JobsListView extends Component {
super(props);
this.state = {
isRefreshing: false,
jobsSummaryList: [],
filteredJobsSummaryList: [],
fullJobsList: {},
@ -204,7 +214,6 @@ export class JobsListView extends Component {
return this.showCreateWatchFlyout;
}
selectJobChange = (selectedJobs) => {
this.setState({ selectedJobs });
}
@ -229,6 +238,14 @@ export class JobsListView extends Component {
});
}
onRefreshClick = () => {
this.setState({ isRefreshing: true });
this.refreshJobSummaryList(true);
}
isDoneRefreshing = () => {
this.setState({ isRefreshing: false });
}
refreshJobSummaryList(forceRefresh = false) {
if (forceRefresh === true || this.blockRefresh === false) {
const expandedJobsIds = Object.keys(this.state.itemIdToExpandedRowMap);
@ -246,12 +263,13 @@ export class JobsListView extends Component {
const filteredJobsSummaryList = filterJobs(jobsSummaryList, this.state.filterClauses);
this.setState({ jobsSummaryList, filteredJobsSummaryList, fullJobsList }, () => {
this.refreshSelectedJobs();
this.props.updateJobStats(jobsSummaryList);
});
Object.keys(this.updateFunctions).forEach((j) => {
this.updateFunctions[j].setState({ job: fullJobsList[j] });
});
this.isDoneRefreshing();
})
.catch((error) => {
console.error(error);
@ -259,7 +277,7 @@ export class JobsListView extends Component {
}
}
render() {
renderJobsListComponents() {
const jobIds = this.state.jobsSummaryList.map(j => j.id);
return (
<div>
@ -310,7 +328,41 @@ export class JobsListView extends Component {
</div>
);
}
render() {
const { isRefreshing, jobsSummaryList } = this.state;
return (
<React.Fragment>
<JobStatsBar
jobsSummaryList={jobsSummaryList}
/>
<div className="job-management">
<NodeAvailableWarning />
<header>
<div className="job-buttons-container">
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<RefreshJobsListButton
onRefreshClick={this.onRefreshClick}
isRefreshing={isRefreshing}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<NewJobButton />
</EuiFlexItem>
</EuiFlexGroup>
</div>
</header>
<div className="clear" />
<EuiSpacer size="s" />
{ this.renderJobsListComponents() }
</ div>
</React.Fragment>
);
}
}
JobsListView.propTypes = {
updateJobStats: PropTypes.func.isRequired,
};

View file

@ -8,3 +8,15 @@
& > div:nth-child(2) {
}
}
.job-management {
padding: 20px;
}
.job-buttons-container {
float: right;
}
.clear {
clear: both;
}

View file

@ -9,9 +9,7 @@ import './styles/main.less';
import { JOB_STATE, DATAFEED_STATE } from 'plugins/ml/../common/constants/states';
import PropTypes from 'prop-types';
import React, {
Component,
} from 'react';
import React from 'react';
function createJobStats(jobsSummaryList) {
@ -76,46 +74,20 @@ Stat.propTypes = {
stat: PropTypes.object.isRequired,
};
export class JobStatsBar extends Component {
constructor(props) {
super(props);
this.state = {
jobsSummaryList: [],
jobStats: {},
};
}
export const JobStatsBar = ({ jobsSummaryList }) => {
const jobStats = createJobStats(jobsSummaryList);
const stats = Object.keys(jobStats).map(k => jobStats[k]);
updateJobStats = (jobsSummaryList) => {
const jobStats = createJobStats(jobsSummaryList);
this.setState({
jobsSummaryList,
jobStats,
});
};
componentDidMount() {
this.props.setUpdateJobStats(this.updateJobStats);
}
componentWillUnmount() {
this.props.unsetUpdateJobStats();
}
render() {
const { jobStats } = this.state;
const stats = Object.keys(jobStats).map(k => jobStats[k]);
return (
<div className="jobs-stats-bar">
{
stats.filter(s => (s.show)).map(s => <Stat key={s.label} stat={s} />)
}
</div>
);
}
}
JobStatsBar.propTypes = {
setUpdateJobStats: PropTypes.func.isRequired,
unsetUpdateJobStats: PropTypes.func.isRequired,
return (
<div className="jobs-stats-bar">
{
stats.filter(s => (s.show)).map(s => <Stat key={s.label} stat={s} />)
}
</div>
);
};
JobStatsBar.propTypes = {
jobsSummaryList: PropTypes.array.isRequired,
};

View file

@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { RefreshJobsListButton } from './refresh_jobs_list_button';

View file

@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import {
EuiButtonEmpty,
} from '@elastic/eui';
export const RefreshJobsListButton = ({ onRefreshClick, isRefreshing }) => (
<EuiButtonEmpty
onClick={onRefreshClick}
isLoading={isRefreshing}
>
Refresh
</EuiButtonEmpty>
);
RefreshJobsListButton.propTypes = {
onRefreshClick: PropTypes.func.isRequired,
isRefreshing: PropTypes.bool.isRequired
};

View file

@ -5,60 +5,11 @@
*/
import './styles/main.less';
import { NewJobButton } from './components/new_job_button';
import { JobsListView } from './components/jobs_list_view';
import { JobStatsBar } from './components/jobs_stats_bar';
import { NodeAvailableWarning } from './components/node_available_warning';
import React, {
Component
} from 'react';
import {
EuiSpacer,
} from '@elastic/eui';
import React from 'react';
export class JobsPage extends Component {
constructor(props) {
super(props);
this.state = {
jobsSummaryList: [],
updateJobStats: () => {},
};
}
export const JobsPage = () => (
<JobsListView />
);
setUpdateJobStats = (updateJobStats) => {
this.setState({ updateJobStats });
}
unsetUpdateJobStats = () => {
this.setUpdateJobStats(() => {});
}
render() {
return (
<React.Fragment>
<JobStatsBar
setUpdateJobStats={this.setUpdateJobStats}
unsetUpdateJobStats={this.unsetUpdateJobStats}
/>
<div className="job-management">
<NodeAvailableWarning />
<header>
<div className="new-job-button-container">
<NewJobButton />
</div>
</header>
<div className="clear" />
<EuiSpacer size="s" />
<JobsListView updateJobStats={this.state.updateJobStats} />
</div>
</React.Fragment>
);
}
}

View file

@ -1,11 +0,0 @@
.job-management {
padding: 20px;
}
.new-job-button-container {
float: right;
}
.clear {
clear: both;
}