[Actionable Observability] - Rule Details Page - Update tag component to show tags out of the popover as an option (#134468)

* Add spread prop to tag

* Use spread prop in Rule Details page

* Add unit test for spread prop

* Update tag props type

* Fix type guard

* use generic type to make code cleaner

* Updating var name

* Update data test obj

* [Code Review] Fix lazy import

Co-authored-by: Xavier Mouligneau <xavier.mouligneau@elastic.co>
This commit is contained in:
Faisal Kanout 2022-06-23 20:50:08 +03:00 committed by GitHub
parent 5989f1df49
commit 6aa6a50e30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 76 additions and 37 deletions

View file

@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useState } from 'react';
import React from 'react';
import moment from 'moment';
import { EuiText, EuiFlexGroup, EuiFlexItem, EuiBadge, EuiSpacer } from '@elastic/eui';
import { PageHeaderProps } from '../types';
@ -14,12 +14,7 @@ import { getHealthColor } from '../../rules/config';
export function PageTitle({ rule }: PageHeaderProps) {
const { triggersActionsUi } = useKibana().services;
const [isTagsPopoverOpen, setIsTagsPopoverOpen] = useState<boolean>(false);
const tagsClicked = () =>
setIsTagsPopoverOpen(
(oldStateIsTagsPopoverOpen) => rule.tags.length > 0 && !oldStateIsTagsPopoverOpen
);
const closeTagsPopover = () => setIsTagsPopoverOpen(false);
return (
<>
<EuiFlexGroup alignItems="center">
@ -37,7 +32,7 @@ export function PageTitle({ rule }: PageHeaderProps) {
</EuiText>
<EuiSpacer size="s" />
</EuiFlexItem>
<EuiFlexGroup alignItems="baseline">
<EuiFlexGroup direction="column" alignItems="flexStart">
<EuiFlexItem component="span" grow={false}>
<EuiText color="subdued" size="xs">
<b>{LAST_UPDATED_MESSAGE}</b> {BY_WORD} {rule.updatedBy} {ON_WORD}&nbsp;
@ -46,15 +41,14 @@ export function PageTitle({ rule }: PageHeaderProps) {
{moment(rule.createdAt).format('ll')}
</EuiText>
</EuiFlexItem>
{rule.tags.length > 0 &&
triggersActionsUi.getRuleTagBadge({
isOpen: isTagsPopoverOpen,
tags: rule.tags,
onClick: () => tagsClicked(),
onClose: () => closeTagsPopover(),
})}
<EuiSpacer size="xs" />
</EuiFlexGroup>
{rule.tags.length > 0 &&
triggersActionsUi.getRuleTagBadge<'tagsOutPopover'>({
tagsOutPopover: true,
tags: rule.tags,
})}
<EuiSpacer size="xs" />
</>
);
}

View file

@ -199,7 +199,7 @@ function RulesPage() {
'data-test-subj': 'rulesTableCell-tagsPopover',
render: (ruleTags: string[], item: RuleTableItem) => {
return ruleTags.length > 0
? triggersActionsUi.getRuleTagBadge({
? triggersActionsUi.getRuleTagBadge<'default'>({
isOpen: tagPopoverOpenIndex === item.index,
tags: ruleTags,
onClick: () => setTagPopoverOpenIndex(item.index),

View file

@ -40,9 +40,6 @@ export const RuleTagFilter = suspendedComponentWithProps(
export const RuleStatusFilter = suspendedComponentWithProps(
lazy(() => import('./rules_list/components/rule_status_filter'))
);
export const RuleTagBadge = suspendedComponentWithProps(
lazy(() => import('./rules_list/components/rule_tag_badge'))
);
export const RuleEventLogList = suspendedComponentWithProps(
lazy(() => import('./rule_details/components/rule_event_log_list'))
);
@ -52,3 +49,6 @@ export const RulesList = suspendedComponentWithProps(
export const RulesListNotifyBadge = suspendedComponentWithProps(
lazy(() => import('./rules_list/components/rules_list_notify_badge'))
);
export const RuleTagBadge = suspendedComponentWithProps(
lazy(() => import('./rules_list/components/rule_tag_badge'))
);

View file

@ -56,4 +56,12 @@ describe('RuleTagBadge', () => {
expect(onClickMock).toHaveBeenCalledTimes(2);
});
it('shows all the tags without clicking when passing "spread" props with "true"', () => {
const wrapper = mountWithIntl(<RuleTagBadge tags={tags} tagsOutPopover={true} />);
expect(wrapper.find('[data-test-subj="tagsOutPopover"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-a"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-b"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-c"]').exists()).toBeTruthy();
});
});

View file

@ -7,7 +7,7 @@
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiPopover, EuiBadge, EuiPopoverTitle } from '@elastic/eui';
import { EuiPopover, EuiBadge, EuiPopoverTitle, EuiFlexGroup } from '@elastic/eui';
const tagTitle = i18n.translate(
'xpack.triggersActionsUI.sections.rules_list.rules_tag_badge.tagTitle',
@ -16,32 +16,43 @@ const tagTitle = i18n.translate(
}
);
export interface RuleTagBadgeProps {
export type RuleTagBadgeOptions = 'tagsOutPopover' | 'default';
export interface RuleTagBadgeBasicOptions {
isOpen: boolean;
tags: string[];
onClick: React.MouseEventHandler<HTMLButtonElement>;
onClose: () => void;
}
export interface RuleTagBadgeCommonProps {
tagsOutPopover?: boolean;
tags: string[];
badgeDataTestSubj?: string;
titleDataTestSubj?: string;
tagItemDataTestSubj?: (tag: string) => string;
}
export type RuleTagBadgeProps<T extends RuleTagBadgeOptions = 'default'> = T extends 'default'
? RuleTagBadgeBasicOptions & RuleTagBadgeCommonProps
: T extends 'tagsOutPopover'
? RuleTagBadgeCommonProps
: never;
const containerStyle = {
width: '300px',
};
const getTagItemDataTestSubj = (tag: string) => `ruleTagBadgeItem-${tag}`;
export const RuleTagBadge = (props: RuleTagBadgeProps) => {
export const RuleTagBadge = <T extends RuleTagBadgeOptions>(props: RuleTagBadgeProps<T>) => {
const {
isOpen = false,
tagsOutPopover = false,
tags = [],
onClick,
onClose,
badgeDataTestSubj = 'ruleTagBadge',
titleDataTestSubj = 'ruleTagPopoverTitle',
tagItemDataTestSubj = getTagItemDataTestSubj,
} = props;
const { isOpen, onClose, onClick } = props as RuleTagBadgeBasicOptions;
const badge = useMemo(() => {
return (
@ -59,7 +70,7 @@ export const RuleTagBadge = (props: RuleTagBadgeProps) => {
{tags.length}
</EuiBadge>
);
}, [tags, badgeDataTestSubj, onClick]);
}, [badgeDataTestSubj, onClick, tags.length]);
const tagBadges = useMemo(
() =>
@ -76,9 +87,22 @@ export const RuleTagBadge = (props: RuleTagBadgeProps) => {
)),
[tags, tagItemDataTestSubj]
);
if (tagsOutPopover) {
return (
// Put 0 to fix negative left margin value.
<EuiFlexGroup data-test-subj="tagsOutPopover" style={{ marginLeft: 0 }} wrap={true}>
{tagBadges}
</EuiFlexGroup>
);
}
return (
<EuiPopover button={badge} anchorPosition="upCenter" isOpen={isOpen} closePopover={onClose}>
<EuiPopover
button={badge}
anchorPosition="upCenter"
isOpen={isOpen} // The props exists as it's required in props types
closePopover={onClose}
>
<EuiPopoverTitle data-test-subj={titleDataTestSubj}>{tagTitle}</EuiPopoverTitle>
<div style={containerStyle}>{tagBadges}</div>
</EuiPopover>

View file

@ -6,9 +6,13 @@
*/
import React from 'react';
import {
RuleTagBadgeProps,
RuleTagBadgeOptions,
} from '../application/sections/rules_list/components/rule_tag_badge';
import { RuleTagBadge } from '../application/sections';
import type { RuleTagBadgeProps } from '../application/sections/rules_list/components/rule_tag_badge';
export const getRuleTagBadgeLazy = (props: RuleTagBadgeProps) => {
export const getRuleTagBadgeLazy = <T extends RuleTagBadgeOptions = 'default'>(
props: RuleTagBadgeProps<T>
) => {
return <RuleTagBadge {...props} />;
};

View file

@ -21,6 +21,8 @@ import {
RuleTypeModel,
AlertsTableProps,
AlertsTableConfigurationRegistry,
RuleTagBadgeOptions,
RuleTagBadgeProps,
} from './types';
import { getAlertsTableLazy } from './common/get_alerts_table';
import { getRuleStatusDropdownLazy } from './common/get_rule_status_dropdown';
@ -90,8 +92,8 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart {
getRuleStatusFilter: (props) => {
return getRuleStatusFilterLazy(props);
},
getRuleTagBadge: (props) => {
return getRuleTagBadgeLazy(props);
getRuleTagBadge: <T extends RuleTagBadgeOptions>(props: RuleTagBadgeProps<T>) => {
return getRuleTagBadgeLazy<T>(props);
},
getRuleEventLogList: (props) => {
return getRuleEventLogListLazy(props);

View file

@ -54,6 +54,7 @@ import type {
RuleTagFilterProps,
RuleStatusFilterProps,
RuleTagBadgeProps,
RuleTagBadgeOptions,
RuleEventLogListProps,
RulesListNotifyBadgeProps,
AlertsTableConfigurationRegistry,
@ -98,7 +99,9 @@ export interface TriggersAndActionsUIPublicPluginStart {
getRuleStatusDropdown: (props: RuleStatusDropdownProps) => ReactElement<RuleStatusDropdownProps>;
getRuleTagFilter: (props: RuleTagFilterProps) => ReactElement<RuleTagFilterProps>;
getRuleStatusFilter: (props: RuleStatusFilterProps) => ReactElement<RuleStatusFilterProps>;
getRuleTagBadge: (props: RuleTagBadgeProps) => ReactElement<RuleTagBadgeProps>;
getRuleTagBadge: <T extends RuleTagBadgeOptions>(
props: RuleTagBadgeProps<T>
) => ReactElement<RuleTagBadgeProps<T>>;
getRuleEventLogList: (props: RuleEventLogListProps) => ReactElement<RuleEventLogListProps>;
getRulesListNotifyBadge: (
props: RulesListNotifyBadgeProps
@ -305,7 +308,7 @@ export class Plugin
getRuleStatusFilter: (props: RuleStatusFilterProps) => {
return getRuleStatusFilterLazy(props);
},
getRuleTagBadge: (props: RuleTagBadgeProps) => {
getRuleTagBadge: <T extends RuleTagBadgeOptions>(props: RuleTagBadgeProps<T>) => {
return getRuleTagBadgeLazy(props);
},
getRuleEventLogList: (props: RuleEventLogListProps) => {

View file

@ -50,7 +50,10 @@ import { TypeRegistry } from './application/type_registry';
import type { ComponentOpts as RuleStatusDropdownProps } from './application/sections/rules_list/components/rule_status_dropdown';
import type { RuleTagFilterProps } from './application/sections/rules_list/components/rule_tag_filter';
import type { RuleStatusFilterProps } from './application/sections/rules_list/components/rule_status_filter';
import type { RuleTagBadgeProps } from './application/sections/rules_list/components/rule_tag_badge';
import type {
RuleTagBadgeProps,
RuleTagBadgeOptions,
} from './application/sections/rules_list/components/rule_tag_badge';
import type { RuleEventLogListProps } from './application/sections/rule_details/components/rule_event_log_list';
import type { CreateConnectorFlyoutProps } from './application/sections/action_connector_form/create_connector_flyout';
import type { EditConnectorFlyoutProps } from './application/sections/action_connector_form/edit_connector_flyout';
@ -90,6 +93,7 @@ export type {
RuleTagFilterProps,
RuleStatusFilterProps,
RuleTagBadgeProps,
RuleTagBadgeOptions,
RuleEventLogListProps,
CreateConnectorFlyoutProps,
EditConnectorFlyoutProps,