Merge branch 'master' into alund/components

This commit is contained in:
Stefan Ålund
2020-04-15 20:33:29 +02:00
55 changed files with 1045 additions and 161 deletions
@@ -26,10 +26,12 @@ You can now view example-app in the browser.
Run the following commands if you have Docker environment
```bash
$ docker build . -t spotify/backstage
$ yarn docker-build
$ docker run --rm -it -p 80:80 spotify/backstage
```
Then open http://localhost/ on your browser.
> See [package.json](/package.json) for other yarn commands/options.
[Back to Docs](README.md)
+4
View File
@@ -51,6 +51,10 @@ module.exports = {
bundledDependencies: true,
},
],
'@typescript-eslint/no-unused-vars': [
'warn',
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true },
],
},
overrides: [
{
@@ -1,12 +1,19 @@
{
"name": "root",
"version": "1.0.0",
"private": true,
"engines": {
"node": ">=12.0.0"
},
"scripts": {
"start": "yarn build && yarn workspace app start",
"start": "yarn workspace app start",
"bundle": "yarn build && yarn workspace app bundle",
"build": "lerna run build",
"test": "cross-env CI=true lerna run test -- --coverage",
"create-plugin": "backstage-cli create-plugin",
"lint": "lerna run lint"
"test": "yarn build && lerna run test --since origin/master -- --coverage",
"test:all": "yarn build && lerna run test -- --coverage",
"lint": "lerna run lint --since origin/master --",
"lint:all": "lerna run lint --",
"create-plugin": "backstage-cli create-plugin"
},
"workspaces": {
"packages": [
@@ -14,10 +21,8 @@
"plugins/*"
]
},
"version": "1.0.0",
"devDependencies": {
"@backstage/cli": "^{{version}}",
"cross-env": "^7.0.0",
"lerna": "^3.20.2",
"prettier": "^1.19.1"
}
@@ -21,7 +21,7 @@
},
"scripts": {
"start": "backstage-cli app:serve",
"build": "backstage-cli app:build",
"bundle": "backstage-cli app:build",
"test": "backstage-cli test",
"lint": "backstage-cli lint"
},
-22
View File
@@ -1,22 +0,0 @@
module.exports = {
stories: [
'../src/layout/**/*.stories.tsx',
'../src/components/**/*.stories.tsx',
],
addons: ['@storybook/addon-actions', '@storybook/addon-links'],
webpackFinal: async config => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
use: [
{
loader: require.resolve('ts-loader'),
options: {
transpileOnly: true,
},
},
],
});
config.resolve.extensions.push('.ts', '.tsx');
return config;
},
};
+2 -1
View File
@@ -43,8 +43,9 @@
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.3",
"@backstage/test-utils": "^0.1.1-alpha.3",
"@backstage/test-utils-core": "^0.1.1-alpha.3",
"@backstage/theme": "^0.1.1-alpha.3",
"@storybook/addon-storysource": "^5.3.18",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
@@ -19,7 +19,7 @@ import ApiProvider, { useApi, withApis } from './ApiProvider';
import ApiRef from './ApiRef';
import ApiRegistry from './ApiRegistry';
import { render } from '@testing-library/react';
import { withLogCollector } from '@backstage/test-utils';
import { withLogCollector } from '@backstage/test-utils-core';
describe('ApiProvider', () => {
type Api = () => string;
@@ -16,7 +16,6 @@
import React from 'react';
import { render } from '@testing-library/react';
import { COLORS } from '@backstage/theme';
import { wrapInThemedTestApp } from '@backstage/test-utils';
import CircleProgress, { getProgressColor } from './CircleProgress';
@@ -52,17 +51,22 @@ describe('<CircleProgress />', () => {
getByText('10m');
});
const ok = '#111';
const warning = '#222';
const error = '#333';
const palette = { status: { ok, warning, error } };
it('colors the progress correctly', () => {
expect(getProgressColor()).toBe('#ddd');
expect(getProgressColor(10)).toBe(COLORS.STATUS.ERROR);
expect(getProgressColor(50)).toBe(COLORS.STATUS.WARNING);
expect(getProgressColor(90)).toBe(COLORS.STATUS.OK);
expect(getProgressColor(palette)).toBe('#ddd');
expect(getProgressColor(palette, 10)).toBe(error);
expect(getProgressColor(palette, 50)).toBe(warning);
expect(getProgressColor(palette, 90)).toBe(ok);
});
it('colors the inverse progress correctly', () => {
expect(getProgressColor()).toBe('#ddd');
expect(getProgressColor(10, true)).toBe(COLORS.STATUS.OK);
expect(getProgressColor(50, true)).toBe(COLORS.STATUS.WARNING);
expect(getProgressColor(90, true)).toBe(COLORS.STATUS.ERROR);
expect(getProgressColor(palette)).toBe('#ddd');
expect(getProgressColor(palette, 10, true)).toBe(ok);
expect(getProgressColor(palette, 50, true)).toBe(warning);
expect(getProgressColor(palette, 90, true)).toBe(error);
});
});
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import { makeStyles } from '@material-ui/core';
import { BackstageTheme, COLORS } from '@backstage/theme';
import { makeStyles, useTheme } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
import { Circle } from 'rc-progress';
import React, { FC } from 'react';
@@ -55,7 +55,7 @@ const defaultProps = {
max: 100,
};
export function getProgressColor(value, inverse, max) {
export function getProgressColor(palette, value, inverse, max) {
if (isNaN(value)) {
return '#ddd';
}
@@ -64,16 +64,17 @@ export function getProgressColor(value, inverse, max) {
const actualValue = inverse ? actualMax - value : value;
if (actualValue < actualMax / 3) {
return COLORS.STATUS.ERROR;
return palette.status.error;
} else if (actualValue < actualMax * (2 / 3)) {
return COLORS.STATUS.WARNING;
return palette.status.warning;
}
return COLORS.STATUS.OK;
return palette.status.ok;
}
const CircleProgress: FC<Props> = props => {
const classes = useStyles(props);
const theme = useTheme();
const { value, fractional, inverse, unit, max } = {
...defaultProps,
...props,
@@ -89,7 +90,7 @@ const CircleProgress: FC<Props> = props => {
percent={asPercentage}
strokeWidth={12}
trailWidth={12}
strokeColor={getProgressColor(asActual, inverse, max)}
strokeColor={getProgressColor(theme.palette, asActual, inverse, max)}
className={classes.circle}
/>
<div className={classes.overlay}>
@@ -16,9 +16,8 @@
import React, { FC } from 'react';
import { makeStyles } from '@material-ui/core';
import InfoCard from 'layout/InfoCard';
import { Props as BottomLinkProps } from 'layout/InfoCard/BottomLink';
import { Props as BottomLinkProps } from '../layout/BottomLink';
import CircleProgress from './CircleProgress';
type Props = {
@@ -171,7 +171,7 @@ const DataTableRow = pure(({ row, columns, handleRowClick, style }) => {
* @example
* render {
* const data = [
* { id: 'buffalos', amount: 1, status: <Error />, statusValue: 2 }
* { id: 'buffalos', amount: 1, status: <Error />, statusValue: 2 },
* { id: 'milk', amount: 3, status: <Warning />, statusValue: 1 }
* ];
* const columns = [
@@ -0,0 +1,57 @@
/*
* 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 { StatusError, StatusOK, StatusWarning } from './Status';
import SortableTable from './SortableTable';
export default {
title: 'Sortable Table',
component: SortableTable,
};
const containerStyle = { width: 600, padding: 20 };
const data = [
{ id: 'buffalos', amount: 1, status: <StatusError />, statusValue: 2 },
{ id: 'milk', amount: 3, status: <StatusWarning />, statusValue: 1 },
{ id: 'cheese', amount: 8, status: <StatusWarning />, statusValue: 1 },
{ id: 'bread', amount: 2, status: <StatusOK />, statusValue: 0 },
];
const columns = [
{ id: 'id', label: 'ID' },
{ id: 'amount', disablePadding: false, numeric: true, label: 'AMOUNT' },
{ id: 'status', label: 'STATUS', sortValue: row => row.statusValue },
];
const footerData = [
{ id: 'total', amount: 4, statusValue: 2, status: <StatusError /> },
];
export const Default = () => (
<div style={containerStyle}>
<SortableTable orderBy="desc" data={data} columns={columns} />
</div>
);
export const WithFooter = () => (
<div style={containerStyle}>
<SortableTable
orderBy="asc"
data={data}
columns={columns}
footerData={footerData}
/>
</div>
);
@@ -0,0 +1,67 @@
/*
* 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 {
StatusError,
StatusFailed,
StatusNA,
StatusOK,
StatusPending,
StatusRunning,
StatusWarning,
} from './Status';
export default {
title: 'Status',
component: StatusOK,
};
export const statusOK = () => (
<>
<StatusOK /> Status OK
</>
);
export const statusWarning = () => (
<>
<StatusWarning /> Status Warning
</>
);
export const statusError = () => (
<>
<StatusError /> Status Error
</>
);
export const statusFailed = () => (
<>
<StatusFailed /> Status Failed
</>
);
export const statusPending = () => (
<>
<StatusPending /> Status Pending
</>
);
export const statusRunning = () => (
<>
<StatusRunning /> Status Running
</>
);
export const statusNA = () => (
<>
<StatusNA /> Status NA
</>
);
@@ -0,0 +1,80 @@
/*
* 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, { Component } from 'react';
import PropTypes from 'prop-types';
import { Typography, withStyles } from '@material-ui/core';
import ErrorOutline from '@material-ui/icons/ErrorOutline';
const errorOutlineStyles = theme => ({
root: {
marginRight: theme.spacing(1),
fill: theme.palette.warningText,
},
});
const ErrorOutlineStyled = withStyles(errorOutlineStyles)(ErrorOutline);
const styles = theme => ({
message: {
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(1.5),
backgroundColor: theme.palette.warningBackground,
color: theme.palette.warningText,
verticalAlign: 'middle',
},
header: {
display: 'flex',
flexDirection: 'row',
marginBottom: theme.spacing(1),
},
headerText: {
color: theme.palette.warningText,
},
messageText: {
color: theme.palette.warningText,
},
});
/**
* WarningPanel. Show a user friendly error message to a user similar to ErrorPanel except that the warning panel
* only shows the warning message to the user
*/
class WarningPanel extends Component {
static propTypes = {
message: PropTypes.node.isRequired,
};
render() {
const { classes, title, message, children } = this.props;
return (
<div className={classes.message}>
<div className={classes.header}>
<ErrorOutlineStyled />
<Typography className={classes.headerText} variant="subtitle1">
{title}
</Typography>
</div>
{message && (
<Typography className={classes.messageText}>{message}</Typography>
)}
{children}
</div>
);
}
}
export default withStyles(styles)(WarningPanel);
@@ -0,0 +1,45 @@
/*
* 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 WarningPanel from '.';
import { Link, Button } from '@material-ui/core';
export default {
title: 'Warning Panel',
component: WarningPanel,
};
export const Default = () => (
<WarningPanel
title="Example Warning Title"
message={
<>
This example entity is missing something. If this is unexpected, please
make sure you have set up everything correctly by following{' '}
<Link href="http://example.com">this guide</Link>.
</>
}
/>
);
export const Children = () => (
<WarningPanel title="Example Warning Title">
<Button variant="outlined" color="primary">
Supports custom children - for example this button
</Button>
</WarningPanel>
);
@@ -0,0 +1,40 @@
/*
* 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 } from '@testing-library/react';
import { wrapInThemedTestApp } from '@backstage/test-utils';
import WarningPanel from './WarningPanel';
const minProps = { title: 'Mock title', message: 'Some more info' };
describe('<WarningPanel />', () => {
it('renders without exploding', () => {
const { getByText } = render(
wrapInThemedTestApp(<WarningPanel {...minProps} />),
);
expect(getByText('Mock title')).toBeInTheDocument();
});
it('renders message and children', () => {
const { getByText } = render(
wrapInThemedTestApp(<WarningPanel {...minProps}>children</WarningPanel>),
);
expect(getByText('Some more info')).toBeInTheDocument();
expect(getByText('children')).toBeInTheDocument();
});
});
@@ -0,0 +1,16 @@
/*
* 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 './WarningPanel';
+1
View File
@@ -36,3 +36,4 @@ export { default as SortableTable } from './components/SortableTable';
export { default as StructuredMetadataTable } from './components/StructuredMetadataTable';
export { FeatureCalloutCircular } from './components/FeatureDiscovery/FeatureCalloutCircular';
export * from './components/Status';
export { default as WarningPanel } from './components/WarningPanel';
@@ -14,18 +14,18 @@
* limitations under the License.
*/
import React, { FC } from 'react';
import {
Divider,
Link,
ListItem,
ListItemIcon,
Divider,
ListItemText,
makeStyles,
} from '@material-ui/core';
import Box from '@material-ui/core/Box';
import grey from '@material-ui/core/colors/grey';
import ArrowIcon from '@material-ui/icons/ArrowForward';
import React, { FC } from 'react';
import grey from '@material-ui/core/colors/grey';
import Box from '@material-ui/core/Box';
const useStyles = makeStyles(theme => ({
root: {
@@ -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, Props } from './BottomLink';
@@ -1,81 +0,0 @@
/*
* 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, { Component } from 'react';
import PropTypes from 'prop-types';
import { Tooltip, Link, withStyles } from '@material-ui/core';
import { StatusError } from 'components/Status';
import HeaderLabel from './HeaderLabel';
const style = theme => ({
notVerified: {
color: theme.palette.status.error,
borderRadius: 4,
padding: '3px 6px',
fontSize: '8pt',
opacity: 0.8,
fontWeight: 'bold',
position: 'relative',
top: -4,
backgroundColor: 'pink',
float: 'right',
marginLeft: 14,
},
label: { float: 'left' },
});
class OwnerHeaderLabel extends Component {
static propTypes = {
owner: PropTypes.object.isRequired,
};
render() {
const { owner, classes } = this.props;
const isBadSquad = owner.type !== 'squad';
const notVerified = isBadSquad && (
<Link href="https://spotify.stackenterprise.co/a/4412/23">
<span className={classes.notVerified}>
<StatusError style={{ position: 'relative', top: 2 }} /> Squad not
verified!
</span>
</Link>
);
const label = (
<Tooltip
title="This component is not owned by an existing squad. Click the badge to learn how to fix this."
placement="bottom"
>
<span>
<span className={classes.label}>{owner.name}</span>
</span>
</Tooltip>
);
return (
<>
<HeaderLabel
label="Owner"
value={isBadSquad ? label : owner.name}
url={owner.name ? `/org/${owner.name}` : ''}
/>
{notVerified}
</>
);
}
}
export default withStyles(style)(OwnerHeaderLabel);
@@ -25,7 +25,7 @@ import {
makeStyles,
} from '@material-ui/core';
import ErrorBoundary from 'layout/ErrorBoundary/ErrorBoundary';
import BottomLink, { Props as BottomLinkProps } from './BottomLink';
import BottomLink, { Props as BottomLinkProps } from '../BottomLink';
const useStyles = makeStyles(theme => ({
header: {
+3 -2
View File
@@ -18,8 +18,9 @@ import { makeStyles } from '@material-ui/core';
import clsx from 'clsx';
import React, { FC, useRef, useState } from 'react';
import { sidebarConfig, SidebarContext } from './config';
import { BackstageTheme } from '@backstage/theme';
const useStyles = makeStyles(theme => ({
const useStyles = makeStyles<typeof BackstageTheme>(theme => ({
root: {
zIndex: 1000,
position: 'relative',
@@ -35,7 +36,7 @@ const useStyles = makeStyles(theme => ({
top: 0,
bottom: 0,
padding: 0,
background: '#171717',
background: theme.palette.sidebar,
overflowX: 'hidden',
width: sidebarConfig.drawerWidthClosed,
transition: theme.transitions.create('width', {
@@ -0,0 +1,77 @@
/*
* 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, { useState } from 'react';
import { TabbedCard, CardTab } from '.';
export default {
title: 'Tabbed Card',
component: TabbedCard,
};
export const Default = () => {
return (
<TabbedCard title="Default Example Header">
<CardTab label="Option 1">some content 1</CardTab>
<CardTab label="Option 2">some content 2</CardTab>
<CardTab label="Option 3">some content 3</CardTab>
<CardTab label="Option 4">some content 4</CardTab>
</TabbedCard>
);
};
const linkInfo = { title: 'Go to XYZ Location', link: '#' };
export const WithFooterLink = () => {
return (
<TabbedCard title="Footer Link Example Header" deepLink={linkInfo}>
<CardTab label="Option 1">some content 1</CardTab>
<CardTab label="Option 2">some content 2</CardTab>
<CardTab label="Option 3">some content 3</CardTab>
<CardTab label="Option 4">some content 4</CardTab>
</TabbedCard>
);
};
export const WithControlledTabValue = () => {
const [selectedTab, setSelectedTab] = useState('one');
const handleChange = (_ev, newSelectedTab) => setSelectedTab(newSelectedTab);
return (
<>
<span>Selected tab is {selectedTab}</span>
<TabbedCard
value={selectedTab}
onChange={handleChange}
title="Controlled Value Example"
>
<CardTab value="one" label="Option 1">
some content 1
</CardTab>
<CardTab value="two" label="Option 2">
some content 2
</CardTab>
<CardTab value="three" label="Option 3">
some content 3
</CardTab>
<CardTab value="four" label="Option 4">
some content 4
</CardTab>
</TabbedCard>
</>
);
};
@@ -0,0 +1,105 @@
/*
* 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, fireEvent } from '@testing-library/react';
import { wrapInTestApp } from '@backstage/test-utils';
import { TabbedCard, CardTab } from '.';
const minProps = {
title: 'Some title',
deepLink: {
title: 'A deepLink title',
link: '/mocked',
},
};
describe('<TabbedCard />', () => {
it('renders without exploding', () => {
const rendered = render(
wrapInTestApp(
<TabbedCard title={minProps.title}>
<CardTab label="Test 1">Test Content</CardTab>
<CardTab label="Test 2">Test Content</CardTab>
</TabbedCard>,
),
);
expect(rendered.getByText('Some title')).toBeInTheDocument();
});
it('renders a deepLink when prop is set', () => {
const rendered = render(
wrapInTestApp(
<TabbedCard deepLink={minProps.deepLink}>
<CardTab label="Test 1">Test Content</CardTab>
<CardTab label="Test 2">Test Content</CardTab>
</TabbedCard>,
),
);
expect(rendered.getByText('A deepLink title')).toBeInTheDocument();
});
it('switches tabs when clicking', () => {
const rendered = render(
wrapInTestApp(
<TabbedCard>
<CardTab label="Test 1">Test Content 1</CardTab>
<CardTab label="Test 2">Test Content 2</CardTab>
</TabbedCard>,
),
);
expect(rendered.getByText('Test Content 1')).toBeInTheDocument();
fireEvent.click(rendered.getByText('Test 2'));
expect(rendered.getByText('Test Content 2')).toBeInTheDocument();
});
it('switches tabs when clicking in controlled mode', () => {
let selectedTab = 'one';
const handleTabChange = jest.fn(
(_ev, newSelectedTab) => (selectedTab = newSelectedTab),
);
const rendered = render(
wrapInTestApp(
<TabbedCard value={selectedTab} onChange={handleTabChange}>
<CardTab value="one" label="Test 1">
Test Content 1
</CardTab>
<CardTab value="two" label="Test 2">
Test Content 2
</CardTab>
</TabbedCard>,
),
);
expect(rendered.getByText('Test Content 1')).toBeInTheDocument();
fireEvent.click(rendered.getByText('Test 2'));
expect(handleTabChange.mock.calls.length).toBe(1);
rendered.rerender(
<TabbedCard value={selectedTab} onChange={handleTabChange}>
<CardTab value="one" label="Test 1">
Test Content 1
</CardTab>
<CardTab value="two" label="Test 2">
Test Content 2
</CardTab>
</TabbedCard>,
);
expect(rendered.getByText('Test Content 2')).toBeInTheDocument();
});
});
@@ -0,0 +1,127 @@
/*
* 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, { FC, useState, ReactElement, ReactNode } from 'react';
import {
Card,
CardContent,
CardHeader,
Divider,
withStyles,
makeStyles,
Tabs,
Tab,
TabProps,
} from '@material-ui/core';
import BottomLink, { Props as BottomLinkProps } from '../BottomLink';
import ErrorBoundary from '../ErrorBoundary/ErrorBoundary';
const useTabsStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(0, 2, 0, 2.5),
minHeight: theme.spacing(3),
},
indicator: {
backgroundColor: theme.palette.info.main,
height: theme.spacing(0.3),
},
}));
const BoldHeader = withStyles(theme => ({
root: { padding: theme.spacing(2, 2, 2, 2.5), display: 'inline-block' },
title: { fontWeight: 700 },
subheader: { paddingTop: theme.spacing(1) },
}))(CardHeader);
type Props = {
slackChannel?: string;
children?: ReactElement<TabProps>[];
onChange?: (event: React.ChangeEvent<{}>, value: number | string) => void;
title?: string;
value?: number | string;
deepLink?: BottomLinkProps;
};
const TabbedCard: FC<Props> = ({
slackChannel = '#backstage',
children,
title,
deepLink,
value,
onChange,
}) => {
const tabsClasses = useTabsStyles();
const [selectedIndex, selectIndex] = useState(0);
const handleChange = onChange
? onChange
: (_ev, newSelectedIndex: number) => selectIndex(newSelectedIndex);
let selectedTabContent: ReactNode;
if (!value) {
React.Children.map(children, (child, index) => {
if (index === selectedIndex) selectedTabContent = child?.props.children;
});
} else {
React.Children.map(children, child => {
if (child?.props.value === value)
selectedTabContent = child?.props.children;
});
}
return (
<Card>
<ErrorBoundary slackChannel={slackChannel}>
{title && <BoldHeader title={title} />}
<Tabs
classes={tabsClasses}
value={value || selectedIndex}
onChange={handleChange}
>
{children}
</Tabs>
<Divider />
<CardContent>{selectedTabContent}</CardContent>
{deepLink && <BottomLink {...deepLink} />}
</ErrorBoundary>
</Card>
);
};
const useCardTabStyles = makeStyles(theme => ({
root: {
minWidth: theme.spacing(6),
minHeight: theme.spacing(3),
margin: theme.spacing(0, 2, 0, 0),
padding: theme.spacing(0.5, 0, 0.5, 0),
textTransform: 'none',
},
selected: {
fontWeight: 'bold',
},
}));
type CardTabProps = TabProps & {
children: ReactNode;
};
const CardTab: FC<CardTabProps> = ({ children, ...props }) => {
const classes = useCardTabStyles();
return <Tab disableRipple classes={classes} {...props} />;
};
export { TabbedCard, CardTab };
@@ -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 * from './TabbedCard';
+10
View File
@@ -0,0 +1,10 @@
import React from 'react';
import { addDecorator } from '@storybook/react';
import { BackstageTheme } from '@backstage/theme';
import { CssBaseline, ThemeProvider } from '@material-ui/core';
addDecorator(story => (
<ThemeProvider theme={BackstageTheme}>
<CssBaseline>{story()}</CssBaseline>
</ThemeProvider>
));
+5 -1
View File
@@ -5,7 +5,11 @@ module.exports = {
'../../core/src/layout/**/*.stories.tsx',
'../../core/src/components/**/*.stories.tsx',
],
addons: ['@storybook/addon-actions', '@storybook/addon-links'],
addons: [
'@storybook/addon-actions',
'@storybook/addon-links',
'@storybook/addon-storysource',
],
webpackFinal: async config => {
config.resolve.alias = {
...config.resolve.alias,
+1
View File
@@ -15,6 +15,7 @@
"devDependencies": {
"@storybook/addon-actions": "^5.3.17",
"@storybook/addon-links": "^5.3.17",
"@storybook/addon-storysource": "^5.3.18",
"@storybook/addons": "^5.3.17",
"@storybook/react": "^5.3.17"
}
+1
View File
@@ -0,0 +1 @@
registry=https://registry.npmjs.org/
+15
View File
@@ -0,0 +1,15 @@
# @backstage/test-utils-core
This package provides utilities for testing the Backstage core packages.
## Installation
This package should not be used directly, use `@backstage/test-utils` instead. All exports from this
package are re-exported by `@backstage/test-utils`.
The reason this package exists is to allow the Backstage core packages to use the testing utils exposed in this package. Since `@backstage/test-utils` needs to depend on `@backstage/core`, core is not able to use those test-utils. We put any test-utils that don't need to depend on other Backstage packages in this package, in order for them to be usable by any other `@backstage` packages.
## Documentation
- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md)
- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md)
+34
View File
@@ -0,0 +1,34 @@
{
"name": "@backstage/test-utils-core",
"description": "Utilities to test Backstage core",
"version": "0.1.1-alpha.3",
"private": false,
"publishConfig": {
"access": "public"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/spotify/backstage",
"directory": "packages/test-utils-core"
},
"keywords": [
"backstage"
],
"license": "Apache-2.0",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "backstage-cli plugin:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test"
},
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@types/jest": "^24.0.0",
"@types/node": "^12.0.0",
"react": "^16.12.0",
"react-dom": "^16.12.0"
}
}
+16
View File
@@ -0,0 +1,16 @@
/*
* 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 * from './testUtils';
@@ -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.
*/
import '@testing-library/jest-dom/extend-expect';
@@ -23,7 +23,7 @@ const codes = {
Esc: 27,
};
export default class Keyboard {
export class Keyboard {
static async type(target, input) {
await new Keyboard(target).type(input);
}
@@ -15,7 +15,7 @@
*/
import React from 'react';
import Keyboard from './Keyboard';
import { Keyboard } from './Keyboard';
import { render } from '@testing-library/react';
describe('testUtils.Keyboard', () => {
@@ -0,0 +1,19 @@
/*
* 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 * from './Keyboard';
export * from './logCollector';
export * from './testingLibrary';
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.json",
"include": ["src"],
"compilerOptions": {
"baseUrl": "src"
}
}
+1
View File
@@ -39,6 +39,7 @@
},
"peerDependencies": {
"@backstage/theme": "^0.1.1-alpha.3",
"@backstage/test-utils-core": "^0.1.1-alpha.3",
"@material-ui/core": "^4.9.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
+1
View File
@@ -14,3 +14,4 @@
* limitations under the License.
*/
export * from './testUtils';
export * from '@backstage/test-utils-core';
@@ -0,0 +1,35 @@
/*
* 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 } from '@testing-library/react';
import { wrapInTestApp } from './appWrappers';
import { Route } from 'react-router';
describe('wrapInTestApp', () => {
it('should provide routing', () => {
const rendered = render(
wrapInTestApp(
<>
<Route path="/route1">Route 1</Route>
<Route path="/route2">Route 2</Route>
</>,
['/route2'],
),
);
expect(rendered.getByText('Route 2')).toBeInTheDocument();
});
});
@@ -20,10 +20,6 @@ import { MemoryRouter } from 'react-router';
import { Route } from 'react-router-dom';
import { BackstageTheme } from '@backstage/theme';
export { default as Keyboard } from './Keyboard';
export { default as mockBreakpoint } from './mockBreakpoint';
export * from './logCollector';
export function wrapInTestApp(
Component: ComponentType | ReactNode,
initialRouterEntries: string[] = ['/'],
@@ -14,8 +14,5 @@
* limitations under the License.
*/
export { default as Keyboard } from './Keyboard';
export { default as mockBreakpoint } from './mockBreakpoint';
export * from './appWrappers';
export * from './logCollector';
export * from './testingLibrary';
+3 -1
View File
@@ -20,10 +20,11 @@ import { blue, yellow } from '@material-ui/core/colors';
import { BackstageMuiTheme, BackstageMuiThemeOptions } from './types';
export const COLORS = {
const COLORS = {
PAGE_BACKGROUND: '#F8F8F8',
DEFAULT_PAGE_THEME_COLOR: '#7C3699',
DEFAULT_PAGE_THEME_LIGHT_COLOR: '#ECDBF2',
SIDEBAR_BACKGROUND_COLOR: '#171717',
ERROR_BACKGROUND_COLOR: '#FFEBEE',
ERROR_TEXT_COLOR: '#CA001B',
INFO_TEXT_COLOR: '#004e8a',
@@ -86,6 +87,7 @@ const extendedThemeConfig: BackstageMuiThemeOptions = {
linkHover: COLORS.LINK_TEXT_HOVER,
link: COLORS.LINK_TEXT,
gold: yellow.A700,
sidebar: COLORS.SIDEBAR_BACKGROUND_COLOR,
},
navigation: {
width: 220,
+3 -2
View File
@@ -20,11 +20,11 @@ import { blue, yellow } from '@material-ui/core/colors';
import { BackstageMuiTheme, BackstageMuiThemeOptions } from './types';
export const COLORS = {
const COLORS = {
PAGE_BACKGROUND: '#282828',
EFAULT_PAGE_THEME_COLOR: '#232323',
DEFAULT_PAGE_THEME_COLOR: '#7C3699',
DEFAULT_PAGE_THEME_LIGHT_COLOR: '#ECDBF2',
SIDEBAR_BACKGROUND_COLOR: '#424242',
ERROR_BACKGROUND_COLOR: '#FFEBEE',
ERROR_TEXT_COLOR: '#CA001B',
INFO_TEXT_COLOR: '#004e8a',
@@ -91,6 +91,7 @@ const extendedThemeConfig: BackstageMuiThemeOptions = {
linkHover: COLORS.LINK_TEXT_HOVER,
link: COLORS.LINK_TEXT,
gold: yellow.A700,
sidebar: COLORS.SIDEBAR_BACKGROUND_COLOR,
},
navigation: {
width: 220,
+3 -1
View File
@@ -20,10 +20,11 @@ import { blue, yellow } from '@material-ui/core/colors';
import { BackstageMuiTheme, BackstageMuiThemeOptions } from './types';
export const COLORS = {
const COLORS = {
PAGE_BACKGROUND: '#F8F8F8',
DEFAULT_PAGE_THEME_COLOR: '#7C3699',
DEFAULT_PAGE_THEME_LIGHT_COLOR: '#ECDBF2',
SIDEBAR_BACKGROUND_COLOR: '#171717',
ERROR_BACKGROUND_COLOR: '#FFEBEE',
ERROR_TEXT_COLOR: '#CA001B',
INFO_TEXT_COLOR: '#004e8a',
@@ -89,6 +90,7 @@ const extendedThemeConfig: BackstageMuiThemeOptions = {
linkHover: COLORS.LINK_TEXT_HOVER,
link: COLORS.LINK_TEXT,
gold: yellow.A700,
sidebar: COLORS.SIDEBAR_BACKGROUND_COLOR,
},
navigation: {
width: 220,
+1 -1
View File
@@ -15,4 +15,4 @@
*/
export { default as BackstageThemeLight } from './BackstageThemeLight';
export { default as BackstageThemeDark } from './BackstageThemeDark';
export { default as BackstageTheme, COLORS } from './BackstageTheme';
export { default as BackstageTheme } from './BackstageTheme';
+1
View File
@@ -38,6 +38,7 @@ export type BackstageMuiPalette = Theme['palette'] & {
linkHover: string;
link: string;
gold: string;
sidebar: string;
bursts: {
fontColor: string;
slackChannelText: string;
@@ -15,22 +15,28 @@
*/
import React, { FC } from 'react';
import { Sparklines, SparklinesLine, SparklinesProps } from 'react-sparklines';
import { COLORS } from '@backstage/theme';
import { useTheme } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
function color(data: number[]): string | undefined {
function color(
data: number[],
theme: typeof BackstageTheme,
): string | undefined {
const lastNum = data[data.length - 1];
if (!lastNum) return undefined;
if (lastNum >= 0.9) return COLORS.STATUS.OK;
if (lastNum >= 0.5) return COLORS.STATUS.WARNING;
return COLORS.STATUS.ERROR;
if (lastNum >= 0.9) return theme.palette.status.ok;
if (lastNum >= 0.5) return theme.palette.status.warning;
return theme.palette.status.error;
}
const CategoryTrendline: FC<SparklinesProps & { title?: string }> = props => {
const theme = useTheme<typeof BackstageTheme>();
if (!props.data) return null;
return (
<Sparklines width={120} height={30} min={0} max={1} {...props}>
{props.title && <title>{props.title}</title>}
<SparklinesLine color={color(props.data)} />
<SparklinesLine color={color(props.data, theme)} />
</Sparklines>
);
};
+156 -1
View File
@@ -2901,6 +2901,25 @@
qs "^6.6.0"
ts-dedent "^1.1.0"
"@storybook/addon-storysource@^5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-5.3.18.tgz#003374d76c0898c3b74aa4cd008f444eb6b63c68"
integrity sha512-uMSu505rbcNErAYngV5raVRQ/eMiOtGNA88WxNzD/8h/0EVpEXlBAewiGzAmooNhCa/3O3iPuwka8Cky6aWPOA==
dependencies:
"@storybook/addons" "5.3.18"
"@storybook/components" "5.3.18"
"@storybook/router" "5.3.18"
"@storybook/source-loader" "5.3.18"
"@storybook/theming" "5.3.18"
core-js "^3.0.1"
estraverse "^4.2.0"
loader-utils "^1.2.3"
prettier "^1.16.4"
prop-types "^15.7.2"
react-syntax-highlighter "^11.0.2"
regenerator-runtime "^0.13.3"
util-deprecate "^1.0.2"
"@storybook/addons@5.3.17", "@storybook/addons@^5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/addons/-/addons-5.3.17.tgz#8efab65904040b0b8578eedc9a5772dbcbf6fa83"
@@ -2914,6 +2933,19 @@
global "^4.3.2"
util-deprecate "^1.0.2"
"@storybook/addons@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/addons/-/addons-5.3.18.tgz#5cbba6407ef7a802041c5ee831473bc3bed61f64"
integrity sha512-ZQjDgTUDFRLvAiBg2d8FgPgghfQ+9uFyXQbtiGlTBLinrPCeQd7J86qiUES0fcGoohCCw0wWKtvB0WF2z1XNDg==
dependencies:
"@storybook/api" "5.3.18"
"@storybook/channels" "5.3.18"
"@storybook/client-logger" "5.3.18"
"@storybook/core-events" "5.3.18"
core-js "^3.0.1"
global "^4.3.2"
util-deprecate "^1.0.2"
"@storybook/api@5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/api/-/api-5.3.17.tgz#1c0dad3309afef6b0a5585cb59c65824fb4d2721"
@@ -2940,6 +2972,32 @@
telejson "^3.2.0"
util-deprecate "^1.0.2"
"@storybook/api@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/api/-/api-5.3.18.tgz#95582ab90d947065e0e34ed603650a3630dcbd16"
integrity sha512-QXaccNCARHzPWOuxYndiebGWBZmwiUvRgB9ji0XTJBS3y8K0ZPb5QyuqiKPaEWUj8dBA8rzdDtkW3Yt95Namaw==
dependencies:
"@reach/router" "^1.2.1"
"@storybook/channels" "5.3.18"
"@storybook/client-logger" "5.3.18"
"@storybook/core-events" "5.3.18"
"@storybook/csf" "0.0.1"
"@storybook/router" "5.3.18"
"@storybook/theming" "5.3.18"
"@types/reach__router" "^1.2.3"
core-js "^3.0.1"
fast-deep-equal "^2.0.1"
global "^4.3.2"
lodash "^4.17.15"
memoizerific "^1.11.3"
prop-types "^15.6.2"
react "^16.8.3"
semver "^6.0.0"
shallow-equal "^1.1.0"
store2 "^2.7.1"
telejson "^3.2.0"
util-deprecate "^1.0.2"
"@storybook/channel-postmessage@5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-5.3.17.tgz#807b6316cd0e52d9f27363d5092ad1cd896b694c"
@@ -2958,6 +3016,13 @@
dependencies:
core-js "^3.0.1"
"@storybook/channels@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/channels/-/channels-5.3.18.tgz#490c9eaa8292b0571c0f665052b12addf7c35f21"
integrity sha512-scP/6td/BJSEOgfN+qaYGDf3E793xye7tIw6W+sYqwg+xdMFO39wVXgVZNpQL6sLEwpJZTaPywCjC6p6ksErqQ==
dependencies:
core-js "^3.0.1"
"@storybook/client-api@5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-5.3.17.tgz#fc1d247caf267ebcc6ddf957fca7e02ae752d99e"
@@ -2988,6 +3053,13 @@
dependencies:
core-js "^3.0.1"
"@storybook/client-logger@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-5.3.18.tgz#27c9d09d788965db0164be6e168bc3f03adbf88f"
integrity sha512-RZjxw4uqZX3Yk27IirbB/pQG+wRsQSSRlKqYa8KQ5bSanm4IrcV9VA1OQbuySW9njE+CexAnakQJ/fENdmurNg==
dependencies:
core-js "^3.0.1"
"@storybook/components@5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/components/-/components-5.3.17.tgz#287430fc9c5f59b1d3590b50b3c7688355b22639"
@@ -3015,6 +3087,33 @@
simplebar-react "^1.0.0-alpha.6"
ts-dedent "^1.1.0"
"@storybook/components@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/components/-/components-5.3.18.tgz#528f6ab1660981e948993a04b407a6fad7751589"
integrity sha512-LIN4aVCCDY7klOwtuqQhfYz4tHaMADhXEzZpij+3r8N68Inck6IJ1oo9A9umXQPsTioQi8e6FLobH1im90j/2A==
dependencies:
"@storybook/client-logger" "5.3.18"
"@storybook/theming" "5.3.18"
"@types/react-syntax-highlighter" "11.0.4"
"@types/react-textarea-autosize" "^4.3.3"
core-js "^3.0.1"
global "^4.3.2"
lodash "^4.17.15"
markdown-to-jsx "^6.9.1"
memoizerific "^1.11.3"
polished "^3.3.1"
popper.js "^1.14.7"
prop-types "^15.7.2"
react "^16.8.3"
react-dom "^16.8.3"
react-focus-lock "^2.1.0"
react-helmet-async "^1.0.2"
react-popper-tooltip "^2.8.3"
react-syntax-highlighter "^11.0.2"
react-textarea-autosize "^7.1.0"
simplebar-react "^1.0.0-alpha.6"
ts-dedent "^1.1.0"
"@storybook/core-events@5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.3.17.tgz#698ce0a36c29fe8fa04608f56ccca53aa1d31638"
@@ -3022,6 +3121,13 @@
dependencies:
core-js "^3.0.1"
"@storybook/core-events@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.3.18.tgz#e5d335f8a2c7dd46502b8f505006f1e111b46d49"
integrity sha512-uQ6NYJ5WODXK8DJ7m8y3yUAtWB3n+6XtYztjY+tdkCsLYvTYDXNS+epV+f5Hu9+gB+/Dm+b5Su4jDD+LZB2QWA==
dependencies:
core-js "^3.0.1"
"@storybook/core@5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/core/-/core-5.3.17.tgz#abd09dc416f87c7954ef3615bc3f4898c93e2b45"
@@ -3162,6 +3268,37 @@
qs "^6.6.0"
util-deprecate "^1.0.2"
"@storybook/router@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/router/-/router-5.3.18.tgz#8ab22f1f2f7f957e78baf992030707a62289076e"
integrity sha512-6B2U2C75KTSVaCuYYgcubeJGcCSnwsXuEf50hEd5mGqWgHZfojCtGvB7Ko4X+0h8rEC+eNA4p7YBOhlUv9WNrQ==
dependencies:
"@reach/router" "^1.2.1"
"@storybook/csf" "0.0.1"
"@types/reach__router" "^1.2.3"
core-js "^3.0.1"
global "^4.3.2"
lodash "^4.17.15"
memoizerific "^1.11.3"
qs "^6.6.0"
util-deprecate "^1.0.2"
"@storybook/source-loader@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-5.3.18.tgz#39ba28d9664ab8204d6b04ee757772369931e7e5"
integrity sha512-oljmLt3KMu17W9FAaEgocsI6wrloWczS26SVXadzbDbL+8Tu6vTeFJSd6HcY+e4JIIKjuze0kmaVhpi6wwPbZQ==
dependencies:
"@storybook/addons" "5.3.18"
"@storybook/client-logger" "5.3.18"
"@storybook/csf" "0.0.1"
core-js "^3.0.1"
estraverse "^4.2.0"
global "^4.3.2"
loader-utils "^1.2.3"
prettier "^1.16.4"
prop-types "^15.7.2"
regenerator-runtime "^0.13.3"
"@storybook/theming@5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/theming/-/theming-5.3.17.tgz#cf6278c4857229c7167faf04d5b2206bc5ee04e1"
@@ -3180,6 +3317,24 @@
resolve-from "^5.0.0"
ts-dedent "^1.1.0"
"@storybook/theming@5.3.18":
version "5.3.18"
resolved "https://registry.npmjs.org/@storybook/theming/-/theming-5.3.18.tgz#35e78de79d9cf8f1248af0dd1c7fa60555761312"
integrity sha512-lfFTeLoYwLMKg96N3gn0umghMdAHgJBGuk2OM8Ll84yWtdl9RGnzfiI1Fl7Cr5k95dCF7drLJlJCao1VxUkFSA==
dependencies:
"@emotion/core" "^10.0.20"
"@emotion/styled" "^10.0.17"
"@storybook/client-logger" "5.3.18"
core-js "^3.0.1"
deep-object-diff "^1.1.0"
emotion-theming "^10.0.19"
global "^4.3.2"
memoizerific "^1.11.3"
polished "^3.3.1"
prop-types "^15.7.2"
resolve-from "^5.0.0"
ts-dedent "^1.1.0"
"@storybook/ui@5.3.17":
version "5.3.17"
resolved "https://registry.npmjs.org/@storybook/ui/-/ui-5.3.17.tgz#2d47617896a2d928fb79dc8a0e709cee9b57cc50"
@@ -15765,7 +15920,7 @@ prepend-http@^1.0.0, prepend-http@^1.0.1:
resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
prettier@^1.18.2, prettier@^1.19.1:
prettier@^1.16.4, prettier@^1.18.2, prettier@^1.19.1:
version "1.19.1"
resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==