diff --git a/.changeset/sour-insects-marry.md b/.changeset/sour-insects-marry.md
new file mode 100644
index 0000000000..27e72977d5
--- /dev/null
+++ b/.changeset/sour-insects-marry.md
@@ -0,0 +1,31 @@
+---
+'@backstage/core': minor
+'@backstage/plugin-pagerduty': patch
+---
+
+- Adds onClick and other props to IconLinkVertical;
+- Allows TriggerButton component to render when pager duty key is missing;
+- Refactors TriggerButton and PagerDutyCard not to have shared state;
+- Removes the `action` prop of the IconLinkVertical component while adding `onClick`.
+
+ Instead of having an action including a button with onClick, now the whole component can be clickable making it easier to implement and having a better UX.
+
+ Before:
+
+ ```ts
+ const myLink: IconLinkVerticalProps = {
+ label: 'Click me',
+ action: ,
+ icon: ,
+ };
+ ```
+
+ After:
+
+ ```ts
+ const myLink: IconLinkVerticalProps = {
+ label: 'Click me',
+ onClick: myAction,
+ icon: ,
+ };
+ ```
diff --git a/packages/core/src/components/HeaderIconLinkRow/IconLinkVertical.tsx b/packages/core/src/components/HeaderIconLinkRow/IconLinkVertical.tsx
index 7a9078c3e4..b3e665820a 100644
--- a/packages/core/src/components/HeaderIconLinkRow/IconLinkVertical.tsx
+++ b/packages/core/src/components/HeaderIconLinkRow/IconLinkVertical.tsx
@@ -20,11 +20,13 @@ import LinkIcon from '@material-ui/icons/Link';
import { Link as RouterLink } from '../Link';
export type IconLinkVerticalProps = {
- icon?: React.ReactNode;
- href?: string;
+ color?: 'primary' | 'secondary';
disabled?: boolean;
+ href?: string;
+ icon?: React.ReactNode;
label: string;
- action?: React.ReactNode;
+ onClick?: React.MouseEventHandler;
+ title?: string;
};
const useIconStyles = makeStyles(theme => ({
@@ -33,56 +35,61 @@ const useIconStyles = makeStyles(theme => ({
justifyItems: 'center',
gridGap: 4,
textAlign: 'center',
+ '&:active': {
+ cursor: 'grabbing',
+ },
},
disabled: {
color: 'gray',
},
+ primary: {
+ color: theme.palette.primary.main,
+ },
+ secondary: {
+ color: theme.palette.secondary.main,
+ },
label: {
fontSize: '0.7rem',
textTransform: 'uppercase',
fontWeight: 600,
letterSpacing: 1.2,
},
- linkStyle: {
- color: theme.palette.secondary.main,
- },
}));
export function IconLinkVertical({
- icon = ,
- href = '#',
+ color = 'primary',
disabled = false,
- action,
- ...props
+ href = '#',
+ icon = ,
+ label,
+ onClick,
+ title,
}: IconLinkVerticalProps) {
const classes = useIconStyles();
if (disabled) {
return (
{icon}
- {props.label}
-
- );
- }
-
- if (action) {
- return (
-
- {icon}
- {action}
+ {label}
);
}
return (
-
+
{icon}
- {props.label}
+ {label}
);
}
diff --git a/packages/core/src/components/HeaderIconLinkRow/index.ts b/packages/core/src/components/HeaderIconLinkRow/index.ts
index 82fb27cfad..fb25f9b7ed 100644
--- a/packages/core/src/components/HeaderIconLinkRow/index.ts
+++ b/packages/core/src/components/HeaderIconLinkRow/index.ts
@@ -15,3 +15,5 @@
*/
export { HeaderIconLinkRow } from './HeaderIconLinkRow';
+
+export type { IconLinkVerticalProps } from './IconLinkVertical';
diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx
index 449d3c1fc9..57b6b419e0 100644
--- a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx
+++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx
@@ -19,12 +19,12 @@ import {
SOURCE_LOCATION_ANNOTATION,
EDIT_URL_ANNOTATION,
} from '@backstage/catalog-model';
-import { render } from '@testing-library/react';
+import { render, act, fireEvent } from '@testing-library/react';
import React from 'react';
import { AboutCard } from './AboutCard';
describe(' GitHub', () => {
- it('renders info and "view source" link', () => {
+ it('renders info and "view source" link', async () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
@@ -41,7 +41,7 @@ describe(' GitHub', () => {
lifecycle: 'production',
},
};
- const { getByText } = render(
+ const { getByText, getByTitle } = render(
,
@@ -51,15 +51,21 @@ describe(' GitHub', () => {
'href',
'https://github.com/backstage/backstage/blob/master/software.yaml',
);
- expect(getByText('View Source').closest('a')).toHaveAttribute(
- 'edithref',
- 'https://github.com/backstage/backstage/edit/master/software.yaml',
+
+ const editButton = getByTitle('Edit Metadata');
+ window.open = jest.fn();
+ await act(async () => {
+ fireEvent.click(editButton);
+ });
+ expect(window.open).toHaveBeenCalledWith(
+ `https://github.com/backstage/backstage/edit/master/software.yaml`,
+ '_blank',
);
});
});
describe(' GitLab', () => {
- it('renders info and "view source" link', () => {
+ it('renders info and "view source" link', async () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
@@ -76,25 +82,32 @@ describe(' GitLab', () => {
lifecycle: 'production',
},
};
- const { getByText } = render(
+ const { getByText, getByTitle } = render(
,
);
+
expect(getByText('service')).toBeInTheDocument();
expect(getByText('View Source').closest('a')).toHaveAttribute(
'href',
'https://gitlab.com/backstage/backstage/-/blob/master/software.yaml',
);
- expect(getByText('View Source').closest('a')).toHaveAttribute(
- 'edithref',
- 'https://gitlab.com/backstage/backstage/-/edit/master/software.yaml',
+
+ const editButton = getByTitle('Edit Metadata');
+ window.open = jest.fn();
+ await act(async () => {
+ fireEvent.click(editButton);
+ });
+ expect(window.open).toHaveBeenCalledWith(
+ `https://gitlab.com/backstage/backstage/-/edit/master/software.yaml`,
+ '_blank',
);
});
});
describe(' BitBucket', () => {
- it('renders info and "view source" link', () => {
+ it('renders info and "view source" link', async () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
@@ -111,7 +124,7 @@ describe(' BitBucket', () => {
lifecycle: 'production',
},
};
- const { getByText } = render(
+ const { getByText, getByTitle } = render(
,
@@ -121,15 +134,21 @@ describe(' BitBucket', () => {
'href',
'https://bitbucket.org/backstage/backstage/src/master/software.yaml',
);
- expect(getByText('View Source').closest('a')).toHaveAttribute(
- 'edithref',
- 'https://bitbucket.org/backstage/backstage/src/master/software.yaml?mode=edit&spa=0&at=master',
+
+ const editButton = getByTitle('Edit Metadata');
+ window.open = jest.fn();
+ await act(async () => {
+ fireEvent.click(editButton);
+ });
+ expect(window.open).toHaveBeenCalledWith(
+ `https://bitbucket.org/backstage/backstage/src/master/software.yaml?mode=edit&spa=0&at=master`,
+ '_blank',
);
});
});
describe(' custom links', () => {
- it('renders info and "view source" link', () => {
+ it('renders info and "view source" link', async () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
@@ -149,7 +168,7 @@ describe(' custom links', () => {
lifecycle: 'production',
},
};
- const { getByText } = render(
+ const { getByText, getByTitle } = render(
,
@@ -159,9 +178,12 @@ describe(' custom links', () => {
'href',
'https://another.place/backstage.git',
);
- expect(getByText('View Source').closest('a')).toHaveAttribute(
- 'edithref',
- 'https://another.place',
- );
+
+ const editButton = getByTitle('Edit Metadata');
+ window.open = jest.fn();
+ await act(async () => {
+ fireEvent.click(editButton);
+ });
+ expect(window.open).toHaveBeenCalledWith(`https://another.place`, '_blank');
});
});
diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx
index 64564f5ec4..ff411f80d4 100644
--- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx
+++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx
@@ -21,7 +21,7 @@ import {
SOURCE_LOCATION_ANNOTATION,
RELATION_PROVIDES_API,
} from '@backstage/catalog-model';
-import { HeaderIconLinkRow } from '@backstage/core';
+import { HeaderIconLinkRow, IconLinkVerticalProps } from '@backstage/core';
import { useEntity } from '@backstage/plugin-catalog-react';
import {
Card,
@@ -105,11 +105,12 @@ export function AboutCard({ variant }: AboutCardProps) {
const codeLink = getCodeLinkInfo(entity);
// TODO: Also support RELATION_CONSUMES_API here
const hasApis = entity.relations?.some(r => r.type === RELATION_PROVIDES_API);
- const viewInSource = {
+ const viewInSource: IconLinkVerticalProps = {
label: 'View Source',
- ...codeLink,
+ href: codeLink.href,
+ icon: codeLink.icon,
};
- const viewInTechDocs = {
+ const viewInTechDocs: IconLinkVerticalProps = {
label: 'View TechDocs',
disabled: !entity.metadata.annotations?.['backstage.io/techdocs-ref'],
icon: ,
@@ -117,7 +118,7 @@ export function AboutCard({ variant }: AboutCardProps) {
entity.kind
}/${entity.metadata.name}`,
};
- const viewApi = {
+ const viewApi: IconLinkVerticalProps = {
title: hasApis ? '' : 'No APIs available',
label: 'View API',
disabled: !hasApis,
diff --git a/plugins/pagerduty/README.md b/plugins/pagerduty/README.md
index 1c22053b88..0e5d75d41c 100644
--- a/plugins/pagerduty/README.md
+++ b/plugins/pagerduty/README.md
@@ -39,7 +39,7 @@ import {
{
isPagerDutyAvailable(entity) && (
-
+
);
}
diff --git a/plugins/pagerduty/src/components/PagerDutyCard.test.tsx b/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx
similarity index 93%
rename from plugins/pagerduty/src/components/PagerDutyCard.test.tsx
rename to plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx
index a719a27f86..397f897f65 100644
--- a/plugins/pagerduty/src/components/PagerDutyCard.test.tsx
+++ b/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx
@@ -15,7 +15,7 @@
*/
import React from 'react';
import { render, waitFor, fireEvent, act } from '@testing-library/react';
-import { PagerDutyCard } from './PagerDutyCard';
+import { PagerDutyCard } from '../PagerDutyCard';
import { Entity } from '@backstage/catalog-model';
import { EntityProvider } from '@backstage/plugin-catalog-react';
import { wrapInTestApp } from '@backstage/test-utils';
@@ -25,8 +25,8 @@ import {
ApiRegistry,
createApiRef,
} from '@backstage/core';
-import { pagerDutyApiRef, UnauthorizedError, PagerDutyClient } from '../api';
-import { Service } from './types';
+import { pagerDutyApiRef, UnauthorizedError, PagerDutyClient } from '../../api';
+import { Service } from '../types';
const mockPagerDutyApi: Partial = {
getServiceByIntegrationKey: async () => [],
@@ -138,7 +138,7 @@ describe('PageDutyCard', () => {
.fn()
.mockImplementationOnce(async () => [service]);
- const { getByText, queryByTestId, getByTestId, getByRole } = render(
+ const { getByText, queryByTestId, getByRole } = render(
wrapInTestApp(
@@ -149,10 +149,10 @@ describe('PageDutyCard', () => {
);
await waitFor(() => !queryByTestId('progress'));
expect(getByText('Service Directory')).toBeInTheDocument();
- expect(getByText('Create Incident')).toBeInTheDocument();
- const triggerButton = getByTestId('trigger-button');
+
+ const triggerLink = getByText('Create Incident');
await act(async () => {
- fireEvent.click(triggerButton);
+ fireEvent.click(triggerLink);
});
expect(getByRole('dialog')).toBeInTheDocument();
});
diff --git a/plugins/pagerduty/src/components/PagerDutyCard.tsx b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx
similarity index 55%
rename from plugins/pagerduty/src/components/PagerDutyCard.tsx
rename to plugins/pagerduty/src/components/PagerDutyCard/index.tsx
index 7ec425a062..5a361518a7 100644
--- a/plugins/pagerduty/src/components/PagerDutyCard.tsx
+++ b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx
@@ -14,43 +14,50 @@
* limitations under the License.
*/
import React, { useState, useCallback } from 'react';
-import { useApi, Progress, HeaderIconLinkRow } from '@backstage/core';
+import {
+ useApi,
+ Progress,
+ HeaderIconLinkRow,
+ IconLinkVerticalProps,
+} from '@backstage/core';
import { Entity } from '@backstage/catalog-model';
-import { useEntity } from '@backstage/plugin-catalog-react';
import { Card, CardHeader, Divider, CardContent } from '@material-ui/core';
-import { Incidents } from './Incident';
-import { EscalationPolicy } from './Escalation';
+import { Incidents } from '../Incident';
+import { EscalationPolicy } from '../Escalation';
import { useAsync } from 'react-use';
import { Alert } from '@material-ui/lab';
-import { pagerDutyApiRef, UnauthorizedError } from '../api';
+import { pagerDutyApiRef, UnauthorizedError } from '../../api';
import AlarmAddIcon from '@material-ui/icons/AlarmAdd';
-import { MissingTokenError } from './Errors/MissingTokenError';
+import { MissingTokenError } from '../Errors/MissingTokenError';
import WebIcon from '@material-ui/icons/Web';
-import { PAGERDUTY_INTEGRATION_KEY } from './constants';
-import { TriggerButton, useShowDialog } from './TriggerButton';
+import { usePagerdutyEntity } from '../../hooks';
+import { PAGERDUTY_INTEGRATION_KEY } from '../constants';
+import { TriggerDialog } from '../TriggerDialog';
export const isPluginApplicableToEntity = (entity: Entity) =>
Boolean(entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY]);
export const PagerDutyCard = () => {
- const { entity } = useEntity();
+ const { integrationKey } = usePagerdutyEntity();
const api = useApi(pagerDutyApiRef);
const [refreshIncidents, setRefreshIncidents] = useState(false);
- const integrationKey = entity.metadata.annotations![
- PAGERDUTY_INTEGRATION_KEY
- ];
- const setShowDialog = useShowDialog()[1];
+ const [dialogShown, setDialogShown] = useState(false);
const showDialog = useCallback(() => {
- setShowDialog(true);
- }, [setShowDialog]);
+ setDialogShown(true);
+ }, [setDialogShown]);
+ const hideDialog = useCallback(() => {
+ setDialogShown(false);
+ }, [setDialogShown]);
const handleRefresh = useCallback(() => {
setRefreshIncidents(x => !x);
}, []);
const { value: service, loading, error } = useAsync(async () => {
- const services = await api.getServiceByIntegrationKey(integrationKey);
+ const services = await api.getServiceByIntegrationKey(
+ integrationKey as string,
+ );
return {
id: services[0].id,
@@ -76,32 +83,41 @@ export const PagerDutyCard = () => {
return ;
}
- const serviceLink = {
+ const serviceLink: IconLinkVerticalProps = {
label: 'Service Directory',
href: service!.url,
icon: ,
};
- const triggerLink = {
+ const triggerLink: IconLinkVerticalProps = {
label: 'Create Incident',
- action: ,
- icon: ,
+ onClick: showDialog,
+ icon: ,
+ color: 'secondary',
};
return (
-
- }
- />
-
-
-
+
+ }
/>
-
-
-
+
+
+
+
+
+
+
+ >
);
};
diff --git a/plugins/pagerduty/src/components/TriggerButton/index.test.tsx b/plugins/pagerduty/src/components/TriggerButton/index.test.tsx
new file mode 100644
index 0000000000..1ba6368d72
--- /dev/null
+++ b/plugins/pagerduty/src/components/TriggerButton/index.test.tsx
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import { act, fireEvent, screen, waitFor } from '@testing-library/react';
+import { renderInTestApp } from '@backstage/test-utils';
+import {
+ ApiRegistry,
+ alertApiRef,
+ createApiRef,
+ ApiProvider,
+ IdentityApi,
+ identityApiRef,
+} from '@backstage/core';
+import { pagerDutyApiRef } from '../../api';
+import { Entity } from '@backstage/catalog-model';
+import { EntityProvider } from '@backstage/plugin-catalog-react';
+import { TriggerButton } from './';
+
+describe('TriggerButton', () => {
+ const mockIdentityApi: Partial = {
+ getUserId: () => 'guest@example.com',
+ };
+
+ const mockTriggerAlarmFn = jest.fn();
+ const mockPagerDutyApi = {
+ triggerAlarm: mockTriggerAlarmFn,
+ };
+
+ const apis = ApiRegistry.from([
+ [
+ alertApiRef,
+ createApiRef({
+ id: 'core.alert',
+ description: 'Used to report alerts and forward them to the app',
+ }),
+ ],
+ [identityApiRef, mockIdentityApi],
+ [pagerDutyApiRef, mockPagerDutyApi],
+ ]);
+
+ it('renders the trigger button, opens and closes dialog', async () => {
+ const entity: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'pagerduty-test',
+ annotations: {
+ 'pagerduty.com/integration-key': 'abc123',
+ },
+ },
+ };
+
+ await renderInTestApp(
+
+
+
+
+ ,
+ );
+
+ const triggerButton = screen.getByText('Create Incident');
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+
+ await act(async () => {
+ fireEvent.click(triggerButton);
+ });
+ await waitFor(() => {
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
+ });
+
+ const closeButton = screen.getByText('Close');
+ await act(async () => {
+ fireEvent.click(closeButton);
+ });
+ await waitFor(() => {
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+ });
+ });
+
+ it('renders the trigger button with children', async () => {
+ const entity: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'pagerduty-test',
+ annotations: {
+ 'pagerduty.com/integration-key': 'abc123',
+ },
+ },
+ };
+
+ await renderInTestApp(
+
+
+ Send an alert
+
+ ,
+ );
+
+ expect(screen.getByText('Send an alert')).toBeInTheDocument();
+ });
+
+ it('renders a disabled trigger button if entity does not include key', async () => {
+ const entity: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'pagerduty-test',
+ },
+ };
+
+ await renderInTestApp(
+
+
+
+
+ ,
+ );
+
+ const triggerButton = screen.getByText('Missing integration key');
+
+ await act(async () => {
+ fireEvent.click(triggerButton);
+ });
+ await waitFor(() => {
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/plugins/pagerduty/src/components/TriggerButton/index.tsx b/plugins/pagerduty/src/components/TriggerButton/index.tsx
index 833e01d8aa..6aa2c1c3d4 100644
--- a/plugins/pagerduty/src/components/TriggerButton/index.tsx
+++ b/plugins/pagerduty/src/components/TriggerButton/index.tsx
@@ -13,19 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, { useCallback, PropsWithChildren } from 'react';
-import { createGlobalState } from 'react-use';
+import React, { useCallback, PropsWithChildren, useState } from 'react';
import { makeStyles, Button } from '@material-ui/core';
-import { useEntity } from '@backstage/plugin-catalog-react';
import { BackstageTheme } from '@backstage/theme';
+import { usePagerdutyEntity } from '../../hooks';
import { TriggerDialog } from '../TriggerDialog';
-import { PAGERDUTY_INTEGRATION_KEY } from '../constants';
-export interface TriggerButtonProps {
- design: 'link' | 'button';
- onIncidentCreated?: () => void;
-}
+export type TriggerButtonProps = {};
const useStyles = makeStyles(theme => ({
buttonStyle: {
@@ -35,31 +30,14 @@ const useStyles = makeStyles(theme => ({
backgroundColor: theme.palette.error.dark,
},
},
- triggerAlarm: {
- paddingTop: 0,
- paddingBottom: 0,
- fontSize: '0.7rem',
- textTransform: 'uppercase',
- fontWeight: 600,
- letterSpacing: 1.2,
- lineHeight: 1.5,
- '&:hover, &:focus, &.focus': {
- backgroundColor: 'transparent',
- textDecoration: 'none',
- },
- },
}));
-export const useShowDialog = createGlobalState(false);
-
export function TriggerButton({
- design,
- onIncidentCreated,
children,
}: PropsWithChildren) {
- const { buttonStyle, triggerAlarm } = useStyles();
- const { entity } = useEntity();
- const [dialogShown = false, setDialogShown] = useShowDialog();
+ const { buttonStyle } = useStyles();
+ const { integrationKey } = usePagerdutyEntity();
+ const [dialogShown, setDialogShown] = useState(false);
const showDialog = useCallback(() => {
setDialogShown(true);
@@ -68,27 +46,22 @@ export function TriggerButton({
setDialogShown(false);
}, [setDialogShown]);
- const integrationKey = entity.metadata.annotations![
- PAGERDUTY_INTEGRATION_KEY
- ];
-
+ const disabled = !integrationKey;
return (
<>
-
+ {integrationKey && (
+
+ )}
>
);
}
diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx
index 8073ab44b5..c710aae0fa 100644
--- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx
+++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import React from 'react';
-import { render, fireEvent, act } from '@testing-library/react';
-import { wrapInTestApp } from '@backstage/test-utils';
+import { fireEvent, act } from '@testing-library/react';
+import { renderInTestApp } from '@backstage/test-utils';
import {
ApiRegistry,
alertApiRef,
@@ -26,6 +26,7 @@ import {
} from '@backstage/core';
import { pagerDutyApiRef } from '../../api';
import { Entity } from '@backstage/catalog-model';
+import { EntityProvider } from '@backstage/plugin-catalog-react';
import { TriggerDialog } from './TriggerDialog';
describe('TriggerDialog', () => {
@@ -62,18 +63,16 @@ describe('TriggerDialog', () => {
},
};
- const { getByText, getByRole, getByTestId } = render(
- wrapInTestApp(
-
+ const { getByText, getByRole, getByTestId } = await renderInTestApp(
+
+
{}}
- name={entity.metadata.name}
- integrationKey="abc123"
onIncidentCreated={() => {}}
/>
- ,
- ),
+
+ ,
);
expect(getByRole('dialog')).toBeInTheDocument();
diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx
index c2084a41ed..3d7d4a19b4 100644
--- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx
+++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx
@@ -29,22 +29,20 @@ import { useApi, alertApiRef, identityApiRef } from '@backstage/core';
import { useAsyncFn } from 'react-use';
import { pagerDutyApiRef } from '../../api';
import { Alert } from '@material-ui/lab';
+import { usePagerdutyEntity } from '../../hooks';
type Props = {
- name: string;
- integrationKey: string;
showDialog: boolean;
handleDialog: () => void;
onIncidentCreated?: () => void;
};
export const TriggerDialog = ({
- name,
- integrationKey,
showDialog,
handleDialog,
onIncidentCreated: onIncidentCreated,
}: Props) => {
+ const { name, integrationKey } = usePagerdutyEntity();
const alertApi = useApi(alertApiRef);
const identityApi = useApi(identityApiRef);
const userName = identityApi.getUserId();
@@ -54,7 +52,7 @@ export const TriggerDialog = ({
const [{ value, loading, error }, handleTriggerAlarm] = useAsyncFn(
async (descriptions: string) =>
await api.triggerAlarm({
- integrationKey,
+ integrationKey: integrationKey as string,
source: window.location.toString(),
description: descriptions,
userName,
diff --git a/plugins/pagerduty/src/hooks/index.ts b/plugins/pagerduty/src/hooks/index.ts
new file mode 100644
index 0000000000..6ba9c09699
--- /dev/null
+++ b/plugins/pagerduty/src/hooks/index.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useEntity } from '@backstage/plugin-catalog-react';
+
+import { PAGERDUTY_INTEGRATION_KEY } from '../components/constants';
+
+export function usePagerdutyEntity() {
+ const { entity } = useEntity();
+ const integrationKey =
+ entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY];
+ const name = entity.metadata.name;
+
+ return { integrationKey, name };
+}
diff --git a/plugins/splunk-on-call/src/components/SplunkOnCallCard.test.tsx b/plugins/splunk-on-call/src/components/SplunkOnCallCard.test.tsx
index b1280ab21c..0cbaab649e 100644
--- a/plugins/splunk-on-call/src/components/SplunkOnCallCard.test.tsx
+++ b/plugins/splunk-on-call/src/components/SplunkOnCallCard.test.tsx
@@ -132,12 +132,13 @@ describe('SplunkOnCallCard', () => {
),
).toBeInTheDocument();
});
+
it('opens the dialog when trigger button is clicked', async () => {
mockSplunkOnCallApi.getUsers = jest
.fn()
.mockImplementationOnce(async () => [MOCKED_USER]);
- const { getByText, queryByTestId, getByTestId, getByRole } = render(
+ const { getByText, queryByTestId, getByRole } = render(
wrapInTestApp(
@@ -146,7 +147,7 @@ describe('SplunkOnCallCard', () => {
);
await waitFor(() => !queryByTestId('progress'));
expect(getByText('Create Incident')).toBeInTheDocument();
- const triggerButton = getByTestId('trigger-button');
+ const triggerButton = getByText('Create Incident');
await act(async () => {
fireEvent.click(triggerButton);
});
diff --git a/plugins/splunk-on-call/src/components/SplunkOnCallCard.tsx b/plugins/splunk-on-call/src/components/SplunkOnCallCard.tsx
index 84d94d60d3..1e28be1483 100644
--- a/plugins/splunk-on-call/src/components/SplunkOnCallCard.tsx
+++ b/plugins/splunk-on-call/src/components/SplunkOnCallCard.tsx
@@ -21,11 +21,10 @@ import {
MissingAnnotationEmptyState,
configApiRef,
EmptyState,
+ IconLinkVerticalProps,
} from '@backstage/core';
import { Entity } from '@backstage/catalog-model';
import {
- Button,
- makeStyles,
Card,
CardHeader,
Divider,
@@ -42,22 +41,6 @@ import { TriggerDialog } from './TriggerDialog';
import { MissingApiKeyOrApiIdError } from './Errors/MissingApiKeyOrApiIdError';
import { User } from './types';
-const useStyles = makeStyles({
- triggerAlarm: {
- paddingTop: 0,
- paddingBottom: 0,
- fontSize: '0.7rem',
- textTransform: 'uppercase',
- fontWeight: 600,
- letterSpacing: 1.2,
- lineHeight: 1.5,
- '&:hover, &:focus, &.focus': {
- backgroundColor: 'transparent',
- textDecoration: 'none',
- },
- },
-});
-
export const SPLUNK_ON_CALL_TEAM = 'splunk.com/on-call-team';
export const MissingTeamAnnotation = () => (
@@ -82,7 +65,6 @@ type Props = {
};
export const SplunkOnCallCard = ({ entity }: Props) => {
- const classes = useStyles();
const config = useApi(configApiRef);
const api = useApi(splunkOnCallApiRef);
const [showDialog, setShowDialog] = useState(false);
@@ -159,19 +141,11 @@ export const SplunkOnCallCard = ({ entity }: Props) => {
);
};
- const triggerLink = {
+ const triggerLink: IconLinkVerticalProps = {
label: 'Create Incident',
- action: (
-
- ),
- icon: ,
+ onClick: handleDialog,
+ color: 'secondary',
+ icon: ,
};
return (