mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[RAM][Maintenance Window] Add categories to banner title (#169704)
## Summary Fixes #169591 Adds category names to the Maintenance Window banner title. This will make it clearer why the banner is showing up, and which rules it affects. The banner will still **always** appear in Stack Management if there's an active maintenance window, because Stack Management displays all rules. <img width="818" alt="Screenshot 2023-10-24 at 1 13 05 PM" src="e5ad6486
-4afd-42fa-b507-63de9374710b"> <img width="993" alt="Screenshot 2023-10-24 at 1 12 51 PM" src="10b8cc13
-618d-4ff2-9100-c007ebc637c7"> <img width="922" alt="Screenshot 2023-10-24 at 1 33 23 PM" src="12502962
-2c13-4094-96dd-54e03e5d5d8d"> <img width="949" alt="Screenshot 2023-10-24 at 1 00 31 PM" src="9d2853e9
-7481-40e8-9ece-d17849d33e75"> ### Checklist - [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] [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 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e09e14f3b8
commit
444e080fe1
7 changed files with 175 additions and 19 deletions
|
@ -79,7 +79,7 @@ describe('MaintenanceWindowCallout', () => {
|
|||
{ wrapper: TestProviders }
|
||||
);
|
||||
|
||||
expect(await findAllByText('Maintenance window is running')).toHaveLength(1);
|
||||
expect(await findAllByText('One or more maintenance windows are running')).toHaveLength(1);
|
||||
expect(fetchActiveMaintenanceWindowsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
@ -94,7 +94,7 @@ describe('MaintenanceWindowCallout', () => {
|
|||
{ wrapper: TestProviders }
|
||||
);
|
||||
|
||||
expect(await findAllByText('Maintenance window is running')).toHaveLength(1);
|
||||
expect(await findAllByText('One or more maintenance windows are running')).toHaveLength(1);
|
||||
expect(fetchActiveMaintenanceWindowsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
@ -106,7 +106,7 @@ describe('MaintenanceWindowCallout', () => {
|
|||
{ wrapper: TestProviders }
|
||||
);
|
||||
|
||||
expect(await findByText('Maintenance window is running')).toBeInTheDocument();
|
||||
expect(await findByText('One or more maintenance windows are running')).toBeInTheDocument();
|
||||
expect(fetchActiveMaintenanceWindowsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
@ -134,6 +134,85 @@ describe('MaintenanceWindowCallout', () => {
|
|||
expect(fetchActiveMaintenanceWindowsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should be visible if there is a "running" maintenance window that matches the specified category', async () => {
|
||||
fetchActiveMaintenanceWindowsMock.mockResolvedValue([
|
||||
{
|
||||
...RUNNING_MAINTENANCE_WINDOW_1,
|
||||
categoryIds: ['observability'],
|
||||
},
|
||||
]);
|
||||
|
||||
const { findByText } = render(
|
||||
<MaintenanceWindowCallout
|
||||
kibanaServices={kibanaServicesMock}
|
||||
categories={['observability']}
|
||||
/>,
|
||||
{ wrapper: TestProviders }
|
||||
);
|
||||
|
||||
expect(
|
||||
await findByText('A maintenance window is running for Observability rules')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should NOT be visible if there is a "running" maintenance window that does not match the specified category', async () => {
|
||||
fetchActiveMaintenanceWindowsMock.mockResolvedValue([
|
||||
{
|
||||
...RUNNING_MAINTENANCE_WINDOW_1,
|
||||
categoryIds: ['observability'],
|
||||
},
|
||||
]);
|
||||
|
||||
const { container } = render(
|
||||
<MaintenanceWindowCallout
|
||||
kibanaServices={kibanaServicesMock}
|
||||
categories={['securitySolution']}
|
||||
/>,
|
||||
{ wrapper: TestProviders }
|
||||
);
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('should be visible if there is a "running" maintenance window with a category, and no categories are specified', async () => {
|
||||
fetchActiveMaintenanceWindowsMock.mockResolvedValue([
|
||||
{
|
||||
...RUNNING_MAINTENANCE_WINDOW_1,
|
||||
categoryIds: ['observability'],
|
||||
},
|
||||
]);
|
||||
|
||||
const { findByText } = render(
|
||||
<MaintenanceWindowCallout kibanaServices={kibanaServicesMock} />,
|
||||
{ wrapper: TestProviders }
|
||||
);
|
||||
|
||||
expect(
|
||||
await findByText('A maintenance window is running for Observability rules')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should only display the specified categories in the callout title for a maintenance window that matches muliple categories', async () => {
|
||||
fetchActiveMaintenanceWindowsMock.mockResolvedValue([
|
||||
{
|
||||
...RUNNING_MAINTENANCE_WINDOW_1,
|
||||
categoryIds: ['observability', 'securitySolution', 'management'],
|
||||
},
|
||||
]);
|
||||
|
||||
const { findByText } = render(
|
||||
<MaintenanceWindowCallout
|
||||
kibanaServices={kibanaServicesMock}
|
||||
categories={['observability', 'management']}
|
||||
/>,
|
||||
{ wrapper: TestProviders }
|
||||
);
|
||||
|
||||
expect(
|
||||
await findByText('A maintenance window is running for Observability and Stack rules')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should see an error toast if there was an error while fetching maintenance windows', async () => {
|
||||
const createReactQueryWrapper = () => {
|
||||
const queryClient = new QueryClient({
|
||||
|
@ -169,7 +248,7 @@ describe('MaintenanceWindowCallout', () => {
|
|||
expect(kibanaServicesMock.notifications.toasts.addError).toHaveBeenCalledTimes(1);
|
||||
expect(kibanaServicesMock.notifications.toasts.addError).toHaveBeenCalledWith(mockError, {
|
||||
title: 'Failed to check if maintenance windows are active',
|
||||
toastMessage: 'Rule notifications are stopped while the maintenance window is running.',
|
||||
toastMessage: 'Rule notifications are stopped while maintenance windows are running.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -195,7 +274,7 @@ describe('MaintenanceWindowCallout', () => {
|
|||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('should work as expected if window maintenance privilege is READ ', async () => {
|
||||
it('should work as expected if window maintenance privilege is READ', async () => {
|
||||
const servicesMock = {
|
||||
...kibanaServicesMock,
|
||||
application: {
|
||||
|
@ -213,7 +292,7 @@ describe('MaintenanceWindowCallout', () => {
|
|||
wrapper: TestProviders,
|
||||
});
|
||||
|
||||
expect(await findByText('Maintenance window is running')).toBeInTheDocument();
|
||||
expect(await findByText('One or more maintenance windows are running')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display the callout if the category ids contains the specified category', async () => {
|
||||
|
|
|
@ -9,22 +9,44 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import { MaintenanceWindowStatus, KibanaServices } from './types';
|
||||
import { useFetchActiveMaintenanceWindows } from './use_fetch_active_maintenance_windows';
|
||||
|
||||
const MAINTENANCE_WINDOW_FEATURE_ID = 'maintenanceWindow';
|
||||
const MAINTENANCE_WINDOW_RUNNING = i18n.translate(
|
||||
'alertsUIShared.maintenanceWindowCallout.maintenanceWindowActive',
|
||||
{
|
||||
defaultMessage: 'Maintenance window is running',
|
||||
}
|
||||
);
|
||||
const MAINTENANCE_WINDOW_RUNNING_DESCRIPTION = i18n.translate(
|
||||
'alertsUIShared.maintenanceWindowCallout.maintenanceWindowActiveDescription',
|
||||
{
|
||||
defaultMessage: 'Rule notifications are stopped while the maintenance window is running.',
|
||||
defaultMessage: 'Rule notifications are stopped while maintenance windows are running.',
|
||||
}
|
||||
);
|
||||
const MAINTENANCE_WINDOW_NO_CATEGORY_TITLE = i18n.translate(
|
||||
'alertsUIShared.maintenanceWindowCallout.maintenanceWindowActiveNoCategories',
|
||||
{
|
||||
defaultMessage: 'One or more maintenance windows are running',
|
||||
}
|
||||
);
|
||||
|
||||
const maintenanceWindowTwoCategoryNames = (names: string[]) =>
|
||||
i18n.translate('alertsUIShared.maintenanceWindowCallout.maintenanceWindowTwoCategoryNames', {
|
||||
defaultMessage: '{first} and {second}',
|
||||
values: { first: names[0], second: names[1] },
|
||||
});
|
||||
const maintenanceWindowMultipleCategoryNames = (names: string[]) =>
|
||||
i18n.translate('alertsUIShared.maintenanceWindowCallout.maintenanceWindowMultipleCategoryNames', {
|
||||
defaultMessage: '{commaSeparatedList}, and {last}', // Oxford comma, e.g. "First, second, and third"
|
||||
values: { commaSeparatedList: names.slice(0, -1).join(', '), last: names.slice(-1).join('') },
|
||||
});
|
||||
|
||||
const APP_CATEGORIES = {
|
||||
...DEFAULT_APP_CATEGORIES,
|
||||
management: {
|
||||
...DEFAULT_APP_CATEGORIES.management,
|
||||
label: i18n.translate('alertsUIShared.maintenanceWindowCallout.managementCategoryLabel', {
|
||||
defaultMessage: 'Stack',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export function MaintenanceWindowCallout({
|
||||
kibanaServices,
|
||||
|
@ -51,6 +73,8 @@ export function MaintenanceWindowCallout({
|
|||
if (activeMaintenanceWindows.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If categories is omitted, always display the callout
|
||||
if (!Array.isArray(categories)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -66,6 +90,32 @@ export function MaintenanceWindowCallout({
|
|||
});
|
||||
}, [categories, activeMaintenanceWindows]);
|
||||
|
||||
const categoryNames = useMemo(() => {
|
||||
const activeCategoryIds = Array.from(
|
||||
new Set(
|
||||
activeMaintenanceWindows
|
||||
.map(({ categoryIds }) =>
|
||||
// If the categories array is provided, only display category names that are included in it
|
||||
categoryIds?.filter((categoryId) => !categories || categories.includes(categoryId))
|
||||
)
|
||||
.flat()
|
||||
)
|
||||
);
|
||||
const activeCategories = activeCategoryIds
|
||||
.map(
|
||||
(categoryId) =>
|
||||
Object.values(APP_CATEGORIES).find((c) => c.id === categoryId)?.label ?? categoryId
|
||||
)
|
||||
.filter(Boolean) as string[];
|
||||
return activeCategories.length === 0
|
||||
? null
|
||||
: activeCategories.length === 1
|
||||
? `${activeCategories}`
|
||||
: activeCategories.length === 2
|
||||
? maintenanceWindowTwoCategoryNames(activeCategories)
|
||||
: maintenanceWindowMultipleCategoryNames(activeCategories);
|
||||
}, [activeMaintenanceWindows, categories]);
|
||||
|
||||
if (isMaintenanceWindowDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
@ -76,7 +126,18 @@ export function MaintenanceWindowCallout({
|
|||
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={MAINTENANCE_WINDOW_RUNNING}
|
||||
title={
|
||||
!categoryNames
|
||||
? MAINTENANCE_WINDOW_NO_CATEGORY_TITLE
|
||||
: i18n.translate('alertsUIShared.maintenanceWindowCallout.maintenanceWindowActive', {
|
||||
defaultMessage:
|
||||
'{activeWindowCount, plural, one {A maintenance window is} other {Maintenance windows are}} running for {categories} rules',
|
||||
values: {
|
||||
categories: categoryNames,
|
||||
activeWindowCount: activeMaintenanceWindows.length,
|
||||
},
|
||||
})
|
||||
}
|
||||
color="warning"
|
||||
iconType="iInCircle"
|
||||
data-test-subj="maintenanceWindowCallout"
|
||||
|
|
|
@ -37,6 +37,6 @@ const FETCH_ERROR = i18n.translate('alertsUIShared.maintenanceWindowCallout.fetc
|
|||
const FETCH_ERROR_DESCRIPTION = i18n.translate(
|
||||
'alertsUIShared.maintenanceWindowCallout.fetchErrorDescription',
|
||||
{
|
||||
defaultMessage: 'Rule notifications are stopped while the maintenance window is running.',
|
||||
defaultMessage: 'Rule notifications are stopped while maintenance windows are running.',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"@kbn/i18n-react",
|
||||
"@kbn/alerting-plugin",
|
||||
"@kbn/rrule",
|
||||
"@kbn/actions-plugin"
|
||||
"@kbn/actions-plugin",
|
||||
"@kbn/core-application-common"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -271,7 +271,22 @@ describe('rules_list component empty', () => {
|
|||
it('renders MaintenanceWindowCallout if one exists', async () => {
|
||||
fetchActiveMaintenanceWindowsMock.mockResolvedValue([RUNNING_MAINTENANCE_WINDOW_1]);
|
||||
renderWithProviders(<RulesList />);
|
||||
expect(await screen.findByText('Maintenance window is running')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText(
|
||||
'Rule notifications are stopped while maintenance windows are running.'
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
expect(fetchActiveMaintenanceWindowsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("hides MaintenanceWindowCallout if filterConsumers does not match the running maintenance window's category", async () => {
|
||||
fetchActiveMaintenanceWindowsMock.mockResolvedValue([
|
||||
{ ...RUNNING_MAINTENANCE_WINDOW_1, categoryIds: ['securitySolution'] },
|
||||
]);
|
||||
renderWithProviders(<RulesList filterConsumers={['observability']} />);
|
||||
await expect(
|
||||
screen.findByText('Rule notifications are stopped while maintenance windows are running.')
|
||||
).rejects.toThrow();
|
||||
expect(fetchActiveMaintenanceWindowsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
|
|
@ -749,7 +749,7 @@ export const RulesList = ({
|
|||
{showSearchBar && !isEmpty(filters.ruleParams) ? (
|
||||
<RulesListClearRuleFilterBanner onClickClearFilter={handleClearRuleParamFilter} />
|
||||
) : null}
|
||||
<MaintenanceWindowCallout kibanaServices={kibanaServices} />
|
||||
<MaintenanceWindowCallout kibanaServices={kibanaServices} categories={filterConsumers} />
|
||||
<RulesListPrompts
|
||||
showNoAuthPrompt={showNoAuthPrompt}
|
||||
showCreateFirstRulePrompt={showCreateFirstRulePrompt}
|
||||
|
|
|
@ -68,7 +68,7 @@ describe(
|
|||
it('Displays the callout when there are running maintenance windows', () => {
|
||||
visit(RULES_MANAGEMENT_URL);
|
||||
|
||||
cy.contains('Maintenance window is running');
|
||||
cy.contains('A maintenance window is running for Security rules');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue