[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:
Jiawei Wu 2022-10-03 12:53:10 -07:00 committed by GitHub
parent 059fecd311
commit 3bad88157a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 986 additions and 83 deletions

View file

@ -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: () => [],
};
};

View 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;
};

View 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.
*/
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];
},
};
};

View 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>
);
};

View file

@ -5,4 +5,6 @@
* 2.0.
*/
module.exports = require('@kbn/storybook').defaultConfig;
import { defaultConfig } from '@kbn/storybook';
module.exports = defaultConfig;

View 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,
});

View 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 />
</>;
},
},
};

View file

@ -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({});

View file

@ -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();

View file

@ -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(),

View file

@ -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 }),
};

View file

@ -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,
};

View file

@ -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'],
};

View file

@ -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({});

View file

@ -7,6 +7,7 @@
"declarationMap": true
},
"include": [
".storybook/**/*",
"server/**/*",
"public/**/*",
"common/**/*",