mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Lens] Add feature to ignore global filters at layer level (#159248)
## Summary Fixes #143493 * Add the switch control in Layer Settings * [x] Make sure it does not duplicate on Annotation Layer Settings * [x] Data Layers * [x] Reference line layers * [x] Extended dataView picker to support multiple icons * [x] Added unit tests * [x] Functional tests <img width="351" alt="Screenshot 2023-06-07 at 15 28 19" src="00dc5523
-0bec-4e9c-b1d0-4d36804b29f9"> <img width="340" alt="Screenshot 2023-06-07 at 15 31 31" src="d36ca147
-5d8c-4123-9be3-2932844cbd15"> <img width="331" alt="Screenshot 2023-06-07 at 15 28 25" src="c7d4f166
-b8ab-4439-a83c-debf82b913ad"> <img width="324" alt="Screenshot 2023-06-07 at 15 27 59" src="3738a7e0
-6e49-4e22-b857-965a953b4b84"> <img width="323" alt="Screenshot 2023-06-07 at 15 27 53" src="5965bf1c
-0e25-4c0e-b54f-fa315157fd44"> * Create `IgnoreGlobalFilter` shared component folder * [x] Layer setting control component * [x] Info badge component * Extends `esaggs_fn` to support the flag * [x] Avoid to pass the filter to the handler if set * [x] Add unit tests * Notification badges * [x] Extends the badge component in Embeddable to support grouped messages * [x] Added unit tests <img width="750" alt="Screenshot 2023-06-07 at 15 31 39" src="01bf8203
-9133-4429-9b79-17ec67613c7e"> <img width="828" alt="Screenshot 2023-06-07 at 15 30 57" src="9acb78f2
-d061-4225-a4af-b3a66e7454fc"> <img width="756" alt="Screenshot 2023-06-07 at 15 27 43" src="b9f79aed
-7c02-4060-9c0f-61f438dc031d"> * Add support for Open in Lens * [x] Add unit tests for each converter * [x] Functional tests ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
parent
b6a2611361
commit
e325d4102b
56 changed files with 1266 additions and 263 deletions
|
@ -34,6 +34,7 @@ interface Arguments {
|
|||
timeFields?: string[];
|
||||
probability?: number;
|
||||
samplerSeed?: number;
|
||||
ignoreGlobalFilters?: boolean;
|
||||
}
|
||||
|
||||
export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition<
|
||||
|
@ -111,6 +112,12 @@ export const getEsaggsMeta: () => Omit<EsaggsExpressionFunctionDefinition, 'fn'>
|
|||
'The seed to generate the random sampling of documents. Uses random sampler.',
|
||||
}),
|
||||
},
|
||||
ignoreGlobalFilters: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('data.search.functions.esaggs.ignoreGlobalFilters.help', {
|
||||
defaultMessage: 'Whether to ignore or use global query and filters',
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -153,4 +153,28 @@ describe('esaggs expression function - public', () => {
|
|||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('does not forward filters and query if ignoreGlobalFilters is enabled', async () => {
|
||||
const input = {
|
||||
type: 'kibana_context' as 'kibana_context',
|
||||
filters: [{ $state: {}, meta: {}, query: {} }],
|
||||
query: {
|
||||
query: 'hiya',
|
||||
language: 'painless',
|
||||
},
|
||||
timeRange: { from: 'a', to: 'b' },
|
||||
} as KibanaContext;
|
||||
|
||||
await definition()
|
||||
.fn(input, { ...args, ignoreGlobalFilters: true }, mockHandlers)
|
||||
.toPromise();
|
||||
|
||||
expect(handleEsaggsRequest).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
filters: undefined,
|
||||
query: undefined,
|
||||
timeRange: input.timeRange,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -66,10 +66,10 @@ export function getFunctionDefinition({
|
|||
return handleEsaggsRequest({
|
||||
abortSignal,
|
||||
aggs: aggConfigs,
|
||||
filters: get(input, 'filters', undefined),
|
||||
filters: args.ignoreGlobalFilters ? undefined : get(input, 'filters', undefined),
|
||||
indexPattern,
|
||||
inspectorAdapters,
|
||||
query: get(input, 'query', undefined) as any,
|
||||
query: args.ignoreGlobalFilters ? undefined : (get(input, 'query', undefined) as any),
|
||||
searchSessionId: getSearchSessionId(),
|
||||
searchSourceService: searchSource,
|
||||
timeFields: args.timeFields,
|
||||
|
|
|
@ -98,6 +98,7 @@ export const convertToLens: ConvertGaugeVisToLensVisualization = async (vis, tim
|
|||
layerId,
|
||||
columns: columns.map(excludeMetaFromColumn),
|
||||
columnOrder: [],
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
],
|
||||
configuration: getConfiguration(
|
||||
|
|
|
@ -95,6 +95,7 @@ export const convertToLens: ConvertGoalVisToLensVisualization = async (vis, time
|
|||
layerId,
|
||||
columns: columns.map(excludeMetaFromColumn),
|
||||
columnOrder: [],
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
],
|
||||
configuration: getConfiguration(
|
||||
|
|
|
@ -89,6 +89,7 @@ export const convertToLens: ConvertHeatmapToLensVisualization = async (vis, time
|
|||
layerId,
|
||||
columns: layerConfig.columns.map(excludeMetaFromColumn),
|
||||
columnOrder: [],
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
],
|
||||
configuration,
|
||||
|
|
|
@ -85,6 +85,7 @@ export const convertToLens: ConvertMetricVisToLensVisualization = async (vis, ti
|
|||
layerId,
|
||||
columns: layerConfig.columns.map(excludeMetaFromColumn),
|
||||
columnOrder: [],
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
],
|
||||
configuration: getConfiguration(
|
||||
|
|
|
@ -73,6 +73,7 @@ export const convertToLens: ConvertPieToLensVisualization = async (vis, timefilt
|
|||
layerId,
|
||||
columns: layerConfig.columns.map(excludeMetaFromColumn),
|
||||
columnOrder: [],
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
],
|
||||
configuration: getConfiguration(layerId, vis, layerConfig),
|
||||
|
|
|
@ -99,6 +99,7 @@ export const convertToLens: ConvertTableToLensVisualization = async (vis, timefi
|
|||
layerId,
|
||||
columns: layerConfig.columns.map(excludeMetaFromColumn),
|
||||
columnOrder: [],
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
],
|
||||
configuration: getConfiguration(layerId, vis.params, layerConfig),
|
||||
|
|
|
@ -23,7 +23,7 @@ export function YesNo({
|
|||
name,
|
||||
value,
|
||||
disabled,
|
||||
'data-test-subj': dataTestSubj,
|
||||
'data-test-subj': dataTestSubj = name,
|
||||
onChange,
|
||||
}: YesNoProps) {
|
||||
const handleChange = useCallback(
|
||||
|
|
|
@ -204,4 +204,60 @@ describe('convertToLens', () => {
|
|||
expect(result).toBeDefined();
|
||||
expect(result?.type).toBe('lnsMetric');
|
||||
});
|
||||
|
||||
test('should carry the ignoreGlobalFilters flag if set on the panel', async () => {
|
||||
mockGetMetricsColumns.mockReturnValue([metricColumn]);
|
||||
mockGetSeriesAgg.mockReturnValue({ metrics: [metric] });
|
||||
mockGetConfigurationForGauge.mockReturnValue({});
|
||||
|
||||
const result = await convertToLens(
|
||||
{
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
}),
|
||||
],
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
} as Vis<Panel>,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
expect(result?.layers.every((l) => l.ignoreGlobalFilters)).toBe(true);
|
||||
});
|
||||
|
||||
test('should carry the ignoreGlobalFilters flag if set on the series', async () => {
|
||||
mockGetMetricsColumns.mockReturnValue([metricColumn]);
|
||||
mockGetSeriesAgg.mockReturnValue({ metrics: [metric] });
|
||||
mockGetConfigurationForGauge.mockReturnValue({});
|
||||
|
||||
const result = await convertToLens(
|
||||
{
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
}),
|
||||
],
|
||||
ignore_global_filter: 0,
|
||||
}),
|
||||
} as Vis<Panel>,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -101,6 +101,7 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (
|
|||
|
||||
const extendedLayer: ExtendedLayer = {
|
||||
indexPatternId,
|
||||
ignoreGlobalFilters: Boolean(model.ignore_global_filter || series.ignore_global_filter),
|
||||
layerId: uuidv4(),
|
||||
columns: [...metricsColumns, ...(bucket ? [bucket] : [])],
|
||||
columnOrder: [],
|
||||
|
|
|
@ -32,6 +32,7 @@ export const createSeries = (partialSeries?: Partial<Series>): Series => ({
|
|||
series_index_pattern: { id: 'test' },
|
||||
series_max_bars: 0,
|
||||
steps: 0,
|
||||
ignore_global_filter: 0,
|
||||
...partialSeries,
|
||||
});
|
||||
|
||||
|
@ -57,5 +58,6 @@ export const createPanel = (parialPanel?: Partial<Panel>): Panel => ({
|
|||
show_grid: 0,
|
||||
show_legend: 0,
|
||||
type: PANEL_TYPES.TIMESERIES,
|
||||
ignore_global_filter: 0,
|
||||
...parialPanel,
|
||||
});
|
||||
|
|
|
@ -18,6 +18,17 @@ jest.mock('../palette', () => ({
|
|||
getPalette: jest.fn(() => mockGetPalette()),
|
||||
}));
|
||||
|
||||
function createEmptyLensLayer(partialLayer: Partial<Layer>): Layer {
|
||||
return {
|
||||
columns: [],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
ignoreGlobalFilters: false,
|
||||
layerId: '0',
|
||||
...partialLayer,
|
||||
};
|
||||
}
|
||||
|
||||
describe('getConfigurationForMetric', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
@ -26,15 +37,11 @@ describe('getConfigurationForMetric', () => {
|
|||
|
||||
const metricId = 'some-id';
|
||||
const metric = { id: metricId, type: METRIC_TYPES.COUNT };
|
||||
|
||||
test('should return null if no series was provided', () => {
|
||||
const layerId = 'layer-id-1';
|
||||
const model = createPanel({ series: [] });
|
||||
const layer: Layer = {
|
||||
columns: [],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
const layer = createEmptyLensLayer({ layerId });
|
||||
const config = getConfigurationForMetric(model, layer);
|
||||
|
||||
expect(config).toBeNull();
|
||||
|
@ -47,12 +54,7 @@ describe('getConfigurationForMetric', () => {
|
|||
const model = createPanel({
|
||||
series: [createSeries({ metrics: [metric1] })],
|
||||
});
|
||||
const layer: Layer = {
|
||||
columns: [],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
const layer = createEmptyLensLayer({ layerId });
|
||||
const config = getConfigurationForMetric(model, layer);
|
||||
|
||||
expect(config).toBeNull();
|
||||
|
@ -69,12 +71,7 @@ describe('getConfigurationForMetric', () => {
|
|||
createSeries({ metrics: [metric, metric2] }),
|
||||
],
|
||||
});
|
||||
const layer: Layer = {
|
||||
columns: [],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
const layer = createEmptyLensLayer({ layerId });
|
||||
const config = getConfigurationForMetric(model, layer);
|
||||
|
||||
expect(config).toBeNull();
|
||||
|
@ -94,7 +91,9 @@ describe('getConfigurationForMetric', () => {
|
|||
const model = createPanel({
|
||||
series: [createSeries({ metrics: [metric, metric1] }), createSeries({ metrics: [metric2] })],
|
||||
});
|
||||
const layer: Layer = {
|
||||
|
||||
const layer = createEmptyLensLayer({
|
||||
layerId,
|
||||
columns: [
|
||||
{
|
||||
columnId: columnId1,
|
||||
|
@ -105,10 +104,7 @@ describe('getConfigurationForMetric', () => {
|
|||
meta: { metricId: metricId2 },
|
||||
},
|
||||
] as Column[],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
});
|
||||
const config = getConfigurationForMetric(model, layer);
|
||||
|
||||
expect(config).toEqual({
|
||||
|
@ -131,7 +127,7 @@ describe('getConfigurationForMetric', () => {
|
|||
series: [createSeries({ metrics: [metric] })],
|
||||
});
|
||||
const bucket = { columnId: bucketColumnId } as Column;
|
||||
const layer: Layer = {
|
||||
const layer = createEmptyLensLayer({
|
||||
columns: [
|
||||
{
|
||||
columnId,
|
||||
|
@ -144,10 +140,8 @@ describe('getConfigurationForMetric', () => {
|
|||
meta: { metricId },
|
||||
},
|
||||
],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
});
|
||||
const config = getConfigurationForMetric(model, layer, bucket);
|
||||
|
||||
expect(config).toEqual({
|
||||
|
@ -169,7 +163,7 @@ describe('getConfigurationForMetric', () => {
|
|||
const model = createPanel({
|
||||
series: [createSeries({ metrics: [metric] })],
|
||||
});
|
||||
const layer: Layer = {
|
||||
const layer = createEmptyLensLayer({
|
||||
columns: [
|
||||
{
|
||||
columnId,
|
||||
|
@ -182,10 +176,8 @@ describe('getConfigurationForMetric', () => {
|
|||
meta: { metricId },
|
||||
},
|
||||
],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
});
|
||||
const config = getConfigurationForMetric(model, layer);
|
||||
expect(config).toBeNull();
|
||||
expect(mockGetPalette).toBeCalledTimes(1);
|
||||
|
@ -215,12 +207,7 @@ describe('getConfigurationForGauge', () => {
|
|||
test('should return null if no series was provided', () => {
|
||||
const layerId = 'layer-id-1';
|
||||
const model = createPanel({ series: [] });
|
||||
const layer: Layer = {
|
||||
columns: [],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
const layer = createEmptyLensLayer({ layerId });
|
||||
const config = getConfigurationForGauge(model, layer, undefined, gaugeMaxColumn);
|
||||
|
||||
expect(config).toBeNull();
|
||||
|
@ -233,12 +220,7 @@ describe('getConfigurationForGauge', () => {
|
|||
const model = createPanel({
|
||||
series: [createSeries({ metrics: [metric1] })],
|
||||
});
|
||||
const layer: Layer = {
|
||||
columns: [],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
const layer = createEmptyLensLayer({ layerId });
|
||||
const config = getConfigurationForGauge(model, layer, undefined, gaugeMaxColumn);
|
||||
|
||||
expect(config).toBeNull();
|
||||
|
@ -252,7 +234,7 @@ describe('getConfigurationForGauge', () => {
|
|||
const model = createPanel({
|
||||
series: [createSeries({ metrics: [metric] })],
|
||||
});
|
||||
const layer: Layer = {
|
||||
const layer = createEmptyLensLayer({
|
||||
columns: [
|
||||
{
|
||||
columnId,
|
||||
|
@ -265,10 +247,8 @@ describe('getConfigurationForGauge', () => {
|
|||
meta: { metricId },
|
||||
},
|
||||
],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
});
|
||||
const config = getConfigurationForGauge(model, layer, undefined, gaugeMaxColumn);
|
||||
expect(config).toBeNull();
|
||||
expect(mockGetPalette).toBeCalledTimes(1);
|
||||
|
@ -279,12 +259,7 @@ describe('getConfigurationForGauge', () => {
|
|||
const metric1 = { id: 'metric-id-1', type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'sum' };
|
||||
const color = '#fff';
|
||||
const model = createPanel({ series: [createSeries({ metrics: [metric, metric1], color })] });
|
||||
const layer: Layer = {
|
||||
columns: [],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
const layer = createEmptyLensLayer({ layerId });
|
||||
const config = getConfigurationForGauge(model, layer, undefined, gaugeMaxColumn);
|
||||
|
||||
expect(config).toEqual({
|
||||
|
@ -312,7 +287,7 @@ describe('getConfigurationForGauge', () => {
|
|||
const model = createPanel({ series: [createSeries({ metrics: [metric, metric1], color })] });
|
||||
const bucketColumnId = 'bucket-column-id-1';
|
||||
const bucket = { columnId: bucketColumnId } as Column;
|
||||
const layer: Layer = {
|
||||
const layer = createEmptyLensLayer({
|
||||
columns: [
|
||||
{
|
||||
columnId: columnId1,
|
||||
|
@ -325,10 +300,8 @@ describe('getConfigurationForGauge', () => {
|
|||
meta: { metricId },
|
||||
},
|
||||
],
|
||||
columnOrder: [],
|
||||
indexPatternId: 'some-index-pattern',
|
||||
layerId,
|
||||
};
|
||||
});
|
||||
const config = getConfigurationForGauge(model, layer, bucket, gaugeMaxColumn);
|
||||
|
||||
expect(config).toEqual({
|
||||
|
|
|
@ -68,6 +68,7 @@ describe('getLayers', () => {
|
|||
{
|
||||
indexPatternId: 'test',
|
||||
layerId: 'test-layer-1',
|
||||
ignoreGlobalFilters: false,
|
||||
columns: [
|
||||
{
|
||||
operationType: 'count',
|
||||
|
@ -111,6 +112,7 @@ describe('getLayers', () => {
|
|||
{
|
||||
indexPatternId: 'test',
|
||||
layerId: 'test-layer-1',
|
||||
ignoreGlobalFilters: false,
|
||||
columns: [
|
||||
{
|
||||
operationType: 'static_value',
|
||||
|
@ -131,6 +133,7 @@ describe('getLayers', () => {
|
|||
{
|
||||
indexPatternId: 'test',
|
||||
layerId: 'test-layer-1',
|
||||
ignoreGlobalFilters: false,
|
||||
columns: [
|
||||
{
|
||||
operationType: 'percentile',
|
||||
|
@ -164,6 +167,7 @@ describe('getLayers', () => {
|
|||
{
|
||||
indexPatternId: 'test',
|
||||
layerId: 'test-layer-1',
|
||||
ignoreGlobalFilters: false,
|
||||
columns: [
|
||||
{
|
||||
operationType: 'percentile_rank',
|
||||
|
|
|
@ -283,4 +283,73 @@ describe('convertToLens', () => {
|
|||
expect(result?.type).toBe('lnsMetric');
|
||||
expect(mockGetConfigurationForMetric).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should set the ignoreGlobalFilters if set on the panel', async () => {
|
||||
mockGetBucketsColumns.mockReturnValueOnce([bucket]);
|
||||
mockGetBucketsColumns.mockReturnValueOnce([bucket]);
|
||||
mockGetMetricsColumns.mockReturnValueOnce([metric]);
|
||||
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
}),
|
||||
],
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers.every((l) => l.ignoreGlobalFilters)).toBe(true);
|
||||
});
|
||||
|
||||
test('should set the ignoreGlobalFilters if set on the series', async () => {
|
||||
mockGetBucketsColumns.mockReturnValueOnce([bucket]);
|
||||
mockGetBucketsColumns.mockReturnValueOnce([bucket]);
|
||||
mockGetMetricsColumns.mockReturnValueOnce([metric]);
|
||||
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(true);
|
||||
});
|
||||
|
||||
test('should ignore the ignoreGlobalFilters if set on hidden series', async () => {
|
||||
mockGetBucketsColumns.mockReturnValueOnce([bucket]);
|
||||
mockGetBucketsColumns.mockReturnValueOnce([bucket]);
|
||||
mockGetMetricsColumns.mockReturnValueOnce([metric]);
|
||||
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: true,
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -117,6 +117,11 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (
|
|||
const [bucket] = uniqueBuckets;
|
||||
|
||||
const extendedLayer: ExtendedLayer = {
|
||||
ignoreGlobalFilters: Boolean(
|
||||
model.ignore_global_filter ||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
visibleSeries.some(({ ignore_global_filter }) => ignore_global_filter)
|
||||
),
|
||||
indexPatternId: indexPatternId as string,
|
||||
layerId: uuidv4(),
|
||||
columns: [...metrics, ...(bucket ? [bucket] : [])],
|
||||
|
|
|
@ -232,4 +232,65 @@ describe('convertToLens', () => {
|
|||
expect(result?.type).toBe('lnsDatatable');
|
||||
expect(mockIsValidMetrics).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should set the ignoreGlobalFilters if set on the panel', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
}),
|
||||
],
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
uiState: {
|
||||
get: () => ({}),
|
||||
},
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers.every((l) => l.ignoreGlobalFilters)).toBe(true);
|
||||
});
|
||||
|
||||
test('should set the ignoreGlobalFilters if set on the series', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
uiState: {
|
||||
get: () => ({}),
|
||||
},
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(true);
|
||||
});
|
||||
|
||||
test('should ignore the ignoreGlobalFilters if set on hidden series', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: true,
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
uiState: {
|
||||
get: () => ({}),
|
||||
},
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -38,7 +38,8 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (
|
|||
const dataViews = getDataViewsStart();
|
||||
|
||||
try {
|
||||
const seriesNum = model.series.filter((series) => !series.hidden).length;
|
||||
const visibleSeries = model.series.filter((series) => !series.hidden);
|
||||
const seriesNum = visibleSeries.length;
|
||||
const sortConfig = uiState.get('table')?.sort ?? {};
|
||||
|
||||
const datasourceInfo = await extractOrGenerateDatasourceInfo(
|
||||
|
@ -165,6 +166,11 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (
|
|||
}
|
||||
|
||||
const extendedLayer: ExtendedLayer = {
|
||||
ignoreGlobalFilters: Boolean(
|
||||
model.ignore_global_filter ||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
visibleSeries.some(({ ignore_global_filter }) => ignore_global_filter)
|
||||
),
|
||||
indexPatternId: indexPatternId as string,
|
||||
layerId: uuidv4(),
|
||||
columns: [...metrics, ...commonBucketsColumns, ...bucketsColumns],
|
||||
|
|
|
@ -162,4 +162,51 @@ describe('convertToLens', () => {
|
|||
expect(result?.type).toBe('lnsXY');
|
||||
expect(mockIsValidMetrics).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('should set the ignoreGlobalFilters if set on the panel', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
}),
|
||||
],
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers.every((l) => l.ignoreGlobalFilters)).toBe(true);
|
||||
});
|
||||
|
||||
test('should set the ignoreGlobalFilters if set on the series', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(true);
|
||||
});
|
||||
|
||||
test('should ignore the ignoreGlobalFilters if set on hidden series', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: true,
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: false,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -111,6 +111,7 @@ export const convertToLens: ConvertTsvbToLensVisualization = async ({ params: mo
|
|||
|
||||
const layerId = uuidv4();
|
||||
extendedLayers[layerIdx] = {
|
||||
ignoreGlobalFilters: Boolean(model.ignore_global_filter || series.ignore_global_filter),
|
||||
indexPatternId,
|
||||
layerId,
|
||||
columns: isReferenceLine
|
||||
|
|
|
@ -133,4 +133,51 @@ describe('convertToLens', () => {
|
|||
expect(result?.type).toBe('lnsXY');
|
||||
expect(mockIsValidMetrics).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('should set the ignoreGlobalFilters if set on the panel', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
}),
|
||||
],
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers.every((l) => l.ignoreGlobalFilters)).toBe(true);
|
||||
});
|
||||
|
||||
test('should set the ignoreGlobalFilters if set on the series', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(true);
|
||||
});
|
||||
|
||||
test('should ignore the ignoreGlobalFilters if set on hidden series', async () => {
|
||||
const result = await convertToLens({
|
||||
params: createPanel({
|
||||
series: [
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
hidden: true,
|
||||
ignore_global_filter: 1,
|
||||
}),
|
||||
|
||||
createSeries({
|
||||
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
} as Vis<Panel>);
|
||||
expect(result?.layers[0].ignoreGlobalFilters).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,7 +37,8 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (
|
|||
const dataViews = getDataViewsStart();
|
||||
try {
|
||||
const extendedLayers: Record<number, ExtendedLayer> = {};
|
||||
const seriesNum = model.series.filter((series) => !series.hidden).length;
|
||||
const visibleSeries = model.series.filter((series) => !series.hidden);
|
||||
const seriesNum = visibleSeries.length;
|
||||
|
||||
// handle multiple layers/series
|
||||
for (const [layerIdx, series] of model.series.entries()) {
|
||||
|
@ -85,6 +86,11 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (
|
|||
|
||||
const layerId = uuidv4();
|
||||
extendedLayers[layerIdx] = {
|
||||
ignoreGlobalFilters: Boolean(
|
||||
model.ignore_global_filter ||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
visibleSeries.some(({ ignore_global_filter }) => ignore_global_filter)
|
||||
),
|
||||
indexPatternId,
|
||||
layerId,
|
||||
columns: [...metricsColumns, ...bucketsColumns],
|
||||
|
|
|
@ -28,6 +28,7 @@ describe('getConfiguration', () => {
|
|||
seriesIdsMap: { 1: '1' },
|
||||
collapseFn: 'max',
|
||||
isReferenceLineLayer: false,
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
{
|
||||
indexPatternId: '',
|
||||
|
@ -41,6 +42,7 @@ describe('getConfiguration', () => {
|
|||
seriesIdsMap: { 4: '2' },
|
||||
collapseFn: undefined,
|
||||
isReferenceLineLayer: false,
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
{
|
||||
indexPatternId: '',
|
||||
|
@ -51,6 +53,7 @@ describe('getConfiguration', () => {
|
|||
seriesIdsMap: {},
|
||||
collapseFn: undefined,
|
||||
isReferenceLineLayer: true,
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
];
|
||||
const series = [
|
||||
|
|
|
@ -26,6 +26,7 @@ export interface Layer {
|
|||
seriesIdsMap: Record<string, string>;
|
||||
isReferenceLineLayer: boolean;
|
||||
collapseFn?: CollapseFunction;
|
||||
ignoreGlobalFilters: boolean;
|
||||
}
|
||||
|
||||
const SIBBLING_PIPELINE_AGGS: string[] = [
|
||||
|
@ -190,6 +191,7 @@ export const convertToLens: ConvertXYToLensVisualization = async (vis, timefilte
|
|||
seriesIdsMap,
|
||||
collapseFn,
|
||||
isReferenceLineLayer: false,
|
||||
ignoreGlobalFilters: false,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -202,6 +204,7 @@ export const convertToLens: ConvertXYToLensVisualization = async (vis, timefilte
|
|||
columnOrder: [],
|
||||
metrics: [staticValueColumn.columnId],
|
||||
isReferenceLineLayer: true,
|
||||
ignoreGlobalFilters: false,
|
||||
collapseFn: undefined,
|
||||
seriesIdsMap: {},
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ export interface Layer {
|
|||
layerId: string;
|
||||
columns: Column[];
|
||||
columnOrder: string[];
|
||||
ignoreGlobalFilters: boolean;
|
||||
}
|
||||
|
||||
export interface NavigateToLensContext<T extends Configuration = Configuration> {
|
||||
|
|
|
@ -66,6 +66,12 @@ export class VisualBuilderPageObject extends FtrService {
|
|||
}
|
||||
}
|
||||
|
||||
private async toggleYesNoSwitch(testSubj: string, value: boolean) {
|
||||
const option = await this.testSubjects.find(`${testSubj}-${value ? 'yes' : 'no'}`);
|
||||
(await option.findByCssSelector('label')).click();
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
public async checkTabIsSelected(chartType: string) {
|
||||
const chartTypeBtn = await this.testSubjects.find(`${chartType}TsvbTypeBtn`);
|
||||
const isSelected = await chartTypeBtn.getAttribute('aria-selected');
|
||||
|
@ -588,17 +594,11 @@ export class VisualBuilderPageObject extends FtrService {
|
|||
}
|
||||
|
||||
public async setDropLastBucket(value: boolean) {
|
||||
const option = await this.testSubjects.find(`metricsDropLastBucket-${value ? 'yes' : 'no'}`);
|
||||
(await option.findByCssSelector('label')).click();
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
await this.toggleYesNoSwitch('metricsDropLastBucket', value);
|
||||
}
|
||||
|
||||
public async setOverrideIndexPattern(value: boolean) {
|
||||
const option = await this.testSubjects.find(
|
||||
`seriesOverrideIndexPattern-${value ? 'yes' : 'no'}`
|
||||
);
|
||||
(await option.findByCssSelector('label')).click();
|
||||
await this.header.waitUntilLoadingHasFinished();
|
||||
await this.toggleYesNoSwitch('seriesOverrideIndexPattern', value);
|
||||
}
|
||||
|
||||
public async waitForIndexPatternTimeFieldOptionsLoaded() {
|
||||
|
@ -912,6 +912,10 @@ export class VisualBuilderPageObject extends FtrService {
|
|||
await this.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
public async setIgnoreFilters(value: boolean) {
|
||||
await this.toggleYesNoSwitch('ignore_global_filter', value);
|
||||
}
|
||||
|
||||
public async setMetricsDataTimerangeMode(value: string) {
|
||||
const dataTimeRangeMode = await this.testSubjects.find('dataTimeRangeMode');
|
||||
return await this.comboBox.setElement(dataTimeRangeMode, value);
|
||||
|
|
|
@ -542,6 +542,9 @@ describe('IndexPattern Data Source', () => {
|
|||
"type": "expression",
|
||||
},
|
||||
],
|
||||
"ignoreGlobalFilters": Array [
|
||||
false,
|
||||
],
|
||||
"index": Array [
|
||||
Object {
|
||||
"chain": Array [
|
||||
|
@ -1831,6 +1834,7 @@ describe('IndexPattern Data Source', () => {
|
|||
columns: {},
|
||||
sampling: 1,
|
||||
linkToLayers: ['link-to-id'],
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -1921,6 +1925,7 @@ describe('IndexPattern Data Source', () => {
|
|||
indexPatternId: '1',
|
||||
columnOrder: [],
|
||||
columns: {},
|
||||
ignoreGlobalFilters: false,
|
||||
linkToLayers: ['some-layer'],
|
||||
sampling: 1,
|
||||
},
|
||||
|
@ -1951,7 +1956,12 @@ describe('IndexPattern Data Source', () => {
|
|||
newState: {
|
||||
...state,
|
||||
layers: {
|
||||
first: { ...state.layers.first, linkToLayers: undefined, sampling: 1 },
|
||||
first: {
|
||||
...state.layers.first,
|
||||
linkToLayers: undefined,
|
||||
sampling: 1,
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -997,6 +997,7 @@ function blankLayer(indexPatternId: string, linkToLayers?: string[]): FormBasedL
|
|||
columns: {},
|
||||
columnOrder: [],
|
||||
sampling: 1,
|
||||
ignoreGlobalFilters: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1577,6 +1577,7 @@ describe('IndexPattern Data Source suggestions', () => {
|
|||
},
|
||||
],
|
||||
indexPatternId: '1',
|
||||
ignoreGlobalFilters: false,
|
||||
},
|
||||
] as Layer[];
|
||||
function stateWithoutLayer() {
|
||||
|
@ -2084,6 +2085,27 @@ describe('IndexPattern Data Source suggestions', () => {
|
|||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should repserve the ignoreGlobalFilter flag if set', () => {
|
||||
const updatedContext = [{ ...context[0], ignoreGlobalFilters: true }];
|
||||
const suggestions = getDatasourceSuggestionsForVisualizeCharts(
|
||||
stateWithoutLayer(),
|
||||
updatedContext,
|
||||
expectedIndexPatterns
|
||||
);
|
||||
|
||||
expect(suggestions).toContainEqual(
|
||||
expect.objectContaining({
|
||||
state: expect.objectContaining({
|
||||
layers: {
|
||||
test: expect.objectContaining({
|
||||
ignoreGlobalFilters: true,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getDatasourceSuggestionsForVisualizeField', () => {
|
||||
|
|
|
@ -312,6 +312,7 @@ function createNewLayerWithMetricAggregationFromVizEditor(
|
|||
) {
|
||||
const columns = convertToColumnChange(layer.columns, indexPattern);
|
||||
let newLayer: FormBasedLayer = {
|
||||
ignoreGlobalFilters: layer.ignoreGlobalFilters,
|
||||
indexPatternId: indexPattern.id,
|
||||
columns: {},
|
||||
columnOrder: [],
|
||||
|
|
|
@ -14,6 +14,7 @@ import { RandomSamplingSlider } from '@kbn/random-sampling';
|
|||
import type { DatasourceLayerSettingsProps } from '../../types';
|
||||
import type { FormBasedPrivateState } from './types';
|
||||
import { isSamplingValueEnabled } from './utils';
|
||||
import { IgnoreGlobalFilterRowControl } from '../../shared_components/ignore_global_filter';
|
||||
|
||||
const samplingValues = [0.00001, 0.0001, 0.001, 0.01, 0.1, 1];
|
||||
|
||||
|
@ -28,81 +29,95 @@ export function LayerSettingsPanel({
|
|||
: state.layers[layerId].sampling;
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
display="rowCompressed"
|
||||
data-test-subj="lns-indexPattern-random-sampling-row"
|
||||
fullWidth
|
||||
helpText={
|
||||
<>
|
||||
<EuiSpacer size="s" />
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.lens.indexPattern.randomSampling.help"
|
||||
defaultMessage="Lower sampling percentages increases the performance, but lowers the accuracy. Lower sampling percentages are best for large datasets. {link}"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/master/search-aggregations-random-sampler-aggregation.html"
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.lens.indexPattern.randomSampling.learnMore"
|
||||
defaultMessage="View documentation"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
label={
|
||||
<>
|
||||
{i18n.translate('xpack.lens.indexPattern.randomSampling.label', {
|
||||
defaultMessage: 'Sampling',
|
||||
})}{' '}
|
||||
<EuiToolTip
|
||||
content={i18n.translate('xpack.lens.indexPattern.randomSampling.experimentalLabel', {
|
||||
defaultMessage: 'Technical preview',
|
||||
})}
|
||||
>
|
||||
<EuiBetaBadge
|
||||
css={css`
|
||||
vertical-align: middle;
|
||||
`}
|
||||
iconType="beaker"
|
||||
label={i18n.translate('xpack.lens.indexPattern.randomSampling.experimentalLabel', {
|
||||
<>
|
||||
<EuiFormRow
|
||||
display="rowCompressed"
|
||||
data-test-subj="lns-indexPattern-random-sampling-row"
|
||||
fullWidth
|
||||
helpText={
|
||||
<>
|
||||
<EuiSpacer size="s" />
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.lens.indexPattern.randomSampling.help"
|
||||
defaultMessage="Lower sampling percentages increases the performance, but lowers the accuracy. Lower sampling percentages are best for large datasets. {link}"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/master/search-aggregations-random-sampler-aggregation.html"
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.lens.indexPattern.randomSampling.learnMore"
|
||||
defaultMessage="View documentation"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
label={
|
||||
<>
|
||||
{i18n.translate('xpack.lens.indexPattern.randomSampling.label', {
|
||||
defaultMessage: 'Sampling',
|
||||
})}{' '}
|
||||
<EuiToolTip
|
||||
content={i18n.translate('xpack.lens.indexPattern.randomSampling.experimentalLabel', {
|
||||
defaultMessage: 'Technical preview',
|
||||
})}
|
||||
size="s"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<RandomSamplingSlider
|
||||
disabled={isSamplingValueDisabled}
|
||||
disabledReason={i18n.translate('xpack.lens.indexPattern.randomSampling.disabledMessage', {
|
||||
defaultMessage:
|
||||
'In order to select a reduced sampling percentage, you must remove any maximum or minimum functions applied on this layer.',
|
||||
})}
|
||||
values={samplingValues}
|
||||
currentValue={currentValue}
|
||||
data-test-subj="lns-indexPattern-random-sampling-slider"
|
||||
onChange={(newSamplingValue) => {
|
||||
setState({
|
||||
...state,
|
||||
layers: {
|
||||
...state.layers,
|
||||
[layerId]: {
|
||||
...state.layers[layerId],
|
||||
sampling: newSamplingValue,
|
||||
>
|
||||
<EuiBetaBadge
|
||||
css={css`
|
||||
vertical-align: middle;
|
||||
`}
|
||||
iconType="beaker"
|
||||
label={i18n.translate('xpack.lens.indexPattern.randomSampling.experimentalLabel', {
|
||||
defaultMessage: 'Technical preview',
|
||||
})}
|
||||
size="s"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<RandomSamplingSlider
|
||||
disabled={isSamplingValueDisabled}
|
||||
disabledReason={i18n.translate('xpack.lens.indexPattern.randomSampling.disabledMessage', {
|
||||
defaultMessage:
|
||||
'In order to select a reduced sampling percentage, you must remove any maximum or minimum functions applied on this layer.',
|
||||
})}
|
||||
values={samplingValues}
|
||||
currentValue={currentValue}
|
||||
data-test-subj="lns-indexPattern-random-sampling-slider"
|
||||
onChange={(newSamplingValue) => {
|
||||
setState({
|
||||
...state,
|
||||
layers: {
|
||||
...state.layers,
|
||||
[layerId]: {
|
||||
...state.layers[layerId],
|
||||
sampling: newSamplingValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<IgnoreGlobalFilterRowControl
|
||||
checked={!state.layers[layerId].ignoreGlobalFilters}
|
||||
onChange={() => {
|
||||
const newLayer = {
|
||||
...state.layers[layerId],
|
||||
ignoreGlobalFilters: !state.layers[layerId].ignoreGlobalFilters,
|
||||
};
|
||||
const newLayers = { ...state.layers };
|
||||
newLayers[layerId] = newLayer;
|
||||
setState({ ...state, layers: newLayers });
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import type { DatasourceLayerPanelProps } from '../../types';
|
|||
import type { FormBasedPrivateState } from './types';
|
||||
import { ChangeIndexPattern } from '../../shared_components/dataview_picker/dataview_picker';
|
||||
import { getSamplingValue } from './utils';
|
||||
import { getIgnoreGlobalFilterIcon } from '../../shared_components/ignore_global_filter';
|
||||
|
||||
export interface FormBasedLayerPanelProps extends DatasourceLayerPanelProps<FormBasedPrivateState> {
|
||||
state: FormBasedPrivateState;
|
||||
|
@ -41,24 +42,28 @@ export function LayerPanel({
|
|||
});
|
||||
|
||||
const samplingValue = getSamplingValue(layer);
|
||||
const extraIconLabelProps =
|
||||
samplingValue !== 1
|
||||
? {
|
||||
icon: {
|
||||
component: (
|
||||
<RandomSamplingIcon color={euiTheme.colors.disabledText} fill="currentColor" />
|
||||
),
|
||||
value: `${samplingValue * 100}%`,
|
||||
tooltipValue: i18n.translate('xpack.lens.indexPattern.randomSamplingInfo', {
|
||||
defaultMessage: '{value}% sampling',
|
||||
values: {
|
||||
value: samplingValue * 100,
|
||||
},
|
||||
}),
|
||||
'data-test-subj': 'lnsChangeIndexPatternSamplingInfo',
|
||||
},
|
||||
}
|
||||
: {};
|
||||
const extraIcons = [];
|
||||
if (layer.ignoreGlobalFilters) {
|
||||
extraIcons.push(
|
||||
getIgnoreGlobalFilterIcon({
|
||||
color: euiTheme.colors.disabledText,
|
||||
dataTestSubj: 'lnsChangeIndexPatternIgnoringFilters',
|
||||
})
|
||||
);
|
||||
}
|
||||
if (samplingValue !== 1) {
|
||||
extraIcons.push({
|
||||
component: <RandomSamplingIcon color={euiTheme.colors.disabledText} fill="currentColor" />,
|
||||
value: `${samplingValue * 100}%`,
|
||||
tooltipValue: i18n.translate('xpack.lens.indexPattern.randomSamplingInfo', {
|
||||
defaultMessage: '{value}% sampling',
|
||||
values: {
|
||||
value: samplingValue * 100,
|
||||
},
|
||||
}),
|
||||
'data-test-subj': 'lnsChangeIndexPatternSamplingInfo',
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ChangeIndexPattern
|
||||
|
@ -69,7 +74,7 @@ export function LayerPanel({
|
|||
'data-test-subj': 'lns_layerIndexPatternLabel',
|
||||
size: 's',
|
||||
fontWeight: 'normal',
|
||||
...extraIconLabelProps,
|
||||
extraIcons,
|
||||
}}
|
||||
indexPatternId={layer.indexPatternId}
|
||||
indexPatternRefs={indexPatternRefs}
|
||||
|
|
|
@ -426,6 +426,7 @@ function getExpressionForLayer(
|
|||
timeFields: allDateHistogramFields,
|
||||
probability: getSamplingValue(layer),
|
||||
samplerSeed: seedrandom(searchSessionId).int32(),
|
||||
ignoreGlobalFilters: Boolean(layer.ignoreGlobalFilters),
|
||||
}).toAst(),
|
||||
{
|
||||
type: 'function',
|
||||
|
|
|
@ -55,6 +55,7 @@ export interface FormBasedLayer {
|
|||
// Partial columns represent the temporary invalid states
|
||||
incompleteColumns?: Record<string, IncompleteColumn>;
|
||||
sampling?: number;
|
||||
ignoreGlobalFilters?: boolean;
|
||||
}
|
||||
|
||||
export interface FormBasedPersistedState {
|
||||
|
|
|
@ -60,6 +60,7 @@ import { supportsRarityRanking } from './operations/definitions/terms';
|
|||
import { DEFAULT_MAX_DOC_COUNT } from './operations/definitions/terms/constants';
|
||||
import { getOriginalId } from '../../../common/expressions/datatable/transpose_helpers';
|
||||
import { ReducedSamplingSectionEntries } from './info_badges';
|
||||
import { IgnoredGlobalFiltersEntries } from '../../shared_components/ignore_global_filter';
|
||||
|
||||
function isMinOrMaxColumn(
|
||||
column?: GenericIndexPatternColumn
|
||||
|
@ -508,14 +509,13 @@ export function getNotifiableFeatures(
|
|||
if (!visualizationInfo) {
|
||||
return [];
|
||||
}
|
||||
const layersWithCustomSamplingValues = Object.entries(state.layers).filter(
|
||||
const features: UserMessage[] = [];
|
||||
const layers = Object.entries(state.layers);
|
||||
const layersWithCustomSamplingValues = layers.filter(
|
||||
([, layer]) => getSamplingValue(layer) !== 1
|
||||
);
|
||||
if (!layersWithCustomSamplingValues.length) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
if (layersWithCustomSamplingValues.length) {
|
||||
features.push({
|
||||
uniqueId: 'random_sampling_info',
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
|
@ -530,8 +530,32 @@ export function getNotifiableFeatures(
|
|||
/>
|
||||
),
|
||||
displayLocations: [{ id: 'embeddableBadge' }],
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
const layersWithIgnoreGlobalFilters = layers.filter(([, layer]) => layer.ignoreGlobalFilters);
|
||||
if (layersWithIgnoreGlobalFilters.length) {
|
||||
features.push({
|
||||
uniqueId: 'ignoring-global-filters-layers',
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
shortMessage: i18n.translate('xpack.lens.xyChart.layerAnnotationsIgnoreTitle', {
|
||||
defaultMessage: 'Layers ignoring global filters',
|
||||
}),
|
||||
longMessage: (
|
||||
<IgnoredGlobalFiltersEntries
|
||||
layers={layersWithIgnoreGlobalFilters.map(([layerId, { indexPatternId }]) => ({
|
||||
layerId,
|
||||
indexPatternId,
|
||||
}))}
|
||||
visualizationInfo={visualizationInfo}
|
||||
dataViews={frame.dataViews}
|
||||
/>
|
||||
),
|
||||
displayLocations: [{ id: 'embeddableBadge' }],
|
||||
});
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { EmbeddableFeatureBadge } from './embeddable_info_badges';
|
||||
import { UserMessage } from '../types';
|
||||
|
||||
describe('EmbeddableFeatureBadge', () => {
|
||||
function renderPopup(messages: UserMessage[], count: number = messages.length) {
|
||||
render(<EmbeddableFeatureBadge messages={messages} />);
|
||||
userEvent.click(screen.getByText(`${count}`));
|
||||
}
|
||||
|
||||
it('should render no badge', () => {
|
||||
render(<EmbeddableFeatureBadge messages={[]} />);
|
||||
expect(screen.queryByText('0')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the message in the popover', async () => {
|
||||
render(
|
||||
<EmbeddableFeatureBadge
|
||||
messages={[
|
||||
{
|
||||
shortMessage: 'Short message',
|
||||
longMessage: 'Long text',
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('1')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByText('1'));
|
||||
expect(await screen.findByText('Long text')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render a description of the badge in a tooltip on hover', async () => {
|
||||
renderPopup([
|
||||
{
|
||||
shortMessage: 'Short message',
|
||||
longMessage: 'Long text',
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
]);
|
||||
expect(await screen.findByText('1 visualization modifier')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render a separate section for each unique-id', async () => {
|
||||
renderPopup([
|
||||
{
|
||||
uniqueId: '1',
|
||||
shortMessage: 'Section1',
|
||||
longMessage: 'Long text',
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
{
|
||||
uniqueId: '2',
|
||||
shortMessage: 'Section2',
|
||||
longMessage: 'Long text 2',
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
]);
|
||||
expect(await screen.findByText('Section1')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Section2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should group multiple messages with same id', async () => {
|
||||
renderPopup(
|
||||
[
|
||||
{
|
||||
uniqueId: '1',
|
||||
shortMessage: 'Section1',
|
||||
longMessage: <div>LongText1</div>,
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
{
|
||||
uniqueId: '1',
|
||||
shortMessage: 'Section1',
|
||||
longMessage: <div>LongText2</div>,
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
],
|
||||
1 // messages are grouped
|
||||
);
|
||||
expect(await screen.findByText('Section1')).toBeInTheDocument();
|
||||
expect(await screen.findAllByText('Section1')).toHaveLength(1);
|
||||
expect(await screen.findAllByText('LongText', { exact: false })).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should render messages without id first, then grouped messages', async () => {
|
||||
renderPopup(
|
||||
[
|
||||
{
|
||||
shortMessage: 'Section2',
|
||||
longMessage: <div>AnotherText</div>,
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
{
|
||||
uniqueId: '1',
|
||||
shortMessage: 'Section1',
|
||||
longMessage: <div>LongText1</div>,
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
{
|
||||
uniqueId: '1',
|
||||
shortMessage: 'Section1',
|
||||
longMessage: <div>LongText2</div>,
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
],
|
||||
2 // last two messages are grouped
|
||||
);
|
||||
expect(await screen.findAllByText('Section', { exact: false })).toHaveLength(2);
|
||||
// now check the order
|
||||
const longMessages = await screen.findAllByText('Text', { exact: false });
|
||||
expect(longMessages[0]).toHaveTextContent('AnotherText');
|
||||
expect(longMessages[1]).toHaveTextContent('LongText1');
|
||||
});
|
||||
|
||||
describe('Horizontal rules', () => {
|
||||
it('should render no rule for single message', async () => {
|
||||
renderPopup([
|
||||
{
|
||||
shortMessage: `Section1`,
|
||||
longMessage: <div>hello</div>,
|
||||
severity: 'info',
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
]);
|
||||
expect(
|
||||
await screen.queryByTestId('lns-feature-badges-horizontal-rule')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
it('should apply an horizontal if there are multiple messages without id', async () => {
|
||||
const messages = [1, 2, 3].map((id) => ({
|
||||
shortMessage: `Section${id}`,
|
||||
longMessage: <div>{id}</div>,
|
||||
severity: 'info' as const,
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
}));
|
||||
renderPopup(messages);
|
||||
expect(await screen.getAllByTestId('lns-feature-badges-horizontal-rule')).toHaveLength(
|
||||
messages.length - 1
|
||||
);
|
||||
});
|
||||
|
||||
it('should apply a rule between messages without id and grouped ones', async () => {
|
||||
const messages = [
|
||||
{
|
||||
uniqueId: 'myId',
|
||||
shortMessage: `Section1`,
|
||||
longMessage: <div>Grouped</div>,
|
||||
severity: 'info' as const,
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
{
|
||||
shortMessage: `Section2`,
|
||||
longMessage: <div>NoId</div>,
|
||||
severity: 'info' as const,
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
];
|
||||
renderPopup(messages);
|
||||
expect(await screen.getAllByTestId('lns-feature-badges-horizontal-rule')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should apply rules taking into account grouped messages', async () => {
|
||||
const messages = [
|
||||
{
|
||||
shortMessage: `Section2`,
|
||||
longMessage: <div>NoId</div>,
|
||||
severity: 'info' as const,
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
// #1 rule here
|
||||
{
|
||||
uniqueId: 'myId',
|
||||
shortMessage: `Section1`,
|
||||
longMessage: <div>Grouped</div>,
|
||||
severity: 'info' as const,
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
{
|
||||
uniqueId: 'myId',
|
||||
shortMessage: `Section1`,
|
||||
longMessage: <div>Grouped 2</div>,
|
||||
severity: 'info' as const,
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
// #2 rule here
|
||||
{
|
||||
uniqueId: 'myOtherId',
|
||||
shortMessage: `Section2`,
|
||||
longMessage: <div>Grouped3</div>,
|
||||
severity: 'info' as const,
|
||||
fixableInEditor: false,
|
||||
displayLocations: [],
|
||||
},
|
||||
];
|
||||
renderPopup(messages, 3);
|
||||
expect(await screen.getAllByTestId('lns-feature-badges-horizontal-rule')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -16,7 +16,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import { useState } from 'react';
|
||||
import type { UserMessage } from '../types';
|
||||
import './embeddable_info_badges.scss';
|
||||
|
@ -36,6 +36,20 @@ export const EmbeddableFeatureBadge = ({ messages }: { messages: UserMessage[] }
|
|||
count: messages.length,
|
||||
},
|
||||
});
|
||||
|
||||
const messagesWithoutUniqueId = messages.filter(({ uniqueId }) => !uniqueId);
|
||||
// compact messages be grouping longMessage together on matching unique-id
|
||||
const messagesGroupedByUniqueId: Record<string, UserMessage[]> = {};
|
||||
for (const message of messages) {
|
||||
if (message.uniqueId) {
|
||||
if (!messagesGroupedByUniqueId[message.uniqueId]) {
|
||||
messagesGroupedByUniqueId[message.uniqueId] = [];
|
||||
}
|
||||
messagesGroupedByUniqueId[message.uniqueId].push(message);
|
||||
}
|
||||
}
|
||||
const messageCount =
|
||||
messagesWithoutUniqueId.length + Object.keys(messagesGroupedByUniqueId).length;
|
||||
return (
|
||||
<EuiPopover
|
||||
panelPaddingSize="none"
|
||||
|
@ -61,7 +75,7 @@ export const EmbeddableFeatureBadge = ({ messages }: { messages: UserMessage[] }
|
|||
`}
|
||||
iconType="wrench"
|
||||
>
|
||||
{messages.length}
|
||||
{messageCount}
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
}
|
||||
|
@ -72,13 +86,18 @@ export const EmbeddableFeatureBadge = ({ messages }: { messages: UserMessage[] }
|
|||
css={css`
|
||||
max-width: 280px;
|
||||
`}
|
||||
data-test-subj="lns-feature-badges-panel"
|
||||
>
|
||||
{messages.map(({ shortMessage, longMessage }, index) => {
|
||||
{messagesWithoutUniqueId.map(({ shortMessage, longMessage }, index) => {
|
||||
return (
|
||||
<>
|
||||
{index ? <EuiHorizontalRule margin="none" /> : null}
|
||||
<Fragment key={`${shortMessage}-${index}`}>
|
||||
{index ? (
|
||||
<EuiHorizontalRule
|
||||
margin="none"
|
||||
data-test-subj="lns-feature-badges-horizontal-rule"
|
||||
/>
|
||||
) : null}
|
||||
<aside
|
||||
key={`${shortMessage}-${index}`}
|
||||
css={css`
|
||||
padding: ${euiTheme.size.base};
|
||||
`}
|
||||
|
@ -88,7 +107,35 @@ export const EmbeddableFeatureBadge = ({ messages }: { messages: UserMessage[] }
|
|||
</EuiTitle>
|
||||
<ul className="lnsEmbeddablePanelFeatureList">{longMessage}</ul>
|
||||
</aside>
|
||||
</>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
{Object.entries(messagesGroupedByUniqueId).map(([uniqueId, messagesByUniqueId], index) => {
|
||||
const hasHorizontalRule = messagesWithoutUniqueId.length || index;
|
||||
const [{ shortMessage }] = messagesByUniqueId;
|
||||
return (
|
||||
<Fragment key={uniqueId}>
|
||||
{hasHorizontalRule ? (
|
||||
<EuiHorizontalRule
|
||||
margin="none"
|
||||
data-test-subj="lns-feature-badges-horizontal-rule"
|
||||
/>
|
||||
) : null}
|
||||
<aside
|
||||
css={css`
|
||||
padding: ${euiTheme.size.base};
|
||||
`}
|
||||
>
|
||||
<EuiTitle size="xxs" css={css`color=${euiTheme.colors.title}`}>
|
||||
<h3>{shortMessage}</h3>
|
||||
</EuiTitle>
|
||||
<ul className="lnsEmbeddablePanelFeatureList">
|
||||
{messagesByUniqueId.map(({ longMessage }, i) => (
|
||||
<Fragment key={`${uniqueId}-${i}`}>{longMessage}</Fragment>
|
||||
))}
|
||||
</ul>
|
||||
</aside>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import '@testing-library/jest-dom';
|
||||
import { TriggerButton } from './trigger';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
|
||||
describe('TriggerButton', () => {
|
||||
describe('base version (no icons)', () => {
|
||||
it('should render the basic button', () => {
|
||||
render(
|
||||
<TriggerButton togglePopover={jest.fn()} label={'Trigger label'} dataTestSubj="test-id" />
|
||||
);
|
||||
expect(screen.getByText('Trigger label')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the title if provided', () => {
|
||||
render(
|
||||
<TriggerButton
|
||||
togglePopover={jest.fn()}
|
||||
label={'Trigger'}
|
||||
dataTestSubj="test-id"
|
||||
title="My title"
|
||||
/>
|
||||
);
|
||||
expect(screen.getByTitle('My title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call the toggle callback on click', () => {
|
||||
const toggleFn = jest.fn();
|
||||
render(
|
||||
<TriggerButton
|
||||
togglePopover={toggleFn}
|
||||
label={'Trigger'}
|
||||
dataTestSubj="test-id"
|
||||
title="My title"
|
||||
/>
|
||||
);
|
||||
userEvent.click(screen.getByText('Trigger'));
|
||||
|
||||
expect(toggleFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render the main label as red if missing', () => {
|
||||
render(
|
||||
<TriggerButton
|
||||
togglePopover={jest.fn()}
|
||||
label={'Trigger'}
|
||||
dataTestSubj="test-id"
|
||||
title="My title"
|
||||
isMissingCurrent
|
||||
/>
|
||||
);
|
||||
// EUI danger red: rgb(171, 35, 28)
|
||||
expect(screen.getByTestId('test-id')).toHaveStyle({ color: 'rgb(171, 35, 28)' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('with icons', () => {
|
||||
it('should render one icon', () => {
|
||||
render(
|
||||
<TriggerButton
|
||||
togglePopover={jest.fn()}
|
||||
label={'Trigger label'}
|
||||
dataTestSubj="test-id"
|
||||
extraIcons={[
|
||||
{
|
||||
component: <EuiIcon type={'filterIgnore'} color={'red'} />,
|
||||
tooltipValue: 'Ignore global filters',
|
||||
'data-test-subj': 'ignore-global-filters',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('ignore-global-filters')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render multiple icons', () => {
|
||||
const indexes = [1, 2, 3];
|
||||
render(
|
||||
<TriggerButton
|
||||
togglePopover={jest.fn()}
|
||||
label={'Trigger label'}
|
||||
dataTestSubj="test-id"
|
||||
extraIcons={indexes.map((index) => ({
|
||||
component: <EuiIcon type={'filterIgnore'} color={'red'} />,
|
||||
tooltipValue: 'Ignore global filters',
|
||||
'data-test-subj': `ignore-global-filters-${index}`,
|
||||
}))}
|
||||
/>
|
||||
);
|
||||
|
||||
for (const index of indexes) {
|
||||
expect(screen.getByTestId(`ignore-global-filters-${index}`)).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
it('should render the value together with the provided component', () => {
|
||||
render(
|
||||
<TriggerButton
|
||||
togglePopover={jest.fn()}
|
||||
label={'Trigger label'}
|
||||
dataTestSubj="test-id"
|
||||
extraIcons={[
|
||||
{
|
||||
component: <EuiIcon type={'filterIgnore'} color={'red'} />,
|
||||
tooltipValue: 'Ignore global filters',
|
||||
'data-test-subj': 'ignore-global-filters',
|
||||
value: 'Ignore filters',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Ignore filters')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -12,12 +12,12 @@ import { ToolbarButton, ToolbarButtonProps } from './toolbar_button';
|
|||
|
||||
interface TriggerLabelProps {
|
||||
label: string;
|
||||
icon?: {
|
||||
extraIcons?: Array<{
|
||||
component: React.ReactElement;
|
||||
value?: string;
|
||||
tooltipValue?: string;
|
||||
'data-test-subj': string;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
export type ChangeIndexPatternTriggerProps = ToolbarButtonProps &
|
||||
|
@ -27,10 +27,10 @@ export type ChangeIndexPatternTriggerProps = ToolbarButtonProps &
|
|||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
function TriggerLabel({ label, icon }: TriggerLabelProps) {
|
||||
function TriggerLabel({ label, extraIcons }: TriggerLabelProps) {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
if (!icon) {
|
||||
if (!extraIcons?.length) {
|
||||
return <>{label}</>;
|
||||
}
|
||||
return (
|
||||
|
@ -44,28 +44,35 @@ function TriggerLabel({ label, icon }: TriggerLabelProps) {
|
|||
>
|
||||
{label}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
data-test-subj={icon['data-test-subj']}
|
||||
css={css`
|
||||
display: block;
|
||||
*:hover &,
|
||||
*:focus & {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<EuiToolTip content={icon.tooltipValue} position="top">
|
||||
<EuiFlexGroup alignItems="center" gutterSize="xs" responsive={false}>
|
||||
<EuiFlexItem grow={false}>{icon.component}</EuiFlexItem>
|
||||
{icon.value ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTextColor color={euiTheme.colors.disabledText}>{icon.value}</EuiTextColor>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
{extraIcons.map((icon) => (
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
data-test-subj={icon['data-test-subj']}
|
||||
css={css`
|
||||
display: block;
|
||||
*:hover &,
|
||||
*:focus & {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
`}
|
||||
key={icon['data-test-subj']}
|
||||
>
|
||||
<EuiToolTip
|
||||
content={icon.tooltipValue}
|
||||
position="top"
|
||||
data-test-subj={`${icon['data-test-subj']}-tooltip`}
|
||||
>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="xs" responsive={false}>
|
||||
<EuiFlexItem grow={false}>{icon.component}</EuiFlexItem>
|
||||
{icon.value ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTextColor color={euiTheme.colors.disabledText}>{icon.value}</EuiTextColor>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
@ -75,7 +82,7 @@ export function TriggerButton({
|
|||
title,
|
||||
togglePopover,
|
||||
isMissingCurrent,
|
||||
icon,
|
||||
extraIcons,
|
||||
...rest
|
||||
}: ChangeIndexPatternTriggerProps & {
|
||||
togglePopover: () => void;
|
||||
|
@ -96,7 +103,7 @@ export function TriggerButton({
|
|||
{...rest}
|
||||
textProps={{ style: { width: '100%' } }}
|
||||
>
|
||||
<TriggerLabel label={label} icon={icon} />
|
||||
<TriggerLabel label={label} extraIcons={extraIcons} />
|
||||
</ToolbarButton>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 { EuiIcon } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Retrieve the IgnoreGlobalfilter shared icon to be put into the dataViewPicker within a Layer panel
|
||||
* @param dataTestSubj test id to be applied
|
||||
* @returns
|
||||
*/
|
||||
export const getIgnoreGlobalFilterIcon = ({
|
||||
color,
|
||||
dataTestSubj,
|
||||
}: {
|
||||
color: string;
|
||||
dataTestSubj: string;
|
||||
}) => {
|
||||
return {
|
||||
component: (
|
||||
<EuiIcon
|
||||
type={'filterIgnore'}
|
||||
color={color}
|
||||
css={css`
|
||||
margin-top: 15px;
|
||||
`}
|
||||
/>
|
||||
),
|
||||
tooltipValue: i18n.translate('xpack.lens.layerPanel.ignoreGlobalFilters', {
|
||||
defaultMessage: 'Ignore global filters',
|
||||
}),
|
||||
'data-test-subj': dataTestSubj,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { getIgnoreGlobalFilterIcon } from './data_view_picker_icon';
|
||||
export { IgnoredGlobalFiltersEntries } from './info_badge';
|
||||
export { IgnoreGlobalFilterRowControl } from './settings_control';
|
|
@ -7,16 +7,15 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { InfoBadge } from '../../shared_components/info_badges/info_badge';
|
||||
import { InfoBadge } from '../info_badges/info_badge';
|
||||
import { FramePublicAPI, VisualizationInfo } from '../../types';
|
||||
import { XYAnnotationLayerConfig } from './types';
|
||||
|
||||
export function IgnoredGlobalFiltersEntries({
|
||||
layers,
|
||||
visualizationInfo,
|
||||
dataViews,
|
||||
}: {
|
||||
layers: XYAnnotationLayerConfig[];
|
||||
layers: Array<{ layerId: string; indexPatternId: string }>;
|
||||
visualizationInfo: VisualizationInfo;
|
||||
dataViews: FramePublicAPI['dataViews'];
|
||||
}) {
|
||||
|
@ -27,8 +26,8 @@ export function IgnoredGlobalFiltersEntries({
|
|||
const layerInfo = visualizationInfo.layers.find(({ layerId }) => layerId === layer.layerId);
|
||||
const layerTitle =
|
||||
layerInfo?.label ||
|
||||
i18n.translate('xpack.lens.xyChart.layerAnnotationsLabel', {
|
||||
defaultMessage: 'Annotations',
|
||||
i18n.translate('xpack.lens.layerTitle.fallbackLabel', {
|
||||
defaultMessage: 'Layer',
|
||||
});
|
||||
const layerPalette = layerInfo?.palette;
|
||||
return (
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiFormRow, EuiSwitch } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
|
||||
export function IgnoreGlobalFilterRowControl({
|
||||
checked,
|
||||
onChange,
|
||||
}: {
|
||||
checked: boolean;
|
||||
onChange: (newValue: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<EuiFormRow
|
||||
display="columnCompressedSwitch"
|
||||
label={i18n.translate('xpack.lens.layerSettings.ignoreGlobalFilters', {
|
||||
defaultMessage: 'Use global filters',
|
||||
})}
|
||||
>
|
||||
<EuiSwitch
|
||||
label={i18n.translate('xpack.lens.layerSettings.ignoreGlobalFilters', {
|
||||
defaultMessage: 'Use global filters',
|
||||
})}
|
||||
showLabel={false}
|
||||
checked={checked}
|
||||
data-test-subj="lns-layerSettings-ignoreGlobalFilters"
|
||||
onChange={() => onChange(!checked)}
|
||||
compressed
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
}
|
|
@ -5,9 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiFormRow, EuiSwitch } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { IgnoreGlobalFilterRowControl } from '../../shared_components/ignore_global_filter';
|
||||
import type { VisualizationLayerSettingsProps } from '../../types';
|
||||
import type { XYState } from './types';
|
||||
import { isAnnotationsLayer } from './visualization_helpers';
|
||||
|
@ -26,28 +25,15 @@ export function LayerSettings({
|
|||
return null;
|
||||
}
|
||||
return (
|
||||
<EuiFormRow
|
||||
display="columnCompressedSwitch"
|
||||
label={i18n.translate('xpack.lens.xyChart.ignoreGlobalFilters', {
|
||||
defaultMessage: 'Use global filters',
|
||||
})}
|
||||
>
|
||||
<EuiSwitch
|
||||
label={i18n.translate('xpack.lens.xyChart.ignoreGlobalFilters', {
|
||||
defaultMessage: 'Use global filters',
|
||||
})}
|
||||
showLabel={false}
|
||||
checked={!layer.ignoreGlobalFilters}
|
||||
data-test-subj="lnsXY-layerSettings-ignoreGlobalFilters"
|
||||
onChange={() => {
|
||||
const layerIndex = state.layers.findIndex((l) => l === layer);
|
||||
const newLayer = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters };
|
||||
const newLayers = [...state.layers];
|
||||
newLayers[layerIndex] = newLayer;
|
||||
setState({ ...state, layers: newLayers });
|
||||
}}
|
||||
compressed
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<IgnoreGlobalFilterRowControl
|
||||
checked={!layer.ignoreGlobalFilters}
|
||||
onChange={() => {
|
||||
const layerIndex = state.layers.findIndex((l) => l === layer);
|
||||
const newLayer = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters };
|
||||
const newLayers = [...state.layers];
|
||||
newLayers[layerIndex] = newLayer;
|
||||
setState({ ...state, layers: newLayers });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -110,8 +110,8 @@ import { defaultAnnotationLabel } from './annotations/helpers';
|
|||
import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils';
|
||||
import { createAnnotationActions } from './annotations/actions';
|
||||
import { AddLayerButton } from './add_layer';
|
||||
import { IgnoredGlobalFiltersEntries } from './info_badges';
|
||||
import { LayerSettings } from './layer_settings';
|
||||
import { IgnoredGlobalFiltersEntries } from '../../shared_components/ignore_global_filter';
|
||||
|
||||
const XY_ID = 'lnsXY';
|
||||
|
||||
|
@ -1187,7 +1187,10 @@ function getNotifiableFeatures(
|
|||
}),
|
||||
longMessage: (
|
||||
<IgnoredGlobalFiltersEntries
|
||||
layers={annotationsWithIgnoreFlag}
|
||||
layers={annotationsWithIgnoreFlag.map(({ layerId, indexPatternId }) => ({
|
||||
layerId,
|
||||
indexPatternId,
|
||||
}))}
|
||||
visualizationInfo={visualizationInfo}
|
||||
dataViews={frame.dataViews}
|
||||
/>
|
||||
|
|
|
@ -18,8 +18,8 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { ToolbarButton } from '@kbn/kibana-react-plugin/public';
|
||||
import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons';
|
||||
import { css } from '@emotion/react';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { getIgnoreGlobalFilterIcon } from '../../../shared_components/ignore_global_filter/data_view_picker_icon';
|
||||
import type {
|
||||
VisualizationLayerHeaderContentProps,
|
||||
VisualizationLayerWidgetProps,
|
||||
|
@ -123,25 +123,6 @@ function AnnotationLayerHeaderContent({
|
|||
const layer = state.layers[layerIndex] as XYAnnotationLayerConfig;
|
||||
const currentIndexPattern = frame.dataViews.indexPatterns[layer.indexPatternId];
|
||||
|
||||
const extraIconLabelProps = !layer.ignoreGlobalFilters
|
||||
? {}
|
||||
: {
|
||||
icon: {
|
||||
component: (
|
||||
<EuiIcon
|
||||
type={'filterIgnore'}
|
||||
color={euiTheme.colors.disabledText}
|
||||
css={css`
|
||||
margin-top: 15px;
|
||||
`}
|
||||
/>
|
||||
),
|
||||
tooltipValue: i18n.translate('xpack.lens.layerPanel.ignoreGlobalFilters', {
|
||||
defaultMessage: 'Ignore global filters',
|
||||
}),
|
||||
'data-test-subj': 'lnsChangeIndexPatternIgnoringFilters',
|
||||
},
|
||||
};
|
||||
return (
|
||||
<ChangeIndexPattern
|
||||
data-test-subj="indexPattern-switcher"
|
||||
|
@ -151,7 +132,14 @@ function AnnotationLayerHeaderContent({
|
|||
'data-test-subj': 'lns_layerIndexPatternLabel',
|
||||
size: 's',
|
||||
fontWeight: 'normal',
|
||||
...extraIconLabelProps,
|
||||
extraIcons: layer.ignoreGlobalFilters
|
||||
? [
|
||||
getIgnoreGlobalFilterIcon({
|
||||
color: euiTheme.colors.disabledText,
|
||||
dataTestSubj: 'lnsChangeIndexPatternIgnoringFilters',
|
||||
}),
|
||||
]
|
||||
: undefined,
|
||||
}}
|
||||
indexPatternId={layer.indexPatternId}
|
||||
indexPatternRefs={frame.dataViews.indexPatternRefs}
|
||||
|
|
|
@ -21219,7 +21219,6 @@
|
|||
"xpack.lens.xyChart.iconSelect.starLabel": "Étoile",
|
||||
"xpack.lens.xyChart.iconSelect.tagIconLabel": "Balise",
|
||||
"xpack.lens.xyChart.iconSelect.triangleIconLabel": "Triangle",
|
||||
"xpack.lens.xyChart.ignoreGlobalFilters": "Utiliser les filtres globaux",
|
||||
"xpack.lens.xyChart.layerAnnotation": "Annotation",
|
||||
"xpack.lens.xyChart.layerAnnotationsIgnoreTitle": "Calques ignorant les filtres globaux",
|
||||
"xpack.lens.xyChart.layerAnnotationsLabel": "Annotations",
|
||||
|
|
|
@ -21219,7 +21219,6 @@
|
|||
"xpack.lens.xyChart.iconSelect.starLabel": "星",
|
||||
"xpack.lens.xyChart.iconSelect.tagIconLabel": "タグ",
|
||||
"xpack.lens.xyChart.iconSelect.triangleIconLabel": "三角形",
|
||||
"xpack.lens.xyChart.ignoreGlobalFilters": "グローバルフィルターを使用",
|
||||
"xpack.lens.xyChart.layerAnnotation": "注釈",
|
||||
"xpack.lens.xyChart.layerAnnotationsIgnoreTitle": "グローバルフィルターを無視するレイヤー",
|
||||
"xpack.lens.xyChart.layerAnnotationsLabel": "注釈",
|
||||
|
|
|
@ -21219,7 +21219,6 @@
|
|||
"xpack.lens.xyChart.iconSelect.starLabel": "五角星",
|
||||
"xpack.lens.xyChart.iconSelect.tagIconLabel": "标签",
|
||||
"xpack.lens.xyChart.iconSelect.triangleIconLabel": "三角形",
|
||||
"xpack.lens.xyChart.ignoreGlobalFilters": "使用全局筛选",
|
||||
"xpack.lens.xyChart.layerAnnotation": "标注",
|
||||
"xpack.lens.xyChart.layerAnnotationsIgnoreTitle": "忽略全局筛选的图层",
|
||||
"xpack.lens.xyChart.layerAnnotationsLabel": "标注",
|
||||
|
|
|
@ -50,6 +50,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(await testSubjects.getVisibleText('lnsChangeIndexPatternSamplingInfo')).to.be('1%');
|
||||
});
|
||||
|
||||
it('should expose the ignore global filters control for a data layer', async () => {
|
||||
await PageObjects.lens.openLayerContextMenu();
|
||||
expect(
|
||||
await testSubjects.exists('lns-layerPanel-0 > lnsChangeIndexPatternIgnoringFilters')
|
||||
).to.be(false);
|
||||
// click on open layer settings
|
||||
await testSubjects.click('lnsLayerSettings');
|
||||
// annotations settings have only ignore filters
|
||||
await testSubjects.click('lns-layerSettings-ignoreGlobalFilters');
|
||||
expect(
|
||||
await testSubjects.exists('lns-layerPanel-0 > lnsChangeIndexPatternIgnoringFilters')
|
||||
).to.be(true);
|
||||
await testSubjects.click('lns-indexPattern-dimensionContainerBack');
|
||||
});
|
||||
|
||||
it('should add an annotation layer and settings shoud be available with ignore filters', async () => {
|
||||
// configure a date histogram
|
||||
await PageObjects.lens.configureDimension({
|
||||
|
@ -66,15 +81,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
// add annotation layer
|
||||
await PageObjects.lens.createLayer('annotations');
|
||||
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
expect(
|
||||
await testSubjects.exists('lns-layerPanel-1 > lnsChangeIndexPatternIgnoringFilters')
|
||||
).to.be(true);
|
||||
|
||||
await PageObjects.lens.openLayerContextMenu(1);
|
||||
await testSubjects.click('lnsLayerSettings');
|
||||
// annotations settings have only ignore filters
|
||||
await testSubjects.click('lnsXY-layerSettings-ignoreGlobalFilters');
|
||||
await testSubjects.click('lns-layerSettings-ignoreGlobalFilters');
|
||||
// now close the panel and check the dataView picker has no icon
|
||||
await testSubjects.click('lns-indexPattern-dimensionContainerBack');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(false);
|
||||
expect(
|
||||
await testSubjects.exists('lns-layerPanel-1 > lnsChangeIndexPatternIgnoringFilters')
|
||||
).to.be(false);
|
||||
});
|
||||
|
||||
it('should add a new visualization layer and disable the sampling if max operation is chosen', async () => {
|
||||
|
@ -118,6 +137,29 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await testSubjects.click('lns-indexPattern-dimensionContainerBack');
|
||||
});
|
||||
|
||||
it('should expose sampling and ignore filters settings for reference lines layer', async () => {
|
||||
await PageObjects.lens.createLayer('referenceLine');
|
||||
|
||||
await PageObjects.lens.openLayerContextMenu(3);
|
||||
// click on open layer settings
|
||||
await testSubjects.click('lnsLayerSettings');
|
||||
// random sampling available
|
||||
await testSubjects.existOrFail('lns-indexPattern-random-sampling-row');
|
||||
// tweak the value
|
||||
await PageObjects.lens.dragRangeInput('lns-indexPattern-random-sampling-slider', 2, 'left');
|
||||
// annotations settings have only ignore filters
|
||||
await testSubjects.click('lns-layerSettings-ignoreGlobalFilters');
|
||||
await testSubjects.click('lns-indexPattern-dimensionContainerBack');
|
||||
// Check both sampling and ignore filters are now present
|
||||
await testSubjects.existOrFail('lnsChangeIndexPatternSamplingInfo');
|
||||
expect(
|
||||
await testSubjects.getVisibleText('lns-layerPanel-3 > lnsChangeIndexPatternSamplingInfo')
|
||||
).to.be('1%');
|
||||
expect(
|
||||
await testSubjects.exists('lns-layerPanel-3 > lnsChangeIndexPatternIgnoringFilters')
|
||||
).to.be(true);
|
||||
});
|
||||
|
||||
it('should switch to pie chart and have layer settings available', async () => {
|
||||
await PageObjects.lens.switchToVisualization('pie');
|
||||
await PageObjects.lens.openLayerContextMenu();
|
||||
|
|
|
@ -126,5 +126,23 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at series level over', async () => {
|
||||
await visualBuilder.clickSeriesOption();
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('mtrVis');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at panel level over', async () => {
|
||||
await visualBuilder.clickPanelOptions('gauge');
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('mtrVis');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -127,5 +127,23 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at series level over', async () => {
|
||||
await visualBuilder.clickSeriesOption();
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('mtrVis');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at panel level over', async () => {
|
||||
await visualBuilder.clickPanelOptions('metric');
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('mtrVis');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -217,5 +217,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at panel level over', async () => {
|
||||
await visualBuilder.clickPanelOptions('table');
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('lnsDataTable');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -187,5 +187,23 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
expect(await dimensions[2].getVisibleText()).to.eql('Top 10 values of extension.raw');
|
||||
});
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at series level over', async () => {
|
||||
await visualBuilder.clickSeriesOption();
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('xyVisChart');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at panel level over', async () => {
|
||||
await visualBuilder.clickPanelOptions('timeSeries');
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('xyVisChart');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -179,5 +179,23 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
expect(await queryBar.getQueryString()).to.equal('machine.os : ios');
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at series level over', async () => {
|
||||
await visualBuilder.clickSeriesOption();
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('xyVisChart');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
|
||||
it('should bring the ignore global filters configured at panel level over', async () => {
|
||||
await visualBuilder.clickPanelOptions('topN');
|
||||
await visualBuilder.setIgnoreFilters(true);
|
||||
await header.waitUntilLoadingHasFinished();
|
||||
await visualize.navigateToLensFromAnotherVisulization();
|
||||
await lens.waitForVisualization('xyVisChart');
|
||||
expect(await testSubjects.exists('lnsChangeIndexPatternIgnoringFilters')).to.be(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue