[ML] Migrate bucket span estimator button to React/EUI. (#19045)

This migrates the bucket span estimator button to use React/EUI. The existing mlBucketSpanEstimator is refactored to use React without ngReact to have more detailed control in regards to the $scope being transformed to a trimmed down props object with only the information necessary for the React component. This also allows more control in regards to the $scope attributes being watched. Instead of custom styles and classes this now uses the options available for EuiButton. However, there were some minimal overrides necessary to the EUI styles to replicate the narrower height and padding to fit within the input element.
This commit is contained in:
Walter Rafelsberger 2018-05-17 09:26:01 +02:00 committed by GitHub
parent 13b97ac294
commit a9d47320d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 175 additions and 38 deletions

View file

@ -9,8 +9,6 @@ import React from 'react';
import { ValidateJob } from './validate_job_view';
jest.mock('ui/chrome', () => { }, { virtual: true });
const job = {
job_id: 'test-id'
};

View file

@ -0,0 +1,49 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BucketSpanEstimator renders the button 1`] = `
<div
className="bucket-span-estimator"
>
<EuiToolTip
content="Experimental feature for estimating bucket span."
position="bottom"
>
<EuiButton
color="primary"
disabled={true}
fill={true}
iconSide="right"
isLoading={true}
onClick={[Function]}
size="s"
type="button"
>
Estimating bucket span
</EuiButton>
</EuiToolTip>
</div>
`;
exports[`BucketSpanEstimator renders the loading button 1`] = `
<div
className="bucket-span-estimator"
>
<EuiToolTip
content="Experimental feature for estimating bucket span."
position="bottom"
>
<EuiButton
color="primary"
disabled={true}
fill={true}
iconSide="right"
isLoading={true}
onClick={[Function]}
size="s"
type="button"
>
Estimating bucket span
</EuiButton>
</EuiToolTip>
</div>
`;

View file

@ -1,13 +0,0 @@
<div class='bucket-span-estimator'>
<button
ng-click="guessBucketSpan()"
ng-disabled="ui.showJobInput===false || ui.formValid === false || formConfig.agg.type===undefined || jobStateWrapper.jobState===JOB_STATE.RUNNING || jobStateWrapper.jobState===JOB_STATE.STOPPING || jobStateWrapper.jobState===JOB_STATE.FINISHED || ui.bucketSpanEstimator.status===STATUS.RUNNING"
type="button"
tooltip="Experimental feature for estimating bucket span."
tooltip-append-to-body="true"
ng-class='{"running": (ui.bucketSpanEstimator.status===STATUS.RUNNING)}'
class="kuiButton kuiButton--basic kuiButton--small">
{{ui.bucketSpanEstimator.status===STATUS.RUNNING ? 'Estimating bucket span ' : 'Estimate bucket span'}}
<i ng-show='ui.bucketSpanEstimator.status===STATUS.RUNNING' class="fa fa-spinner fa-spin"></i>
</button>
</div>

View file

@ -4,9 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import template from './bucket_span_estimator.html';
import { BucketSpanEstimator } from './bucket_span_estimator_view';
import { getQueryFromSavedSearch } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
import { EVENT_RATE_COUNT_FIELD } from 'plugins/ml/jobs/new_job/simple/components/constants/general';
import { ml } from 'plugins/ml/services/ml_api_service';
@ -26,15 +27,13 @@ module.directive('mlBucketSpanEstimator', function () {
ui: '=ui',
exportedFunctions: '='
},
template,
link: function ($scope) {
link: function ($scope, $element) {
const STATUS = {
FAILED: -1,
NOT_RUNNING: 0,
RUNNING: 1,
FINISHED: 2
};
$scope.STATUS = STATUS;
const errorHandler = (error) => {
console.log('Bucket span could not be estimated', error);
@ -101,6 +100,38 @@ module.directive('mlBucketSpanEstimator', function () {
$scope.exportedFunctions.guessBucketSpan = $scope.guessBucketSpan;
}
// watch for these changes
$scope.$watch('formConfig.agg.type', updateButton, true);
$scope.$watch('jobStateWrapper.jobState', updateButton, true);
$scope.$watch('[ui.showJobInput,ui.formValid,ui.bucketSpanEstimator.status]', updateButton, true);
function updateButton() {
const buttonDisabled = (
$scope.ui.showJobInput === false ||
$scope.ui.formValid === false ||
$scope.formConfig.agg.type === undefined ||
$scope.jobStateWrapper.jobState === $scope.JOB_STATE.RUNNING ||
$scope.jobStateWrapper.jobState === $scope.JOB_STATE.STOPPING ||
$scope.jobStateWrapper.jobState === $scope.JOB_STATE.FINISHED ||
$scope.ui.bucketSpanEstimator.status === STATUS.RUNNING
);
const estimatorRunning = ($scope.ui.bucketSpanEstimator.status === STATUS.RUNNING);
const buttonText = (estimatorRunning) ? 'Estimating bucket span' : 'Estimate bucket span';
const props = {
buttonDisabled,
estimatorRunning,
guessBucketSpan: $scope.guessBucketSpan,
buttonText
};
ReactDOM.render(
React.createElement(BucketSpanEstimator, props),
$element[0]
);
}
updateButton();
}
};
});

View file

@ -0,0 +1,42 @@
/*
* 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 PropTypes from 'prop-types';
import React from 'react';
import {
EuiButton,
EuiToolTip
} from '@elastic/eui';
export function BucketSpanEstimator({ buttonDisabled, buttonText, estimatorRunning, guessBucketSpan }) {
return (
<div className="bucket-span-estimator">
<EuiToolTip
content="Experimental feature for estimating bucket span."
position="bottom"
>
<EuiButton
disabled={buttonDisabled}
fill
iconSide="right"
isLoading={estimatorRunning}
onClick={guessBucketSpan}
size="s"
>
{buttonText}
</EuiButton>
</EuiToolTip>
</div>
);
}
BucketSpanEstimator.propTypes = {
buttonDisabled: PropTypes.bool.isRequired,
buttonText: PropTypes.string.isRequired,
estimatorRunning: PropTypes.bool.isRequired,
guessBucketSpan: PropTypes.func.isRequired
};

View file

@ -0,0 +1,38 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { BucketSpanEstimator } from './bucket_span_estimator_view';
describe('BucketSpanEstimator', () => {
const props = {
buttonDisabled: false,
estimatorRunning: false,
guessBucketSpan: () => {},
buttonText: 'Estimate bucket span'
};
const component = (
<BucketSpanEstimator {...props} />
);
const wrapper = shallow(component);
test('renders the button', () => {
expect(wrapper).toMatchSnapshot();
});
props.buttonDisabled = true;
props.estimatorRunning = true;
props.buttonText = 'Estimating bucket span';
wrapper.setProps(props);
test('renders the loading button', () => {
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -1,15 +1,14 @@
.bucket-span-estimator {
float: right;
margin-right: 5px;
margin-top: -27px;
button {
float: right;
margin-right: 5px;
margin-top: -27px;
}
button:disabled {
background-color: #D9D9D9;
}
button.running {
color: #2D2D2D;
button.euiButton.euiButton--small {
font-size: 12px;
height: 22px;
.euiButton__content {
padding: 2px 8px 3px;
}
}
}

View file

@ -19,10 +19,9 @@
.form-controls {
.bucket-span-container {
white-space: nowrap;
width: calc(~"100% - 40px");
.bucket-span-input {
width: calc(~"100% - 40px");
display: inline-block;
background-color: transparent;
float: left;
}
@ -31,12 +30,6 @@
background-color: #D9D9D9;
}
.bucket-span-estimator {
button {
margin-right: 45px;
}
}
.validation-error {
margin-bottom: -25px;
}