Merge branch 'master' into feature/yarn-clean
This commit is contained in:
+1
-1
@@ -17,7 +17,7 @@ and a large flora of components.
|
||||
|
||||
Additional open sourced plugins would be added to the `plugins` directory in this monorepo.
|
||||
|
||||
While we encourage using the open soure model, integrators that want to experiment with
|
||||
While we encourage using the open source model, integrators that want to experiment with
|
||||
Backstage internally may also choose to develop closed source plugins in a manner that suits
|
||||
them best, for example in their respective Backstage source repository.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -54,7 +54,7 @@ export default createPlugin({
|
||||
});
|
||||
```
|
||||
|
||||
This is where the plugin is created and where it hooks into the app by declaring what component should be shown on what url. See reference docs for [createPlugin(coming soon)](http://github.com/spotify/backstage/) or [router(coming soon)](http://github.com/spotify/backstage/).
|
||||
This is where the plugin is created and where it hooks into the app by declaring what component should be shown on what url. See reference docs for [createPlugin](../reference/createPlugin.md) or [router](../reference/createPlugin-router.md).
|
||||
|
||||
## Components
|
||||
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@
|
||||
],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.1.1-alpha.3"
|
||||
"version": "0.1.1-alpha.4"
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.3",
|
||||
"@backstage/core": "^0.1.1-alpha.3",
|
||||
"@backstage/theme": "^0.1.1-alpha.3",
|
||||
"@backstage/plugin-home-page": "^0.1.1-alpha.3",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.3",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.3",
|
||||
"@backstage/cli": "^0.1.1-alpha.4",
|
||||
"@backstage/core": "^0.1.1-alpha.4",
|
||||
"@backstage/plugin-home-page": "^0.1.1-alpha.4",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.4",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.4",
|
||||
"@backstage/theme": "^0.1.1-alpha.4",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
|
||||
@@ -24,8 +24,8 @@ describe('App', () => {
|
||||
value: jest.fn(() => {
|
||||
return {
|
||||
matches: true,
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -41,9 +41,9 @@ export function useThemeType(themeId: string): [string, () => void] {
|
||||
setTheme('auto');
|
||||
}
|
||||
};
|
||||
mql.addEventListener('change', darkListener);
|
||||
mql.addListener(darkListener);
|
||||
return () => {
|
||||
mql.removeEventListener('change', darkListener);
|
||||
mql.removeListener(darkListener);
|
||||
};
|
||||
});
|
||||
function toggleTheme() {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { default as HomePagePlugin } from '@backstage/plugin-home-page';
|
||||
import { default as WelcomePlugin } from '@backstage/plugin-welcome';
|
||||
import { default as LighthousePlugin } from '@backstage/plugin-lighthouse';
|
||||
export { HomePagePlugin, WelcomePlugin, LighthousePlugin };
|
||||
export { plugin as HomePagePlugin } from '@backstage/plugin-home-page';
|
||||
export { plugin as WelcomePlugin } from '@backstage/plugin-welcome';
|
||||
export { plugin as LighthousePlugin } from '@backstage/plugin-lighthouse';
|
||||
|
||||
@@ -51,6 +51,10 @@ module.exports = {
|
||||
bundledDependencies: true,
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true },
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -122,7 +122,7 @@ export async function addPluginToApp(rootDir: string, pluginName: string) {
|
||||
.split('-')
|
||||
.map(name => capitalize(name))
|
||||
.join('');
|
||||
const pluginImport = `import { default as ${pluginNameCapitalized} } from '${pluginPackage}';`;
|
||||
const pluginImport = `import { plugin as ${pluginNameCapitalized} } from '${pluginPackage}';`;
|
||||
const pluginExport = `export { ${pluginNameCapitalized} };`;
|
||||
const pluginsFilePath = 'packages/app/src/plugins.ts';
|
||||
const pluginsFile = resolvePath(rootDir, pluginsFilePath);
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
{
|
||||
"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",
|
||||
"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",
|
||||
"clean": "lerna run clean",
|
||||
"lint": "lerna run lint"
|
||||
"clean": "lerna run clean"
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
@@ -15,10 +22,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"
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default as WelcomePlugin } from 'plugin-welcome';
|
||||
export { plugin as WelcomePlugin } from 'plugin-welcome';
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from './plugin';
|
||||
export { plugin } from './plugin';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import plugin from './plugin';
|
||||
import { plugin } from './plugin';
|
||||
|
||||
describe('welcome', () => {
|
||||
it('should export plugin', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import WelcomePage from './components/WelcomePage';
|
||||
|
||||
export default createPlugin({
|
||||
export const plugin = createPlugin({
|
||||
id: 'welcome',
|
||||
register({ router }) {
|
||||
router.registerRoute('/', WelcomePage);
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { default } from './plugin';
|
||||
export { plugin } from './plugin';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import plugin from './plugin';
|
||||
import { plugin } from './plugin';
|
||||
|
||||
describe('{{ id }}', () => {
|
||||
it('should export plugin', () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import ExampleComponent from './components/ExampleComponent';
|
||||
|
||||
export default createPlugin({
|
||||
export const plugin = createPlugin({
|
||||
id: '{{ id }}',
|
||||
register({ router }) {
|
||||
router.registerRoute('/{{ id }}', ExampleComponent);
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core",
|
||||
"description": "Core API used by Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -42,9 +42,10 @@
|
||||
"recompose": "0.30.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.3",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.3",
|
||||
"@backstage/theme": "^0.1.1-alpha.3",
|
||||
"@backstage/cli": "^0.1.1-alpha.4",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.4",
|
||||
"@backstage/theme": "^0.1.1-alpha.4",
|
||||
"@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';
|
||||
@@ -33,3 +33,4 @@ export { default as SupportButton } from './components/SupportButton';
|
||||
export { default as SortableTable } from './components/SortableTable';
|
||||
export { FeatureCalloutCircular } from './components/FeatureDiscovery/FeatureCalloutCircular';
|
||||
export * from './components/Status';
|
||||
export { default as WarningPanel } from './components/WarningPanel';
|
||||
|
||||
+4
-4
@@ -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: {
|
||||
|
||||
@@ -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';
|
||||
@@ -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,8 +5,16 @@ 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,
|
||||
'@backstage/theme': path.resolve(__dirname, '../../theme/src'),
|
||||
};
|
||||
config.resolve.modules.push(path.resolve(__dirname, '../../core/src'));
|
||||
config.module.rules.push(
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "storybook",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"description": "Storybook build for core package",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
registry=https://registry.npmjs.org/
|
||||
@@ -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)
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@backstage/test-utils-core",
|
||||
"description": "Utilities to test Backstage core",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
+1
-1
@@ -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';
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { withLogCollector } from './logCollector';
|
||||
|
||||
describe('logCollector', () => {
|
||||
it('should collect some logs synchronously', () => {
|
||||
const logs = withLogCollector(() => {
|
||||
console.log('a');
|
||||
console.warn('b');
|
||||
console.error('c');
|
||||
console.error('3');
|
||||
console.warn('2');
|
||||
console.log('1');
|
||||
});
|
||||
|
||||
expect(logs.log).toEqual(['a', '1']);
|
||||
expect(logs.warn).toEqual(['b', '2']);
|
||||
expect(logs.error).toEqual(['c', '3']);
|
||||
});
|
||||
|
||||
it('should collect some logs asynchrnously', async () => {
|
||||
const logs = await withLogCollector(async () => {
|
||||
console.log('a');
|
||||
console.warn('b');
|
||||
console.error('c');
|
||||
console.error('3');
|
||||
console.warn('2');
|
||||
console.log('1');
|
||||
});
|
||||
|
||||
expect(logs.log).toEqual(['a', '1']);
|
||||
expect(logs.warn).toEqual(['b', '2']);
|
||||
expect(logs.error).toEqual(['c', '3']);
|
||||
});
|
||||
|
||||
it('should collect specific logs synchronously', () => {
|
||||
const missedLogs = withLogCollector(() => {
|
||||
const logs = withLogCollector(['warn', 'log'], () => {
|
||||
console.log('a');
|
||||
console.warn('b');
|
||||
console.error('c');
|
||||
console.error('3');
|
||||
console.warn('2');
|
||||
console.log('1');
|
||||
});
|
||||
|
||||
expect(logs.log).toEqual(['a', '1']);
|
||||
expect(logs.warn).toEqual(['b', '2']);
|
||||
// @ts-ignore
|
||||
expect(logs.error).toEqual([]);
|
||||
});
|
||||
|
||||
expect(missedLogs.log).toEqual([]);
|
||||
expect(missedLogs.warn).toEqual([]);
|
||||
expect(missedLogs.error).toEqual(['c', '3']);
|
||||
});
|
||||
|
||||
it('should collect specific logs asynchrnously', async () => {
|
||||
const missedLogs = await withLogCollector(async () => {
|
||||
const logs = await withLogCollector(['error'], async () => {
|
||||
console.log('a');
|
||||
console.warn('b');
|
||||
console.error('c');
|
||||
console.error('3');
|
||||
console.warn('2');
|
||||
console.log('1');
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
expect(logs.log).toEqual([]);
|
||||
// @ts-ignore
|
||||
expect(logs.warn).toEqual([]);
|
||||
expect(logs.error).toEqual(['c', '3']);
|
||||
});
|
||||
|
||||
expect(missedLogs.log).toEqual(['a', '1']);
|
||||
expect(missedLogs.warn).toEqual(['b', '2']);
|
||||
expect(missedLogs.error).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
export type LogFuncs = 'log' | 'warn' | 'error';
|
||||
export type AsyncLogCollector = () => Promise<void>;
|
||||
export type SyncLogCollector = () => void;
|
||||
export type LogCollector = AsyncLogCollector | SyncLogCollector;
|
||||
export type CollectedLogs<T extends LogFuncs> = { [key in T]: string[] };
|
||||
|
||||
const allCategories = ['log', 'warn', 'error'];
|
||||
|
||||
// Asynchronous log collector with that collects all categories
|
||||
export function withLogCollector(
|
||||
callback: AsyncLogCollector,
|
||||
): Promise<CollectedLogs<LogFuncs>>;
|
||||
|
||||
// Synchronous log collector with that collects all categories
|
||||
export function withLogCollector(
|
||||
callback: SyncLogCollector,
|
||||
): CollectedLogs<LogFuncs>;
|
||||
|
||||
// Asynchronous log collector with that only collects selected categories
|
||||
export function withLogCollector<T extends LogFuncs>(
|
||||
logsToCollect: T[],
|
||||
callback: AsyncLogCollector,
|
||||
): Promise<CollectedLogs<T>>;
|
||||
|
||||
// Synchronous log collector with that only collects selected categories
|
||||
export function withLogCollector<T extends LogFuncs>(
|
||||
logsToCollect: T[],
|
||||
callback: SyncLogCollector,
|
||||
): CollectedLogs<T>;
|
||||
|
||||
export function withLogCollector(
|
||||
logsToCollect: LogFuncs[] | LogCollector,
|
||||
callback?: LogCollector,
|
||||
): CollectedLogs<LogFuncs> | Promise<CollectedLogs<LogFuncs>> {
|
||||
const oneArg = !callback;
|
||||
const actualCallback = (oneArg ? logsToCollect : callback) as LogCollector;
|
||||
const categories = (oneArg ? allCategories : logsToCollect) as LogFuncs[];
|
||||
|
||||
const logs = {
|
||||
log: new Array<string>(),
|
||||
warn: new Array<string>(),
|
||||
error: new Array<string>(),
|
||||
};
|
||||
|
||||
const origLog = console.log;
|
||||
const origWarn = console.warn;
|
||||
const origError = console.error;
|
||||
|
||||
if (categories.includes('log')) {
|
||||
console.log = (message: string) => {
|
||||
logs.log.push(message);
|
||||
};
|
||||
}
|
||||
if (categories.includes('warn')) {
|
||||
console.warn = (message: string) => {
|
||||
logs.warn.push(message);
|
||||
};
|
||||
}
|
||||
if (categories.includes('error')) {
|
||||
console.error = (message: string) => {
|
||||
logs.error.push(message);
|
||||
};
|
||||
}
|
||||
|
||||
const restore = () => {
|
||||
console.log = origLog;
|
||||
console.warn = origWarn;
|
||||
console.error = origError;
|
||||
};
|
||||
|
||||
try {
|
||||
const ret = actualCallback();
|
||||
|
||||
if (!ret || !ret.then) {
|
||||
restore();
|
||||
return logs;
|
||||
}
|
||||
|
||||
return ret.then(
|
||||
() => {
|
||||
restore();
|
||||
return logs;
|
||||
},
|
||||
error => {
|
||||
restore();
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
restore();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -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 { ReactElement } from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, RenderResult } from '@testing-library/react';
|
||||
|
||||
// Components using useEffect to perform an asynchronous action (such as fetch) must be rendered within an async
|
||||
// act call to properly get the final state, even with mocked responses. This utility method makes the signature a bit
|
||||
// cleaner, since act doesn't return the result of the evaluated function.
|
||||
// https://github.com/testing-library/react-testing-library/issues/281
|
||||
// https://github.com/facebook/react/pull/14853
|
||||
export async function renderWithEffects(
|
||||
nodes: ReactElement,
|
||||
): Promise<RenderResult> {
|
||||
let value: RenderResult;
|
||||
await act(() => {
|
||||
value = render(nodes);
|
||||
});
|
||||
// @ts-ignore
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/test-utils",
|
||||
"description": "Utilities to test Backstage plugins and apps.",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -25,8 +25,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.3",
|
||||
"@backstage/theme": "^0.1.1-alpha.3",
|
||||
"@backstage/cli": "^0.1.1-alpha.4",
|
||||
"@backstage/theme": "^0.1.1-alpha.4",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
@@ -39,6 +39,7 @@
|
||||
"react-router-dom": "^5.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.3",
|
||||
"@backstage/theme": "^0.1.1-alpha.3",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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, { ComponentType, ReactNode, FunctionComponent } from 'react';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
|
||||
export function wrapInTestApp(
|
||||
Component: ComponentType | ReactNode,
|
||||
initialRouterEntries: string[] = ['/'],
|
||||
) {
|
||||
let Wrapper: ComponentType;
|
||||
if (Component instanceof Function) {
|
||||
Wrapper = Component;
|
||||
} else {
|
||||
Wrapper = (() => Component) as FunctionComponent;
|
||||
}
|
||||
|
||||
return (
|
||||
<MemoryRouter initialEntries={initialRouterEntries}>
|
||||
<Route component={Wrapper} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export function wrapInThemedTestApp(
|
||||
component: ReactNode,
|
||||
initialRouterEntries: string[] = ['/'],
|
||||
) {
|
||||
const themed = (
|
||||
<ThemeProvider theme={BackstageTheme}>{component}</ThemeProvider>
|
||||
);
|
||||
return wrapInTestApp(themed, initialRouterEntries);
|
||||
}
|
||||
|
||||
export const wrapInTheme = (component: ReactNode, theme = BackstageTheme) => (
|
||||
<ThemeProvider theme={theme}>{component}</ThemeProvider>
|
||||
);
|
||||
@@ -14,67 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, {
|
||||
ComponentType,
|
||||
ReactNode,
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
} from 'react';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, RenderResult } from '@testing-library/react';
|
||||
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[] = ['/'],
|
||||
) {
|
||||
let Wrapper: ComponentType;
|
||||
if (Component instanceof Function) {
|
||||
Wrapper = Component;
|
||||
} else {
|
||||
Wrapper = (() => Component) as FunctionComponent;
|
||||
}
|
||||
|
||||
return (
|
||||
<MemoryRouter initialEntries={initialRouterEntries}>
|
||||
<Route component={Wrapper} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export function wrapInThemedTestApp(
|
||||
component: ReactNode,
|
||||
initialRouterEntries: string[] = ['/'],
|
||||
) {
|
||||
const themed = (
|
||||
<ThemeProvider theme={BackstageTheme}>{component}</ThemeProvider>
|
||||
);
|
||||
return wrapInTestApp(themed, initialRouterEntries);
|
||||
}
|
||||
|
||||
export const wrapInTheme = (component: ReactNode, theme = BackstageTheme) => (
|
||||
<ThemeProvider theme={theme}>{component}</ThemeProvider>
|
||||
);
|
||||
|
||||
// Components using useEffect to perform an asynchronous action (such as fetch) must be rendered within an async
|
||||
// act call to properly get the final state, even with mocked responses. This utility method makes the signature a bit
|
||||
// cleaner, since act doesn't return the result of the evaluated function.
|
||||
// https://github.com/testing-library/react-testing-library/issues/281
|
||||
// https://github.com/facebook/react/pull/14853
|
||||
export async function renderWithEffects(
|
||||
nodes: ReactElement,
|
||||
): Promise<RenderResult> {
|
||||
let value: RenderResult;
|
||||
await act(() => {
|
||||
value = render(nodes);
|
||||
});
|
||||
// @ts-ignore
|
||||
return value;
|
||||
}
|
||||
export * from './appWrappers';
|
||||
|
||||
@@ -1,80 +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.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
// If the callback function is async this one will be too.
|
||||
export function withLogCollector(logsToCollect, callback) {
|
||||
if (typeof logsToCollect === 'function') {
|
||||
callback = logsToCollect;
|
||||
logsToCollect = ['log', 'warn', 'error'];
|
||||
}
|
||||
const logs = {
|
||||
log: [],
|
||||
warn: [],
|
||||
error: [],
|
||||
};
|
||||
|
||||
const origLog = console.log;
|
||||
const origWarn = console.warn;
|
||||
const origError = console.error;
|
||||
|
||||
if (logsToCollect.includes('log')) {
|
||||
console.log = message => {
|
||||
logs.log.push(message);
|
||||
};
|
||||
}
|
||||
if (logsToCollect.includes('warn')) {
|
||||
console.warn = message => {
|
||||
logs.warn.push(message);
|
||||
};
|
||||
}
|
||||
if (logsToCollect.includes('error')) {
|
||||
console.error = message => {
|
||||
logs.error.push(message);
|
||||
};
|
||||
}
|
||||
|
||||
const restore = () => {
|
||||
console.log = origLog;
|
||||
console.warn = origWarn;
|
||||
console.error = origError;
|
||||
};
|
||||
|
||||
try {
|
||||
const ret = callback();
|
||||
|
||||
if (!ret || !ret.then) {
|
||||
restore();
|
||||
return logs;
|
||||
}
|
||||
|
||||
return ret.then(
|
||||
() => {
|
||||
restore();
|
||||
return logs;
|
||||
},
|
||||
error => {
|
||||
restore();
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
restore();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/theme",
|
||||
"description": "material-ui theme for use with Backstage.",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -24,7 +24,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.3",
|
||||
"@backstage/cli": "^0.1.1-alpha.4",
|
||||
"@material-ui/core": "^4.9.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -38,6 +38,7 @@ export type BackstageMuiPalette = Theme['palette'] & {
|
||||
linkHover: string;
|
||||
link: string;
|
||||
gold: string;
|
||||
sidebar: string;
|
||||
bursts: {
|
||||
fontColor: string;
|
||||
slackChannelText: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-home-page",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -12,9 +12,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.3",
|
||||
"@backstage/core": "^0.1.1-alpha.3",
|
||||
"@backstage/theme": "^0.1.1-alpha.3",
|
||||
"@backstage/cli": "^0.1.1-alpha.4",
|
||||
"@backstage/core": "^0.1.1-alpha.4",
|
||||
"@backstage/theme": "^0.1.1-alpha.4",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { default } from './plugin';
|
||||
export { plugin } from './plugin';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import plugin from './plugin';
|
||||
import { plugin } from './plugin';
|
||||
|
||||
describe('home-page', () => {
|
||||
it('should export plugin', () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import HomePage from 'components/HomePage';
|
||||
|
||||
export default createPlugin({
|
||||
export const plugin = createPlugin({
|
||||
id: 'home-page',
|
||||
register({ router }) {
|
||||
router.registerRoute('/home', HomePage);
|
||||
|
||||
@@ -7,7 +7,7 @@ A frontend for [lighthouse-audit-service](https://github.com/spotify/lighthouse-
|
||||
### Use cases
|
||||
|
||||
Google's [Lighthouse](https://developers.google.com/web/tools/lighthouse) auditing tool for websites
|
||||
is a great open-source resource forbenchmarking and improving the accessibility, performance, SEO, and best practices of your site.
|
||||
is a great open-source resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your site.
|
||||
At Spotify, we keep track of Lighthouse audit scores over time to look at trends and overall areas for investment.
|
||||
|
||||
This plugin allows you to generate on-demand Lighthouse audits for websites, and to track the trends for the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-lighthouse",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -17,10 +17,10 @@
|
||||
"react-sparklines": "^1.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.3",
|
||||
"@backstage/core": "^0.1.1-alpha.3",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.3",
|
||||
"@backstage/theme": "^0.1.1-alpha.3",
|
||||
"@backstage/cli": "^0.1.1-alpha.4",
|
||||
"@backstage/core": "^0.1.1-alpha.4",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.4",
|
||||
"@backstage/theme": "^0.1.1-alpha.4",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { default } from './plugin';
|
||||
export { plugin } from './plugin';
|
||||
export * from './api';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import plugin from './plugin';
|
||||
import { plugin } from './plugin';
|
||||
|
||||
describe('lighthouse', () => {
|
||||
it('should export plugin', () => {
|
||||
|
||||
@@ -19,7 +19,7 @@ import AuditList from './components/AuditList';
|
||||
import AuditView from './components/AuditView';
|
||||
import CreateAudit from './components/CreateAudit';
|
||||
|
||||
export default createPlugin({
|
||||
export const plugin = createPlugin({
|
||||
id: 'lighthouse',
|
||||
register({ router }) {
|
||||
router.registerRoute('/lighthouse', AuditList);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-welcome",
|
||||
"version": "0.1.1-alpha.3",
|
||||
"version": "0.1.1-alpha.4",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"private": true,
|
||||
@@ -12,9 +12,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.3",
|
||||
"@backstage/core": "^0.1.1-alpha.3",
|
||||
"@backstage/theme": "^0.1.1-alpha.3",
|
||||
"@backstage/cli": "^0.1.1-alpha.4",
|
||||
"@backstage/core": "^0.1.1-alpha.4",
|
||||
"@backstage/theme": "^0.1.1-alpha.4",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { default } from './plugin';
|
||||
export { plugin } from './plugin';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import plugin from './plugin';
|
||||
import { plugin } from './plugin';
|
||||
|
||||
describe('welcome', () => {
|
||||
it('should export plugin', () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import WelcomePage from 'components/WelcomePage';
|
||||
|
||||
export default createPlugin({
|
||||
export const plugin = createPlugin({
|
||||
id: 'welcome',
|
||||
register({ router, featureFlags }) {
|
||||
router.registerRoute('/', WelcomePage);
|
||||
|
||||
@@ -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"
|
||||
@@ -7071,7 +7226,7 @@ cyclist@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
||||
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
|
||||
|
||||
cypress@*, cypress@4.2.0, cypress@^4.2.0:
|
||||
cypress@*, cypress@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmjs.org/cypress/-/cypress-4.2.0.tgz#45673fb648b1a77b9a78d73e58b89ed05212d243"
|
||||
integrity sha512-8LdreL91S/QiTCLYLNbIjLL8Ht4fJmu/4HGLxUI20Tc7JSfqEfCmXELrRfuPT0kjosJwJJZacdSji9XSRkPKUw==
|
||||
@@ -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==
|
||||
|
||||
Reference in New Issue
Block a user