[8.4] [Kubernetes Security] Add beta tag to dashboard kubernetes title and grouped nav (#137211) (#137490)

* Add beta tag to dashboard kubernetes title and grouped nav

* Add props used to BetaBadge

* Fix PR comments

* Implement workaround for eui beta badge type

* Move flex link to styles file and comment

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit bb93f02740)

Co-authored-by: Jack <zizhou.wang@elastic.co>
This commit is contained in:
Kibana Machine 2022-07-28 13:30:03 -04:00 committed by GitHub
parent 6adce2d03f
commit 4471e81098
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 114 additions and 9 deletions

View file

@ -31,6 +31,7 @@ const formatNavLinkItems = (appLinks: AppLinkItems): NavLinkItem[] =>
...(link.landingIcon != null ? { icon: link.landingIcon } : {}),
...(link.landingImage != null ? { image: link.landingImage } : {}),
...(link.skipUrlState != null ? { skipUrlState: link.skipUrlState } : {}),
...(link.isBeta != null ? { isBeta: link.isBeta } : {}),
...(link.links && link.links.length
? {
links: formatNavLinkItems(link.links),

View file

@ -24,6 +24,7 @@ const manageNavLink: NavLinkItem = {
id: SecurityPageName.endpoints,
title: 'title 2',
description: 'description 2',
isBeta: true,
},
],
};
@ -129,6 +130,7 @@ describe('SecuritySideNav', () => {
label: 'title 2',
description: 'description 2',
href: '/endpoints',
isBeta: true,
},
],
},
@ -148,6 +150,7 @@ describe('SecuritySideNav', () => {
title: 'title 2',
description: 'description 2',
disabled: true,
isBeta: true,
},
],
},

View file

@ -77,6 +77,7 @@ const useFormatSideNavItem = (): FormatSideNavItems => {
id: current.id,
label: current.title,
description: current.description,
isBeta: current.isBeta,
...getSecuritySolutionLinkProps({ deepLinkId: current.id }),
});
}

View file

@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EuiPanel } from '@elastic/eui';
import { EuiBetaBadge, EuiPanel } from '@elastic/eui';
import styled from 'styled-components';
export const EuiPanelStyled = styled(EuiPanel)<{ $bottomOffset?: string }>`
@ -30,3 +30,14 @@ export const EuiPanelStyled = styled(EuiPanel)<{ $bottomOffset?: string }>`
inset 0 -${theme.eui.euiSizeXS} ${theme.eui.euiSizeXS} -${theme.eui.euiSizeXS} rgb(0 0 0 / 6%);
`}
`;
// Remove explicit typing after eui update https://github.com/elastic/eui/pull/6086
export const EuiBetaBadgeStyled: typeof EuiBetaBadge = styled(EuiBetaBadge)`
margin-left: ${({ theme }) => theme.eui.euiSizeS};
color: ${(props) => props.theme.eui.euiTextColor};
`;
export const FlexLink = styled.a`
display: flex;
align-items: center;
`;

View file

@ -14,6 +14,7 @@ import type { SolutionNavPanelProps } from './solution_grouped_nav_panel';
import { SolutionNavPanel } from './solution_grouped_nav_panel';
import type { DefaultSideNavItem } from './types';
import { bottomNavOffset } from '../../../lib/helpers';
import { BETA } from '@kbn/kubernetes-security-plugin/common/translations';
const mockUseIsWithinBreakpoints = jest.fn(() => true);
jest.mock('@elastic/eui', () => {
@ -37,8 +38,17 @@ const mockItems: DefaultSideNavItem[] = [
href: '/network',
description: 'Network description',
},
{
id: SecurityPageName.kubernetes,
label: 'Kubernetes',
href: '/kubernetes',
description: 'Kubernetes description',
isBeta: true,
},
];
const betaMockItemsCount = mockItems.filter((item) => item.isBeta).length;
const mockCategories: LinkCategories = [
{
label: 'HOSTS CATEGORY',
@ -86,6 +96,7 @@ describe('SolutionGroupedNav', () => {
expect(result.getByText(item.description)).toBeInTheDocument();
}
});
expect(result.queryAllByText(BETA).length).toBe(betaMockItemsCount);
});
it('should only render categories with items', () => {

View file

@ -23,9 +23,10 @@ import {
useIsWithinBreakpoints,
} from '@elastic/eui';
import classNames from 'classnames';
import { EuiPanelStyled } from './solution_grouped_nav_panel.styles';
import { EuiBetaBadgeStyled, EuiPanelStyled, FlexLink } from './solution_grouped_nav_panel.styles';
import type { DefaultSideNavItem } from './types';
import type { LinkCategories } from '../../../links/types';
import { BETA } from '../../../translations';
export interface SolutionNavPanelProps {
onClose: () => void;
@ -155,10 +156,10 @@ const SolutionNavPanelCategories: React.FC<SolutionNavPanelCategoriesProps> = ({
const SolutionNavPanelItems: React.FC<SolutionNavPanelItemsProps> = ({ items, onClose }) => (
<>
{items.map(({ id, href, onClick, label, description }) => (
{items.map(({ id, href, onClick, label, description, isBeta }) => (
<Fragment key={id}>
<EuiDescriptionListTitle>
<a
<FlexLink
data-test-subj={`groupedNavPanelLink-${id}`}
href={href}
onClick={(ev) => {
@ -169,7 +170,8 @@ const SolutionNavPanelItems: React.FC<SolutionNavPanelItemsProps> = ({ items, on
}}
>
{label}
</a>
{isBeta && <EuiBetaBadgeStyled label={BETA} size="s" />}
</FlexLink>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>{description}</EuiDescriptionListDescription>
</Fragment>

View file

@ -17,6 +17,7 @@ export interface DefaultSideNavItem {
description?: string;
items?: DefaultSideNavItem[];
categories?: LinkCategories;
isBeta?: boolean;
}
export interface CustomSideNavItem {

View file

@ -110,4 +110,5 @@ export interface NavLinkItem {
image?: string;
title: string;
skipUrlState?: boolean;
isBeta?: boolean;
}

View file

@ -21,6 +21,7 @@ export const links: LinkItem = {
}),
path: KUBERNETES_PATH,
globalNavEnabled: false,
isBeta: true,
experimentalKey: 'kubernetesEnabled',
globalSearchKeywords: ['Kubernetes'],
globalNavOrder: 9005,

View file

@ -7,6 +7,7 @@
import { render } from '@testing-library/react';
import React from 'react';
import { BETA } from '@kbn/kubernetes-security-plugin/common/translations';
import { SecurityPageName } from '../../app/types';
import type { NavLinkItem } from '../../common/components/navigation/types';
import { TestProviders } from '../../common/mock';
@ -19,6 +20,14 @@ const DEFAULT_NAV_ITEM: NavLinkItem = {
image: 'TEST_IMAGE.png',
};
const BETA_NAV_ITEM: NavLinkItem = {
id: SecurityPageName.kubernetes,
title: 'TEST LABEL',
description: 'TEST DESCRIPTION',
image: 'TEST_IMAGE.png',
isBeta: true,
};
jest.mock('../../common/lib/kibana/kibana_react', () => {
return {
useKibana: jest.fn().mockReturnValue({
@ -56,6 +65,26 @@ describe('LandingLinksImages', () => {
expect(getByTestId('LandingLinksImage')).toHaveAttribute('src', image);
});
it('renders beta tag when isBeta is true', () => {
const { queryByText } = render(
<TestProviders>
<LandingLinksImages items={[BETA_NAV_ITEM]} />
</TestProviders>
);
expect(queryByText(BETA)).toBeInTheDocument();
});
it('does not render beta tag when isBeta is false', () => {
const { queryByText } = render(
<TestProviders>
<LandingLinksImages items={[DEFAULT_NAV_ITEM]} />
</TestProviders>
);
expect(queryByText(BETA)).not.toBeInTheDocument();
});
});
describe('LandingImageCards', () => {
@ -83,4 +112,24 @@ describe('LandingImageCards', () => {
expect(getByTestId('LandingImageCard-image')).toHaveAttribute('src', image);
});
it('renders beta tag when isBeta is true', () => {
const { queryByText } = render(
<TestProviders>
<LandingImageCards items={[BETA_NAV_ITEM]} />
</TestProviders>
);
expect(queryByText(BETA)).toBeInTheDocument();
});
it('does not render beta tag when isBeta is false', () => {
const { queryByText } = render(
<TestProviders>
<LandingImageCards items={[DEFAULT_NAV_ITEM]} />
</TestProviders>
);
expect(queryByText(BETA)).not.toBeInTheDocument();
});
});

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import {
EuiBetaBadge,
EuiCard,
EuiFlexGroup,
EuiFlexItem,
@ -17,6 +18,7 @@ import React from 'react';
import styled from 'styled-components';
import { withSecuritySolutionLink } from '../../common/components/links';
import type { NavLinkItem } from '../../common/components/navigation/types';
import { BETA } from '../../common/translations';
interface LandingImagesProps {
items: NavLinkItem[];
@ -43,11 +45,27 @@ const Content = styled(EuiFlexItem)`
padding-left: ${({ theme }) => theme.eui.euiSizeS};
`;
const FlexTitle = styled.div`
display: flex;
align-items: center;
`;
const TitleText = styled.h2`
display: inline;
`;
// Remove explicit typing after eui update https://github.com/elastic/eui/pull/6086
const BetaBadge: typeof EuiBetaBadge = styled(EuiBetaBadge)`
vertical-align: text-top;
margin-left: ${({ theme }) => theme.eui.euiSizeS};
color: ${(props) => props.theme.eui.euiTextColor};
`;
const SecuritySolutionLink = withSecuritySolutionLink(Link);
export const LandingLinksImages: React.FC<LandingImagesProps> = ({ items }) => (
<EuiFlexGroup direction="column">
{items.map(({ title, description, image, id }) => (
{items.map(({ title, description, image, id, isBeta }) => (
<EuiFlexItem key={id} data-test-subj="LandingItem">
<SecuritySolutionLink deepLinkId={id} tabIndex={-1}>
{/* Empty onClick is to force hover style on `EuiPanel` */}
@ -66,7 +84,10 @@ export const LandingLinksImages: React.FC<LandingImagesProps> = ({ items }) => (
</StyledFlexItem>
<Content>
<PrimaryEuiTitle size="s">
<h2>{title}</h2>
<FlexTitle>
<TitleText>{title}</TitleText>
{isBeta && <BetaBadge label={BETA} size="s" />}
</FlexTitle>
</PrimaryEuiTitle>
<LandingLinksDescripton size="s" color="text">
{description}
@ -101,7 +122,7 @@ const SecuritySolutionCard = withSecuritySolutionLink(PrimaryTitleCard);
export const LandingImageCards: React.FC<LandingImagesProps> = React.memo(({ items }) => (
<EuiFlexGroup direction="row" wrap>
{items.map(({ id, image, title, description }) => (
{items.map(({ id, image, title, description, isBeta }) => (
<LandingImageCardItem key={id} data-test-subj="LandingImageCard-item" grow={false}>
<SecuritySolutionCard
deepLinkId={id}
@ -121,7 +142,10 @@ export const LandingImageCards: React.FC<LandingImagesProps> = React.memo(({ ite
}
title={
<PrimaryEuiTitle size="xs">
<h2>{title}</h2>
<div>
<TitleText>{title}</TitleText>
{isBeta && <BetaBadge label={BETA} size="s" />}
</div>
</PrimaryEuiTitle>
}
description={<LandingCardDescription>{description}</LandingCardDescription>}