diff --git a/docs/getting-started/development-environment.md b/docs/getting-started/development-environment.md index bb5b24333e..e4fe69af68 100644 --- a/docs/getting-started/development-environment.md +++ b/docs/getting-started/development-environment.md @@ -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) diff --git a/packages/cli/config/eslint.js b/packages/cli/config/eslint.js index c169e354d3..983a423a57 100644 --- a/packages/cli/config/eslint.js +++ b/packages/cli/config/eslint.js @@ -51,6 +51,10 @@ module.exports = { bundledDependencies: true, }, ], + '@typescript-eslint/no-unused-vars': [ + 'warn', + { vars: 'all', args: 'after-used', ignoreRestSiblings: true }, + ], }, overrides: [ { diff --git a/packages/cli/templates/default-app/package.json.hbs b/packages/cli/templates/default-app/package.json.hbs index def7b3178b..7b5fab5221 100644 --- a/packages/cli/templates/default-app/package.json.hbs +++ b/packages/cli/templates/default-app/package.json.hbs @@ -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" } diff --git a/packages/cli/templates/default-app/packages/app/package.json.hbs b/packages/cli/templates/default-app/packages/app/package.json.hbs index 6f24814a71..a5b2b07b96 100644 --- a/packages/cli/templates/default-app/packages/app/package.json.hbs +++ b/packages/cli/templates/default-app/packages/app/package.json.hbs @@ -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" }, diff --git a/packages/core/.storybook/main.js b/packages/core/.storybook/main.js deleted file mode 100644 index 160ecb3b0c..0000000000 --- a/packages/core/.storybook/main.js +++ /dev/null @@ -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; - }, -}; diff --git a/packages/core/package.json b/packages/core/package.json index a41f82a0fc..d60dcf91be 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -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", diff --git a/packages/core/src/api/apis/ApiProvider.test.tsx b/packages/core/src/api/apis/ApiProvider.test.tsx index 5734899f68..f572907c2f 100644 --- a/packages/core/src/api/apis/ApiProvider.test.tsx +++ b/packages/core/src/api/apis/ApiProvider.test.tsx @@ -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; diff --git a/packages/core/src/components/ProgressBars/CircleProgress.test.js b/packages/core/src/components/ProgressBars/CircleProgress.test.js index 68e8f027c2..4975e00cb8 100644 --- a/packages/core/src/components/ProgressBars/CircleProgress.test.js +++ b/packages/core/src/components/ProgressBars/CircleProgress.test.js @@ -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('', () => { 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); }); }); diff --git a/packages/core/src/components/ProgressBars/CircleProgress.tsx b/packages/core/src/components/ProgressBars/CircleProgress.tsx index d5cd191b3d..4d9c5c3340 100644 --- a/packages/core/src/components/ProgressBars/CircleProgress.tsx +++ b/packages/core/src/components/ProgressBars/CircleProgress.tsx @@ -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 => { const classes = useStyles(props); + const theme = useTheme(); const { value, fractional, inverse, unit, max } = { ...defaultProps, ...props, @@ -89,7 +90,7 @@ const CircleProgress: FC = props => { percent={asPercentage} strokeWidth={12} trailWidth={12} - strokeColor={getProgressColor(asActual, inverse, max)} + strokeColor={getProgressColor(theme.palette, asActual, inverse, max)} className={classes.circle} />
diff --git a/packages/core/src/components/ProgressBars/ProgressCard.tsx b/packages/core/src/components/ProgressBars/ProgressCard.tsx index 1964f382e9..dc3ee7aa35 100644 --- a/packages/core/src/components/ProgressBars/ProgressCard.tsx +++ b/packages/core/src/components/ProgressBars/ProgressCard.tsx @@ -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 = { diff --git a/packages/core/src/components/SortableTable.js b/packages/core/src/components/SortableTable.js index e04b844048..ab4e1af2de 100644 --- a/packages/core/src/components/SortableTable.js +++ b/packages/core/src/components/SortableTable.js @@ -171,7 +171,7 @@ const DataTableRow = pure(({ row, columns, handleRowClick, style }) => { * @example * render { * const data = [ - * { id: 'buffalos', amount: 1, status: , statusValue: 2 } + * { id: 'buffalos', amount: 1, status: , statusValue: 2 }, * { id: 'milk', amount: 3, status: , statusValue: 1 } * ]; * const columns = [ diff --git a/packages/core/src/components/SortableTable.stories.tsx b/packages/core/src/components/SortableTable.stories.tsx new file mode 100644 index 0000000000..c534457d53 --- /dev/null +++ b/packages/core/src/components/SortableTable.stories.tsx @@ -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: , statusValue: 2 }, + { id: 'milk', amount: 3, status: , statusValue: 1 }, + { id: 'cheese', amount: 8, status: , statusValue: 1 }, + { id: 'bread', amount: 2, status: , 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: }, +]; + +export const Default = () => ( +
+ +
+); + +export const WithFooter = () => ( +
+ +
+); diff --git a/packages/core/src/components/Status/Status.stories.tsx b/packages/core/src/components/Status/Status.stories.tsx new file mode 100644 index 0000000000..91ef53d7fe --- /dev/null +++ b/packages/core/src/components/Status/Status.stories.tsx @@ -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 = () => ( + <> + Status OK + +); +export const statusWarning = () => ( + <> + Status Warning + +); +export const statusError = () => ( + <> + Status Error + +); +export const statusFailed = () => ( + <> + Status Failed + +); +export const statusPending = () => ( + <> + Status Pending + +); +export const statusRunning = () => ( + <> + Status Running + +); +export const statusNA = () => ( + <> + Status NA + +); diff --git a/packages/core/src/components/WarningPanel/WarningPanel.js b/packages/core/src/components/WarningPanel/WarningPanel.js new file mode 100644 index 0000000000..679f7511d8 --- /dev/null +++ b/packages/core/src/components/WarningPanel/WarningPanel.js @@ -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 ( +
+
+ + + {title} + +
+ {message && ( + {message} + )} + {children} +
+ ); + } +} + +export default withStyles(styles)(WarningPanel); diff --git a/packages/core/src/components/WarningPanel/WarningPanel.stories.tsx b/packages/core/src/components/WarningPanel/WarningPanel.stories.tsx new file mode 100644 index 0000000000..2fd5395db5 --- /dev/null +++ b/packages/core/src/components/WarningPanel/WarningPanel.stories.tsx @@ -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 = () => ( + + This example entity is missing something. If this is unexpected, please + make sure you have set up everything correctly by following{' '} + this guide. + + } + /> +); + +export const Children = () => ( + + + +); diff --git a/packages/core/src/components/WarningPanel/WarningPanel.test.js b/packages/core/src/components/WarningPanel/WarningPanel.test.js new file mode 100644 index 0000000000..4094c65a1f --- /dev/null +++ b/packages/core/src/components/WarningPanel/WarningPanel.test.js @@ -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('', () => { + it('renders without exploding', () => { + const { getByText } = render( + wrapInThemedTestApp(), + ); + expect(getByText('Mock title')).toBeInTheDocument(); + }); + + it('renders message and children', () => { + const { getByText } = render( + wrapInThemedTestApp(children), + ); + expect(getByText('Some more info')).toBeInTheDocument(); + expect(getByText('children')).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/components/WarningPanel/index.js b/packages/core/src/components/WarningPanel/index.js new file mode 100644 index 0000000000..704453fb47 --- /dev/null +++ b/packages/core/src/components/WarningPanel/index.js @@ -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'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 40a4bd452e..93288b9b68 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -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'; diff --git a/packages/core/src/layout/InfoCard/BottomLink.test.tsx b/packages/core/src/layout/BottomLink/BottomLink.test.tsx similarity index 100% rename from packages/core/src/layout/InfoCard/BottomLink.test.tsx rename to packages/core/src/layout/BottomLink/BottomLink.test.tsx diff --git a/packages/core/src/layout/InfoCard/BottomLink.tsx b/packages/core/src/layout/BottomLink/BottomLink.tsx similarity index 100% rename from packages/core/src/layout/InfoCard/BottomLink.tsx rename to packages/core/src/layout/BottomLink/BottomLink.tsx index 6721eda0f1..9250685504 100644 --- a/packages/core/src/layout/InfoCard/BottomLink.tsx +++ b/packages/core/src/layout/BottomLink/BottomLink.tsx @@ -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: { diff --git a/packages/core/src/layout/BottomLink/index.ts b/packages/core/src/layout/BottomLink/index.ts new file mode 100644 index 0000000000..5d4845064b --- /dev/null +++ b/packages/core/src/layout/BottomLink/index.ts @@ -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'; diff --git a/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js b/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js deleted file mode 100644 index abe0ee8b33..0000000000 --- a/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js +++ /dev/null @@ -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 && ( - - - Squad not - verified! - - - ); - const label = ( - - - {owner.name} - - - ); - return ( - <> - - {notVerified} - - ); - } -} - -export default withStyles(style)(OwnerHeaderLabel); diff --git a/packages/core/src/layout/InfoCard/InfoCard.tsx b/packages/core/src/layout/InfoCard/InfoCard.tsx index caa5f16513..f3d03d29c8 100644 --- a/packages/core/src/layout/InfoCard/InfoCard.tsx +++ b/packages/core/src/layout/InfoCard/InfoCard.tsx @@ -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: { diff --git a/packages/core/src/layout/Sidebar/Bar.tsx b/packages/core/src/layout/Sidebar/Bar.tsx index 23ac14b6a4..4cf3db63ab 100644 --- a/packages/core/src/layout/Sidebar/Bar.tsx +++ b/packages/core/src/layout/Sidebar/Bar.tsx @@ -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(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', { diff --git a/packages/core/src/layout/TabbedCard/TabbedCard.stories.tsx b/packages/core/src/layout/TabbedCard/TabbedCard.stories.tsx new file mode 100644 index 0000000000..d670b0b908 --- /dev/null +++ b/packages/core/src/layout/TabbedCard/TabbedCard.stories.tsx @@ -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 ( + + some content 1 + some content 2 + some content 3 + some content 4 + + ); +}; + +const linkInfo = { title: 'Go to XYZ Location', link: '#' }; + +export const WithFooterLink = () => { + return ( + + some content 1 + some content 2 + some content 3 + some content 4 + + ); +}; + +export const WithControlledTabValue = () => { + const [selectedTab, setSelectedTab] = useState('one'); + + const handleChange = (_ev, newSelectedTab) => setSelectedTab(newSelectedTab); + + return ( + <> + Selected tab is {selectedTab} + + + + some content 1 + + + some content 2 + + + some content 3 + + + some content 4 + + + + ); +}; diff --git a/packages/core/src/layout/TabbedCard/TabbedCard.test.tsx b/packages/core/src/layout/TabbedCard/TabbedCard.test.tsx new file mode 100644 index 0000000000..ceeae8d49d --- /dev/null +++ b/packages/core/src/layout/TabbedCard/TabbedCard.test.tsx @@ -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('', () => { + it('renders without exploding', () => { + const rendered = render( + wrapInTestApp( + + Test Content + Test Content + , + ), + ); + expect(rendered.getByText('Some title')).toBeInTheDocument(); + }); + + it('renders a deepLink when prop is set', () => { + const rendered = render( + wrapInTestApp( + + Test Content + Test Content + , + ), + ); + expect(rendered.getByText('A deepLink title')).toBeInTheDocument(); + }); + + it('switches tabs when clicking', () => { + const rendered = render( + wrapInTestApp( + + Test Content 1 + Test Content 2 + , + ), + ); + 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( + + + Test Content 1 + + + Test Content 2 + + , + ), + ); + expect(rendered.getByText('Test Content 1')).toBeInTheDocument(); + + fireEvent.click(rendered.getByText('Test 2')); + expect(handleTabChange.mock.calls.length).toBe(1); + rendered.rerender( + + + Test Content 1 + + + Test Content 2 + + , + ); + expect(rendered.getByText('Test Content 2')).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/layout/TabbedCard/TabbedCard.tsx b/packages/core/src/layout/TabbedCard/TabbedCard.tsx new file mode 100644 index 0000000000..bd7443e924 --- /dev/null +++ b/packages/core/src/layout/TabbedCard/TabbedCard.tsx @@ -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[]; + onChange?: (event: React.ChangeEvent<{}>, value: number | string) => void; + title?: string; + value?: number | string; + deepLink?: BottomLinkProps; +}; + +const TabbedCard: FC = ({ + 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 ( + + + {title && } + + {children} + + + {selectedTabContent} + {deepLink && } + + + ); +}; + +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 = ({ children, ...props }) => { + const classes = useCardTabStyles(); + + return ; +}; + +export { TabbedCard, CardTab }; diff --git a/packages/core/src/layout/TabbedCard/index.ts b/packages/core/src/layout/TabbedCard/index.ts new file mode 100644 index 0000000000..f5019f2451 --- /dev/null +++ b/packages/core/src/layout/TabbedCard/index.ts @@ -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'; diff --git a/packages/storybook/.storybook/config.js b/packages/storybook/.storybook/config.js new file mode 100644 index 0000000000..b083bc0e9e --- /dev/null +++ b/packages/storybook/.storybook/config.js @@ -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 => ( + + {story()} + +)); diff --git a/packages/storybook/.storybook/main.js b/packages/storybook/.storybook/main.js index cf81bf382d..f086fa0d4a 100644 --- a/packages/storybook/.storybook/main.js +++ b/packages/storybook/.storybook/main.js @@ -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, diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 4dbf2990fa..f884da5f07 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -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" } diff --git a/packages/test-utils-core/.npmrc b/packages/test-utils-core/.npmrc new file mode 100644 index 0000000000..214c29d139 --- /dev/null +++ b/packages/test-utils-core/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npmjs.org/ diff --git a/packages/test-utils-core/README.md b/packages/test-utils-core/README.md new file mode 100644 index 0000000000..b99250857d --- /dev/null +++ b/packages/test-utils-core/README.md @@ -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) diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json new file mode 100644 index 0000000000..79d36a3525 --- /dev/null +++ b/packages/test-utils-core/package.json @@ -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" + } +} diff --git a/packages/test-utils-core/src/index.ts b/packages/test-utils-core/src/index.ts new file mode 100644 index 0000000000..43faf28a4a --- /dev/null +++ b/packages/test-utils-core/src/index.ts @@ -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'; diff --git a/packages/test-utils-core/src/setupTests.ts b/packages/test-utils-core/src/setupTests.ts new file mode 100644 index 0000000000..8925258421 --- /dev/null +++ b/packages/test-utils-core/src/setupTests.ts @@ -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'; diff --git a/packages/test-utils/src/testUtils/Keyboard.js b/packages/test-utils-core/src/testUtils/Keyboard.js similarity index 99% rename from packages/test-utils/src/testUtils/Keyboard.js rename to packages/test-utils-core/src/testUtils/Keyboard.js index e3225f2342..8f4a48b0b4 100644 --- a/packages/test-utils/src/testUtils/Keyboard.js +++ b/packages/test-utils-core/src/testUtils/Keyboard.js @@ -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); } diff --git a/packages/test-utils/src/testUtils/Keyboard.test.js b/packages/test-utils-core/src/testUtils/Keyboard.test.js similarity index 98% rename from packages/test-utils/src/testUtils/Keyboard.test.js rename to packages/test-utils-core/src/testUtils/Keyboard.test.js index 31e0885dc1..48b8522c0c 100644 --- a/packages/test-utils/src/testUtils/Keyboard.test.js +++ b/packages/test-utils-core/src/testUtils/Keyboard.test.js @@ -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', () => { diff --git a/packages/test-utils-core/src/testUtils/index.tsx b/packages/test-utils-core/src/testUtils/index.tsx new file mode 100644 index 0000000000..7558f32819 --- /dev/null +++ b/packages/test-utils-core/src/testUtils/index.tsx @@ -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'; diff --git a/packages/test-utils/src/testUtils/logCollector.test.ts b/packages/test-utils-core/src/testUtils/logCollector.test.ts similarity index 100% rename from packages/test-utils/src/testUtils/logCollector.test.ts rename to packages/test-utils-core/src/testUtils/logCollector.test.ts diff --git a/packages/test-utils/src/testUtils/logCollector.ts b/packages/test-utils-core/src/testUtils/logCollector.ts similarity index 100% rename from packages/test-utils/src/testUtils/logCollector.ts rename to packages/test-utils-core/src/testUtils/logCollector.ts diff --git a/packages/test-utils/src/testUtils/testingLibrary.ts b/packages/test-utils-core/src/testUtils/testingLibrary.ts similarity index 100% rename from packages/test-utils/src/testUtils/testingLibrary.ts rename to packages/test-utils-core/src/testUtils/testingLibrary.ts diff --git a/packages/test-utils-core/tsconfig.json b/packages/test-utils-core/tsconfig.json new file mode 100644 index 0000000000..7b73db2f0f --- /dev/null +++ b/packages/test-utils-core/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"], + "compilerOptions": { + "baseUrl": "src" + } +} diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 55b7436aae..ab52ca94a1 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -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", diff --git a/packages/test-utils/src/index.ts b/packages/test-utils/src/index.ts index 43faf28a4a..57b7fd6773 100644 --- a/packages/test-utils/src/index.ts +++ b/packages/test-utils/src/index.ts @@ -14,3 +14,4 @@ * limitations under the License. */ export * from './testUtils'; +export * from '@backstage/test-utils-core'; diff --git a/packages/test-utils/src/testUtils/appWrappers.test.tsx b/packages/test-utils/src/testUtils/appWrappers.test.tsx new file mode 100644 index 0000000000..e2f1b6b7cb --- /dev/null +++ b/packages/test-utils/src/testUtils/appWrappers.test.tsx @@ -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 1 + Route 2 + , + ['/route2'], + ), + ); + expect(rendered.getByText('Route 2')).toBeInTheDocument(); + }); +}); diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index 6aac9c5542..93f92ec537 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -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[] = ['/'], diff --git a/packages/test-utils/src/testUtils/index.tsx b/packages/test-utils/src/testUtils/index.tsx index 3e8305e0fb..ea33de2f31 100644 --- a/packages/test-utils/src/testUtils/index.tsx +++ b/packages/test-utils/src/testUtils/index.tsx @@ -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'; diff --git a/packages/theme/src/BackstageTheme.ts b/packages/theme/src/BackstageTheme.ts index 202bdbc08c..4f61437400 100644 --- a/packages/theme/src/BackstageTheme.ts +++ b/packages/theme/src/BackstageTheme.ts @@ -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, diff --git a/packages/theme/src/BackstageThemeDark.ts b/packages/theme/src/BackstageThemeDark.ts index b8b89c8f0a..9952a5eeaa 100644 --- a/packages/theme/src/BackstageThemeDark.ts +++ b/packages/theme/src/BackstageThemeDark.ts @@ -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, diff --git a/packages/theme/src/BackstageThemeLight.ts b/packages/theme/src/BackstageThemeLight.ts index 14bc942910..a0a063ad24 100644 --- a/packages/theme/src/BackstageThemeLight.ts +++ b/packages/theme/src/BackstageThemeLight.ts @@ -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, diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts index 136355dc37..8aa7e30ef5 100644 --- a/packages/theme/src/index.ts +++ b/packages/theme/src/index.ts @@ -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'; diff --git a/packages/theme/src/types.ts b/packages/theme/src/types.ts index 9a3e93c3a9..cbd3de4efa 100644 --- a/packages/theme/src/types.ts +++ b/packages/theme/src/types.ts @@ -38,6 +38,7 @@ export type BackstageMuiPalette = Theme['palette'] & { linkHover: string; link: string; gold: string; + sidebar: string; bursts: { fontColor: string; slackChannelText: string; diff --git a/plugins/lighthouse/src/components/CategoryTrendline/index.tsx b/plugins/lighthouse/src/components/CategoryTrendline/index.tsx index 95622ed918..a46aff52e5 100644 --- a/plugins/lighthouse/src/components/CategoryTrendline/index.tsx +++ b/plugins/lighthouse/src/components/CategoryTrendline/index.tsx @@ -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 = props => { + const theme = useTheme(); + if (!props.data) return null; return ( {props.title && {props.title}} - + ); }; diff --git a/yarn.lock b/yarn.lock index 03f14af1bf..22ba268f8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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==