mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[RAM] Storybook implementation for triggers actions UI shareable components (#139157)
* Storybook implementation for triggers actions UI shareable components * Fix storybooks useUiSettings * Fix API renaming and add KPI Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Xavier Mouligneau <xavier.mouligneau@elastic.co>
This commit is contained in:
parent
059fecd311
commit
3bad88157a
15 changed files with 986 additions and 83 deletions
|
@ -5,6 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiThemeProviderDecorator } from '@kbn/kibana-react-plugin/common';
|
||||
|
||||
export const decorators = [EuiThemeProviderDecorator];
|
||||
export const getActionTypeRegistry = () => {
|
||||
return {
|
||||
has: () => true,
|
||||
register: () => {},
|
||||
get: () => {},
|
||||
list: () => [],
|
||||
};
|
||||
};
|
305
x-pack/plugins/triggers_actions_ui/.storybook/context/http.ts
Normal file
305
x-pack/plugins/triggers_actions_ui/.storybook/context/http.ts
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* 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 uuid from 'uuid';
|
||||
import { DecoratorFn } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { HttpStart, HttpFetchOptions, HttpHandler } from '@kbn/core/public';
|
||||
import {
|
||||
mockLogResponse,
|
||||
getMockLogResponse,
|
||||
} from '../../public/application/sections/rule_details/components/test_helpers';
|
||||
|
||||
const getMockRule = () => {
|
||||
const id = uuid.v4();
|
||||
return {
|
||||
id,
|
||||
name: `test rule - ${id}`,
|
||||
tags: ['tag1', 'tag2', 'tag3'],
|
||||
enabled: true,
|
||||
ruleTypeId: 'test_rule_type',
|
||||
schedule: { interval: '1s' },
|
||||
actions: [],
|
||||
consumer: 'alerts',
|
||||
params: { name: 'test rule type name' },
|
||||
scheduledTaskId: null,
|
||||
createdBy: null,
|
||||
updatedBy: null,
|
||||
apiKeyOwner: null,
|
||||
throttle: '1m',
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
executionStatus: {
|
||||
status: 'active',
|
||||
lastDuration: 500,
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
error: null,
|
||||
},
|
||||
monitoring: {
|
||||
execution: {
|
||||
history: [
|
||||
{
|
||||
success: true,
|
||||
duration: 1000000,
|
||||
},
|
||||
{
|
||||
success: true,
|
||||
duration: 200000,
|
||||
},
|
||||
{
|
||||
success: false,
|
||||
duration: 300000,
|
||||
},
|
||||
],
|
||||
calculated_metrics: {
|
||||
success_ratio: 0.66,
|
||||
p50: 200000,
|
||||
p95: 300000,
|
||||
p99: 300000,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const mockRuleTypes = [
|
||||
{
|
||||
id: 'test_rule_type',
|
||||
name: 'some rule type',
|
||||
action_groups: [{ id: 'default', name: 'Default' }],
|
||||
recovery_action_group: { id: 'recovered', name: 'Recovered' },
|
||||
action_variables: { context: [], state: [] },
|
||||
default_action_group_id: 'default',
|
||||
producer: 'alerts',
|
||||
minimum_license_required: 'basic',
|
||||
enabled_in_license: true,
|
||||
authorized_consumers: {
|
||||
alerts: { read: true, all: true },
|
||||
},
|
||||
rule_task_timeout: '1m',
|
||||
},
|
||||
];
|
||||
|
||||
const mockConfig = {
|
||||
minimumScheduleInterval: {
|
||||
value: '1m',
|
||||
enforce: false,
|
||||
},
|
||||
isUsingSecurity: true,
|
||||
};
|
||||
|
||||
const mockConnectorTypes = [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
},
|
||||
{
|
||||
id: 'test2',
|
||||
name: 'Test2',
|
||||
},
|
||||
];
|
||||
|
||||
const mockHealth = {
|
||||
isAlertsAvailable: true,
|
||||
};
|
||||
|
||||
const mockAggregation = {
|
||||
rule_execution_status: { ok: 0, active: 0, error: 0, pending: 0, unknown: 0, warning: 0 },
|
||||
rule_enabled_status: { enabled: 0, disabled: 0 },
|
||||
rule_muted_status: { muted: 0, unmuted: 0 },
|
||||
rule_snoozed_status: { snoozed: 0 },
|
||||
rule_tags: ['a', 'b'],
|
||||
};
|
||||
|
||||
const mockConnectors: any[] = [];
|
||||
|
||||
const mockRuleSummary = {
|
||||
id: 'rule-id',
|
||||
name: 'rule-name',
|
||||
tags: ['tag-1', 'tag-2'],
|
||||
rule_type_id: 'test-rule-type-id',
|
||||
consumer: 'rule-consumer',
|
||||
status: 'OK',
|
||||
mute_all: false,
|
||||
throttle: '',
|
||||
enabled: true,
|
||||
error_messages: [],
|
||||
status_start_date: '2022-03-21T07:40:46-07:00',
|
||||
status_end_date: '2022-03-25T07:40:46-07:00',
|
||||
alerts: {
|
||||
foo: {
|
||||
status: 'OK',
|
||||
muted: false,
|
||||
actionGroupId: 'testActionGroup',
|
||||
},
|
||||
},
|
||||
execution_duration: {
|
||||
average: 100,
|
||||
valuesWithTimestamp: {},
|
||||
},
|
||||
};
|
||||
|
||||
const getMockErrorLog = () => {
|
||||
return {
|
||||
id: '66b9c04a-d5d3-4ed4-aa7c-94ddaca3ac1d',
|
||||
timestamp: '2022-03-31T18:03:33.133Z',
|
||||
type: 'alerting',
|
||||
message:
|
||||
"rule execution failure: .es-query:d87fcbd0-b11b-11ec-88f6-293354dba871: 'Mine' - x_content_parse_exception: [parsing_exception] Reason: unknown query [match_allxxxx] did you mean [match_all]?",
|
||||
};
|
||||
};
|
||||
|
||||
const baseRulesListGetResponse = (path: string) => {
|
||||
if (path === '/internal/triggers_actions_ui/_config') {
|
||||
return mockConfig;
|
||||
}
|
||||
if (path === '/internal/triggers_actions_ui/_health') {
|
||||
return mockHealth;
|
||||
}
|
||||
if (path === '/api/actions/connectors') {
|
||||
return mockConnectors;
|
||||
}
|
||||
if (path === '/api/alerting/rule_types') {
|
||||
return mockRuleTypes;
|
||||
}
|
||||
if (path === '/api/actions/connector_types') {
|
||||
return mockConnectorTypes;
|
||||
}
|
||||
if (path === '/internal/alerting/rules/_aggregate') {
|
||||
return mockAggregation;
|
||||
}
|
||||
};
|
||||
|
||||
const emptyRulesListGetResponse = (path: string) => {
|
||||
if (path === '/internal/alerting/rules/_find') {
|
||||
return {
|
||||
data: [],
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
return baseRulesListGetResponse(path);
|
||||
};
|
||||
|
||||
const rulesListGetResponse = (path: string) => {
|
||||
if (path === '/internal/alerting/rules/_find') {
|
||||
return {
|
||||
data: [getMockRule(), getMockRule(), getMockRule(), getMockRule()],
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 4,
|
||||
};
|
||||
}
|
||||
return baseRulesListGetResponse(path);
|
||||
};
|
||||
|
||||
const rulesListGetPaginatedResponse = (path: string) => {
|
||||
if (path === '/internal/alerting/rules/_find') {
|
||||
return {
|
||||
data: Array.from(Array(10), () => getMockRule()),
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 50,
|
||||
};
|
||||
}
|
||||
return baseRulesListGetResponse(path);
|
||||
};
|
||||
|
||||
const baseEventLogListGetResponse = (path: string) => {
|
||||
if (path.endsWith('/_alert_summary')) {
|
||||
return {
|
||||
...mockRuleSummary,
|
||||
execution_duration: {
|
||||
...mockRuleSummary.execution_duration,
|
||||
valuesWithTimestamp: {
|
||||
'2022-08-18T23:07:28.662Z': 68,
|
||||
'2022-08-18T23:07:29.662Z': 59,
|
||||
'2022-08-18T23:07:30.662Z': 20,
|
||||
'2022-08-18T23:07:31.662Z': 140,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
if (path.endsWith('/_action_error_log')) {
|
||||
return {
|
||||
errors: Array.from(Array(4), () => getMockErrorLog()),
|
||||
totalErrors: 4,
|
||||
};
|
||||
}
|
||||
if (path.endsWith('/_execution_kpi')) {
|
||||
return {
|
||||
activeAlerts: 49,
|
||||
erroredActions: 36,
|
||||
failure: 30,
|
||||
newAlerts: 1,
|
||||
recoveredAlerts: 20,
|
||||
success: 49,
|
||||
triggeredActions: 49,
|
||||
unknown: 10,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const emptyEventLogListGetResponse = (path: string) => {
|
||||
if (path.endsWith('/_alert_summary')) {
|
||||
return mockRuleSummary;
|
||||
}
|
||||
if (path.endsWith('/_execution_log')) {
|
||||
return {
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
return baseEventLogListGetResponse(path);
|
||||
};
|
||||
|
||||
const eventLogListGetResponse = (path: string) => {
|
||||
if (path.endsWith('/_execution_log')) {
|
||||
return mockLogResponse;
|
||||
}
|
||||
return baseEventLogListGetResponse(path);
|
||||
};
|
||||
|
||||
const paginatedEventLogListGetResponse = (path: string) => {
|
||||
if (path.endsWith('/_execution_log')) {
|
||||
return {
|
||||
data: Array.from(Array(10), () => getMockLogResponse()),
|
||||
total: 500,
|
||||
};
|
||||
}
|
||||
return baseEventLogListGetResponse(path);
|
||||
};
|
||||
|
||||
export const getHttp = (context: Parameters<DecoratorFn>[1]) => {
|
||||
return {
|
||||
get: (async (path: string, options: HttpFetchOptions) => {
|
||||
const { id } = context;
|
||||
if (id === 'app-ruleslist--empty') {
|
||||
return emptyRulesListGetResponse(path);
|
||||
}
|
||||
if (id === 'app-ruleslist--with-rules') {
|
||||
return rulesListGetResponse(path);
|
||||
}
|
||||
if (id === 'app-ruleslist--with-paginated-rules') {
|
||||
return rulesListGetPaginatedResponse(path);
|
||||
}
|
||||
if (id === 'app-ruleeventloglist--empty') {
|
||||
return emptyEventLogListGetResponse(path);
|
||||
}
|
||||
if (id === 'app-ruleeventloglist--with-events') {
|
||||
return eventLogListGetResponse(path);
|
||||
}
|
||||
if (id === 'app-ruleeventloglist--with-paginated-events') {
|
||||
return paginatedEventLogListGetResponse(path);
|
||||
}
|
||||
}) as HttpHandler,
|
||||
post: (async (path: string, options: HttpFetchOptions) => {
|
||||
action('POST')(path, options);
|
||||
}) as HttpHandler,
|
||||
} as unknown as HttpStart;
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const mockRuleType = {
|
||||
id: 'test_rule_type',
|
||||
iconClass: 'test',
|
||||
description: 'Rule when testing',
|
||||
documentationUrl: 'https://localhost.local/docs',
|
||||
validate: () => {
|
||||
return { errors: {} };
|
||||
},
|
||||
ruleParamsExpression: () => null,
|
||||
requiresAppContext: false,
|
||||
};
|
||||
|
||||
export const getRuleTypeRegistry = () => {
|
||||
return {
|
||||
has: () => true,
|
||||
register: () => {},
|
||||
get: () => {
|
||||
return mockRuleType;
|
||||
},
|
||||
list: () => {
|
||||
return [mockRuleType];
|
||||
},
|
||||
};
|
||||
};
|
124
x-pack/plugins/triggers_actions_ui/.storybook/decorator.tsx
Normal file
124
x-pack/plugins/triggers_actions_ui/.storybook/decorator.tsx
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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 uuid from 'uuid';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { DecoratorFn } from '@storybook/react';
|
||||
import { EMPTY, of } from 'rxjs';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import type { NotificationsStart, ApplicationStart } from '@kbn/core/public';
|
||||
import { KibanaContextProvider } from '../public/common/lib/kibana';
|
||||
import { ExperimentalFeaturesService } from '../public/common/experimental_features_service';
|
||||
import { getHttp } from './context/http';
|
||||
import { getRuleTypeRegistry } from './context/rule_type_registry';
|
||||
import { getActionTypeRegistry } from './context/action_type_registry';
|
||||
|
||||
interface StorybookContextDecoratorProps {
|
||||
context: Parameters<DecoratorFn>[1];
|
||||
}
|
||||
|
||||
const handler = (type: string, ...rest: any[]) => {
|
||||
action(`${type} Toast`)(rest);
|
||||
return { id: uuid() };
|
||||
};
|
||||
|
||||
const notifications: NotificationsStart = {
|
||||
toasts: {
|
||||
add: (params) => handler('add', params),
|
||||
addDanger: (params) => handler('danger', params),
|
||||
addError: (params) => handler('error', params),
|
||||
addWarning: (params) => handler('warning', params),
|
||||
addSuccess: (params) => handler('success', params),
|
||||
addInfo: (params) => handler('info', params),
|
||||
remove: () => {},
|
||||
get$: () => of([]),
|
||||
},
|
||||
};
|
||||
|
||||
const applications = new Map();
|
||||
|
||||
const application: ApplicationStart = {
|
||||
currentAppId$: of('fleet'),
|
||||
navigateToUrl: async (url: string) => {
|
||||
action(`Navigate to: ${url}`);
|
||||
},
|
||||
navigateToApp: async (app: string) => {
|
||||
action(`Navigate to: ${app}`);
|
||||
},
|
||||
getUrlForApp: (url: string) => url,
|
||||
capabilities: {
|
||||
actions: {
|
||||
show: true,
|
||||
save: true,
|
||||
execute: true,
|
||||
delete: true,
|
||||
},
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
fleet: {
|
||||
read: true,
|
||||
all: true,
|
||||
},
|
||||
fleetv2: {
|
||||
read: true,
|
||||
all: true,
|
||||
},
|
||||
},
|
||||
applications$: of(applications),
|
||||
};
|
||||
|
||||
export const StorybookContextDecorator: React.FC<StorybookContextDecoratorProps> = (props) => {
|
||||
const { children, context } = props;
|
||||
const { globals } = context;
|
||||
const { euiTheme } = globals;
|
||||
|
||||
const darkMode = ['v8.dark', 'v7.dark'].includes(euiTheme);
|
||||
ExperimentalFeaturesService.init({
|
||||
experimentalFeatures: {
|
||||
rulesListDatagrid: true,
|
||||
internalAlertsTable: true,
|
||||
ruleTagFilter: true,
|
||||
ruleStatusFilter: true,
|
||||
rulesDetailLogs: true,
|
||||
},
|
||||
});
|
||||
return (
|
||||
<I18nProvider>
|
||||
<EuiThemeProvider darkMode={darkMode}>
|
||||
<KibanaThemeProvider theme$={EMPTY}>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
notifications,
|
||||
uiSettings: {
|
||||
get: () => {
|
||||
if (context.componentId === 'app-ruleslist') {
|
||||
return 'format:number:defaultPattern';
|
||||
}
|
||||
},
|
||||
get$: () => {
|
||||
if (context.componentId === 'app-ruleslist') {
|
||||
return of('format:number:defaultPattern');
|
||||
}
|
||||
},
|
||||
},
|
||||
application,
|
||||
http: getHttp(context),
|
||||
actionTypeRegistry: getActionTypeRegistry(),
|
||||
ruleTypeRegistry: getRuleTypeRegistry(),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</KibanaContextProvider>
|
||||
</KibanaThemeProvider>
|
||||
</EuiThemeProvider>
|
||||
</I18nProvider>
|
||||
);
|
||||
};
|
|
@ -5,4 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
module.exports = require('@kbn/storybook').defaultConfig;
|
||||
import { defaultConfig } from '@kbn/storybook';
|
||||
|
||||
module.exports = defaultConfig;
|
20
x-pack/plugins/triggers_actions_ui/.storybook/manager.ts
Normal file
20
x-pack/plugins/triggers_actions_ui/.storybook/manager.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 { addons } from '@storybook/addons';
|
||||
import { create } from '@storybook/theming';
|
||||
import { PANEL_ID } from '@storybook/addon-actions';
|
||||
|
||||
addons.setConfig({
|
||||
theme: create({
|
||||
base: 'light',
|
||||
brandTitle: 'Triggers Actions UI Storybook',
|
||||
brandUrl: 'https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui',
|
||||
}),
|
||||
showPanel: true,
|
||||
selectedPanel: PANEL_ID,
|
||||
});
|
31
x-pack/plugins/triggers_actions_ui/.storybook/preview.tsx
Normal file
31
x-pack/plugins/triggers_actions_ui/.storybook/preview.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 { addDecorator, DecoratorFn } from '@storybook/react';
|
||||
import { Title, Subtitle, Description, Primary, Stories } from '@storybook/addon-docs';
|
||||
import { StorybookContextDecorator } from './decorator';
|
||||
|
||||
const decorator: DecoratorFn = (story, context) => {
|
||||
return <StorybookContextDecorator context={context}>{story()}</StorybookContextDecorator>;
|
||||
};
|
||||
|
||||
addDecorator(decorator);
|
||||
|
||||
export const parameters = {
|
||||
docs: {
|
||||
page: () => {
|
||||
<>
|
||||
<Title />
|
||||
<Subtitle />
|
||||
<Description />
|
||||
<Primary />
|
||||
<Stories />
|
||||
</>;
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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, { ComponentProps } from 'react';
|
||||
import { Meta } from '@storybook/react';
|
||||
import { RuleEventLogList, RuleEventLogListProps } from './rule_event_log_list';
|
||||
import { mockRule, mockRuleType } from './test_helpers';
|
||||
|
||||
type Args = ComponentProps<typeof RuleEventLogList>;
|
||||
|
||||
const rule = mockRule({ ruleTypeId: 'test-rule-type-id' });
|
||||
const ruleType = mockRuleType();
|
||||
|
||||
export default {
|
||||
title: 'app/RuleEventLogList',
|
||||
component: RuleEventLogList,
|
||||
argTypes: {
|
||||
rule: {
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
ruleType: {
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
localStorageKey: {
|
||||
defaultValue: 'xpack.triggersActionsUI.ruleEventLogList.initialColumns',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
refreshToken: {
|
||||
control: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
requestRefresh: {},
|
||||
fetchRuleSummary: {
|
||||
defaultValue: true,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
ruleSummary: {
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
onChangeDuration: {},
|
||||
numberOfExecutions: {
|
||||
control: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
isLoadingRuleSummary: {
|
||||
defaultValue: false,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
args: {
|
||||
rule,
|
||||
ruleType,
|
||||
},
|
||||
} as Meta<Args>;
|
||||
|
||||
const Template = (args: RuleEventLogListProps) => {
|
||||
return <RuleEventLogList {...args} />;
|
||||
};
|
||||
|
||||
export const Empty = Template.bind({});
|
||||
|
||||
export const WithEvents = Template.bind({});
|
||||
|
||||
export const WithPaginatedEvents = Template.bind({});
|
|
@ -19,7 +19,7 @@ import {
|
|||
RULE_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS,
|
||||
GLOBAL_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS,
|
||||
} from '../../../constants';
|
||||
import { mockRule, mockRuleType, mockRuleSummary } from './test_helpers';
|
||||
import { mockRule, mockRuleType, mockRuleSummary, mockLogResponse } from './test_helpers';
|
||||
import { RuleType } from '../../../../types';
|
||||
import { loadActionErrorLog } from '../../../lib/rule_api/load_action_error_log';
|
||||
|
||||
|
@ -33,84 +33,6 @@ const loadActionErrorLogMock = loadActionErrorLog as unknown as jest.MockedFunct
|
|||
typeof loadActionErrorLog
|
||||
>;
|
||||
|
||||
const mockLogResponse: any = {
|
||||
data: [
|
||||
{
|
||||
id: uuid.v4(),
|
||||
timestamp: '2022-03-20T07:40:44-07:00',
|
||||
duration: 5000000,
|
||||
status: 'success',
|
||||
message: 'rule execution #1',
|
||||
version: '8.2.0',
|
||||
num_active_alerts: 2,
|
||||
num_new_alerts: 4,
|
||||
num_recovered_alerts: 3,
|
||||
num_triggered_actions: 10,
|
||||
num_succeeded_actions: 0,
|
||||
num_errored_actions: 4,
|
||||
total_search_duration: 1000000,
|
||||
es_search_duration: 1400000,
|
||||
schedule_delay: 2000000,
|
||||
timed_out: false,
|
||||
},
|
||||
{
|
||||
id: uuid.v4(),
|
||||
timestamp: '2022-03-20T07:40:45-07:00',
|
||||
duration: 6000000,
|
||||
status: 'success',
|
||||
message: 'rule execution #2',
|
||||
version: '8.2.0',
|
||||
num_active_alerts: 4,
|
||||
num_new_alerts: 2,
|
||||
num_recovered_alerts: 4,
|
||||
num_triggered_actions: 5,
|
||||
num_succeeded_actions: 3,
|
||||
num_errored_actions: 0,
|
||||
total_search_duration: 300000,
|
||||
es_search_duration: 300000,
|
||||
schedule_delay: 300000,
|
||||
timed_out: false,
|
||||
},
|
||||
{
|
||||
id: uuid.v4(),
|
||||
timestamp: '2022-03-20T07:40:46-07:00',
|
||||
duration: 340000,
|
||||
status: 'failure',
|
||||
message: 'rule execution #3',
|
||||
version: '8.2.0',
|
||||
num_active_alerts: 8,
|
||||
num_new_alerts: 5,
|
||||
num_recovered_alerts: 0,
|
||||
num_triggered_actions: 1,
|
||||
num_succeeded_actions: 1,
|
||||
num_errored_actions: 4,
|
||||
total_search_duration: 2300000,
|
||||
es_search_duration: 2300000,
|
||||
schedule_delay: 2300000,
|
||||
timed_out: false,
|
||||
},
|
||||
{
|
||||
id: uuid.v4(),
|
||||
timestamp: '2022-03-21T07:40:46-07:00',
|
||||
duration: 3000000,
|
||||
status: 'unknown',
|
||||
message: 'rule execution #4',
|
||||
version: '8.2.0',
|
||||
num_active_alerts: 4,
|
||||
num_new_alerts: 4,
|
||||
num_recovered_alerts: 4,
|
||||
num_triggered_actions: 4,
|
||||
num_succeeded_actions: 4,
|
||||
num_errored_actions: 4,
|
||||
total_search_duration: 400000,
|
||||
es_search_duration: 400000,
|
||||
schedule_delay: 400000,
|
||||
timed_out: false,
|
||||
},
|
||||
],
|
||||
total: 4,
|
||||
};
|
||||
|
||||
const loadExecutionLogAggregationsMock = jest.fn();
|
||||
|
||||
const onChangeDurationMock = jest.fn();
|
||||
|
|
|
@ -8,6 +8,32 @@
|
|||
import uuid from 'uuid';
|
||||
import { Rule, RuleSummary, RuleType } from '../../../../types';
|
||||
|
||||
export const getMockLogResponse = () => {
|
||||
return {
|
||||
id: uuid.v4(),
|
||||
timestamp: '2022-03-20T07:40:44-07:00',
|
||||
duration: 5000000,
|
||||
status: 'success',
|
||||
message: 'rule execution #1',
|
||||
version: '8.2.0',
|
||||
num_active_alerts: 2,
|
||||
num_new_alerts: 4,
|
||||
num_recovered_alerts: 3,
|
||||
num_triggered_actions: 10,
|
||||
num_succeeded_actions: 0,
|
||||
num_errored_actions: 4,
|
||||
total_search_duration: 1000000,
|
||||
es_search_duration: 1400000,
|
||||
schedule_delay: 2000000,
|
||||
timed_out: false,
|
||||
};
|
||||
};
|
||||
|
||||
export const mockLogResponse: any = {
|
||||
data: [getMockLogResponse(), getMockLogResponse(), getMockLogResponse(), getMockLogResponse()],
|
||||
total: 4,
|
||||
};
|
||||
|
||||
export function mockRule(overloads: Partial<Rule> = {}): Rule {
|
||||
return {
|
||||
id: uuid.v4(),
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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, { ComponentProps } from 'react';
|
||||
import { Story } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { RuleStatusDropdown } from './rule_status_dropdown';
|
||||
import { mockRule } from '../../rule_details/components/test_helpers';
|
||||
|
||||
type Args = ComponentProps<typeof RuleStatusDropdown>;
|
||||
|
||||
const rule = mockRule({ ruleTypeId: 'test-rule-type-id' });
|
||||
|
||||
export default {
|
||||
title: 'app/RuleStatusDropdown',
|
||||
component: RuleStatusDropdown,
|
||||
argTypes: {
|
||||
rule: {
|
||||
defaultValue: rule,
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
onRuleChanged: {},
|
||||
enableRule: {},
|
||||
disableRule: {},
|
||||
snoozeRule: {},
|
||||
unsnoozeRule: {},
|
||||
isEditable: {
|
||||
defaultValue: true,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
direction: {
|
||||
defaultValue: 'column',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
hideSnoozeOption: {
|
||||
defaultValue: false,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
args: {
|
||||
rule,
|
||||
onRuleChanged: (...args: any) => action('onRuleChanged')(args),
|
||||
enableRule: (...args: any) => action('enableRule')(args),
|
||||
disableRule: (...args: any) => action('disableRule')(args),
|
||||
snoozeRule: (...args: any) => action('snoozeRule')(args),
|
||||
unsnoozeRule: (...args: any) => action('unsnoozeRule')(args),
|
||||
},
|
||||
};
|
||||
|
||||
const Template: Story<Args> = (args) => {
|
||||
return <RuleStatusDropdown {...args} />;
|
||||
};
|
||||
|
||||
export const EnabledRule = Template.bind({});
|
||||
|
||||
export const DisabledRule = Template.bind({});
|
||||
|
||||
DisabledRule.args = {
|
||||
rule: mockRule({ enabled: false }),
|
||||
};
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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, { ComponentProps } from 'react';
|
||||
import { Story } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { RuleTagBadge } from './rule_tag_badge';
|
||||
|
||||
type Args = ComponentProps<typeof RuleTagBadge>;
|
||||
|
||||
export default {
|
||||
title: 'app/RuleTagBadge',
|
||||
component: RuleTagBadge,
|
||||
argTypes: {
|
||||
isOpen: {
|
||||
defaultValue: false,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
onClick: {},
|
||||
onClose: {},
|
||||
tagsOutPopover: {
|
||||
defaultValue: false,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
tags: {
|
||||
defaultValue: ['tag1', 'tag2', 'tag3'],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
badgeDataTestSubj: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
titleDataTestSubj: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
tagItemDataTestSubj: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
args: {
|
||||
onClick: () => action('onClick')(),
|
||||
onClose: () => action('onClose')(),
|
||||
},
|
||||
};
|
||||
|
||||
const Template: Story<Args> = (args) => {
|
||||
return <RuleTagBadge {...args} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
export const OutPopover = Template.bind({});
|
||||
OutPopover.args = {
|
||||
tagsOutPopover: true,
|
||||
};
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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, { ComponentProps } from 'react';
|
||||
import { Story } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { RuleTagFilter } from './rule_tag_filter';
|
||||
|
||||
type Args = ComponentProps<typeof RuleTagFilter>;
|
||||
|
||||
export default {
|
||||
title: 'app/RuleTagFilter',
|
||||
component: RuleTagFilter,
|
||||
argTypes: {
|
||||
tags: {
|
||||
defaultValue: ['tag1', 'tag2', 'tag3'],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
selectedTags: {
|
||||
defaultValue: [],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
isGrouped: {
|
||||
defaultValue: false,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
isLoading: {
|
||||
defaultValue: false,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
loadingMessage: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
noMatchesMessage: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
emptyMessage: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
errorMessage: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
dataTestSubj: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
selectableDataTestSubj: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
optionDataTestSubj: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
buttonDataTestSubj: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
onChange: {},
|
||||
},
|
||||
args: {
|
||||
onChange: (...args: any) => action('onChange')(args),
|
||||
},
|
||||
};
|
||||
|
||||
const Template: Story<Args> = (args) => {
|
||||
return <RuleTagFilter {...args} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
export const Selected = Template.bind({});
|
||||
|
||||
Selected.args = {
|
||||
selectedTags: ['tag1'],
|
||||
};
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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, { ComponentProps, useEffect } from 'react';
|
||||
import { Meta } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||
import { RulesList, RulesListProps } from './rules_list';
|
||||
|
||||
type Args = ComponentProps<typeof RulesList>;
|
||||
|
||||
export default {
|
||||
title: 'app/RulesList',
|
||||
component: RulesList,
|
||||
decorators: [
|
||||
(StoryComponent) => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<StoryComponent />
|
||||
</MemoryRouter>
|
||||
);
|
||||
},
|
||||
],
|
||||
argTypes: {
|
||||
filteredRuleTypes: {
|
||||
defaultValue: [],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
showActionFilter: {
|
||||
defaultValue: true,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
showCreateRuleButton: {
|
||||
defaultValue: true,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
ruleDetailsRoute: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
statusFilter: {
|
||||
defaultValue: [],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
lastResponseFilter: {
|
||||
defaultValue: [],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
onStatusFilterChange: {
|
||||
action: 'onStatusFilterChange',
|
||||
},
|
||||
onLastResponseFilterChange: {
|
||||
action: 'onLastResponseFilterChange',
|
||||
},
|
||||
refresh: {
|
||||
control: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
rulesListKey: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
visibleColumns: {
|
||||
defaultValue: [
|
||||
'ruleName',
|
||||
'ruleTags',
|
||||
'ruleExecutionStatusLastDate',
|
||||
'ruleSnoozeNotify',
|
||||
'ruleScheduleInterval',
|
||||
'ruleExecutionStatusLastDuration',
|
||||
'ruleExecutionPercentile',
|
||||
'ruleExecutionSuccessRatio',
|
||||
'ruleExecutionStatus',
|
||||
'ruleExecutionState',
|
||||
],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
} as Meta<Args>;
|
||||
|
||||
const Template = (args: RulesListProps) => {
|
||||
const location = useLocation();
|
||||
useEffect(() => {
|
||||
action('location')(location);
|
||||
}, [location]);
|
||||
return <RulesList {...args} />;
|
||||
};
|
||||
|
||||
export const Empty = Template.bind({});
|
||||
|
||||
export const WithRules = Template.bind({});
|
||||
|
||||
export const WithPaginatedRules = Template.bind({});
|
|
@ -7,6 +7,7 @@
|
|||
"declarationMap": true
|
||||
},
|
||||
"include": [
|
||||
".storybook/**/*",
|
||||
"server/**/*",
|
||||
"public/**/*",
|
||||
"common/**/*",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue