tech-radar: add description dialog box
This commit is contained in:
@@ -68,6 +68,7 @@ const RadarComponent = (props: TechRadarComponentProps): JSX.Element => {
|
||||
};
|
||||
}),
|
||||
moved: entry.timeline[0].moved,
|
||||
description: entry.timeline[0].description,
|
||||
url: entry.url,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 { render, screen } from '@testing-library/react';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
|
||||
import RadarDescription, { Props } from './RadarDescription';
|
||||
|
||||
const minProps: Props = {
|
||||
open: true,
|
||||
title: 'example-title',
|
||||
description: 'example-description',
|
||||
onClose: () => {},
|
||||
};
|
||||
|
||||
describe('RadarDescription', () => {
|
||||
it('should render', () => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<RadarDescription {...minProps} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
const radarDescription = screen.getByTestId('radar-description');
|
||||
expect(radarDescription).toBeInTheDocument();
|
||||
expect(screen.getByText(String(minProps.description))).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 Dialog from '@material-ui/core/Dialog';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import { Button, DialogActions, DialogContent } from '@material-ui/core';
|
||||
import LinkIcon from '@material-ui/icons/Link';
|
||||
|
||||
export type Props = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
description: string;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
const RadarDescription = (props: Props): JSX.Element => {
|
||||
// const classes = useStyles(props);
|
||||
const { open, onClose, title, description, url } = props;
|
||||
|
||||
const handleClick = () => {
|
||||
onClose();
|
||||
if (url) {
|
||||
window.location.href = url;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog data-testid="radar-description" open={open} onClose={onClose}>
|
||||
<DialogTitle id="radar-description-dialog-title">{title}</DialogTitle>
|
||||
<DialogContent dividers>{description}</DialogContent>
|
||||
{url && (
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
color="primary"
|
||||
startIcon={<LinkIcon />}
|
||||
variant="contained"
|
||||
>
|
||||
Visit URL
|
||||
</Button>
|
||||
</DialogActions>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default RadarDescription;
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { default } from './RadarDescription';
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import GetBBoxPolyfill from '../../utils/polyfills/getBBox';
|
||||
@@ -29,6 +30,12 @@ const minProps: Props = {
|
||||
color: 'red',
|
||||
};
|
||||
|
||||
const optionalProps: Props = {
|
||||
...minProps,
|
||||
title: 'example-title',
|
||||
description: 'example-description',
|
||||
};
|
||||
|
||||
describe('RadarEntry', () => {
|
||||
beforeAll(() => {
|
||||
GetBBoxPolyfill.create(0, 0, 1000, 500);
|
||||
@@ -38,8 +45,8 @@ describe('RadarEntry', () => {
|
||||
GetBBoxPolyfill.remove();
|
||||
});
|
||||
|
||||
it('should render', () => {
|
||||
const rendered = render(
|
||||
it('should render link only', () => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<svg>
|
||||
<RadarEntry {...minProps} />
|
||||
@@ -47,10 +54,29 @@ describe('RadarEntry', () => {
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
const radarEntry = rendered.getByTestId('radar-entry');
|
||||
const radarEntry = screen.getByTestId('radar-entry');
|
||||
const { x, y } = minProps;
|
||||
expect(radarEntry).toBeInTheDocument();
|
||||
expect(radarEntry.getAttribute('transform')).toBe(`translate(${x}, ${y})`);
|
||||
expect(rendered.getByText(String(minProps.value))).toBeInTheDocument();
|
||||
expect(screen.getByText(String(minProps.value))).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render with description', () => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<svg>
|
||||
<RadarEntry {...optionalProps} />
|
||||
</svg>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByRole('button'));
|
||||
|
||||
const radarEntry = screen.getByTestId('radar-entry');
|
||||
expect(radarEntry).toBeInTheDocument();
|
||||
|
||||
const radarDescription = screen.getByTestId('radar-description');
|
||||
expect(radarDescription).toBeInTheDocument();
|
||||
expect(screen.getByText(String(minProps.value))).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import React from 'react';
|
||||
import { makeStyles, Theme } from '@material-ui/core';
|
||||
import { WithLink } from '../../utils/components';
|
||||
import RadarDescription from '../RadarDescription';
|
||||
|
||||
export type Props = {
|
||||
x: number;
|
||||
@@ -25,6 +26,8 @@ export type Props = {
|
||||
color: string;
|
||||
url?: string;
|
||||
moved?: number;
|
||||
description?: string;
|
||||
title?: string;
|
||||
onMouseEnter?: (event: React.MouseEvent<SVGGElement, MouseEvent>) => void;
|
||||
onMouseLeave?: (event: React.MouseEvent<SVGGElement, MouseEvent>) => void;
|
||||
onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>) => void;
|
||||
@@ -59,9 +62,12 @@ const makeBlip = (color: string, moved?: number) => {
|
||||
|
||||
const RadarEntry = (props: Props): JSX.Element => {
|
||||
const classes = useStyles(props);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const {
|
||||
moved,
|
||||
description,
|
||||
title,
|
||||
color,
|
||||
url,
|
||||
value,
|
||||
@@ -74,6 +80,18 @@ const RadarEntry = (props: Props): JSX.Element => {
|
||||
|
||||
const blip = makeBlip(color, moved);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const toggle = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
return (
|
||||
<g
|
||||
transform={`translate(${x}, ${y})`}
|
||||
@@ -82,9 +100,32 @@ const RadarEntry = (props: Props): JSX.Element => {
|
||||
onClick={onClick}
|
||||
data-testid="radar-entry"
|
||||
>
|
||||
<WithLink url={url} className={classes.link}>
|
||||
{blip}
|
||||
</WithLink>
|
||||
{' '}
|
||||
{description ? (
|
||||
<>
|
||||
<a
|
||||
className={classes.link}
|
||||
onClick={handleClickOpen}
|
||||
href={url ? url : '#'}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyPress={toggle}
|
||||
>
|
||||
{blip}
|
||||
</a>
|
||||
<RadarDescription
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
title={title ? title : 'no title'}
|
||||
description={description}
|
||||
url={url}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<WithLink url={url} className={classes.link}>
|
||||
{blip}
|
||||
</WithLink>
|
||||
)}
|
||||
<text y={3} className={classes.text}>
|
||||
{value}
|
||||
</text>
|
||||
|
||||
@@ -17,6 +17,7 @@ import React from 'react';
|
||||
import { makeStyles, Theme } from '@material-ui/core';
|
||||
import type { Quadrant, Ring, Entry } from '../../utils/types';
|
||||
import { WithLink } from '../../utils/components';
|
||||
import RadarDescription from '../RadarDescription';
|
||||
|
||||
type Segments = {
|
||||
[k: number]: { [k: number]: Entry[] };
|
||||
@@ -105,6 +106,60 @@ const RadarLegend = (props: Props): JSX.Element => {
|
||||
onEntryMouseLeave?: Props['onEntryMouseEnter'];
|
||||
};
|
||||
|
||||
type RadarLegendLinkProps = {
|
||||
url?: string;
|
||||
description?: string;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
const RadarLegendLink = ({
|
||||
url,
|
||||
description,
|
||||
title,
|
||||
}: RadarLegendLinkProps) => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const toggle = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
if (description && url) {
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
className={classes.entryLink}
|
||||
onClick={handleClickOpen}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyPress={toggle}
|
||||
>
|
||||
<span className={classes.entry}>{title}</span>
|
||||
</span>
|
||||
<RadarDescription
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
title={title ? title : ''}
|
||||
url={url}
|
||||
description={description}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<WithLink url={url} className={classes.entryLink}>
|
||||
<span className={classes.entry}>{title}</span>
|
||||
</WithLink>
|
||||
);
|
||||
};
|
||||
|
||||
const RadarLegendRing = ({
|
||||
ring,
|
||||
entries,
|
||||
@@ -129,9 +184,11 @@ const RadarLegend = (props: Props): JSX.Element => {
|
||||
onEntryMouseLeave && (() => onEntryMouseLeave(entry))
|
||||
}
|
||||
>
|
||||
<WithLink url={entry.url} className={classes.entryLink}>
|
||||
<span className={classes.entry}>{entry.title}</span>
|
||||
</WithLink>
|
||||
<RadarLegendLink
|
||||
url={entry.url}
|
||||
title={entry.title}
|
||||
description={entry.description}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
@@ -73,7 +73,9 @@ const RadarPlot = (props: Props): JSX.Element => {
|
||||
color={entry.color || ''}
|
||||
value={(entry?.index || 0) + 1}
|
||||
url={entry.url}
|
||||
description={entry.description}
|
||||
moved={entry.moved}
|
||||
title={entry.title}
|
||||
onMouseEnter={onEntryMouseEnter && (() => onEntryMouseEnter(entry))}
|
||||
onMouseLeave={onEntryMouseLeave && (() => onEntryMouseLeave(entry))}
|
||||
/>
|
||||
|
||||
@@ -68,6 +68,8 @@ export type Entry = {
|
||||
url?: string;
|
||||
// How this entry has recently moved; -1 for "down", +1 for "up", 0 for not moved
|
||||
moved?: MovedState;
|
||||
// Most recent description to display in the UI
|
||||
description?: string;
|
||||
active?: boolean;
|
||||
timeline?: Array<EntrySnapshot>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user