mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution] Add loading overlay during bulk edit preflight requests (#145905)
## Summary (outdated, see discussion in comments below) Relates to: https://github.com/elastic/kibana/issues/144264 - Adds additional `isDryRun` boolean option to the `setLoadingRules` state setter provided by `RulesTableContextProvider`. It is optional and defaults to `false`. - Set loading state during pre-flight (dry run) bulk edit requests to `true`, in order for the loading overlay to display over the Rules Table while the dry run is executed. ## Context We currently [have a check that prevents the loading overlay](https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.tsx#L199) to display during bulk `edit` API requests (also `enable` and `disable`) , since that is the current expected UX. However, we want the loading overlay to display during `dryRun` requests, but not in the subsequent requests, when the `edit`ing is actually being done. That is why we need to differentiate the two types of requests, while maintaining backwards compatibility with the logic already present. Therefore, the proposal of the optional `isDryRun` boolean option. ## Before https://user-images.githubusercontent.com/5354282/203136754-53fb657b-f594-4e91-980f-95de7c11579d.mp4 ## After https://user-images.githubusercontent.com/5354282/203136922-7e90f10d-64d8-4907-8aae-16e7d759107b.mp4 ### Checklist Delete any items that are not applicable to this PR. - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
parent
7f8b1f6383
commit
033dcceb8b
5 changed files with 36 additions and 26 deletions
|
@ -76,7 +76,6 @@ import {
|
|||
editFirstRule,
|
||||
goToRuleDetails,
|
||||
selectNumberOfRules,
|
||||
waitForRulesTableToBeRefreshed,
|
||||
} from '../../tasks/alerts_detection_rules';
|
||||
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
|
||||
import { createTimeline } from '../../tasks/api_calls/timelines';
|
||||
|
@ -263,7 +262,6 @@ describe('Custom query rules', () => {
|
|||
});
|
||||
|
||||
deleteFirstRule();
|
||||
waitForRulesTableToBeRefreshed();
|
||||
|
||||
cy.get(RULES_TABLE)
|
||||
.find(RULES_ROW)
|
||||
|
@ -290,7 +288,6 @@ describe('Custom query rules', () => {
|
|||
|
||||
selectNumberOfRules(numberOfRulesToBeDeleted);
|
||||
deleteSelectedRules();
|
||||
waitForRulesTableToBeRefreshed();
|
||||
|
||||
cy.get(RULES_TABLE)
|
||||
.find(RULES_ROW)
|
||||
|
|
|
@ -74,7 +74,7 @@ export const useBulkActions = ({
|
|||
|
||||
const {
|
||||
state: { isAllSelected, rules, loadingRuleIds, selectedRuleIds },
|
||||
actions: { clearRulesSelection },
|
||||
actions: { clearRulesSelection, setIsPreflightInProgress },
|
||||
} = rulesTableContext;
|
||||
|
||||
const getBulkItemsPopoverContent = useCallback(
|
||||
|
@ -195,6 +195,8 @@ export const useBulkActions = ({
|
|||
|
||||
closePopover();
|
||||
|
||||
setIsPreflightInProgress(true);
|
||||
|
||||
const dryRunResult = await executeBulkActionsDryRun({
|
||||
type: BulkActionType.edit,
|
||||
...(isAllSelected
|
||||
|
@ -203,6 +205,8 @@ export const useBulkActions = ({
|
|||
editPayload: computeDryRunEditPayload(bulkEditActionType),
|
||||
});
|
||||
|
||||
setIsPreflightInProgress(false);
|
||||
|
||||
// User has cancelled edit action or there are no custom rules to proceed
|
||||
const hasActionBeenConfirmed = await showBulkActionConfirmation(
|
||||
dryRunResult,
|
||||
|
@ -464,15 +468,16 @@ export const useBulkActions = ({
|
|||
executeBulkAction,
|
||||
filterQuery,
|
||||
toasts,
|
||||
showBulkDuplicateConfirmation,
|
||||
clearRulesSelection,
|
||||
confirmDeletion,
|
||||
bulkExport,
|
||||
showBulkActionConfirmation,
|
||||
downloadExportedRules,
|
||||
setIsPreflightInProgress,
|
||||
executeBulkActionsDryRun,
|
||||
filterOptions,
|
||||
completeBulkEditForm,
|
||||
downloadExportedRules,
|
||||
showBulkDuplicateConfirmation,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ export const useRulesTableContextMock = {
|
|||
order: 'desc',
|
||||
},
|
||||
isActionInProgress: false,
|
||||
isPreflightInProgress: false,
|
||||
isAllSelected: false,
|
||||
isFetched: true,
|
||||
isFetching: false,
|
||||
|
@ -49,6 +50,7 @@ export const useRulesTableContextMock = {
|
|||
setLoadingRules: jest.fn(),
|
||||
setPage: jest.fn(),
|
||||
setPerPage: jest.fn(),
|
||||
setIsPreflightInProgress: jest.fn(),
|
||||
setSelectedRuleIds: jest.fn(),
|
||||
setSortingOptions: jest.fn(),
|
||||
clearRulesSelection: jest.fn(),
|
||||
|
|
|
@ -59,6 +59,10 @@ export interface RulesTableState {
|
|||
* Is true then there is no cached data and the query is currently fetching.
|
||||
*/
|
||||
isLoading: boolean;
|
||||
/**
|
||||
* Is true when a preflight request (dry-run) is in progress.
|
||||
*/
|
||||
isPreflightInProgress: boolean;
|
||||
/**
|
||||
* Is true whenever a background refetch is in-flight, which does not include initial loading
|
||||
*/
|
||||
|
@ -125,6 +129,7 @@ export interface RulesTableActions {
|
|||
setFilterOptions: (newFilter: Partial<FilterOptions>) => void;
|
||||
setIsAllSelected: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setIsInMemorySorting: (value: boolean) => void;
|
||||
setIsPreflightInProgress: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
/**
|
||||
* enable/disable rules table auto refresh
|
||||
*
|
||||
|
@ -175,7 +180,11 @@ export const RulesTableContextProvider = ({ children }: RulesTableContextProvide
|
|||
const [sortingOptions, setSortingOptions] = useState<SortingOptions>(initialSortingOptions);
|
||||
const [isAllSelected, setIsAllSelected] = useState(false);
|
||||
const [isRefreshOn, setIsRefreshOn] = useState(autoRefreshSettings.on);
|
||||
const [loadingRules, setLoadingRules] = useState<LoadingRules>({ ids: [], action: null });
|
||||
const [loadingRules, setLoadingRules] = useState<LoadingRules>({
|
||||
ids: [],
|
||||
action: null,
|
||||
});
|
||||
const [isPreflightInProgress, setIsPreflightInProgress] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [perPage, setPerPage] = useState(DEFAULT_RULES_PER_PAGE);
|
||||
const [selectedRuleIds, setSelectedRuleIds] = useState<string[]>([]);
|
||||
|
@ -194,12 +203,7 @@ export const RulesTableContextProvider = ({ children }: RulesTableContextProvide
|
|||
[storage]
|
||||
);
|
||||
|
||||
const isActionInProgress = useMemo(() => {
|
||||
if (loadingRules.ids.length > 0) {
|
||||
return !['disable', 'enable', 'edit'].includes(loadingRules.action ?? '');
|
||||
}
|
||||
return false;
|
||||
}, [loadingRules.action, loadingRules.ids.length]);
|
||||
const isActionInProgress = loadingRules.ids.length > 0;
|
||||
|
||||
const pagination = useMemo(() => ({ page, perPage }), [page, perPage]);
|
||||
|
||||
|
@ -262,6 +266,7 @@ export const RulesTableContextProvider = ({ children }: RulesTableContextProvide
|
|||
total: isInMemorySorting ? rules.length : total,
|
||||
},
|
||||
filterOptions,
|
||||
isPreflightInProgress,
|
||||
isActionInProgress,
|
||||
isAllSelected,
|
||||
isFetched,
|
||||
|
@ -287,33 +292,34 @@ export const RulesTableContextProvider = ({ children }: RulesTableContextProvide
|
|||
setPerPage,
|
||||
setSelectedRuleIds,
|
||||
setSortingOptions,
|
||||
setIsPreflightInProgress,
|
||||
clearRulesSelection,
|
||||
},
|
||||
}),
|
||||
[
|
||||
dataUpdatedAt,
|
||||
rulesToDisplay,
|
||||
page,
|
||||
perPage,
|
||||
isInMemorySorting,
|
||||
rules.length,
|
||||
total,
|
||||
filterOptions,
|
||||
handleFilterOptionsChange,
|
||||
isPreflightInProgress,
|
||||
isActionInProgress,
|
||||
isAllSelected,
|
||||
isFetched,
|
||||
isFetching,
|
||||
isInMemorySorting,
|
||||
isLoading,
|
||||
isRefetching,
|
||||
isRefreshOn,
|
||||
loadingRules.action,
|
||||
dataUpdatedAt,
|
||||
loadingRules.ids,
|
||||
page,
|
||||
perPage,
|
||||
refetch,
|
||||
rules.length,
|
||||
rulesToDisplay,
|
||||
loadingRules.action,
|
||||
selectedRuleIds,
|
||||
sortingOptions,
|
||||
refetch,
|
||||
handleFilterOptionsChange,
|
||||
toggleInMemorySorting,
|
||||
setSelectedRuleIds,
|
||||
total,
|
||||
clearRulesSelection,
|
||||
]
|
||||
);
|
||||
|
|
|
@ -72,7 +72,7 @@ export const RulesTables = React.memo<RulesTableProps>(({ selectedTab }) => {
|
|||
state: {
|
||||
rules,
|
||||
filterOptions,
|
||||
isActionInProgress,
|
||||
isPreflightInProgress,
|
||||
isAllSelected,
|
||||
isFetched,
|
||||
isLoading,
|
||||
|
@ -229,7 +229,7 @@ export const RulesTables = React.memo<RulesTableProps>(({ selectedTab }) => {
|
|||
: { 'data-test-subj': 'monitoring-table', columns: monitoringColumns };
|
||||
|
||||
const shouldShowLinearProgress = isFetched && isRefetching;
|
||||
const shouldShowLoadingOverlay = (!isFetched && isRefetching) || isActionInProgress;
|
||||
const shouldShowLoadingOverlay = (!isFetched && isRefetching) || isPreflightInProgress;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue