mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
fix current field candidates list
This commit is contained in:
parent
26cd1a53df
commit
632b711ca1
4 changed files with 59 additions and 43 deletions
|
@ -9,14 +9,16 @@ import { httpServiceMock } from '@kbn/core/public/mocks';
|
||||||
|
|
||||||
import type { FetchFieldCandidatesResponse } from '../queries/fetch_field_candidates';
|
import type { FetchFieldCandidatesResponse } from '../queries/fetch_field_candidates';
|
||||||
|
|
||||||
import { fetchFieldCandidates } from './log_rate_analysis_field_candidates_slice';
|
import { fetchFieldCandidates, getDefaultState } from './log_rate_analysis_field_candidates_slice';
|
||||||
|
|
||||||
const mockHttp = httpServiceMock.createStartContract();
|
const mockHttp = httpServiceMock.createStartContract();
|
||||||
|
|
||||||
describe('fetchFieldCandidates', () => {
|
describe('fetchFieldCandidates', () => {
|
||||||
it('dispatches field candidates', async () => {
|
it('dispatches field candidates', async () => {
|
||||||
const mockDispatch = jest.fn();
|
const mockDispatch = jest.fn();
|
||||||
const mockGetState = jest.fn();
|
const mockGetState = jest.fn().mockReturnValue({
|
||||||
|
logRateAnalysisFieldCandidates: getDefaultState(),
|
||||||
|
});
|
||||||
|
|
||||||
const mockResponse: FetchFieldCandidatesResponse = {
|
const mockResponse: FetchFieldCandidatesResponse = {
|
||||||
isECS: false,
|
isECS: false,
|
||||||
|
@ -60,7 +62,12 @@ describe('fetchFieldCandidates', () => {
|
||||||
payload: {
|
payload: {
|
||||||
fieldSelectionMessage:
|
fieldSelectionMessage:
|
||||||
'2 out of 5 fields were preselected for the analysis. Use the "Fields" dropdown to adjust the selection.',
|
'2 out of 5 fields were preselected for the analysis. Use the "Fields" dropdown to adjust the selection.',
|
||||||
fieldFilterSkippedItems: [
|
initialFieldFilterSkippedItems: [
|
||||||
|
'another-keyword-field',
|
||||||
|
'another-text-field',
|
||||||
|
'yet-another-text-field',
|
||||||
|
],
|
||||||
|
currentFieldFilterSkippedItems: [
|
||||||
'another-keyword-field',
|
'another-keyword-field',
|
||||||
'another-text-field',
|
'another-text-field',
|
||||||
'yet-another-text-field',
|
'yet-another-text-field',
|
||||||
|
|
|
@ -90,10 +90,14 @@ export const fetchFieldCandidates = createAsyncThunk(
|
||||||
...selectedKeywordFieldCandidates,
|
...selectedKeywordFieldCandidates,
|
||||||
...selectedTextFieldCandidates,
|
...selectedTextFieldCandidates,
|
||||||
];
|
];
|
||||||
const fieldFilterSkippedItems = fieldFilterUniqueItems.filter(
|
const initialFieldFilterSkippedItems = fieldFilterUniqueItems.filter(
|
||||||
(d) => !fieldFilterUniqueSelectedItems.includes(d)
|
(d) => !fieldFilterUniqueSelectedItems.includes(d)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const currentFieldFilterSkippedItems = (
|
||||||
|
thunkApi.getState() as { logRateAnalysisFieldCandidates: FieldCandidatesState }
|
||||||
|
).logRateAnalysisFieldCandidates.currentFieldFilterSkippedItems;
|
||||||
|
|
||||||
thunkApi.dispatch(
|
thunkApi.dispatch(
|
||||||
setAllFieldCandidates({
|
setAllFieldCandidates({
|
||||||
fieldSelectionMessage: getFieldSelectionMessage(
|
fieldSelectionMessage: getFieldSelectionMessage(
|
||||||
|
@ -102,7 +106,13 @@ export const fetchFieldCandidates = createAsyncThunk(
|
||||||
fieldFilterUniqueSelectedItems.length
|
fieldFilterUniqueSelectedItems.length
|
||||||
),
|
),
|
||||||
fieldFilterUniqueItems,
|
fieldFilterUniqueItems,
|
||||||
fieldFilterSkippedItems,
|
initialFieldFilterSkippedItems,
|
||||||
|
// If the currentFieldFilterSkippedItems is null, we're on the first load,
|
||||||
|
// only then we set the current skipped fields to the initial skipped fields.
|
||||||
|
currentFieldFilterSkippedItems:
|
||||||
|
currentFieldFilterSkippedItems === null && initialFieldFilterSkippedItems.length > 0
|
||||||
|
? initialFieldFilterSkippedItems
|
||||||
|
: currentFieldFilterSkippedItems,
|
||||||
keywordFieldCandidates,
|
keywordFieldCandidates,
|
||||||
textFieldCandidates,
|
textFieldCandidates,
|
||||||
selectedKeywordFieldCandidates,
|
selectedKeywordFieldCandidates,
|
||||||
|
@ -116,18 +126,20 @@ export interface FieldCandidatesState {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
fieldSelectionMessage?: string;
|
fieldSelectionMessage?: string;
|
||||||
fieldFilterUniqueItems: string[];
|
fieldFilterUniqueItems: string[];
|
||||||
fieldFilterSkippedItems: string[];
|
initialFieldFilterSkippedItems: string[];
|
||||||
|
currentFieldFilterSkippedItems: string[] | null;
|
||||||
keywordFieldCandidates: string[];
|
keywordFieldCandidates: string[];
|
||||||
textFieldCandidates: string[];
|
textFieldCandidates: string[];
|
||||||
selectedKeywordFieldCandidates: string[];
|
selectedKeywordFieldCandidates: string[];
|
||||||
selectedTextFieldCandidates: string[];
|
selectedTextFieldCandidates: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultState(): FieldCandidatesState {
|
export function getDefaultState(): FieldCandidatesState {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
fieldFilterUniqueItems: [],
|
fieldFilterUniqueItems: [],
|
||||||
fieldFilterSkippedItems: [],
|
initialFieldFilterSkippedItems: [],
|
||||||
|
currentFieldFilterSkippedItems: null,
|
||||||
keywordFieldCandidates: [],
|
keywordFieldCandidates: [],
|
||||||
textFieldCandidates: [],
|
textFieldCandidates: [],
|
||||||
selectedKeywordFieldCandidates: [],
|
selectedKeywordFieldCandidates: [],
|
||||||
|
@ -145,6 +157,12 @@ export const logRateAnalysisFieldCandidatesSlice = createSlice({
|
||||||
) => {
|
) => {
|
||||||
return { ...state, ...action.payload };
|
return { ...state, ...action.payload };
|
||||||
},
|
},
|
||||||
|
setCurrentFieldFilterSkippedItems: (
|
||||||
|
state: FieldCandidatesState,
|
||||||
|
action: PayloadAction<string[]>
|
||||||
|
) => {
|
||||||
|
return { ...state, currentFieldFilterSkippedItems: action.payload };
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(fetchFieldCandidates.pending, (state) => {
|
builder.addCase(fetchFieldCandidates.pending, (state) => {
|
||||||
|
@ -157,4 +175,5 @@ export const logRateAnalysisFieldCandidatesSlice = createSlice({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Action creators are generated for each case reducer function
|
// Action creators are generated for each case reducer function
|
||||||
export const { setAllFieldCandidates } = logRateAnalysisFieldCandidatesSlice.actions;
|
export const { setAllFieldCandidates, setCurrentFieldFilterSkippedItems } =
|
||||||
|
logRateAnalysisFieldCandidatesSlice.actions;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
|
import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import {
|
||||||
setSkippedColumns,
|
setSkippedColumns,
|
||||||
type LogRateAnalysisResultsTableColumnName,
|
type LogRateAnalysisResultsTableColumnName,
|
||||||
} from '@kbn/aiops-log-rate-analysis/state/log_rate_analysis_table_slice';
|
} from '@kbn/aiops-log-rate-analysis/state/log_rate_analysis_table_slice';
|
||||||
|
import { setCurrentFieldFilterSkippedItems } from '@kbn/aiops-log-rate-analysis/state/log_rate_analysis_field_candidates_slice';
|
||||||
|
|
||||||
import { ItemFilterPopover as FieldFilterPopover } from './item_filter_popover';
|
import { ItemFilterPopover as FieldFilterPopover } from './item_filter_popover';
|
||||||
import { ItemFilterPopover as ColumnFilterPopover } from './item_filter_popover';
|
import { ItemFilterPopover as ColumnFilterPopover } from './item_filter_popover';
|
||||||
|
@ -82,13 +83,11 @@ const resultsGroupedOnId = 'aiopsLogRateAnalysisGroupingOn';
|
||||||
export interface LogRateAnalysisOptionsProps {
|
export interface LogRateAnalysisOptionsProps {
|
||||||
foundGroups: boolean;
|
foundGroups: boolean;
|
||||||
growFirstItem?: boolean;
|
growFirstItem?: boolean;
|
||||||
onFieldsFilterChange: (skippedFieldsUpdate: string[]) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LogRateAnalysisOptions: FC<LogRateAnalysisOptionsProps> = ({
|
export const LogRateAnalysisOptions: FC<LogRateAnalysisOptionsProps> = ({
|
||||||
foundGroups,
|
foundGroups,
|
||||||
growFirstItem = false,
|
growFirstItem = false,
|
||||||
onFieldsFilterChange,
|
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
@ -96,20 +95,11 @@ export const LogRateAnalysisOptions: FC<LogRateAnalysisOptionsProps> = ({
|
||||||
const { isRunning } = useAppSelector((s) => s.logRateAnalysisStream);
|
const { isRunning } = useAppSelector((s) => s.logRateAnalysisStream);
|
||||||
const fieldCandidates = useAppSelector((s) => s.logRateAnalysisFieldCandidates);
|
const fieldCandidates = useAppSelector((s) => s.logRateAnalysisFieldCandidates);
|
||||||
const { skippedColumns } = useAppSelector((s) => s.logRateAnalysisTable);
|
const { skippedColumns } = useAppSelector((s) => s.logRateAnalysisTable);
|
||||||
const { fieldFilterUniqueItems, fieldFilterSkippedItems } = fieldCandidates;
|
const { fieldFilterUniqueItems, initialFieldFilterSkippedItems } = fieldCandidates;
|
||||||
const fieldFilterButtonDisabled =
|
const fieldFilterButtonDisabled =
|
||||||
isRunning || fieldCandidates.isLoading || fieldFilterUniqueItems.length === 0;
|
isRunning || fieldCandidates.isLoading || fieldFilterUniqueItems.length === 0;
|
||||||
const toggleIdSelected = groupResults ? resultsGroupedOnId : resultsGroupedOffId;
|
const toggleIdSelected = groupResults ? resultsGroupedOnId : resultsGroupedOffId;
|
||||||
|
|
||||||
// null is used as the uninitialized state to identify the first load.
|
|
||||||
const [skippedFields, setSkippedFields] = useState<string[] | null>(null);
|
|
||||||
|
|
||||||
// Set skipped fields only on first load, otherwise we'd overwrite the user's selection.
|
|
||||||
useEffect(() => {
|
|
||||||
if (skippedFields === null && fieldFilterSkippedItems.length > 0)
|
|
||||||
setSkippedFields(fieldFilterSkippedItems);
|
|
||||||
}, [fieldFilterSkippedItems, skippedFields]);
|
|
||||||
|
|
||||||
const onGroupResultsToggle = (optionId: string) => {
|
const onGroupResultsToggle = (optionId: string) => {
|
||||||
dispatch(setGroupResults(optionId === resultsGroupedOnId));
|
dispatch(setGroupResults(optionId === resultsGroupedOnId));
|
||||||
// When toggling the group switch, clear all row selections
|
// When toggling the group switch, clear all row selections
|
||||||
|
@ -120,9 +110,8 @@ export const LogRateAnalysisOptions: FC<LogRateAnalysisOptionsProps> = ({
|
||||||
dispatch(setSkippedColumns(columns));
|
dispatch(setSkippedColumns(columns));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFieldsFilterChangeHandler = (skippedFieldsUpdate: string[]) => {
|
const onFieldsFilterChange = (skippedFieldsUpdate: string[]) => {
|
||||||
setSkippedFields(skippedFieldsUpdate);
|
dispatch(setCurrentFieldFilterSkippedItems(skippedFieldsUpdate));
|
||||||
onFieldsFilterChange(skippedFieldsUpdate);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Disable the grouping switch toggle only if no groups were found,
|
// Disable the grouping switch toggle only if no groups were found,
|
||||||
|
@ -173,8 +162,8 @@ export const LogRateAnalysisOptions: FC<LogRateAnalysisOptionsProps> = ({
|
||||||
popoverButtonTitle={fieldsButton}
|
popoverButtonTitle={fieldsButton}
|
||||||
selectedItemLimit={1}
|
selectedItemLimit={1}
|
||||||
uniqueItemNames={fieldFilterUniqueItems}
|
uniqueItemNames={fieldFilterUniqueItems}
|
||||||
initialSkippedItems={fieldFilterSkippedItems}
|
initialSkippedItems={initialFieldFilterSkippedItems}
|
||||||
onChange={onFieldsFilterChangeHandler}
|
onChange={onFieldsFilterChange}
|
||||||
/>
|
/>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
|
|
|
@ -135,21 +135,27 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
||||||
setEmbeddableOptionsVisible((s) => !s);
|
setEmbeddableOptionsVisible((s) => !s);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFieldsFilterChange = (skippedFieldsUpdate: string[]) => {
|
const { currentFieldFilterSkippedItems, keywordFieldCandidates, textFieldCandidates } =
|
||||||
|
fieldCandidates;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentFieldFilterSkippedItems === null) return;
|
||||||
|
|
||||||
dispatch(resetResults());
|
dispatch(resetResults());
|
||||||
setOverrides({
|
setOverrides({
|
||||||
loaded: 0,
|
loaded: 0,
|
||||||
remainingKeywordFieldCandidates: keywordFieldCandidates.filter(
|
remainingKeywordFieldCandidates: keywordFieldCandidates.filter(
|
||||||
(d) => !skippedFieldsUpdate.includes(d)
|
(d) => !currentFieldFilterSkippedItems.includes(d)
|
||||||
),
|
),
|
||||||
remainingTextFieldCandidates: textFieldCandidates.filter(
|
remainingTextFieldCandidates: textFieldCandidates.filter(
|
||||||
(d) => !skippedFieldsUpdate.includes(d)
|
(d) => !currentFieldFilterSkippedItems.includes(d)
|
||||||
),
|
),
|
||||||
regroupOnly: false,
|
regroupOnly: false,
|
||||||
});
|
});
|
||||||
startHandler(true, false);
|
startHandler(true, false);
|
||||||
};
|
// custom check to trigger on currentFieldFilterSkippedItems change
|
||||||
const { fieldFilterSkippedItems, keywordFieldCandidates, textFieldCandidates } = fieldCandidates;
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [currentFieldFilterSkippedItems]);
|
||||||
|
|
||||||
function cancelHandler() {
|
function cancelHandler() {
|
||||||
abortCtrl.current.abort();
|
abortCtrl.current.abort();
|
||||||
|
@ -209,10 +215,12 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
||||||
dispatch(resetResults());
|
dispatch(resetResults());
|
||||||
setOverrides({
|
setOverrides({
|
||||||
remainingKeywordFieldCandidates: keywordFieldCandidates.filter(
|
remainingKeywordFieldCandidates: keywordFieldCandidates.filter(
|
||||||
(d) => fieldFilterSkippedItems !== null && fieldFilterSkippedItems.includes(d)
|
(d) =>
|
||||||
|
currentFieldFilterSkippedItems === null || !currentFieldFilterSkippedItems.includes(d)
|
||||||
),
|
),
|
||||||
remainingTextFieldCandidates: textFieldCandidates.filter(
|
remainingTextFieldCandidates: textFieldCandidates.filter(
|
||||||
(d) => fieldFilterSkippedItems !== null && fieldFilterSkippedItems.includes(d)
|
(d) =>
|
||||||
|
currentFieldFilterSkippedItems === null || !currentFieldFilterSkippedItems.includes(d)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -325,10 +333,7 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{embeddingOrigin !== AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && (
|
{embeddingOrigin !== AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && (
|
||||||
<LogRateAnalysisOptions
|
<LogRateAnalysisOptions foundGroups={foundGroups} />
|
||||||
foundGroups={foundGroups}
|
|
||||||
onFieldsFilterChange={onFieldsFilterChange}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{embeddingOrigin === AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && (
|
{embeddingOrigin === AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && (
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
|
@ -354,11 +359,7 @@ export const LogRateAnalysisResults: FC<LogRateAnalysisResultsProps> = ({
|
||||||
<>
|
<>
|
||||||
<EuiSpacer size="m" />
|
<EuiSpacer size="m" />
|
||||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||||
<LogRateAnalysisOptions
|
<LogRateAnalysisOptions foundGroups={foundGroups} growFirstItem={true} />
|
||||||
foundGroups={foundGroups}
|
|
||||||
growFirstItem={true}
|
|
||||||
onFieldsFilterChange={onFieldsFilterChange}
|
|
||||||
/>
|
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue