mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Feedback for Alert Page Filters (#154342)
Below features should be added to Alert Page Filters.(Fixes #154044) - Design Feedback - Remove `Save Controls` from Context Menu ( Overflow Menu ) |Before|After| |:---:|:---:| || - Move `Discard Changes -> Context Menu`. |Before|After| |:---:|:---:| || - When Redirected, do not enter in the Edit Mode and give option of Saving/Discarding changes to the user in the Callout itself. |Before|After| |:---:|:---:| || - Functionality Feedback - Status should always be part of page filters. User should not be able to remove it. - Currently users have ability to delete all controls, to persist some pre-defined controls, but whenever users delete those, we add them back when users save the controls. |Before|After| |:---:|:---:| | <video src="https://user-images.githubusercontent.com/7485038/230329833-1dd698d8-6bae-42f9-9133-168b2789f5ff.mov"/> |<video src="https://user-images.githubusercontent.com/7485038/230331343-e372da0a-dcd6-4d9d-a5f7-491b78c72998.mov" />| - Raised the issue with `kibana-presentation` team to disabled `delete` action for a particular controls. https://github.com/elastic/kibana/issues/154068
This commit is contained in:
parent
54646146d7
commit
99db75512a
13 changed files with 198 additions and 85 deletions
|
@ -492,6 +492,7 @@ export const DEFAULT_DETECTION_PAGE_FILTERS = [
|
|||
fieldName: 'kibana.alert.workflow_status',
|
||||
selectedOptions: ['open'],
|
||||
hideActionBar: true,
|
||||
persist: true,
|
||||
},
|
||||
{
|
||||
title: 'Severity',
|
||||
|
|
|
@ -167,8 +167,8 @@ describe('Entity Analytics Dashboard', () => {
|
|||
|
||||
cy.get(OPTION_LIST_LABELS).eq(0).should('include.text', 'Status');
|
||||
cy.get(OPTION_LIST_VALUES(0)).should('include.text', 'open');
|
||||
cy.get(OPTION_LIST_LABELS).eq(3).should('include.text', 'Host');
|
||||
cy.get(OPTION_LIST_VALUES(3)).should('include.text', SIEM_KIBANA_HOST_NAME);
|
||||
cy.get(OPTION_LIST_LABELS).eq(1).should('include.text', 'Host');
|
||||
cy.get(OPTION_LIST_VALUES(1)).should('include.text', SIEM_KIBANA_HOST_NAME);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -232,8 +232,8 @@ describe('Entity Analytics Dashboard', () => {
|
|||
|
||||
cy.get(OPTION_LIST_LABELS).eq(0).should('include.text', 'Status');
|
||||
cy.get(OPTION_LIST_VALUES(0)).should('include.text', 'open');
|
||||
cy.get(OPTION_LIST_LABELS).eq(2).should('include.text', 'User');
|
||||
cy.get(OPTION_LIST_VALUES(2)).should('include.text', TEST_USER_NAME);
|
||||
cy.get(OPTION_LIST_LABELS).eq(1).should('include.text', 'User');
|
||||
cy.get(OPTION_LIST_VALUES(1)).should('include.text', TEST_USER_NAME);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
CONTROL_FRAMES,
|
||||
CONTROL_FRAME_TITLE,
|
||||
FILTER_GROUP_CHANGED_BANNER,
|
||||
FILTER_GROUP_DISCARD_CHANGES,
|
||||
FILTER_GROUP_SAVE_CHANGES_POPOVER,
|
||||
OPTION_LIST_LABELS,
|
||||
OPTION_LIST_VALUES,
|
||||
|
@ -127,7 +126,7 @@ describe.skip('Detections : Page Filters', { testIsolation: false }, () => {
|
|||
});
|
||||
cy.get(CONTROL_FRAME_TITLE).should('contain.text', label);
|
||||
cy.get(FILTER_GROUP_SAVE_CHANGES_POPOVER).should('be.visible');
|
||||
cy.get(FILTER_GROUP_DISCARD_CHANGES).click();
|
||||
discardFilterGroupControls();
|
||||
cy.get(CONTROL_FRAME_TITLE).should('not.contain.text', label);
|
||||
});
|
||||
it('Delete Controls', () => {
|
||||
|
@ -137,7 +136,7 @@ describe.skip('Detections : Page Filters', { testIsolation: false }, () => {
|
|||
cy.get(CONTROL_FRAMES).should((sub) => {
|
||||
expect(sub.length).lt(4);
|
||||
});
|
||||
cy.get(FILTER_GROUP_DISCARD_CHANGES).trigger('click', { force: true });
|
||||
discardFilterGroupControls();
|
||||
});
|
||||
it('should not sync to the URL in edit mode but only in view mode', () => {
|
||||
cy.url().then((urlString) => {
|
||||
|
@ -148,16 +147,17 @@ describe.skip('Detections : Page Filters', { testIsolation: false }, () => {
|
|||
cy.url().should('not.eq', urlString);
|
||||
});
|
||||
});
|
||||
it('should not sync to the localstorage', () => {});
|
||||
});
|
||||
|
||||
it('Page filters are loaded with custom values provided in the URL', () => {
|
||||
const NEW_FILTERS = DEFAULT_DETECTION_PAGE_FILTERS.map((filter) => {
|
||||
if (filter.title === 'Status') {
|
||||
filter.selectedOptions = ['open', 'acknowledged'];
|
||||
const NEW_FILTERS = DEFAULT_DETECTION_PAGE_FILTERS.filter((item) => item.persist).map(
|
||||
(filter) => {
|
||||
if (filter.title === 'Status') {
|
||||
filter.selectedOptions = ['open', 'acknowledged'];
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
return filter;
|
||||
});
|
||||
);
|
||||
|
||||
cy.url().then((url) => {
|
||||
const currURL = new URL(url);
|
||||
|
@ -188,7 +188,7 @@ describe.skip('Detections : Page Filters', { testIsolation: false }, () => {
|
|||
|
||||
waitForAlerts();
|
||||
cy.get(OPTION_LIST_LABELS).should((sub) => {
|
||||
DEFAULT_DETECTION_PAGE_FILTERS.forEach((filter, idx) => {
|
||||
DEFAULT_DETECTION_PAGE_FILTERS.filter((item) => item.persist).forEach((filter, idx) => {
|
||||
if (idx === DEFAULT_DETECTION_PAGE_FILTERS.length - 1) {
|
||||
expect(sub.eq(idx).text()).eq(CUSTOM_URL_FILTER[0].title);
|
||||
} else {
|
||||
|
@ -198,7 +198,7 @@ describe.skip('Detections : Page Filters', { testIsolation: false }, () => {
|
|||
});
|
||||
});
|
||||
|
||||
cy.get(FILTER_GROUP_SAVE_CHANGES_POPOVER).should('be.visible');
|
||||
cy.get(FILTER_GROUP_CHANGED_BANNER).should('be.visible');
|
||||
});
|
||||
|
||||
it(`Alert list is updated when the alerts are updated`, () => {
|
||||
|
|
|
@ -49,6 +49,9 @@ export const FILTER_GROUP_CONTEXT_EDIT_CONTROLS = '[data-test-subj="filter_group
|
|||
|
||||
export const FILTER_GROUP_CONTEXT_SAVE_CONTROLS = '[data-test-subj="filter_group__context--save"]';
|
||||
|
||||
export const FILTER_GROUP_CONTEXT_DISCARD_CHANGES =
|
||||
'[data-test-subj="filter_group__context--discard"]';
|
||||
|
||||
export const FILTER_GROUP_ADD_CONTROL = '[data-test-subj="filter-group__add-control"]';
|
||||
|
||||
export const FILTER_GROUP_SAVE_CHANGES = '[data-test-subj="filter-group__save"]';
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
DETECTION_PAGE_FILTER_GROUP_CONTEXT_MENU,
|
||||
DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON,
|
||||
FILTER_GROUP_ADD_CONTROL,
|
||||
FILTER_GROUP_DISCARD_CHANGES,
|
||||
FILTER_GROUP_CONTEXT_EDIT_CONTROLS,
|
||||
FILTER_GROUP_EDIT_CONTROLS_PANEL,
|
||||
FILTER_GROUP_EDIT_CONTROL_PANEL_ITEMS,
|
||||
|
@ -22,6 +21,7 @@ import {
|
|||
DETECTION_PAGE_FILTER_GROUP_LOADING,
|
||||
DETECTION_PAGE_FILTERS_LOADING,
|
||||
OPTION_LISTS_LOADING,
|
||||
FILTER_GROUP_CONTEXT_DISCARD_CHANGES,
|
||||
} from '../../screens/common/filter_group';
|
||||
import { waitForPageFilters } from '../alerts';
|
||||
|
||||
|
@ -55,8 +55,8 @@ export const saveFilterGroupControls = () => {
|
|||
};
|
||||
|
||||
export const discardFilterGroupControls = () => {
|
||||
cy.get(FILTER_GROUP_DISCARD_CHANGES).trigger('click');
|
||||
cy.get(FILTER_GROUP_DISCARD_CHANGES).should('not.exist');
|
||||
openFilterGroupContextMenu();
|
||||
cy.get(FILTER_GROUP_CONTEXT_DISCARD_CHANGES).trigger('click', { force: true });
|
||||
};
|
||||
|
||||
export const openAddFilterGroupControlPanel = () => {
|
||||
|
|
|
@ -10,23 +10,31 @@ import React from 'react';
|
|||
import type { EuiButtonIconProps } from '@elastic/eui';
|
||||
import { EuiButtonIcon, EuiCallOut, EuiPopover, EuiToolTip } from '@elastic/eui';
|
||||
import { useFilterGroupInternalContext } from './hooks/use_filters';
|
||||
import { DISCARD_CHANGES, PENDING_CHANGES_REMINDER } from './translations';
|
||||
import {
|
||||
ADD_CONTROLS,
|
||||
ADD_CONTROLS_MAX_LIMIT,
|
||||
DISCARD_CHANGES,
|
||||
PENDING_CHANGES_REMINDER,
|
||||
} from './translations';
|
||||
|
||||
interface AddControlProps extends Partial<EuiButtonIconProps> {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export const AddControl: FC<AddControlProps> = ({ onClick, ...rest }) => {
|
||||
const { isDisabled } = rest;
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
size="s"
|
||||
iconSize="m"
|
||||
display="base"
|
||||
data-test-subj={'filter-group__add-control'}
|
||||
onClick={onClick}
|
||||
{...rest}
|
||||
iconType={'plusInCircle'}
|
||||
/>
|
||||
<EuiToolTip content={isDisabled ? ADD_CONTROLS_MAX_LIMIT : ADD_CONTROLS}>
|
||||
<EuiButtonIcon
|
||||
size="s"
|
||||
iconSize="m"
|
||||
display="base"
|
||||
data-test-subj={'filter-group__add-control'}
|
||||
onClick={onClick}
|
||||
{...rest}
|
||||
iconType={'plusInCircle'}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
*/
|
||||
|
||||
export const NUM_OF_CONTROLS = {
|
||||
MIN: 2,
|
||||
MAX: 6,
|
||||
MIN: 0,
|
||||
MAX: 4,
|
||||
};
|
||||
|
|
|
@ -11,9 +11,9 @@ import { useFilterGroupInternalContext } from './hooks/use_filters';
|
|||
import {
|
||||
CONTEXT_MENU_RESET,
|
||||
CONTEXT_MENU_RESET_TOOLTIP,
|
||||
DISCARD_CHANGES,
|
||||
EDIT_CONTROLS,
|
||||
FILTER_GROUP_MENU,
|
||||
SAVE_CONTROLS,
|
||||
} from './translations';
|
||||
|
||||
export const FilterGroupContextMenu = () => {
|
||||
|
@ -28,6 +28,7 @@ export const FilterGroupContextMenu = () => {
|
|||
initialControls,
|
||||
dataViewId,
|
||||
setShowFiltersChangedBanner,
|
||||
discardChangesHandler,
|
||||
} = useFilterGroupInternalContext();
|
||||
|
||||
const toggleContextMenu = useCallback(() => {
|
||||
|
@ -97,18 +98,20 @@ export const FilterGroupContextMenu = () => {
|
|||
const editControlsButton = useMemo(
|
||||
() => (
|
||||
<EuiContextMenuItem
|
||||
icon="pencil"
|
||||
icon={isViewMode ? 'pencil' : 'minusInCircle'}
|
||||
onClick={
|
||||
isViewMode
|
||||
? withContextMenuAction(switchToEditMode)
|
||||
: withContextMenuAction(switchToViewMode)
|
||||
: withContextMenuAction(discardChangesHandler)
|
||||
}
|
||||
data-test-subj={
|
||||
isViewMode ? `filter_group__context--edit` : `filter_group__context--discard`
|
||||
}
|
||||
data-test-subj={isViewMode ? `filter_group__context--edit` : `filter_group__context--save`}
|
||||
>
|
||||
{isViewMode ? EDIT_CONTROLS : SAVE_CONTROLS}
|
||||
{isViewMode ? EDIT_CONTROLS : DISCARD_CHANGES}
|
||||
</EuiContextMenuItem>
|
||||
),
|
||||
[withContextMenuAction, isViewMode, switchToEditMode, switchToViewMode]
|
||||
[withContextMenuAction, isViewMode, switchToEditMode, discardChangesHandler]
|
||||
);
|
||||
|
||||
const contextMenuItems = useMemo(
|
||||
|
|
|
@ -23,6 +23,8 @@ export interface FilterGroupContextType {
|
|||
switchToEditMode: () => void;
|
||||
setHasPendingChanges: (value: boolean) => void;
|
||||
setShowFiltersChangedBanner: (value: boolean) => void;
|
||||
saveChangesHandler: () => void;
|
||||
discardChangesHandler: () => void;
|
||||
}
|
||||
|
||||
export const FilterGroupContext = createContext<FilterGroupContextType | undefined>(undefined);
|
||||
|
|
|
@ -5,11 +5,25 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiButton, EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { FILTER_GROUP_BANNER_MESSAGE, FILTER_GROUP_BANNER_TITLE } from './translations';
|
||||
import {
|
||||
FILTER_GROUP_BANNER_MESSAGE,
|
||||
FILTER_GROUP_BANNER_TITLE,
|
||||
REVERT_CHANGES,
|
||||
SAVE_CHANGES,
|
||||
} from './translations';
|
||||
|
||||
export const FiltersChangedBanner = () => {
|
||||
interface FiltersChangesBanner {
|
||||
saveChangesHandler: () => void;
|
||||
discardChangesHandler: () => void;
|
||||
}
|
||||
|
||||
export const FiltersChangedBanner: FC<FiltersChangesBanner> = ({
|
||||
saveChangesHandler,
|
||||
discardChangesHandler,
|
||||
}) => {
|
||||
return (
|
||||
<EuiFlexGroup alignItems="center" justifyContent="center" gutterSize="s">
|
||||
<EuiFlexItem grow={true}>
|
||||
|
@ -19,6 +33,16 @@ export const FiltersChangedBanner = () => {
|
|||
iconType={'iInCircle'}
|
||||
>
|
||||
<p>{FILTER_GROUP_BANNER_MESSAGE}</p>
|
||||
<EuiButton
|
||||
data-test-subj="filter-group__save"
|
||||
color={'primary'}
|
||||
onClick={saveChangesHandler}
|
||||
>
|
||||
{SAVE_CHANGES}
|
||||
</EuiButton>
|
||||
<EuiButtonEmpty data-test-subj="filter-group__discard" onClick={discardChangesHandler}>
|
||||
{REVERT_CHANGES}
|
||||
</EuiButtonEmpty>
|
||||
</EuiCallOut>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import type { ControlPanelState, OptionsListEmbeddableInput } from '@kbn/controls-plugin/common';
|
||||
import type {
|
||||
ControlGroupInput,
|
||||
controlGroupInputBuilder,
|
||||
|
@ -30,14 +31,14 @@ import { APP_ID } from '../../../../common/constants';
|
|||
import './index.scss';
|
||||
import { FilterGroupLoading } from './loading';
|
||||
import { withSpaceId } from '../with_space_id';
|
||||
import { NUM_OF_CONTROLS } from './config';
|
||||
import { useControlGroupSyncToLocalStorage } from './hooks/use_control_group_sync_to_local_storage';
|
||||
import { useViewEditMode } from './hooks/use_view_edit_mode';
|
||||
import { FilterGroupContextMenu } from './context_menu';
|
||||
import { AddControl, DiscardChanges, SaveControls } from './buttons';
|
||||
import { AddControl, SaveControls } from './buttons';
|
||||
import { getFilterItemObjListFromControlInput } from './utils';
|
||||
import { FiltersChangedBanner } from './filters_changed_banner';
|
||||
import { FilterGroupContext } from './filter_group_context';
|
||||
import { NUM_OF_CONTROLS } from './config';
|
||||
|
||||
type ControlGroupBuilder = typeof controlGroupInputBuilder;
|
||||
|
||||
|
@ -212,8 +213,10 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
|
|||
*
|
||||
* */
|
||||
|
||||
const localInitialControls = cloneDeep(initialControls);
|
||||
const resultControls = cloneDeep(initialControls);
|
||||
const localInitialControls = cloneDeep(initialControls).filter(
|
||||
(control) => control.persist === true
|
||||
);
|
||||
let resultControls = [] as FilterItemObj[];
|
||||
|
||||
let overridingControls = initialUrlParam;
|
||||
if (!initialUrlParam || initialUrlParam.length === 0) {
|
||||
|
@ -231,40 +234,28 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
|
|||
|
||||
// if initialUrlParam Exists... replace localInitialControls with what was provided in the Url
|
||||
if (overridingControls && !urlDataApplied.current) {
|
||||
let maxInitialControlIdx = Math.max(
|
||||
localInitialControls.length - 1,
|
||||
(overridingControls?.length ?? 1) - 1
|
||||
);
|
||||
for (let counter = overridingControls.length - 1; counter >= 0; counter--) {
|
||||
const urlControl = overridingControls[counter];
|
||||
const idx = localInitialControls.findIndex(
|
||||
(item) => item.fieldName === urlControl.fieldName
|
||||
);
|
||||
if (localInitialControls.length > 0) {
|
||||
localInitialControls.forEach((persistControl) => {
|
||||
const doesPersistControlAlreadyExist = overridingControls?.some(
|
||||
(control) => control.fieldName === persistControl.fieldName
|
||||
);
|
||||
|
||||
if (idx !== -1) {
|
||||
// if index found, replace that with what was provided in the Url
|
||||
resultControls[idx] = {
|
||||
...localInitialControls[idx],
|
||||
fieldName: urlControl.fieldName,
|
||||
title: urlControl.title ?? urlControl.fieldName,
|
||||
selectedOptions: urlControl.selectedOptions ?? [],
|
||||
existsSelected: urlControl.existsSelected ?? false,
|
||||
exclude: urlControl.exclude ?? false,
|
||||
};
|
||||
} else {
|
||||
// if url param is not available in initialControl, start replacing the last slot in the
|
||||
// initial Control with the last `not found` element in the Url Param
|
||||
//
|
||||
resultControls[maxInitialControlIdx] = {
|
||||
fieldName: urlControl.fieldName,
|
||||
selectedOptions: urlControl.selectedOptions ?? [],
|
||||
title: urlControl.title ?? urlControl.fieldName,
|
||||
existsSelected: urlControl.existsSelected ?? false,
|
||||
exclude: urlControl.exclude ?? false,
|
||||
};
|
||||
maxInitialControlIdx--;
|
||||
}
|
||||
if (!doesPersistControlAlreadyExist) {
|
||||
resultControls.push(persistControl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resultControls = [
|
||||
...resultControls,
|
||||
...overridingControls.map((item) => ({
|
||||
fieldName: item.fieldName,
|
||||
title: item.title,
|
||||
selectedOptions: item.selectedOptions ?? [],
|
||||
existsSelected: item.existsSelected ?? false,
|
||||
exclude: item.existsSelected,
|
||||
})),
|
||||
];
|
||||
}
|
||||
|
||||
return resultControls;
|
||||
|
@ -333,10 +324,58 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
|
|||
setShowFiltersChangedBanner(false);
|
||||
}, [controlGroup, switchToViewMode, getStoredControlInput, hasPendingChanges]);
|
||||
|
||||
const upsertPersistableControls = useCallback(() => {
|
||||
const persistableControls = initialControls.filter((control) => control.persist === true);
|
||||
if (persistableControls.length > 0) {
|
||||
const currentPanels = Object.values(controlGroup?.getInput().panels ?? []) as Array<
|
||||
ControlPanelState<OptionsListEmbeddableInput>
|
||||
>;
|
||||
const orderedPanels = currentPanels.sort((a, b) => a.order - b.order);
|
||||
let filterControlsDeleted = false;
|
||||
persistableControls.forEach((control) => {
|
||||
const controlExists = currentPanels.some(
|
||||
(currControl) => control.fieldName === currControl.explicitInput.fieldName
|
||||
);
|
||||
if (!controlExists) {
|
||||
// delete current controls
|
||||
if (!filterControlsDeleted) {
|
||||
controlGroup?.updateInput({ panels: {} });
|
||||
filterControlsDeleted = true;
|
||||
}
|
||||
|
||||
// add persitable controls
|
||||
controlGroup?.addOptionsListControl({
|
||||
title: control.title,
|
||||
hideExclude: true,
|
||||
hideSort: true,
|
||||
hidePanelTitles: true,
|
||||
placeholder: '',
|
||||
// option List controls will handle an invalid dataview
|
||||
// & display an appropriate message
|
||||
dataViewId: dataViewId ?? '',
|
||||
selectedOptions: control.selectedOptions,
|
||||
...control,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
orderedPanels.forEach((panel) => {
|
||||
if (panel.explicitInput.fieldName)
|
||||
controlGroup?.addOptionsListControl({
|
||||
selectedOptions: [],
|
||||
fieldName: panel.explicitInput.fieldName,
|
||||
dataViewId: dataViewId ?? '',
|
||||
...panel.explicitInput,
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [controlGroup, dataViewId, initialControls]);
|
||||
|
||||
const saveChangesHandler = useCallback(() => {
|
||||
upsertPersistableControls();
|
||||
switchToViewMode();
|
||||
setShowFiltersChangedBanner(false);
|
||||
}, [switchToViewMode]);
|
||||
}, [switchToViewMode, upsertPersistableControls]);
|
||||
|
||||
const addControlsHandler = useCallback(() => {
|
||||
controlGroup?.openAddDataControlFlyout();
|
||||
|
@ -358,6 +397,8 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
|
|||
openPendingChangesPopover,
|
||||
closePendingChangesPopover,
|
||||
setShowFiltersChangedBanner,
|
||||
saveChangesHandler,
|
||||
discardChangesHandler,
|
||||
}}
|
||||
>
|
||||
<FilterWrapper className="filter-group__wrapper">
|
||||
|
@ -371,19 +412,19 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
|
|||
{!controlGroup ? <FilterGroupLoading /> : null}
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
{!isViewMode &&
|
||||
(Object.keys(controlGroupInputUpdates?.panels ?? {}).length > NUM_OF_CONTROLS.MIN ||
|
||||
Object.keys(controlGroupInputUpdates?.panels ?? {}).length < NUM_OF_CONTROLS.MAX) ? (
|
||||
{!isViewMode && !showFiltersChangedBanner ? (
|
||||
<>
|
||||
<EuiFlexItem grow={false}>
|
||||
<AddControl onClick={addControlsHandler} />
|
||||
<AddControl
|
||||
onClick={addControlsHandler}
|
||||
isDisabled={
|
||||
Object.values(controlGroupInputUpdates.panels).length >= NUM_OF_CONTROLS.MAX
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<SaveControls onClick={saveChangesHandler} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<DiscardChanges onClick={discardChangesHandler} />
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
) : null}
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -393,7 +434,10 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
|
|||
{showFiltersChangedBanner ? (
|
||||
<>
|
||||
<EuiSpacer size="l" />
|
||||
<FiltersChangedBanner />
|
||||
<FiltersChangedBanner
|
||||
saveChangesHandler={saveChangesHandler}
|
||||
discardChangesHandler={discardChangesHandler}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</FilterWrapper>
|
||||
|
|
|
@ -21,6 +21,13 @@ export const EDIT_CONTROLS = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const ADD_CONTROLS = i18n.translate(
|
||||
'xpack.securitySolution.filtersGroup.contextMenu.addControls',
|
||||
{
|
||||
defaultMessage: 'Add Controls',
|
||||
}
|
||||
);
|
||||
|
||||
export const SAVE_CONTROLS = i18n.translate(
|
||||
'xpack.securitySolution.filtersGroup.contextMenu.saveControls',
|
||||
{
|
||||
|
@ -70,3 +77,24 @@ export const CONTEXT_MENU_RESET = i18n.translate(
|
|||
defaultMessage: 'Reset Controls',
|
||||
}
|
||||
);
|
||||
|
||||
export const SAVE_CHANGES = i18n.translate(
|
||||
'xpack.securitySolution.filtersGroup.contextMenu.saveChanges',
|
||||
{
|
||||
defaultMessage: 'Save Changes',
|
||||
}
|
||||
);
|
||||
|
||||
export const REVERT_CHANGES = i18n.translate(
|
||||
'xpack.securitySolution.filtersGroup.contextMenu.revertChanges',
|
||||
{
|
||||
defaultMessage: 'Revert Changes',
|
||||
}
|
||||
);
|
||||
|
||||
export const ADD_CONTROLS_MAX_LIMIT = i18n.translate(
|
||||
'xpack.securitySolution.filtersGroup.contextMenu.addControls.maxLimit',
|
||||
{
|
||||
defaultMessage: 'Maximum of 4 controls can be added.',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -33,7 +33,7 @@ export type FilterGroupHandler = ControlGroupContainer;
|
|||
export type FilterGroupProps = {
|
||||
dataViewId: string | null;
|
||||
onFilterChange?: (newFilters: Filter[]) => void;
|
||||
initialControls: FilterItemObj[];
|
||||
initialControls: Array<FilterItemObj & { persist?: boolean }>;
|
||||
spaceId: string;
|
||||
onInit?: (controlGroupHandler: FilterGroupHandler | undefined) => void;
|
||||
} & Pick<ControlGroupInput, 'timeRange' | 'filters' | 'query' | 'chainingSystem'>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue