diff --git a/x-pack/plugins/apm/jest.config.js b/x-pack/plugins/apm/jest.config.js index ffd3a39e8afd1..849dd7f5c3e2d 100644 --- a/x-pack/plugins/apm/jest.config.js +++ b/x-pack/plugins/apm/jest.config.js @@ -29,7 +29,7 @@ module.exports = { roots: [`${rootDir}/common`, `${rootDir}/public`, `${rootDir}/server`], collectCoverage: true, collectCoverageFrom: [ - ...(jestConfig.collectCoverageFrom ?? []), + ...(jestConfig.collectCoverageFrom || []), '**/*.{js,mjs,jsx,ts,tsx}', '!**/*.stories.{js,mjs,ts,tsx}', '!**/dev_docs/**', diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/DeleteButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/DeleteButton.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.tsx diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/Documentation.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/Documentation.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/Documentation.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/Documentation.tsx diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/FiltersSection.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/FiltersSection.tsx diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FlyoutFooter.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/FlyoutFooter.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FlyoutFooter.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/FlyoutFooter.tsx diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/LinkPreview.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/LinkPreview.tsx diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/LinkSection.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/LinkSection.tsx diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/helper.test.ts similarity index 99% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/helper.test.ts index 5f8e0b9052a65..4af9321152da3 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/helper.test.ts @@ -6,7 +6,7 @@ import { getSelectOptions, replaceTemplateVariables, -} from '../CustomLinkFlyout/helper'; +} from '../CreateEditCustomLinkFlyout/helper'; import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; describe('Custom link helper', () => { diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/helper.ts similarity index 100% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/helper.ts diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/index.tsx similarity index 98% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/index.tsx index 9687846d6c520..c6566af3a8b61 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/index.tsx @@ -37,7 +37,7 @@ interface Props { const filtersEmptyState: Filter[] = [{ key: '', value: '' }]; -export function CustomLinkFlyout({ +export function CreateEditCustomLinkFlyout({ onClose, onSave, onDelete, diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/link_preview.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/link_preview.test.tsx similarity index 97% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/link_preview.test.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/link_preview.test.tsx index 3a2aa01ba3bc4..7fa8e3a025956 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/link_preview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/link_preview.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { LinkPreview } from '../CustomLinkFlyout/LinkPreview'; +import { LinkPreview } from '../CreateEditCustomLinkFlyout/LinkPreview'; import { render, getNodeText, diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/saveCustomLink.ts similarity index 100% rename from x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/saveCustomLink.ts diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx deleted file mode 100644 index 2017aa42e1c5a..0000000000000 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; - -export function Title() { - return ( - - - - - -

- {i18n.translate('xpack.apm.settings.customizeUI.customLink', { - defaultMessage: 'Custom Links', - })} -

-
-
-
-
-
- ); -} diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx index a7feafad11111..96a634828f669 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx @@ -21,7 +21,7 @@ import { expectTextsInDocument, expectTextsNotInDocument, } from '../../../../../utils/testHelpers'; -import * as saveCustomLink from './CustomLinkFlyout/saveCustomLink'; +import * as saveCustomLink from './CreateEditCustomLinkFlyout/saveCustomLink'; import { MockApmPluginContextWrapper } from '../../../../../context/ApmPluginContext/MockApmPluginContext'; const data = [ diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx index d872f6d21ed96..771a8c6154dc0 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx @@ -9,6 +9,7 @@ import { EuiFlexItem, EuiPanel, EuiSpacer, + EuiTitle, EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -20,10 +21,9 @@ import { FETCH_STATUS, useFetcher } from '../../../../../hooks/useFetcher'; import { useLicense } from '../../../../../hooks/useLicense'; import { LicensePrompt } from '../../../../shared/LicensePrompt'; import { CreateCustomLinkButton } from './CreateCustomLinkButton'; -import { CustomLinkFlyout } from './CustomLinkFlyout'; +import { CreateEditCustomLinkFlyout } from './CreateEditCustomLinkFlyout'; import { CustomLinkTable } from './CustomLinkTable'; import { EmptyPrompt } from './EmptyPrompt'; -import { Title } from './Title'; export function CustomLinkOverview() { const license = useLicense(); @@ -35,9 +35,14 @@ export function CustomLinkOverview() { >(); const { data: customLinks = [], status, refetch } = useFetcher( - (callApmApi) => - callApmApi({ endpoint: 'GET /api/apm/settings/custom_links' }), - [] + async (callApmApi) => { + if (hasValidLicense) { + return callApmApi({ + endpoint: 'GET /api/apm/settings/custom_links', + }); + } + }, + [hasValidLicense] ); useEffect(() => { @@ -61,7 +66,7 @@ export function CustomLinkOverview() { return ( <> {isFlyoutOpen && ( - - + <EuiFlexGroup alignItems="center"> + <EuiFlexItem grow={false}> + <EuiTitle> + <EuiFlexGroup + alignItems="center" + gutterSize="s" + responsive={false} + > + <EuiFlexItem grow={false}> + <h2> + {i18n.translate( + 'xpack.apm.settings.customizeUI.customLink', + { + defaultMessage: 'Custom Links', + } + )} + </h2> + </EuiFlexItem> + </EuiFlexGroup> + </EuiTitle> + </EuiFlexItem> + </EuiFlexGroup> </EuiFlexItem> {hasValidLicense && !showEmptyPrompt && ( <EuiFlexItem> diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx deleted file mode 100644 index 62952d1fb501b..0000000000000 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { act, fireEvent, render } from '@testing-library/react'; -import React, { ReactNode } from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; -import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext'; -import { expectTextsInDocument } from '../../../../utils/testHelpers'; -import { CustomLinkPopover } from './CustomLinkPopover'; - -function Wrapper({ children }: { children?: ReactNode }) { - return ( - <MemoryRouter> - <MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper> - </MemoryRouter> - ); -} - -describe('CustomLinkPopover', () => { - const customLinks = [ - { id: '1', label: 'foo', url: 'http://elastic.co' }, - { - id: '2', - label: 'bar', - url: 'http://elastic.co?service.name={{service.name}}', - }, - ] as CustomLink[]; - const transaction = ({ - service: { name: 'foo.bar' }, - } as unknown) as Transaction; - it('renders popover', () => { - const component = render( - <CustomLinkPopover - customLinks={customLinks} - transaction={transaction} - onCreateCustomLinkClick={jest.fn()} - onClose={jest.fn()} - />, - { wrapper: Wrapper } - ); - expectTextsInDocument(component, ['CUSTOM LINKS', 'Create', 'foo', 'bar']); - }); - - it('closes popover', () => { - const handleCloseMock = jest.fn(); - const { getByText } = render( - <CustomLinkPopover - customLinks={customLinks} - transaction={transaction} - onCreateCustomLinkClick={jest.fn()} - onClose={handleCloseMock} - />, - { wrapper: Wrapper } - ); - expect(handleCloseMock).not.toHaveBeenCalled(); - act(() => { - fireEvent.click(getByText('CUSTOM LINKS')); - }); - expect(handleCloseMock).toHaveBeenCalled(); - }); - - it('opens flyout to create new custom link', () => { - const handleCreateCustomLinkClickMock = jest.fn(); - const { getByText } = render( - <CustomLinkPopover - customLinks={customLinks} - transaction={transaction} - onCreateCustomLinkClick={handleCreateCustomLinkClickMock} - onClose={jest.fn()} - />, - { wrapper: Wrapper } - ); - expect(handleCreateCustomLinkClickMock).not.toHaveBeenCalled(); - act(() => { - fireEvent.click(getByText('Create')); - }); - expect(handleCreateCustomLinkClickMock).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx deleted file mode 100644 index 27c6aa82ac674..0000000000000 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React from 'react'; -import { - EuiPopoverTitle, - EuiFlexGroup, - EuiFlexItem, - EuiButtonEmpty, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import styled from 'styled-components'; -import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; -import { CustomLinkSection } from './CustomLinkSection'; -import { ManageCustomLink } from './ManageCustomLink'; -import { px } from '../../../../style/variables'; - -const ScrollableContainer = styled.div` - -ms-overflow-style: none; - max-height: ${px(535)}; - overflow: scroll; -`; - -export function CustomLinkPopover({ - customLinks, - onCreateCustomLinkClick, - onClose, - transaction, -}: { - customLinks: CustomLink[]; - onCreateCustomLinkClick: () => void; - onClose: () => void; - transaction: Transaction; -}) { - return ( - <> - <EuiPopoverTitle> - <EuiFlexGroup> - <EuiFlexItem style={{ alignItems: 'flex-start' }}> - <EuiButtonEmpty - color="text" - size="xs" - onClick={onClose} - iconType="arrowLeft" - style={{ fontWeight: 'bold' }} - flush="left" - > - {i18n.translate( - 'xpack.apm.transactionActionMenu.customLink.popover.title', - { - defaultMessage: 'CUSTOM LINKS', - } - )} - </EuiButtonEmpty> - </EuiFlexItem> - <EuiFlexItem> - <ManageCustomLink - onCreateCustomLinkClick={onCreateCustomLinkClick} - /> - </EuiFlexItem> - </EuiFlexGroup> - </EuiPopoverTitle> - <ScrollableContainer> - <CustomLinkSection - customLinks={customLinks} - transaction={transaction} - /> - </ScrollableContainer> - </> - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx deleted file mode 100644 index 6b421bc370332..0000000000000 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { EuiLink, EuiText } from '@elastic/eui'; -import Mustache from 'mustache'; -import React from 'react'; -import styled from 'styled-components'; -import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; -import { px, truncate, units } from '../../../../style/variables'; - -const LinkContainer = styled.li` - margin-top: ${px(units.half)}; - &:first-of-type { - margin-top: 0; - } -`; - -const TruncateText = styled(EuiText)` - font-weight: 500; - line-height: ${px(units.unit)}; - ${truncate(px(units.unit * 25))} -`; - -export function CustomLinkSection({ - customLinks, - transaction, -}: { - customLinks: CustomLink[]; - transaction: Transaction; -}) { - return ( - <ul> - {customLinks.map((link) => { - let href = link.url; - try { - href = Mustache.render(link.url, transaction); - } catch (e) { - // ignores any error that happens - } - return ( - <LinkContainer key={link.id}> - <EuiLink href={href} target="_blank"> - <TruncateText size="s">{link.label}</TruncateText> - </EuiLink> - </LinkContainer> - ); - })} - </ul> - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx deleted file mode 100644 index d6484f52e84f9..0000000000000 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React from 'react'; -import { - EuiText, - EuiIcon, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiButtonEmpty, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import styled from 'styled-components'; -import { isEmpty } from 'lodash'; -import { CustomLink as CustomLinkType } from '../../../../../common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; -import { - ActionMenuDivider, - SectionSubtitle, -} from '../../../../../../observability/public'; -import { CustomLinkSection } from './CustomLinkSection'; -import { ManageCustomLink } from './ManageCustomLink'; -import { FETCH_STATUS } from '../../../../hooks/useFetcher'; -import { LoadingStatePrompt } from '../../LoadingStatePrompt'; -import { px } from '../../../../style/variables'; - -const SeeMoreButton = styled.button<{ show: boolean }>` - display: ${(props) => (props.show ? 'flex' : 'none')}; - align-items: center; - width: 100%; - justify-content: space-between; - &:hover { - text-decoration: underline; - } -`; - -export function CustomLink({ - customLinks, - status, - onCreateCustomLinkClick, - onSeeMoreClick, - transaction, -}: { - customLinks: CustomLinkType[]; - status: FETCH_STATUS; - onCreateCustomLinkClick: () => void; - onSeeMoreClick: () => void; - transaction: Transaction; -}) { - const renderEmptyPrompt = ( - <> - <EuiText size="xs" grow={false} style={{ width: px(300) }}> - {i18n.translate('xpack.apm.customLink.empty', { - defaultMessage: - 'No custom links found. Set up your own custom links, e.g., a link to a specific Dashboard or external link.', - })} - </EuiText> - <EuiSpacer size="s" /> - <EuiButtonEmpty - iconType="plusInCircle" - size="xs" - onClick={onCreateCustomLinkClick} - > - {i18n.translate('xpack.apm.customLink.buttom.create', { - defaultMessage: 'Create custom link', - })} - </EuiButtonEmpty> - </> - ); - - const renderCustomLinkBottomSection = isEmpty(customLinks) ? ( - renderEmptyPrompt - ) : ( - <SeeMoreButton onClick={onSeeMoreClick} show={customLinks.length > 3}> - <EuiText size="s"> - {i18n.translate('xpack.apm.transactionActionMenu.customLink.seeMore', { - defaultMessage: 'See more', - })} - </EuiText> - <EuiIcon type="arrowRight" /> - </SeeMoreButton> - ); - - return ( - <> - <ActionMenuDivider /> - <EuiFlexGroup> - <EuiFlexItem style={{ justifyContent: 'center' }}> - <EuiText size={'s'} grow={false}> - <h5> - {i18n.translate( - 'xpack.apm.transactionActionMenu.customLink.section', - { - defaultMessage: 'Custom Links', - } - )} - </h5> - </EuiText> - </EuiFlexItem> - <EuiFlexItem> - <ManageCustomLink - onCreateCustomLinkClick={onCreateCustomLinkClick} - showCreateCustomLinkButton={!!customLinks.length} - /> - </EuiFlexItem> - </EuiFlexGroup> - <EuiSpacer size="s" /> - <SectionSubtitle> - {i18n.translate('xpack.apm.transactionActionMenu.customLink.subtitle', { - defaultMessage: 'Links will open in a new window.', - })} - </SectionSubtitle> - <CustomLinkSection - customLinks={customLinks.slice(0, 3)} - transaction={transaction} - /> - <EuiSpacer size="s" /> - {status === FETCH_STATUS.LOADING ? ( - <LoadingStatePrompt /> - ) : ( - renderCustomLinkBottomSection - )} - </> - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkList.test.tsx similarity index 82% rename from x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkList.test.tsx index 88a4137b47200..16d526bda2103 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkList.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; import { render } from '@testing-library/react'; -import { CustomLinkSection } from './CustomLinkSection'; +import { CustomLinkList } from './CustomLinkList'; import { expectTextsInDocument, expectTextsNotInDocument, @@ -13,7 +13,7 @@ import { import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; -describe('CustomLinkSection', () => { +describe('CustomLinkList', () => { const customLinks = [ { id: '1', label: 'foo', url: 'http://elastic.co' }, { @@ -27,14 +27,14 @@ describe('CustomLinkSection', () => { } as unknown) as Transaction; it('shows links', () => { const component = render( - <CustomLinkSection customLinks={customLinks} transaction={transaction} /> + <CustomLinkList customLinks={customLinks} transaction={transaction} /> ); expectTextsInDocument(component, ['foo', 'bar']); }); it('doesnt show any links', () => { const component = render( - <CustomLinkSection customLinks={[]} transaction={transaction} /> + <CustomLinkList customLinks={[]} transaction={transaction} /> ); expectTextsNotInDocument(component, ['foo', 'bar']); }); diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkList.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkList.tsx new file mode 100644 index 0000000000000..0304b850d6cee --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkList.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Mustache from 'mustache'; +import React from 'react'; +import { + SectionLinks, + SectionLink, +} from '../../../../../../observability/public'; +import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; +import { px, unit } from '../../../../style/variables'; + +export function CustomLinkList({ + customLinks, + transaction, +}: { + customLinks: CustomLink[]; + transaction: Transaction; +}) { + return ( + <SectionLinks style={{ maxHeight: px(unit * 10), overflowY: 'auto' }}> + {customLinks.map((link) => { + const href = getHref(link, transaction); + return ( + <SectionLink + key={link.id} + label={link.label} + href={href} + target="_blank" + /> + ); + })} + </SectionLinks> + ); +} + +function getHref(link: CustomLink, transaction: Transaction) { + try { + return Mustache.render(link.url, transaction); + } catch (e) { + return link.url; + } +} diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.test.tsx similarity index 78% rename from x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.test.tsx index 29e93a47629b3..0241167aba1fb 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.test.tsx @@ -12,7 +12,7 @@ import { expectTextsInDocument, expectTextsNotInDocument, } from '../../../../utils/testHelpers'; -import { ManageCustomLink } from './ManageCustomLink'; +import { CustomLinkToolbar } from './CustomLinkToolbar'; function Wrapper({ children }: { children?: ReactNode }) { return ( @@ -22,23 +22,20 @@ function Wrapper({ children }: { children?: ReactNode }) { ); } -describe('ManageCustomLink', () => { +describe('CustomLinkToolbar', () => { it('renders with create button', () => { - const component = render( - <ManageCustomLink onCreateCustomLinkClick={jest.fn()} />, - { wrapper: Wrapper } - ); + const component = render(<CustomLinkToolbar onClickCreate={jest.fn()} />, { + wrapper: Wrapper, + }); expect( component.getByLabelText('Custom links settings page') ).toBeInTheDocument(); expectTextsInDocument(component, ['Create']); }); + it('renders without create button', () => { const component = render( - <ManageCustomLink - onCreateCustomLinkClick={jest.fn()} - showCreateCustomLinkButton={false} - />, + <CustomLinkToolbar onClickCreate={jest.fn()} showCreateButton={false} />, { wrapper: Wrapper } ); expect( @@ -46,12 +43,11 @@ describe('ManageCustomLink', () => { ).toBeInTheDocument(); expectTextsNotInDocument(component, ['Create']); }); + it('opens flyout to create new custom link', () => { const handleCreateCustomLinkClickMock = jest.fn(); const { getByText } = render( - <ManageCustomLink - onCreateCustomLinkClick={handleCreateCustomLinkClickMock} - />, + <CustomLinkToolbar onClickCreate={handleCreateCustomLinkClickMock} />, { wrapper: Wrapper } ); expect(handleCreateCustomLinkClickMock).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.tsx similarity index 85% rename from x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.tsx index 09cdaa26004bb..36b370b4069ae 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/CustomLinkToolbar.tsx @@ -14,12 +14,12 @@ import { import { i18n } from '@kbn/i18n'; import { APMLink } from '../../Links/apm/APMLink'; -export function ManageCustomLink({ - onCreateCustomLinkClick, - showCreateCustomLinkButton = true, +export function CustomLinkToolbar({ + onClickCreate, + showCreateButton = true, }: { - onCreateCustomLinkClick: () => void; - showCreateCustomLinkButton?: boolean; + onClickCreate: () => void; + showCreateButton?: boolean; }) { return ( <EuiFlexGroup> @@ -41,12 +41,12 @@ export function ManageCustomLink({ </APMLink> </EuiToolTip> </EuiFlexItem> - {showCreateCustomLinkButton && ( + {showCreateButton && ( <EuiFlexItem grow={false}> <EuiButtonEmpty iconType="plusInCircle" size="xs" - onClick={onCreateCustomLinkClick} + onClick={onClickCreate} > {i18n.translate('xpack.apm.customLink.buttom.create.title', { defaultMessage: 'Create', diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.test.tsx similarity index 61% rename from x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.test.tsx index 5abeae265dfa6..db7a284f6adff 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.test.tsx @@ -7,11 +7,11 @@ import { act, fireEvent, render } from '@testing-library/react'; import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { CustomLink } from '.'; +import { CustomLinkMenuSection } from '.'; import { CustomLink as CustomLinkType } from '../../../../../common/custom_link/custom_link_types'; import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext'; -import { FETCH_STATUS } from '../../../../hooks/useFetcher'; +import * as useFetcher from '../../../../hooks/useFetcher'; import { expectTextsInDocument, expectTextsNotInDocument, @@ -25,16 +25,27 @@ function Wrapper({ children }: { children?: ReactNode }) { ); } +const transaction = ({ + service: { + name: 'name', + environment: 'env', + }, + transaction: { + name: 'tx name', + type: 'tx type', + }, +} as unknown) as Transaction; + describe('Custom links', () => { it('shows empty message when no custom link is available', () => { + jest.spyOn(useFetcher, 'useFetcher').mockReturnValue({ + data: [], + status: useFetcher.FETCH_STATUS.SUCCESS, + refetch: jest.fn(), + }); + const component = render( - <CustomLink - customLinks={[]} - transaction={({} as unknown) as Transaction} - onCreateCustomLinkClick={jest.fn()} - onSeeMoreClick={jest.fn()} - status={FETCH_STATUS.SUCCESS} - />, + <CustomLinkMenuSection transaction={transaction} />, { wrapper: Wrapper } ); @@ -45,14 +56,14 @@ describe('Custom links', () => { }); it('shows loading while custom links are fetched', () => { + jest.spyOn(useFetcher, 'useFetcher').mockReturnValue({ + data: [], + status: useFetcher.FETCH_STATUS.LOADING, + refetch: jest.fn(), + }); + const { getByTestId } = render( - <CustomLink - customLinks={[]} - transaction={({} as unknown) as Transaction} - onCreateCustomLinkClick={jest.fn()} - onSeeMoreClick={jest.fn()} - status={FETCH_STATUS.LOADING} - />, + <CustomLinkMenuSection transaction={transaction} />, { wrapper: Wrapper } ); expect(getByTestId('loading-spinner')).toBeInTheDocument(); @@ -65,61 +76,68 @@ describe('Custom links', () => { { id: '3', label: 'baz', url: 'baz' }, { id: '4', label: 'qux', url: 'qux' }, ] as CustomLinkType[]; + + jest.spyOn(useFetcher, 'useFetcher').mockReturnValue({ + data: customLinks, + status: useFetcher.FETCH_STATUS.SUCCESS, + refetch: jest.fn(), + }); + const component = render( - <CustomLink - customLinks={customLinks} - transaction={({} as unknown) as Transaction} - onCreateCustomLinkClick={jest.fn()} - onSeeMoreClick={jest.fn()} - status={FETCH_STATUS.SUCCESS} - />, + <CustomLinkMenuSection transaction={transaction} />, { wrapper: Wrapper } ); expectTextsInDocument(component, ['foo', 'bar', 'baz']); expectTextsNotInDocument(component, ['qux']); }); - it('clicks on See more button', () => { + it('clicks "show all" and "show fewer"', () => { const customLinks = [ { id: '1', label: 'foo', url: 'foo' }, { id: '2', label: 'bar', url: 'bar' }, { id: '3', label: 'baz', url: 'baz' }, { id: '4', label: 'qux', url: 'qux' }, ] as CustomLinkType[]; - const onSeeMoreClickMock = jest.fn(); + + jest.spyOn(useFetcher, 'useFetcher').mockReturnValue({ + data: customLinks, + status: useFetcher.FETCH_STATUS.SUCCESS, + refetch: jest.fn(), + }); + const component = render( - <CustomLink - customLinks={customLinks} - transaction={({} as unknown) as Transaction} - onCreateCustomLinkClick={jest.fn()} - onSeeMoreClick={onSeeMoreClickMock} - status={FETCH_STATUS.SUCCESS} - />, + <CustomLinkMenuSection transaction={transaction} />, { wrapper: Wrapper } ); - expect(onSeeMoreClickMock).not.toHaveBeenCalled(); + + expect(component.getAllByRole('listitem').length).toEqual(3); + act(() => { + fireEvent.click(component.getByText('Show all')); + }); + expect(component.getAllByRole('listitem').length).toEqual(4); act(() => { - fireEvent.click(component.getByText('See more')); + fireEvent.click(component.getByText('Show fewer')); }); - expect(onSeeMoreClickMock).toHaveBeenCalled(); + expect(component.getAllByRole('listitem').length).toEqual(3); }); describe('create custom link buttons', () => { it('shows create button below empty message', () => { + jest.spyOn(useFetcher, 'useFetcher').mockReturnValue({ + data: [], + status: useFetcher.FETCH_STATUS.SUCCESS, + refetch: jest.fn(), + }); + const component = render( - <CustomLink - customLinks={[]} - transaction={({} as unknown) as Transaction} - onCreateCustomLinkClick={jest.fn()} - onSeeMoreClick={jest.fn()} - status={FETCH_STATUS.SUCCESS} - />, + <CustomLinkMenuSection transaction={transaction} />, { wrapper: Wrapper } ); expectTextsInDocument(component, ['Create custom link']); expectTextsNotInDocument(component, ['Create']); }); + it('shows create button besides the title', () => { const customLinks = [ { id: '1', label: 'foo', url: 'foo' }, @@ -127,14 +145,15 @@ describe('Custom links', () => { { id: '3', label: 'baz', url: 'baz' }, { id: '4', label: 'qux', url: 'qux' }, ] as CustomLinkType[]; + + jest.spyOn(useFetcher, 'useFetcher').mockReturnValue({ + data: customLinks, + status: useFetcher.FETCH_STATUS.SUCCESS, + refetch: jest.fn(), + }); + const component = render( - <CustomLink - customLinks={customLinks} - transaction={({} as unknown) as Transaction} - onCreateCustomLinkClick={jest.fn()} - onSeeMoreClick={jest.fn()} - status={FETCH_STATUS.SUCCESS} - />, + <CustomLinkMenuSection transaction={transaction} />, { wrapper: Wrapper } ); expectTextsInDocument(component, ['Create']); diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.tsx new file mode 100644 index 0000000000000..2825363b10197 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLinkMenuSection/index.tsx @@ -0,0 +1,207 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useMemo, useState } from 'react'; +import { + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiButtonEmpty, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { isEmpty } from 'lodash'; +import { + ActionMenuDivider, + Section, + SectionSubtitle, + SectionTitle, +} from '../../../../../../observability/public'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; +import { CustomLinkList } from './CustomLinkList'; +import { CustomLinkToolbar } from './CustomLinkToolbar'; +import { FETCH_STATUS, useFetcher } from '../../../../hooks/useFetcher'; +import { LoadingStatePrompt } from '../../LoadingStatePrompt'; +import { px } from '../../../../style/variables'; +import { CreateEditCustomLinkFlyout } from '../../../app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout'; +import { convertFiltersToQuery } from '../../../app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/helper'; +import { + CustomLink, + Filter, +} from '../../../../../common/custom_link/custom_link_types'; + +const DEFAULT_LINKS_TO_SHOW = 3; + +export function CustomLinkMenuSection({ + transaction, +}: { + transaction: Transaction; +}) { + const [showAllLinks, setShowAllLinks] = useState(false); + const [isCreateEditFlyoutOpen, setIsCreateEditFlyoutOpen] = useState(false); + + const filters = useMemo( + () => + [ + { key: 'service.name', value: transaction?.service.name }, + { key: 'service.environment', value: transaction?.service.environment }, + { key: 'transaction.name', value: transaction?.transaction.name }, + { key: 'transaction.type', value: transaction?.transaction.type }, + ].filter((filter): filter is Filter => typeof filter.value === 'string'), + [transaction] + ); + + const { data: customLinks = [], status, refetch } = useFetcher( + (callApmApi) => + callApmApi({ + isCachable: true, + endpoint: 'GET /api/apm/settings/custom_links', + params: { query: convertFiltersToQuery(filters) }, + }), + [filters] + ); + + return ( + <> + {isCreateEditFlyoutOpen && ( + <CreateEditCustomLinkFlyout + defaults={{ filters }} + onClose={() => { + setIsCreateEditFlyoutOpen(false); + }} + onSave={() => { + setIsCreateEditFlyoutOpen(false); + refetch(); + }} + onDelete={() => { + setIsCreateEditFlyoutOpen(false); + refetch(); + }} + /> + )} + + <ActionMenuDivider /> + + <Section> + <EuiFlexGroup> + <EuiFlexItem> + <SectionTitle> + {i18n.translate( + 'xpack.apm.transactionActionMenu.customLink.section', + { + defaultMessage: 'Custom Links', + } + )} + </SectionTitle> + </EuiFlexItem> + <EuiFlexItem> + <CustomLinkToolbar + onClickCreate={() => setIsCreateEditFlyoutOpen(true)} + showCreateButton={customLinks.length > 0} + /> + </EuiFlexItem> + </EuiFlexGroup> + + <EuiSpacer size="s" /> + <SectionSubtitle> + {i18n.translate( + 'xpack.apm.transactionActionMenu.customLink.subtitle', + { + defaultMessage: 'Links will open in a new window.', + } + )} + </SectionSubtitle> + <CustomLinkList + customLinks={ + showAllLinks + ? customLinks + : customLinks.slice(0, DEFAULT_LINKS_TO_SHOW) + } + transaction={transaction} + /> + <EuiSpacer size="s" /> + <BottomSection + status={status} + customLinks={customLinks} + showAllLinks={showAllLinks} + toggleShowAll={() => setShowAllLinks((show) => !show)} + onClickCreate={() => setIsCreateEditFlyoutOpen(true)} + /> + </Section> + </> + ); +} + +function BottomSection({ + status, + customLinks, + showAllLinks, + toggleShowAll, + onClickCreate, +}: { + status: FETCH_STATUS; + customLinks: CustomLink[]; + showAllLinks: boolean; + toggleShowAll: () => void; + onClickCreate: () => void; +}) { + if (status === FETCH_STATUS.LOADING) { + return <LoadingStatePrompt />; + } + + // render empty prompt if there are no custom links + if (isEmpty(customLinks)) { + return ( + <EuiFlexGroup> + <EuiFlexItem> + <EuiText size="xs" grow={false} style={{ width: px(300) }}> + {i18n.translate('xpack.apm.customLink.empty', { + defaultMessage: + 'No custom links found. Set up your own custom links, e.g., a link to a specific Dashboard or external link.', + })} + </EuiText> + <EuiSpacer size="s" /> + <EuiButtonEmpty + iconType="plusInCircle" + size="xs" + onClick={onClickCreate} + > + {i18n.translate('xpack.apm.customLink.buttom.create', { + defaultMessage: 'Create custom link', + })} + </EuiButtonEmpty> + </EuiFlexItem> + </EuiFlexGroup> + ); + } + + // render button to toggle "Show all" / "Show fewer" + if (customLinks.length > DEFAULT_LINKS_TO_SHOW) { + return ( + <EuiFlexGroup> + <EuiFlexItem> + <EuiButtonEmpty + iconType={showAllLinks ? 'arrowUp' : 'arrowDown'} + onClick={toggleShowAll} + > + <EuiText size="s"> + {showAllLinks + ? i18n.translate( + 'xpack.apm.transactionActionMenu.customLink.showFewer', + { defaultMessage: 'Show fewer' } + ) + : i18n.translate( + 'xpack.apm.transactionActionMenu.customLink.showAll', + { defaultMessage: 'Show all' } + )} + </EuiText> + </EuiButtonEmpty> + </EuiFlexItem> + </EuiFlexGroup> + ); + } + + return null; +} diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx index f5a57544209f5..15a85113406e1 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx @@ -6,7 +6,7 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useMemo, useState } from 'react'; +import React, { useState } from 'react'; import { useLocation } from 'react-router-dom'; import { ActionMenu, @@ -17,16 +17,11 @@ import { SectionSubtitle, SectionTitle, } from '../../../../../observability/public'; -import { Filter } from '../../../../common/custom_link/custom_link_types'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; -import { useFetcher } from '../../../hooks/useFetcher'; import { useLicense } from '../../../hooks/useLicense'; import { useUrlParams } from '../../../hooks/useUrlParams'; -import { CustomLinkFlyout } from '../../app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout'; -import { convertFiltersToQuery } from '../../app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper'; -import { CustomLink } from './CustomLink'; -import { CustomLinkPopover } from './CustomLink/CustomLinkPopover'; +import { CustomLinkMenuSection } from './CustomLinkMenuSection'; import { getSections } from './sections'; interface Props { @@ -45,37 +40,13 @@ function ActionMenuButton({ onClick }: { onClick: () => void }) { export function TransactionActionMenu({ transaction }: Props) { const license = useLicense(); - const hasValidLicense = license?.isActive && license?.hasAtLeast('gold'); + const hasGoldLicense = license?.isActive && license?.hasAtLeast('gold'); const { core } = useApmPluginContext(); const location = useLocation(); const { urlParams } = useUrlParams(); const [isActionPopoverOpen, setIsActionPopoverOpen] = useState(false); - const [isCustomLinksPopoverOpen, setIsCustomLinksPopoverOpen] = useState( - false - ); - const [isCustomLinkFlyoutOpen, setIsCustomLinkFlyoutOpen] = useState(false); - - const filters = useMemo( - () => - [ - { key: 'service.name', value: transaction?.service.name }, - { key: 'service.environment', value: transaction?.service.environment }, - { key: 'transaction.name', value: transaction?.transaction.name }, - { key: 'transaction.type', value: transaction?.transaction.type }, - ].filter((filter): filter is Filter => typeof filter.value === 'string'), - [transaction] - ); - - const { data: customLinks = [], status, refetch } = useFetcher( - (callApmApi) => - callApmApi({ - endpoint: 'GET /api/apm/settings/custom_links', - params: { query: convertFiltersToQuery(filters) }, - }), - [filters] - ); const sections = getSections({ transaction, @@ -84,39 +55,11 @@ export function TransactionActionMenu({ transaction }: Props) { urlParams, }); - const closePopover = () => { - setIsActionPopoverOpen(false); - setIsCustomLinksPopoverOpen(false); - }; - - const toggleCustomLinkFlyout = () => { - closePopover(); - setIsCustomLinkFlyoutOpen((isOpen) => !isOpen); - }; - - const toggleCustomLinkPopover = () => { - setIsCustomLinksPopoverOpen((isOpen) => !isOpen); - }; - return ( <> - {isCustomLinkFlyoutOpen && ( - <CustomLinkFlyout - defaults={{ filters }} - onClose={toggleCustomLinkFlyout} - onSave={() => { - toggleCustomLinkFlyout(); - refetch(); - }} - onDelete={() => { - toggleCustomLinkFlyout(); - refetch(); - }} - /> - )} <ActionMenu id="transactionActionMenu" - closePopover={closePopover} + closePopover={() => setIsActionPopoverOpen(false)} isOpen={isActionPopoverOpen} anchorPosition="downRight" button={ @@ -124,52 +67,34 @@ export function TransactionActionMenu({ transaction }: Props) { } > <div> - {isCustomLinksPopoverOpen ? ( - <CustomLinkPopover - customLinks={customLinks.slice(3, customLinks.length)} - onCreateCustomLinkClick={toggleCustomLinkFlyout} - onClose={toggleCustomLinkPopover} - transaction={transaction} - /> - ) : ( - <> - {sections.map((section, idx) => { - const isLastSection = idx !== sections.length - 1; - return ( - <div key={idx}> - {section.map((item) => ( - <Section key={item.key}> - {item.title && ( - <SectionTitle>{item.title}</SectionTitle> - )} - {item.subtitle && ( - <SectionSubtitle>{item.subtitle}</SectionSubtitle> - )} - <SectionLinks> - {item.actions.map((action) => ( - <SectionLink - key={action.key} - label={action.label} - href={action.href} - /> - ))} - </SectionLinks> - </Section> - ))} - {isLastSection && <ActionMenuDivider />} - </div> - ); - })} - {hasValidLicense && ( - <CustomLink - customLinks={customLinks} - status={status} - onCreateCustomLinkClick={toggleCustomLinkFlyout} - onSeeMoreClick={toggleCustomLinkPopover} - transaction={transaction} - /> - )} - </> + {sections.map((section, idx) => { + const isLastSection = idx !== sections.length - 1; + return ( + <div key={idx}> + {section.map((item) => ( + <Section key={item.key}> + {item.title && <SectionTitle>{item.title}</SectionTitle>} + {item.subtitle && ( + <SectionSubtitle>{item.subtitle}</SectionSubtitle> + )} + <SectionLinks> + {item.actions.map((action) => ( + <SectionLink + key={action.key} + label={action.label} + href={action.href} + /> + ))} + </SectionLinks> + </Section> + ))} + {isLastSection && <ActionMenuDivider />} + </div> + ); + })} + + {hasGoldLicense && ( + <CustomLinkMenuSection transaction={transaction} /> )} </div> </ActionMenu> diff --git a/x-pack/plugins/observability/public/components/shared/action_menu/index.tsx b/x-pack/plugins/observability/public/components/shared/action_menu/index.tsx index 55746ff6576a9..4819a0760d88a 100644 --- a/x-pack/plugins/observability/public/components/shared/action_menu/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/action_menu/index.tsx @@ -12,11 +12,11 @@ import { EuiHorizontalRule, EuiListGroupItem, EuiPopoverProps, + EuiListGroupItemProps, } from '@elastic/eui'; - import React, { HTMLAttributes, ReactNode } from 'react'; -import { EuiListGroupItemProps } from '@elastic/eui/src/components/list_group/list_group_item'; import styled from 'styled-components'; +import { EuiListGroupProps } from '@elastic/eui'; type Props = EuiPopoverProps & HTMLAttributes<HTMLDivElement>; @@ -42,9 +42,9 @@ export function SectionSubtitle({ children }: { children?: ReactNode }) { ); } -export function SectionLinks({ children }: { children?: ReactNode }) { +export function SectionLinks({ children, ...props }: { children?: ReactNode } & EuiListGroupProps) { return ( - <EuiListGroup flush={true} bordered={false}> + <EuiListGroup {...props} flush={true} bordered={false}> {children} </EuiListGroup> ); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b5c6a4e3fd03f..cc91c0ccfb3bf 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5190,9 +5190,7 @@ "xpack.apm.transactionActionMenu.actionsButtonLabel": "アクション", "xpack.apm.transactionActionMenu.container.subtitle": "このコンテナーのログとインデックスを表示し、さらに詳細を確認できます。", "xpack.apm.transactionActionMenu.container.title": "コンテナーの詳細", - "xpack.apm.transactionActionMenu.customLink.popover.title": "カスタムリンク", "xpack.apm.transactionActionMenu.customLink.section": "カスタムリンク", - "xpack.apm.transactionActionMenu.customLink.seeMore": "詳細を表示", "xpack.apm.transactionActionMenu.customLink.subtitle": "リンクは新しいウィンドウで開きます。", "xpack.apm.transactionActionMenu.host.subtitle": "ホストログとメトリックを表示し、さらに詳細を確認できます。", "xpack.apm.transactionActionMenu.host.title": "ホストの詳細", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a2d42dcfcdc52..0fa33f57db59b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5194,9 +5194,7 @@ "xpack.apm.transactionActionMenu.actionsButtonLabel": "操作", "xpack.apm.transactionActionMenu.container.subtitle": "查看此容器的日志和指标以获取进一步详情。", "xpack.apm.transactionActionMenu.container.title": "容器详情", - "xpack.apm.transactionActionMenu.customLink.popover.title": "定制链接", "xpack.apm.transactionActionMenu.customLink.section": "定制链接", - "xpack.apm.transactionActionMenu.customLink.seeMore": "查看更多内容", "xpack.apm.transactionActionMenu.customLink.subtitle": "链接将在新窗口打开。", "xpack.apm.transactionActionMenu.host.subtitle": "查看主机日志和指标以获取进一步详情。", "xpack.apm.transactionActionMenu.host.title": "主机详情",