Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/elastic/kibana into alert…
Browse files Browse the repository at this point in the history
…ing/default-es-index-schema
  • Loading branch information
ymao1 committed Apr 8, 2021
2 parents fa502cc + 4a54188 commit b23fca9
Show file tree
Hide file tree
Showing 35 changed files with 313 additions and 4,477 deletions.
12 changes: 7 additions & 5 deletions docs/management/advanced-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,12 @@ When date histograms use the `auto` interval, Kibana attempts to generate this
number of bars.

[[histogram-maxbars]]`histogram:maxBars`::
Date histograms are not generated with more bars than the value of this property,
scaling values when necessary.
To improve performance, limits the density of date and number histograms across {kib}
using a test query. When the test query contains too many buckets,
the interval between buckets increases. This setting applies separately
to each histogram aggregation, and does not apply to other types of aggregations.
To find the maximum value of this setting, divide the {es} `search.max_buckets`
value by the maximum number of aggregations in each visualization.

[[history-limit]]`history:limit`::
In fields that have history, such as query inputs, show this many recent values.
Expand All @@ -134,9 +138,7 @@ Fields that exist outside of `_source`. Kibana merges these fields into the
document when displaying it.

[[metrics-maxbuckets]]`metrics:max_buckets`::
The maximum numbers of buckets that a single data source can return. This might
arise when the user selects a short interval (for example, 1s) for a long time
period (1 year).
Affects the *TSVB* histogram density. Must be set higher than `histogram:maxBars`.

[[query-allowleadingwildcards]]`query:allowLeadingWildcards`::
Allows a wildcard (*) as the first character in a query clause. Only applies
Expand Down
16 changes: 11 additions & 5 deletions src/plugins/data/server/ui_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,23 +304,29 @@ export function getUiSettings(): Record<string, UiSettingsParams<unknown>> {
},
[UI_SETTINGS.HISTOGRAM_BAR_TARGET]: {
name: i18n.translate('data.advancedSettings.histogram.barTargetTitle', {
defaultMessage: 'Target bars',
defaultMessage: 'Target buckets',
}),
value: 50,
description: i18n.translate('data.advancedSettings.histogram.barTargetText', {
defaultMessage:
'Attempt to generate around this many bars when using "auto" interval in date histograms',
'Attempt to generate around this many buckets when using "auto" interval in date and numeric histograms',
}),
schema: schema.number(),
},
[UI_SETTINGS.HISTOGRAM_MAX_BARS]: {
name: i18n.translate('data.advancedSettings.histogram.maxBarsTitle', {
defaultMessage: 'Maximum bars',
defaultMessage: 'Maximum buckets',
}),
value: 100,
description: i18n.translate('data.advancedSettings.histogram.maxBarsText', {
defaultMessage:
'Never show more than this many bars in date histograms, scale values if needed',
defaultMessage: `
Limits the density of date and number histograms across Kibana
for better performance using a test query. If the test query would too many buckets,
the interval between buckets will be increased. This setting applies separately
to each histogram aggregation, and does not apply to other types of aggregation.
To find the maximum value of this setting, divide the Elasticsearch 'search.max_buckets'
value by the maximum number of aggregations in each visualization.
`,
}),
schema: schema.number(),
},
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/vis_type_timeseries/server/ui_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import { MAX_BUCKETS_SETTING } from '../common/constants';
export const uiSettings: Record<string, UiSettingsParams> = {
[MAX_BUCKETS_SETTING]: {
name: i18n.translate('visTypeTimeseries.advancedSettings.maxBucketsTitle', {
defaultMessage: 'Maximum buckets',
defaultMessage: 'TSVB buckets limit',
}),
value: 2000,
description: i18n.translate('visTypeTimeseries.advancedSettings.maxBucketsText', {
defaultMessage: 'The maximum number of buckets a single datasource can return',
defaultMessage:
'Affects the TSVB histogram density. Must be set higher than "histogram:maxBars".',
}),
schema: schema.number(),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,20 @@ jest.mock('../../../contexts/kibana', () => ({
useMlKibana: () => {
return {
services: {
uiSettings: { get: jest.fn() },
uiSettings: {
get: jest.fn().mockReturnValue([
{
from: 'now/d',
to: 'now/d',
display: 'Today',
},
{
from: 'now/w',
to: 'now/w',
display: 'This week',
},
]),
},
data: {
query: {
timefilter: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@
* 2.0.
*/

import React, { FC, useCallback, useEffect, useState } from 'react';
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { Subscription } from 'rxjs';
import { debounce } from 'lodash';

import { EuiSuperDatePicker, OnRefreshProps } from '@elastic/eui';
import { TimeHistoryContract, TimeRange } from 'src/plugins/data/public';
import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/common';

import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service';
import { useUrlState } from '../../../util/url_state';
import { useMlKibana } from '../../../contexts/kibana';

interface TimePickerQuickRange {
from: string;
to: string;
display: string;
}

interface Duration {
start: string;
end: string;
Expand Down Expand Up @@ -71,6 +78,19 @@ export const DatePickerWrapper: FC = () => {
);

const dateFormat = config.get('dateFormat');
const timePickerQuickRanges = config.get<TimePickerQuickRange[]>(
UI_SETTINGS.TIMEPICKER_QUICK_RANGES
);

const commonlyUsedRanges = useMemo(
() =>
timePickerQuickRanges.map(({ from, to, display }) => ({
start: from,
end: to,
label: display,
})),
[timePickerQuickRanges]
);

useEffect(() => {
const subscriptions = new Subscription();
Expand Down Expand Up @@ -141,6 +161,7 @@ export const DatePickerWrapper: FC = () => {
onRefreshChange={updateInterval}
recentlyUsedRanges={recentlyUsedRanges}
dateFormat={dateFormat}
commonlyUsedRanges={commonlyUsedRanges}
/>
</div>
) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
* 2.0.
*/

import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks';

export const kibanaContextMock = {
services: {
uiSettings: { get: jest.fn() },
chrome: { recentlyAccessed: { add: jest.fn() } },
application: { navigateToApp: jest.fn() },
http: {
Expand All @@ -17,6 +20,7 @@ export const kibanaContextMock = {
share: {
urlGenerators: { getUrlGenerator: jest.fn() },
},
data: dataPluginMock.createStartContract(),
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,23 +480,25 @@ describe('<EditRolePage />', () => {
});
});

it('can render if features are not available', async () => {
const { http } = coreMock.createStart();
http.get.mockImplementation(async (path: any) => {
if (path === '/api/features') {
const error = { response: { status: 404 } };
throw error;
}
it('registers fatal error if features endpoint fails unexpectedly', async () => {
const error = { response: { status: 500 } };
const getFeatures = jest.fn().mockRejectedValue(error);
const props = getProps({ action: 'edit' });
const wrapper = mountWithIntl(<EditRolePage {...props} getFeatures={getFeatures} />);

if (path === '/api/spaces/space') {
return buildSpaces();
}
});
await waitForRender(wrapper);
expect(props.fatalErrors.add).toHaveBeenLastCalledWith(error);
expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(0);
});

const wrapper = mountWithIntl(<EditRolePage {...{ ...getProps({ action: 'edit' }), http }} />);
it('can render if features call is not allowed', async () => {
const error = { response: { status: 403 } };
const getFeatures = jest.fn().mockRejectedValue(error);
const props = getProps({ action: 'edit' });
const wrapper = mountWithIntl(<EditRolePage {...props} getFeatures={getFeatures} />);

await waitForRender(wrapper);

expect(props.fatalErrors.add).not.toHaveBeenCalled();
expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
expectSaveFormButtons(wrapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,12 @@ function useFeatures(
// possible that a user with `manage_security` will attempt to visit the role management page without the
// correct Kibana privileges. If that's the case, then they receive a partial view of the role, and the UI does
// not allow them to make changes to that role's kibana privileges. When this user visits the edit role page,
// this API endpoint will throw a 404, which causes view to fail completely. So we instead attempt to detect the
// 404 here, and respond in a way that still allows the UI to render itself.
const unauthorizedForFeatures = err.response?.status === 404;
// this API endpoint will throw a 403, which causes view to fail completely. So we instead attempt to detect the
// 403 here, and respond in a way that still allows the UI to render itself.
const unauthorizedForFeatures = err.response?.status === 403;
if (unauthorizedForFeatures) {
return [] as KibanaFeature[];
}

fatalErrors.add(err);
})
.then((retrievedFeatures) => {
Expand Down Expand Up @@ -296,7 +295,6 @@ export const EditRolePage: FunctionComponent<Props> = ({
// We should keep the same mutable instance of Validator for every re-render since we'll
// eventually enable validation after the first time user tries to save a role.
const { current: validator } = useRef(new RoleValidator({ shouldValidate: false }));

const [formError, setFormError] = useState<RoleValidationResult | null>(null);
const runAsUsers = useRunAsUsers(userAPIClient, fatalErrors);
const indexPatternsTitles = useIndexPatternsTitles(indexPatterns, fatalErrors, notifications);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { mount } from 'enzyme';
import { repeat } from 'lodash/fp';
import React from 'react';

import { LineClamp } from '.';

describe('LineClamp', () => {
const message = repeat(1000, 'abcdefghij '); // 10 characters, with a trailing space

describe('no overflow', () => {
test('it does NOT render the expanded line clamp when isOverflow is falsy', () => {
const wrapper = mount(<LineClamp content={message} />);

expect(wrapper.find('[data-test-subj="expanded-line-clamp"]').exists()).toBe(false);
});

test('it does NOT render the styled line clamp expanded when isOverflow is falsy', () => {
const wrapper = mount(<LineClamp content={message} />);

expect(wrapper.find('[data-test-subj="styled-line-clamp"]').exists()).toBe(false);
});

test('it renders the default line clamp when isOverflow is falsy', () => {
const wrapper = mount(<LineClamp content={message} />);

expect(wrapper.find('[data-test-subj="default-line-clamp"]').first().text()).toBe(message);
});

test('it does NOT render the `Read More` button when isOverflow is falsy', () => {
const wrapper = mount(<LineClamp content={message} />);

expect(wrapper.find('[data-test-subj="summary-view-readmore"]').exists()).toBe(false);
});
});

describe('overflow', () => {
const clientHeight = 400;
const scrollHeight = clientHeight + 100; // scrollHeight is > clientHeight

beforeAll(() => {
Object.defineProperty(HTMLElement.prototype, 'clientHeight', {
configurable: true,
value: clientHeight,
});

Object.defineProperty(HTMLElement.prototype, 'scrollHeight', {
configurable: true,
value: scrollHeight,
});
});

test('it does NOT render the expanded line clamp by default when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

expect(wrapper.find('[data-test-subj="expanded-line-clamp"]').exists()).toBe(false);
});

test('it renders the styled line clamp when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

expect(wrapper.find('[data-test-subj="styled-line-clamp"]').first().text()).toBe(message);
});

test('it does NOT render the default line clamp when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

expect(wrapper.find('[data-test-subj="default-line-clamp"]').exists()).toBe(false);
});

test('it renders the `Read More` button with the expected (default) text when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

expect(wrapper.find('[data-test-subj="summary-view-readmore"]').first().text()).toBe(
'Read More'
);
});

describe('clicking the Read More button', () => {
test('it displays the `Read Less` button text after the user clicks the `Read More` button when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

wrapper.find('[data-test-subj="summary-view-readmore"]').first().simulate('click');
wrapper.update();

expect(wrapper.find('[data-test-subj="summary-view-readmore"]').first().text()).toBe(
'Read Less'
);
});

test('it renders the expanded content after the user clicks the `Read More` button when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

wrapper.find('[data-test-subj="summary-view-readmore"]').first().simulate('click');
wrapper.update();

expect(wrapper.find('[data-test-subj="expanded-line-clamp"]').first().text()).toBe(message);
});
});

test('it renders the expanded content with a max-height of one third the view height when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

wrapper.find('[data-test-subj="summary-view-readmore"]').first().simulate('click');
wrapper.update();

expect(wrapper.find('[data-test-subj="expanded-line-clamp"]').first()).toHaveStyleRule(
'max-height',
'33vh'
);
});

test('it automatically vertically scrolls the content when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

wrapper.find('[data-test-subj="summary-view-readmore"]').first().simulate('click');
wrapper.update();

expect(wrapper.find('[data-test-subj="expanded-line-clamp"]').first()).toHaveStyleRule(
'overflow-y',
'auto'
);
});

test('it does NOT render the styled line clamp after the user clicks the `Read More` button when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

wrapper.find('[data-test-subj="summary-view-readmore"]').first().simulate('click');
wrapper.update();

expect(wrapper.find('[data-test-subj="styled-line-clamp"]').exists()).toBe(false);
});

test('it does NOT render the default line clamp after the user clicks the `Read More` button when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

wrapper.find('[data-test-subj="summary-view-readmore"]').first().simulate('click');
wrapper.update();

expect(wrapper.find('[data-test-subj="default-line-clamp"]').exists()).toBe(false);
});

test('it once again displays the `Read More` button text after the user clicks the `Read Less` when isOverflow is true', () => {
const wrapper = mount(<LineClamp content={message} />);

wrapper.find('[data-test-subj="summary-view-readmore"]').first().simulate('click');
wrapper.update(); // 1st toggle

wrapper.find('[data-test-subj="summary-view-readmore"]').first().simulate('click');
wrapper.update(); // 2nd toggle

expect(wrapper.find('[data-test-subj="summary-view-readmore"]').first().text()).toBe(
'Read More' // after the 2nd toggle, the button once-again says `Read More`
);
});
});
});
Loading

0 comments on commit b23fca9

Please sign in to comment.