Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discover] Deangularize timechart header #66532

Merged
Merged
50 changes: 11 additions & 39 deletions src/plugins/discover/public/application/angular/discover.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,45 +101,17 @@ <h1 class="euiScreenReaderOnly">{{screenTitle}}</h1>
class="dscTimechart"
ng-if="opts.timefield"
>
<header class="dscTimechart__header">
<div class="small">
<label
for="dscResultsIntervalSelector"
tooltip="{{::'discover.howToChangeTheTimeTooltip' | i18n: {defaultMessage: 'To change the time, click the calendar icon in the navigation bar'} }}"
>
{{toMoment(timeRange.from)}} - {{toMoment(timeRange.to)}}
</label>

&mdash;

<span class="form-inline">
<select
id="dscResultsIntervalSelector"
class="dscResults__interval form-control"
ng-model="state.interval"
ng-options="interval.val as interval.display for interval in intervalOptions | filter: intervalEnabled"
ng-blur="toggleInterval()"
data-test-subj="discoverIntervalSelect"
>
</select>
<span ng-if="bucketInterval.scaled">
<icon-tip
content="getBucketIntervalToolTipText()"
position="'top'"
></icon-tip>
<span
data-test-subj="discoverIntervalSelectScaledToDesc"
i18n-id="discover.scaledToDescription"
i18n-default-message="Scaled to {bucketIntervalDescription}"
i18n-values="{
bucketIntervalDescription: bucketInterval.description
}"
></span>
</span>
</span>
</div>

</header>
<timechart-header
from="toMoment(timeRange.from)"
to="toMoment(timeRange.to)"
options="intervalOptions"
on-change-interval="changeInterval"
state-interval="state.interval"
show-scaled-info="bucketInterval.scaled"
bucket-interval-description="bucketInterval.description"
bucket-interval-scale="bucketInterval.scale"
>
</timechart-header>

<discover-histogram
class="dscHistogram"
Expand Down
28 changes: 6 additions & 22 deletions src/plugins/discover/public/application/angular/discover.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,6 @@ function discoverController(
}
);

$scope.intervalEnabled = function(interval) {
return interval.val !== 'custom';
};

let abortController;
$scope.$on('$destroy', () => {
if (abortController) abortController.abort();
Expand Down Expand Up @@ -614,24 +610,6 @@ function discoverController(
$scope.state.index = $scope.indexPattern.id;
$scope.state.sort = getSortArray($scope.state.sort, $scope.indexPattern);

$scope.getBucketIntervalToolTipText = () => {
return i18n.translate('discover.bucketIntervalTooltip', {
defaultMessage:
'This interval creates {bucketsDescription} to show in the selected time range, so it has been scaled to {bucketIntervalDescription}',
values: {
bucketsDescription:
$scope.bucketInterval.scale > 1
? i18n.translate('discover.bucketIntervalTooltip.tooLargeBucketsText', {
defaultMessage: 'buckets that are too large',
})
: i18n.translate('discover.bucketIntervalTooltip.tooManyBucketsText', {
defaultMessage: 'too many buckets',
}),
bucketIntervalDescription: $scope.bucketInterval.description,
},
});
};

$scope.opts = {
// number of records to fetch, then paginate through
sampleSize: config.get(SAMPLE_SIZE_SETTING),
Expand Down Expand Up @@ -688,6 +666,12 @@ function discoverController(
}
});

$scope.changeInterval = interval => {
if (interval) {
setAppState({ interval });
}
};

stratoula marked this conversation as resolved.
Show resolved Hide resolved
$scope.$watchMulti(
['rows', 'fetchStatus'],
(function updateResultState() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. 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.
*/

export { TimechartHeader } from './timechart_header';
export { createTimechartHeaderDirective } from './timechart_header_directive';
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. 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 React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ReactWrapper } from 'enzyme';
import { TimechartHeader, TimechartHeaderProps } from './timechart_header';
import { EuiIconTip } from '@elastic/eui';
// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';

describe('timechart header', function() {
let props: TimechartHeaderProps;
let component: ReactWrapper<TimechartHeaderProps>;

beforeAll(() => {
props = {
from: 'May 14, 2020 @ 11:05:13.590',
to: 'May 14, 2020 @ 11:20:13.590',
stateInterval: 's',
options: [
{
display: 'Auto',
val: 'auto',
},
{
display: 'Millisecond',
val: 'ms',
},
{
display: 'Second',
val: 's',
},
],
onChangeInterval: jest.fn(),
showScaledInfo: undefined,
bucketIntervalDescription: 'second',
bucketIntervalScale: undefined,
};
});

it('TimechartHeader not renders an info text when the showScaledInfo property is not provided', () => {
component = mountWithIntl(<TimechartHeader {...props} />);
expect(component.find(EuiIconTip).length).toBe(0);
});

it('TimechartHeader renders an info text by providing the showScaledInfo property', () => {
props.showScaledInfo = true;
component = mountWithIntl(<TimechartHeader {...props} />);
expect(component.find(EuiIconTip).length).toBe(1);
});

it('expect to render the date range', function() {
component = mountWithIntl(<TimechartHeader {...props} />);
const datetimeRangeText = findTestSubject(component, 'discoverIntervalDateRange');
expect(datetimeRangeText.text()).toBe(
'May 14, 2020 @ 11:05:13.590 - May 14, 2020 @ 11:20:13.590 per'
);
});

it('expects to render a dropdown with the interval options', () => {
component = mountWithIntl(<TimechartHeader {...props} />);
const dropdown = findTestSubject(component, 'discoverIntervalSelect');
expect(dropdown.length).toBe(1);
// @ts-ignore
const values = dropdown.find('option').map(option => option.prop('value'));
expect(values).toEqual(['auto', 'ms', 's']);
// @ts-ignore
const labels = dropdown.find('option').map(option => option.text());
expect(labels).toEqual(['Auto', 'Millisecond', 'Second']);
});

it('should change the interval', function() {
component = mountWithIntl(<TimechartHeader {...props} />);
findTestSubject(component, 'discoverIntervalSelect').simulate('change', {
target: { value: 'ms' },
});
expect(props.onChangeInterval).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. 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 React, { useState, useEffect } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiToolTip,
EuiText,
EuiSelect,
EuiIconTip,
} from '@elastic/eui';
import { I18nProvider } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';

export interface TimechartHeaderProps {
/**
* the query from date string
*/
from: string;
/**
* the query to date string
*/
to: string;
/**
* Interval Options
*/
options: Array<{ display: string; val: string }>;
/**
* changes the interval
*/
onChangeInterval: (interval: string) => void;
/**
* selected interval
*/
stateInterval: string;
/**
* displays the scaled info of the interval
*/
showScaledInfo: boolean | undefined;
/**
* scaled info description
*/
bucketIntervalDescription: string;
/**
* bucket interval scale
*/
bucketIntervalScale: number | undefined;
}

export function TimechartHeader({
from,
to,
options,
onChangeInterval,
stateInterval,
showScaledInfo,
bucketIntervalDescription,
bucketIntervalScale,
}: TimechartHeaderProps) {
const [interval, setInterval] = useState(stateInterval);

useEffect(() => {
setInterval(stateInterval);
}, [stateInterval]);

const handleIntervalChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setInterval(e.target.value);
onChangeInterval(e.target.value);
};

return (
<I18nProvider>
<EuiFlexGroup gutterSize="s" responsive justifyContent="center" alignItems="center">
<EuiFlexItem grow={false}>
<EuiToolTip
content={i18n.translate('discover.howToChangeTheTimeTooltip', {
defaultMessage: 'To change the time, use the global time filter above',
})}
delay="long"
>
<EuiText data-test-subj="discoverIntervalDateRange" size="s">
{`${from} - ${to} ${
interval !== 'auto'
? i18n.translate('discover.timechartHeader.timeIntervalSelect.per', {
defaultMessage: 'per',
})
: ''
}`}
</EuiText>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSelect
aria-label={i18n.translate('discover.timechartHeader.timeIntervalSelect.ariaLabel', {
defaultMessage: 'Time interval',
})}
compressed
id="dscResultsIntervalSelector"
data-test-subj="discoverIntervalSelect"
options={options
.filter(({ val }) => val !== 'custom')
.map(({ display, val }) => {
return {
text: display,
value: val,
label: display,
};
})}
value={interval}
onChange={handleIntervalChange}
append={
showScaledInfo ? (
<EuiIconTip
id="discoverIntervalIconTip"
content={i18n.translate('discover.bucketIntervalTooltip', {
defaultMessage:
'This interval creates {bucketsDescription} to show in the selected time range, so it has been scaled to {bucketIntervalDescription}.',
values: {
bucketsDescription:
bucketIntervalScale && bucketIntervalScale > 1
? i18n.translate('discover.bucketIntervalTooltip.tooLargeBucketsText', {
defaultMessage: 'buckets that are too large',
})
: i18n.translate('discover.bucketIntervalTooltip.tooManyBucketsText', {
defaultMessage: 'too many buckets',
}),
bucketIntervalDescription,
},
})}
color="warning"
size="s"
type="alert"
/>
) : (
undefined
)
}
/>
</EuiFlexItem>
</EuiFlexGroup>
</I18nProvider>
);
}
Loading