From 7812b6ac3cfdd3964c23c0c225ac762c91e7c622 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 20 Apr 2021 11:21:38 +0200 Subject: [PATCH 01/68] Add new shortcut plugin Signed-off-by: Marcus Eide --- packages/app/package.json | 1 + packages/app/src/components/Root/Root.tsx | 3 + packages/app/src/plugins.ts | 2 + plugins/shortcuts/.eslintrc.js | 3 + plugins/shortcuts/README.md | 13 ++ plugins/shortcuts/dev/index.tsx | 26 ++++ plugins/shortcuts/package.json | 51 +++++++ plugins/shortcuts/src/AddShortcut.tsx | 121 +++++++++++++++ plugins/shortcuts/src/EditShortcut.tsx | 134 +++++++++++++++++ plugins/shortcuts/src/ShortcutForm.tsx | 140 ++++++++++++++++++ plugins/shortcuts/src/ShortcutIcon.tsx | 47 ++++++ plugins/shortcuts/src/ShortcutItem.tsx | 98 ++++++++++++ plugins/shortcuts/src/Shortcuts.tsx | 66 +++++++++ .../shortcuts/src/api/LocalStoredShortcuts.ts | 97 ++++++++++++ plugins/shortcuts/src/api/ShortcutApi.ts | 55 +++++++ plugins/shortcuts/src/api/index.ts | 19 +++ plugins/shortcuts/src/index.ts | 18 +++ plugins/shortcuts/src/plugin.test.ts | 22 +++ plugins/shortcuts/src/plugin.ts | 44 ++++++ plugins/shortcuts/src/setupTests.ts | 17 +++ plugins/shortcuts/src/types.ts | 26 ++++ yarn.lock | 8 +- 22 files changed, 1010 insertions(+), 1 deletion(-) create mode 100644 plugins/shortcuts/.eslintrc.js create mode 100644 plugins/shortcuts/README.md create mode 100644 plugins/shortcuts/dev/index.tsx create mode 100644 plugins/shortcuts/package.json create mode 100644 plugins/shortcuts/src/AddShortcut.tsx create mode 100644 plugins/shortcuts/src/EditShortcut.tsx create mode 100644 plugins/shortcuts/src/ShortcutForm.tsx create mode 100644 plugins/shortcuts/src/ShortcutIcon.tsx create mode 100644 plugins/shortcuts/src/ShortcutItem.tsx create mode 100644 plugins/shortcuts/src/Shortcuts.tsx create mode 100644 plugins/shortcuts/src/api/LocalStoredShortcuts.ts create mode 100644 plugins/shortcuts/src/api/ShortcutApi.ts create mode 100644 plugins/shortcuts/src/api/index.ts create mode 100644 plugins/shortcuts/src/index.ts create mode 100644 plugins/shortcuts/src/plugin.test.ts create mode 100644 plugins/shortcuts/src/plugin.ts create mode 100644 plugins/shortcuts/src/setupTests.ts create mode 100644 plugins/shortcuts/src/types.ts diff --git a/packages/app/package.json b/packages/app/package.json index 4d2dbe660a..f052d27109 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -31,6 +31,7 @@ "@backstage/plugin-scaffolder": "^0.9.0", "@backstage/plugin-search": "^0.3.4", "@backstage/plugin-sentry": "^0.3.8", + "@backstage/plugin-shortcuts": "^0.1.1", "@backstage/plugin-tech-radar": "^0.3.9", "@backstage/plugin-techdocs": "^0.7.2", "@backstage/plugin-todo": "^0.1.0", diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 454c0d2e84..9c62ec0b4d 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -39,6 +39,7 @@ import { NavLink } from 'react-router-dom'; import { GraphiQLIcon } from '@backstage/plugin-graphiql'; import { Settings as SidebarSettings } from '@backstage/plugin-user-settings'; import { SidebarSearch } from '@backstage/plugin-search'; +import { Shortcuts } from '@backstage/plugin-shortcuts'; const useSidebarLogoStyles = makeStyles({ root: { @@ -91,6 +92,8 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( + + diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index 7dbea5b995..9ea887ac27 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -17,3 +17,5 @@ // TODO(Rugvip): This plugin is currently not part of the app element tree, // ideally we have an API for the context menu that permits that. export { badgesPlugin } from '@backstage/plugin-badges'; +export { githubDeploymentsPlugin } from '@backstage/plugin-github-deployments'; +export { shortcutsPlugin } from '@backstage/plugin-shortcuts'; diff --git a/plugins/shortcuts/.eslintrc.js b/plugins/shortcuts/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/shortcuts/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/shortcuts/README.md b/plugins/shortcuts/README.md new file mode 100644 index 0000000000..423f03948c --- /dev/null +++ b/plugins/shortcuts/README.md @@ -0,0 +1,13 @@ +# shortcuts + +Welcome to the shortcuts plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/shortcuts](http://localhost:3000/shortcuts). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. diff --git a/plugins/shortcuts/dev/index.tsx b/plugins/shortcuts/dev/index.tsx new file mode 100644 index 0000000000..0da3a90054 --- /dev/null +++ b/plugins/shortcuts/dev/index.tsx @@ -0,0 +1,26 @@ +/* + * Copyright 2021 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 { createDevApp } from '@backstage/dev-utils'; +import { shortcutsPlugin, Shortcuts } from '../src/plugin'; + +createDevApp() + .registerPlugin(shortcutsPlugin) + .addPage({ + element: , + title: 'Root Page', + }) + .render(); diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json new file mode 100644 index 0000000000..32fb41ab78 --- /dev/null +++ b/plugins/shortcuts/package.json @@ -0,0 +1,51 @@ +{ + "name": "@backstage/plugin-shortcuts", + "version": "0.1.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "diff": "backstage-cli plugin:diff", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/core": "^0.7.5", + "@backstage/theme": "^0.2.5", + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "react-hook-form": "^7.1.1", + "react-router": "6.0.0-beta.0", + "react-use": "^15.3.3", + "uuid": "^8.3.2", + "zen-observable": "^0.8.15" + }, + "devDependencies": { + "@backstage/cli": "^0.6.8", + "@backstage/dev-utils": "^0.1.13", + "@backstage/test-utils": "^0.1.10", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^11.2.5", + "@testing-library/user-event": "^12.0.7", + "@types/jest": "^26.0.7", + "@types/node": "^14.14.32", + "cross-fetch": "^3.0.6", + "msw": "^0.21.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/shortcuts/src/AddShortcut.tsx b/plugins/shortcuts/src/AddShortcut.tsx new file mode 100644 index 0000000000..ab845c63a8 --- /dev/null +++ b/plugins/shortcuts/src/AddShortcut.tsx @@ -0,0 +1,121 @@ +/* + * Copyright 2021 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 { useLocation } from 'react-router'; +import { SubmitHandler } from 'react-hook-form'; +import { alertApiRef, useApi } from '@backstage/core'; +import { + Button, + Card, + CardHeader, + makeStyles, + Popover, +} from '@material-ui/core'; +import { ShortcutForm } from './ShortcutForm'; +import { FormValues, Shortcut } from './types'; +import { ShortcutApi } from './api'; + +const useStyles = makeStyles(theme => ({ + card: { + width: 400, + }, + header: { + marginBottom: theme.spacing(1), + }, + button: { + marginTop: theme.spacing(1), + }, +})); + +type Props = { + onClose: () => void; + anchorEl?: Element; + api: ShortcutApi; +}; + +export const AddShortcut = ({ onClose, anchorEl, api }: Props) => { + const classes = useStyles(); + const alertApi = useApi(alertApiRef); + const { pathname } = useLocation(); + const [formValues, setFormValues] = useState(); + const open = Boolean(anchorEl); + + const handleSave: SubmitHandler = async ({ url, title }) => { + const shortcut: Omit = { url, title }; + + try { + await api.add(shortcut); + alertApi.post({ + message: 'Successfully added shortcut', + severity: 'success', + }); + } catch (error) { + alertApi.post({ + message: `Could not add shortcut: ${error.message}`, + severity: 'error', + }); + } + + onClose(); + }; + + const handlePaste = () => { + setFormValues({ url: pathname, title: document.title }); + }; + + const handleClose = () => { + setFormValues(undefined); + onClose(); + }; + + return ( + + + + Paste Current Url + + } + /> + + + + ); +}; diff --git a/plugins/shortcuts/src/EditShortcut.tsx b/plugins/shortcuts/src/EditShortcut.tsx new file mode 100644 index 0000000000..9220c9a73e --- /dev/null +++ b/plugins/shortcuts/src/EditShortcut.tsx @@ -0,0 +1,134 @@ +/* + * Copyright 2021 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 { SubmitHandler } from 'react-hook-form'; +import { alertApiRef, useApi } from '@backstage/core'; +import { + Button, + Card, + CardHeader, + makeStyles, + Popover, +} from '@material-ui/core'; +import { ShortcutForm } from './ShortcutForm'; +import { FormValues, Shortcut } from './types'; +import DeleteIcon from '@material-ui/icons/Delete'; +import { ShortcutApi } from './api'; + +const useStyles = makeStyles(theme => ({ + card: { + width: 400, + }, + header: { + marginBottom: theme.spacing(1), + }, + button: { + marginTop: theme.spacing(1), + }, +})); + +type Props = { + shortcut: Shortcut; + onClose: () => void; + anchorEl?: Element; + api: ShortcutApi; +}; + +export const EditShortcut = ({ shortcut, onClose, anchorEl, api }: Props) => { + const classes = useStyles(); + const alertApi = useApi(alertApiRef); + const open = Boolean(anchorEl); + + const handleSave: SubmitHandler = async ({ url, title }) => { + const newShortcut: Shortcut = { + ...shortcut, + url, + title, + }; + + try { + await api.update(newShortcut); + alertApi.post({ + message: 'Successfully updated shortcut', + severity: 'success', + }); + } catch (error) { + alertApi.post({ + message: `Could not update shortcut: ${error.message}`, + severity: 'error', + }); + } + + onClose(); + }; + + const handleRemove = async () => { + try { + await api.remove(shortcut); + alertApi.post({ + message: 'Successfully deleted shortcut', + severity: 'success', + }); + } catch (error) { + alertApi.post({ + message: `Could not delete shortcut: ${error.message}`, + severity: 'error', + }); + } + }; + + const handleClose = () => { + onClose(); + }; + + return ( + + + } + onClick={handleRemove} + > + Remove + + } + /> + + + + ); +}; diff --git a/plugins/shortcuts/src/ShortcutForm.tsx b/plugins/shortcuts/src/ShortcutForm.tsx new file mode 100644 index 0000000000..6d6e0679c3 --- /dev/null +++ b/plugins/shortcuts/src/ShortcutForm.tsx @@ -0,0 +1,140 @@ +/* + * Copyright 2021 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, { useEffect } from 'react'; +import { useForm, SubmitHandler, Controller } from 'react-hook-form'; +import { + Button, + CardActions, + CardContent, + makeStyles, + TextField, +} from '@material-ui/core'; +import { FormValues } from './types'; + +const useStyles = makeStyles(theme => ({ + field: { + marginBottom: theme.spacing(2), + }, + actionRoot: { + paddingLeft: theme.spacing(2), + paddingBottom: theme.spacing(3), + justifyContent: 'flex-start', + }, +})); + +type Props = { + formValues?: FormValues; + onSave: SubmitHandler; + onClose: () => void; +}; + +export const ShortcutForm = ({ formValues, onSave, onClose }: Props) => { + const classes = useStyles(); + + const { + handleSubmit, + reset, + control, + formState: { errors }, + } = useForm({ + mode: 'onChange', + defaultValues: { + url: formValues?.url ?? '', + title: formValues?.title ?? '', + }, + }); + + useEffect(() => { + reset(formValues); + }, [reset, formValues]); + + return ( + <> + + ( + + )} + /> + ( + + )} + /> + + + + + + + ); +}; diff --git a/plugins/shortcuts/src/ShortcutIcon.tsx b/plugins/shortcuts/src/ShortcutIcon.tsx new file mode 100644 index 0000000000..c737596647 --- /dev/null +++ b/plugins/shortcuts/src/ShortcutIcon.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2021 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'; + +type Props = { + text: string; + color: string; +}; + +export const ShortcutIcon = ({ text, color }: Props) => { + const size = 28; + return ( + + + + {text} + + + ); +}; diff --git a/plugins/shortcuts/src/ShortcutItem.tsx b/plugins/shortcuts/src/ShortcutItem.tsx new file mode 100644 index 0000000000..a52194828c --- /dev/null +++ b/plugins/shortcuts/src/ShortcutItem.tsx @@ -0,0 +1,98 @@ +/* + * Copyright 2021 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 { SidebarItem } from '@backstage/core'; +import { IconButton, makeStyles } from '@material-ui/core'; +import EditIcon from '@material-ui/icons/Edit'; +import { ShortcutIcon } from './ShortcutIcon'; +import { EditShortcut } from './EditShortcut'; +import { ShortcutApi } from './api'; +import { Shortcut } from './types'; + +const useStyles = makeStyles({ + icon: { + color: 'white', + fontSize: 16, + }, +}); + +const getIconText = (title: string) => + title.split(' ').length === 1 + ? // If there's only one word, keep the first two characters + title[0].toUpperCase() + title[1].toLowerCase() + : // If there's more than one word, take the first character of the first two words + title + .replace(/\W+/g, ' ') + .split(' ') + .map(s => s[0]) + .join('') + .slice(0, 2) + .toUpperCase(); + +type Props = { + shortcut: Shortcut; + api: ShortcutApi; +}; + +export const ShortcutItem = ({ shortcut, api }: Props) => { + const classes = useStyles(); + const [displayEdit, setDisplayEdit] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(); + + const handleClick = (event: React.MouseEvent) => { + event.preventDefault(); + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(undefined); + setDisplayEdit(false); + }; + + const handleMouseEnter = () => { + setDisplayEdit(true); + }; + + const handleMouseLeave = () => { + setDisplayEdit(false); + }; + + const text = getIconText(shortcut.title); + const color = api.getColor(shortcut.url); + + return ( +
+ } + > + {displayEdit && ( + + + + )} + + +
+ ); +}; diff --git a/plugins/shortcuts/src/Shortcuts.tsx b/plugins/shortcuts/src/Shortcuts.tsx new file mode 100644 index 0000000000..4d65861bd9 --- /dev/null +++ b/plugins/shortcuts/src/Shortcuts.tsx @@ -0,0 +1,66 @@ +/* + * Copyright 2021 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, { useMemo } from 'react'; +import { useObservable } from 'react-use'; +import { Progress, SidebarItem, useApi } from '@backstage/core'; +import PlayListAddIcon from '@material-ui/icons/PlaylistAdd'; +import { ShortcutItem } from './ShortcutItem'; +import { AddShortcut } from './AddShortcut'; +import { shortcutsApiRef } from './api'; + +export const Shortcuts = () => { + const shortcutApi = useApi(shortcutsApiRef); + const shortcuts = useObservable( + useMemo(() => shortcutApi.observe(), [shortcutApi]), + ); + const [anchorEl, setAnchorEl] = React.useState(); + const loading = Boolean(!shortcuts); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(undefined); + }; + + return ( + <> + + + {loading ? ( + + ) : ( + shortcuts?.map(shortcut => ( + + )) + )} + + ); +}; diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts new file mode 100644 index 0000000000..2f84c0cf76 --- /dev/null +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts @@ -0,0 +1,97 @@ +/* + * Copyright 2021 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 { StorageApi } from '@backstage/core'; +import { pageTheme } from '@backstage/theme'; +import ObservableImpl from 'zen-observable'; +import { v4 as uuid } from 'uuid'; +import { ShortcutApi } from './ShortcutApi'; +import { Shortcut } from '../types'; + +/** + * Implementation of the ShortcutApi that uses the StorageApi to store shortcuts. + */ +export class LocalStoredShortcuts implements ShortcutApi { + constructor(private readonly storageApi: StorageApi) {} + + observe() { + return this.observable; + } + + async add(shortcut: Omit) { + const shortcuts = this.get(); + shortcuts.push({ ...shortcut, id: uuid() }); + + await this.storageApi.set('items', shortcuts); + this.notify(); + } + + async remove(shortcut: Shortcut) { + const shortcuts = this.get().filter(s => s.id !== shortcut.id); + + await this.storageApi.set('items', shortcuts); + this.notify(); + } + + async update(shortcut: Shortcut) { + const shortcuts = this.get().filter(s => s.id !== shortcut.id); + shortcuts.push(shortcut); + + await this.storageApi.set('items', shortcuts); + this.notify(); + } + + getColor(url: string) { + const type = url.split('/')[1]; + const theme = + this.THEME_MAP[type] ?? + (Object.keys(pageTheme).includes(type) ? type : 'tool'); + + return pageTheme[theme].colors[0]; + } + + private subscribers = new Set< + ZenObservable.SubscriptionObserver + >(); + + private readonly observable = new ObservableImpl(subscriber => { + subscriber.next(this.get()); + this.subscribers.add(subscriber); + + return () => { + this.subscribers.delete(subscriber); + }; + }); + + private readonly THEME_MAP: Record = { + catalog: 'home', + docs: 'documentation', + }; + + private get() { + return ( + (this.storageApi.get('items') as Shortcut[])?.sort((a, b) => + a.id >= b.id ? 1 : -1, + ) ?? [] + ); + } + + private notify() { + for (const subscription of this.subscribers) { + subscription.next(this.get()); + } + } +} diff --git a/plugins/shortcuts/src/api/ShortcutApi.ts b/plugins/shortcuts/src/api/ShortcutApi.ts new file mode 100644 index 0000000000..b2b1f772e5 --- /dev/null +++ b/plugins/shortcuts/src/api/ShortcutApi.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2021 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 Observable from 'zen-observable'; +import { createApiRef } from '@backstage/core'; +import { Shortcut } from '../types'; + +export const shortcutsApiRef = createApiRef({ + id: 'plugin.shortcuts.api', + description: 'API to handle shortcuts in a Backstage Sidebar', +}); + +export interface ShortcutApi { + /** + * Returns an Observable that will subscribe to changes. + */ + observe(): Observable; + + /** + * Generates a unique id for the shortcut and then saves it. + */ + add(shortcut: Omit): Promise; + + /** + * Removes the shortcut. + */ + remove(shortcut: Shortcut): Promise; + + /** + * Finds an existing shortcut that matches the ID of the + * supplied shortcut and updates its values. + */ + update(shortcut: Shortcut): Promise; + + /** + * Each shortcut should get a color for its icon based on the url. + * + * Preferably using some abstraction between the url and the actual + * color value. + */ + getColor(url: string): string; +} diff --git a/plugins/shortcuts/src/api/index.ts b/plugins/shortcuts/src/api/index.ts new file mode 100644 index 0000000000..4705702cbe --- /dev/null +++ b/plugins/shortcuts/src/api/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2021 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 { LocalStoredShortcuts } from './LocalStoredShortcuts'; +export { shortcutsApiRef } from './ShortcutApi'; +export type { ShortcutApi } from './ShortcutApi'; diff --git a/plugins/shortcuts/src/index.ts b/plugins/shortcuts/src/index.ts new file mode 100644 index 0000000000..851178710f --- /dev/null +++ b/plugins/shortcuts/src/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 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 { shortcutsPlugin, Shortcuts } from './plugin'; +export * from './api'; +export * from './types'; diff --git a/plugins/shortcuts/src/plugin.test.ts b/plugins/shortcuts/src/plugin.test.ts new file mode 100644 index 0000000000..885b4e226e --- /dev/null +++ b/plugins/shortcuts/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2021 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 { shortcutsPlugin } from './plugin'; + +describe('shortcuts', () => { + it('should export plugin', () => { + expect(shortcutsPlugin).toBeDefined(); + }); +}); diff --git a/plugins/shortcuts/src/plugin.ts b/plugins/shortcuts/src/plugin.ts new file mode 100644 index 0000000000..d33af76f33 --- /dev/null +++ b/plugins/shortcuts/src/plugin.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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 { + createApiFactory, + createComponentExtension, + createPlugin, + errorApiRef, + WebStorage, +} from '@backstage/core'; +import { shortcutsApiRef, LocalStoredShortcuts } from './api'; + +export const shortcutsPlugin = createPlugin({ + id: 'shortcuts', + apis: [ + createApiFactory({ + api: shortcutsApiRef, + deps: { errorApi: errorApiRef }, + factory: ({ errorApi }) => + new LocalStoredShortcuts( + WebStorage.create({ namespace: '@backstage/shortcuts', errorApi }), + ), + }), + ], +}); + +export const Shortcuts = shortcutsPlugin.provide( + createComponentExtension({ + component: { lazy: () => import('./Shortcuts').then(m => m.Shortcuts) }, + }), +); diff --git a/plugins/shortcuts/src/setupTests.ts b/plugins/shortcuts/src/setupTests.ts new file mode 100644 index 0000000000..0cec5b395d --- /dev/null +++ b/plugins/shortcuts/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 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'; +import 'cross-fetch/polyfill'; diff --git a/plugins/shortcuts/src/types.ts b/plugins/shortcuts/src/types.ts new file mode 100644 index 0000000000..33c52f4766 --- /dev/null +++ b/plugins/shortcuts/src/types.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2021 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 type Shortcut = { + id: string; + url: string; + title: string; +}; + +export type FormValues = { + url: string; + title: string; +}; diff --git a/yarn.lock b/yarn.lock index 000b4b55ed..f56a6931ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18569,6 +18569,7 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== dependencies: + encoding "^0.1.12" minipass "^3.1.0" minipass-sized "^1.0.3" minizlib "^2.0.0" @@ -21943,6 +21944,11 @@ react-hook-form@^6.15.4, react-hook-form@^6.6.0: resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.15.4.tgz#328003e1ccc096cd158899ffe7e3b33735a9b024" integrity sha512-K+Sw33DtTMengs8OdqFJI3glzNl1wBzSefD/ksQw/hJf9CnOHQAU6qy82eOrh0IRNt2G53sjr7qnnw1JDjvx1w== +react-hook-form@^7.1.1: + version "7.2.1" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.2.1.tgz#99b3540dd2314499df12e9a53c70587ad63a806c" + integrity sha512-QopAubhVofqQrwlWLr9aK0DF8tNU8fnU8sJIlw1Tb3tGkEvP9yeaA+cx1hlxYni8xBswtHruL1WcDEa6CYQDow== + react-hot-loader@^4.12.21: version "4.13.0" resolved "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.13.0.tgz#c27e9408581c2a678f5316e69c061b226dc6a202" @@ -26095,7 +26101,7 @@ uuid@^7.0.3: resolved "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== -uuid@^8.0.0, uuid@^8.2.0, uuid@^8.3.0: +uuid@^8.0.0, uuid@^8.2.0, uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== From c2c55e88488b4699c839e99ff6fce84bd1eb9b7e Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 20 Apr 2021 15:03:48 +0200 Subject: [PATCH 02/68] Add alertApi to mockApis Signed-off-by: Marcus Eide --- packages/test-utils/src/testUtils/mockApis.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/test-utils/src/testUtils/mockApis.ts b/packages/test-utils/src/testUtils/mockApis.ts index e05a8e6cac..0fe9a30926 100644 --- a/packages/test-utils/src/testUtils/mockApis.ts +++ b/packages/test-utils/src/testUtils/mockApis.ts @@ -18,10 +18,13 @@ import { storageApiRef, errorApiRef, createApiFactory, + AlertApiForwarder, + alertApiRef, } from '@backstage/core-api'; import { MockErrorApi, MockStorageApi } from './apis'; export const mockApis = [ createApiFactory(errorApiRef, new MockErrorApi()), createApiFactory(storageApiRef, MockStorageApi.create()), + createApiFactory(alertApiRef, new AlertApiForwarder()), ]; From 87d9b1170e1747ded9c65093ab97e96d9ab93f0d Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 20 Apr 2021 15:05:25 +0200 Subject: [PATCH 03/68] Add tests Signed-off-by: Marcus Eide --- plugins/shortcuts/src/AddShortcut.test.tsx | 113 +++++++++++++++ plugins/shortcuts/src/EditShortcut.test.tsx | 130 ++++++++++++++++++ plugins/shortcuts/src/ShortcutForm.test.tsx | 73 ++++++++++ plugins/shortcuts/src/ShortcutItem.test.tsx | 128 +++++++++++++++++ plugins/shortcuts/src/Shortcuts.test.tsx | 42 ++++++ .../src/api/LocalStoredShortcuts.test.ts | 82 +++++++++++ 6 files changed, 568 insertions(+) create mode 100644 plugins/shortcuts/src/AddShortcut.test.tsx create mode 100644 plugins/shortcuts/src/EditShortcut.test.tsx create mode 100644 plugins/shortcuts/src/ShortcutForm.test.tsx create mode 100644 plugins/shortcuts/src/ShortcutItem.test.tsx create mode 100644 plugins/shortcuts/src/Shortcuts.test.tsx create mode 100644 plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts diff --git a/plugins/shortcuts/src/AddShortcut.test.tsx b/plugins/shortcuts/src/AddShortcut.test.tsx new file mode 100644 index 0000000000..690bb2c59e --- /dev/null +++ b/plugins/shortcuts/src/AddShortcut.test.tsx @@ -0,0 +1,113 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { AddShortcut } from './AddShortcut'; +import { LocalStoredShortcuts } from './api'; +import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; +import { AlertDisplay } from '@backstage/core'; + +describe('AddShortcut', () => { + const api = new LocalStoredShortcuts(MockStorageApi.create()); + + const props = { + onClose: jest.fn(), + anchorEl: document.createElement('div'), + api, + }; + + beforeEach(() => { + jest.clearAllMocks(); + document.title = 'some document title'; + }); + + it('displays the title', async () => { + render(wrapInTestApp()); + + await waitFor(() => { + expect(screen.getByText('Add Shortcut')).toBeInTheDocument(); + }); + }); + + it('closes the popup', async () => { + render(wrapInTestApp()); + + fireEvent.click(screen.getByText('Cancel')); + await waitFor(() => { + expect(props.onClose).toHaveBeenCalledTimes(1); + }); + }); + + it('saves the input', async () => { + const spy = jest.spyOn(api, 'add'); + + render(wrapInTestApp()); + + const urlInput = screen.getByPlaceholderText('Enter a URL'); + const titleInput = screen.getByPlaceholderText('Enter a display name'); + fireEvent.change(urlInput, { target: { value: '/some-url' } }); + fireEvent.change(titleInput, { target: { value: 'some title' } }); + + fireEvent.click(screen.getByText('Save')); + await waitFor(() => { + expect(spy).toBeCalledWith({ + title: 'some title', + url: '/some-url', + }); + }); + }); + + it('pastes the values', async () => { + const spy = jest.spyOn(api, 'add'); + + render( + wrapInTestApp(, { + routeEntries: ['/some-initial-url'], + }), + ); + + fireEvent.click(screen.getByText('Paste Current Url')); + fireEvent.click(screen.getByText('Save')); + await waitFor(() => { + expect(spy).toBeCalledWith({ + title: 'some document title', + url: '/some-initial-url', + }); + }); + }); + + it('displays errors', async () => { + jest.spyOn(api, 'add').mockRejectedValueOnce(new Error('some add error')); + + render( + wrapInTestApp( + <> + + + , + ), + ); + + fireEvent.click(screen.getByText('Paste Current Url')); + fireEvent.click(screen.getByText('Save')); + await waitFor(() => { + expect( + screen.getByText('Could not add shortcut: some add error'), + ).toBeInTheDocument(); + }); + }); +}); diff --git a/plugins/shortcuts/src/EditShortcut.test.tsx b/plugins/shortcuts/src/EditShortcut.test.tsx new file mode 100644 index 0000000000..bf2396e194 --- /dev/null +++ b/plugins/shortcuts/src/EditShortcut.test.tsx @@ -0,0 +1,130 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { EditShortcut } from './EditShortcut'; +import { Shortcut } from './types'; +import { LocalStoredShortcuts } from './api'; +import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; +import { AlertDisplay } from '@backstage/core'; + +describe('EditShortcut', () => { + const shortcut: Shortcut = { + id: 'id', + url: '/some-url', + title: 'some title', + }; + const api = new LocalStoredShortcuts(MockStorageApi.create()); + + const props = { + onClose: jest.fn(), + anchorEl: document.createElement('div'), + shortcut, + api, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('displays the title', async () => { + render(wrapInTestApp()); + + await waitFor(() => { + expect(screen.getByText('Edit Shortcut')).toBeInTheDocument(); + }); + }); + + it('closes the popup', async () => { + render(wrapInTestApp()); + + fireEvent.click(screen.getByText('Cancel')); + await waitFor(() => { + expect(props.onClose).toHaveBeenCalledTimes(1); + }); + }); + + it('updates the shortcut', async () => { + const spy = jest.spyOn(api, 'update'); + + render(wrapInTestApp()); + + const urlInput = screen.getByPlaceholderText('Enter a URL'); + const titleInput = screen.getByPlaceholderText('Enter a display name'); + fireEvent.change(urlInput, { target: { value: '/some-new-url' } }); + fireEvent.change(titleInput, { target: { value: 'some new title' } }); + + fireEvent.click(screen.getByText('Save')); + await waitFor(() => { + expect(spy).toBeCalledWith({ + id: 'id', + title: 'some new title', + url: '/some-new-url', + }); + expect(props.onClose).toHaveBeenCalledTimes(1); + }); + }); + + it('removes the shortcut', async () => { + const spy = jest.spyOn(api, 'remove'); + + render(wrapInTestApp()); + + fireEvent.click(screen.getByText('Remove')); + await waitFor(() => { + expect(spy).toBeCalledWith({ + id: 'id', + title: 'some title', + url: '/some-url', + }); + }); + }); + + it('displays errors', async () => { + jest + .spyOn(api, 'update') + .mockRejectedValueOnce(new Error('some update error')); + + jest + .spyOn(api, 'remove') + .mockRejectedValueOnce(new Error('some remove error')); + + render( + wrapInTestApp( + <> + + + , + ), + ); + + fireEvent.click(screen.getByText('Save')); + await waitFor(() => { + expect( + screen.getByText('Could not update shortcut: some update error'), + ).toBeInTheDocument(); + }); + fireEvent.click(screen.getByTestId('error-button-close')); + + fireEvent.click(screen.getByText('Remove')); + await waitFor(() => { + expect( + screen.getByText('Could not delete shortcut: some remove error'), + ).toBeInTheDocument(); + }); + }); +}); diff --git a/plugins/shortcuts/src/ShortcutForm.test.tsx b/plugins/shortcuts/src/ShortcutForm.test.tsx new file mode 100644 index 0000000000..4586c484d0 --- /dev/null +++ b/plugins/shortcuts/src/ShortcutForm.test.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { ShortcutForm } from './ShortcutForm'; +import { wrapInTestApp } from '@backstage/test-utils'; + +describe('ShortcutForm', () => { + const props = { + onSave: jest.fn(), + onClose: jest.fn(), + }; + + it('displays validation messages', async () => { + render(wrapInTestApp()); + + const urlInput = screen.getByPlaceholderText('Enter a URL'); + const titleInput = screen.getByPlaceholderText('Enter a display name'); + fireEvent.change(urlInput, { target: { value: 'url' } }); + fireEvent.change(titleInput, { target: { value: 't' } }); + + fireEvent.click(screen.getByText('Save')); + await waitFor(() => { + expect( + screen.getByText('Must be a relative URL (starts with a /)'), + ).toBeInTheDocument(); + expect( + screen.getByText('Must be at least 2 characters'), + ).toBeInTheDocument(); + }); + }); + + it('calls the save handler', async () => { + render( + wrapInTestApp( + , + ), + ); + + fireEvent.click(screen.getByText('Save')); + await waitFor(() => { + expect(props.onSave).toHaveBeenCalledWith( + expect.objectContaining({ title: 'some title', url: '/some-url' }), + expect.anything(), + ); + }); + }); + + it('calls the close handler', async () => { + render(wrapInTestApp()); + + fireEvent.click(screen.getByText('Cancel')); + await waitFor(() => { + expect(props.onClose).toHaveBeenCalled(); + }); + }); +}); diff --git a/plugins/shortcuts/src/ShortcutItem.test.tsx b/plugins/shortcuts/src/ShortcutItem.test.tsx new file mode 100644 index 0000000000..219771909d --- /dev/null +++ b/plugins/shortcuts/src/ShortcutItem.test.tsx @@ -0,0 +1,128 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { ShortcutItem } from './ShortcutItem'; +import { Shortcut } from './types'; +import { SidebarContext } from '@backstage/core'; +import { LocalStoredShortcuts } from './api'; +import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; +import { pageTheme } from '@backstage/theme'; + +describe('ShortcutItem', () => { + const shortcut: Shortcut = { + id: 'id', + url: '/some-url', + title: 'some title', + }; + const api = new LocalStoredShortcuts(MockStorageApi.create()); + + it('displays the shortcut', async () => { + render( + wrapInTestApp( + + + , + ), + ); + await waitFor(() => { + expect(screen.getByText('ST')).toBeInTheDocument(); + expect(screen.getByText('some title')).toBeInTheDocument(); + }); + }); + + it('calculates the shortcut text correctly', async () => { + const shortcut1: Shortcut = { + id: 'id1', + url: '/some-url', + title: 'onetitle', + }; + const shortcut2: Shortcut = { + id: 'id2', + url: '/some-url', + title: 'two title', + }; + const shortcut3: Shortcut = { + id: 'id3', + url: '/some-url', + title: 'more | title words', + }; + + const { rerender } = render( + wrapInTestApp(), + ); + + await waitFor(() => { + expect(screen.getByText('On')).toBeInTheDocument(); + }); + + rerender(wrapInTestApp()); + await waitFor(() => { + expect(screen.getByText('TT')).toBeInTheDocument(); + }); + + rerender(wrapInTestApp()); + await waitFor(() => { + expect(screen.getByText('MT')).toBeInTheDocument(); + }); + }); + + it('displays the edit icon on hover', async () => { + render( + wrapInTestApp( + + + , + ), + ); + + fireEvent.mouseOver(screen.getByText('ST')); + await waitFor(() => { + expect(screen.getByTestId('edit')).toBeInTheDocument(); + }); + + fireEvent.mouseOut(screen.getByText('ST')); + await waitFor(() => { + expect(screen.queryByTestId('edit')).not.toBeInTheDocument(); + }); + }); + + it('gets the color based on the theme', async () => { + const { rerender } = render( + wrapInTestApp(), + ); + + await waitFor(() => { + expect(document.querySelector('circle')?.getAttribute('fill')).toEqual( + pageTheme.tool.colors[0], + ); + }); + + const newShortcut: Shortcut = { + id: 'id', + url: '/catalog', + title: 'some title', + }; + rerender(wrapInTestApp()); + + await waitFor(() => { + expect(document.querySelector('circle')?.getAttribute('fill')).toEqual( + pageTheme.home.colors[0], + ); + }); + }); +}); diff --git a/plugins/shortcuts/src/Shortcuts.test.tsx b/plugins/shortcuts/src/Shortcuts.test.tsx new file mode 100644 index 0000000000..088a3705d9 --- /dev/null +++ b/plugins/shortcuts/src/Shortcuts.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2021 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 { SidebarContext, ApiProvider, ApiRegistry } from '@backstage/core'; +import { wrapInTestApp, MockStorageApi } from '@backstage/test-utils'; +import { render, screen, waitFor } from '@testing-library/react'; +import { Shortcuts } from './Shortcuts'; +import { LocalStoredShortcuts, shortcutsApiRef } from './api'; + +const apis = ApiRegistry.from([ + [shortcutsApiRef, new LocalStoredShortcuts(MockStorageApi.create())], +]); + +describe('Shortcuts', () => { + it('displays an add button', async () => { + render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !screen.queryByTestId('progress')); + expect(screen.getByText('Add Shortcuts')).toBeInTheDocument(); + }); +}); diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts new file mode 100644 index 0000000000..6fd45d73e5 --- /dev/null +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts @@ -0,0 +1,82 @@ +/* + * Copyright 2021 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 { MockStorageApi } from '@backstage/test-utils'; +import { pageTheme } from '@backstage/theme'; +import { Shortcut } from '../types'; +import { LocalStoredShortcuts } from './LocalStoredShortcuts'; +import { ShortcutApi } from './ShortcutApi'; + +describe('LocalStoredShortcuts', () => { + // eslint-disable-next-line jest/no-done-callback + it('should observe shortcuts', async done => { + const shortcutApi: ShortcutApi = new LocalStoredShortcuts( + MockStorageApi.create(), + ); + const shortcut: Shortcut = { id: 'id', title: 'title', url: '/url' }; + + await shortcutApi.add(shortcut); + shortcutApi.observe().subscribe(data => { + expect(data).toEqual( + expect.arrayContaining([{ ...shortcut, id: expect.anything() }]), + ); + done(); + }); + }); + + it('should add shortcuts with ids', async () => { + const storageApi = MockStorageApi.create(); + const shortcutApi: ShortcutApi = new LocalStoredShortcuts(storageApi); + const shortcut: Omit = { title: 'title', url: '/url' }; + const spy = jest.spyOn(storageApi, 'set'); + + await shortcutApi.add(shortcut); + expect(spy).toHaveBeenCalledWith( + 'items', + expect.objectContaining([{ ...shortcut, id: expect.anything() }]), + ); + }); + + it('should update shortcuts', async () => { + const storageApi = MockStorageApi.create(); + const shortcutApi: ShortcutApi = new LocalStoredShortcuts(storageApi); + const shortcut: Shortcut = { id: 'someid', title: 'title', url: '/url' }; + const spy = jest.spyOn(storageApi, 'set'); + + await shortcutApi.update(shortcut); + expect(spy).toHaveBeenCalledWith( + 'items', + expect.objectContaining([shortcut]), + ); + }); + + it('should remove shortcuts', async () => { + const storageApi = MockStorageApi.create(); + const shortcutApi: ShortcutApi = new LocalStoredShortcuts(storageApi); + const shortcut: Shortcut = { id: 'someid', title: 'title', url: '/url' }; + const spy = jest.spyOn(storageApi, 'set'); + + await shortcutApi.remove(shortcut); + expect(spy).toHaveBeenCalledWith('items', []); + }); + + it('should get a color', () => { + const storageApi = MockStorageApi.create(); + const shortcutApi: ShortcutApi = new LocalStoredShortcuts(storageApi); + + expect(shortcutApi.getColor('/catalog')).toEqual(pageTheme.home.colors[0]); + }); +}); From f9a100f39c924aeb92664e15356e6dfa600ae407 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 20 Apr 2021 16:32:51 +0200 Subject: [PATCH 04/68] Remove unintentional export in plugins.ts Signed-off-by: Marcus Eide --- packages/app/src/plugins.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index 9ea887ac27..e02e30e5d9 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -17,5 +17,4 @@ // TODO(Rugvip): This plugin is currently not part of the app element tree, // ideally we have an API for the context menu that permits that. export { badgesPlugin } from '@backstage/plugin-badges'; -export { githubDeploymentsPlugin } from '@backstage/plugin-github-deployments'; export { shortcutsPlugin } from '@backstage/plugin-shortcuts'; From 421563440800c08204af333cea107fe6a70aa20f Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 21 Apr 2021 09:17:34 +0200 Subject: [PATCH 05/68] Dependencies Signed-off-by: Marcus Eide --- plugins/shortcuts/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 32fb41ab78..99e824e835 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -24,7 +24,6 @@ "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", - "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", "react-dom": "^16.13.1", "react-hook-form": "^7.1.1", @@ -42,6 +41,7 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", + "@types/zen-observable": "^0.8.2", "cross-fetch": "^3.0.6", "msw": "^0.21.2" }, diff --git a/yarn.lock b/yarn.lock index f56a6931ea..3c563eee9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6937,7 +6937,7 @@ resolved "https://registry.npmjs.org/@types/yup/-/yup-0.29.8.tgz#83db15735987db9fe5a38772a0fb9500e3c5bf39" integrity sha512-MBSp62AjB1KrSOI3gX9GekddXU5YYQAVA93+aSl78biBqoSzxg876aQY2KJK5Gnfbpqq7O2cadVX5kPAtBqIXw== -"@types/zen-observable@^0.8.0": +"@types/zen-observable@^0.8.0", "@types/zen-observable@^0.8.2": version "0.8.2" resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== From e05f09698c2210d763d98860d5267c8b51d4e648 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 21 Apr 2021 10:00:15 +0200 Subject: [PATCH 06/68] Update alert messages Signed-off-by: Marcus Eide --- plugins/shortcuts/src/AddShortcut.test.tsx | 4 ++-- plugins/shortcuts/src/AddShortcut.tsx | 4 ++-- plugins/shortcuts/src/EditShortcut.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/shortcuts/src/AddShortcut.test.tsx b/plugins/shortcuts/src/AddShortcut.test.tsx index 690bb2c59e..cee5d8cf28 100644 --- a/plugins/shortcuts/src/AddShortcut.test.tsx +++ b/plugins/shortcuts/src/AddShortcut.test.tsx @@ -80,7 +80,7 @@ describe('AddShortcut', () => { }), ); - fireEvent.click(screen.getByText('Paste Current Url')); + fireEvent.click(screen.getByText('Use current page')); fireEvent.click(screen.getByText('Save')); await waitFor(() => { expect(spy).toBeCalledWith({ @@ -102,7 +102,7 @@ describe('AddShortcut', () => { ), ); - fireEvent.click(screen.getByText('Paste Current Url')); + fireEvent.click(screen.getByText('Use current page')); fireEvent.click(screen.getByText('Save')); await waitFor(() => { expect( diff --git a/plugins/shortcuts/src/AddShortcut.tsx b/plugins/shortcuts/src/AddShortcut.tsx index ab845c63a8..74a0653b4c 100644 --- a/plugins/shortcuts/src/AddShortcut.tsx +++ b/plugins/shortcuts/src/AddShortcut.tsx @@ -60,7 +60,7 @@ export const AddShortcut = ({ onClose, anchorEl, api }: Props) => { try { await api.add(shortcut); alertApi.post({ - message: 'Successfully added shortcut', + message: `Added shortcut '${title}' to your sidebar`, severity: 'success', }); } catch (error) { @@ -106,7 +106,7 @@ export const AddShortcut = ({ onClose, anchorEl, api }: Props) => { color="primary" onClick={handlePaste} > - Paste Current Url + Use current page } /> diff --git a/plugins/shortcuts/src/EditShortcut.tsx b/plugins/shortcuts/src/EditShortcut.tsx index 9220c9a73e..8b98406703 100644 --- a/plugins/shortcuts/src/EditShortcut.tsx +++ b/plugins/shortcuts/src/EditShortcut.tsx @@ -63,7 +63,7 @@ export const EditShortcut = ({ shortcut, onClose, anchorEl, api }: Props) => { try { await api.update(newShortcut); alertApi.post({ - message: 'Successfully updated shortcut', + message: `Updated shortcut '${title}'`, severity: 'success', }); } catch (error) { @@ -80,7 +80,7 @@ export const EditShortcut = ({ shortcut, onClose, anchorEl, api }: Props) => { try { await api.remove(shortcut); alertApi.post({ - message: 'Successfully deleted shortcut', + message: `Removed shortcut '${shortcut.title}' from your sidebar`, severity: 'success', }); } catch (error) { From 325d8da1645a7579b4ae3dad74e4bad651e5c3f0 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 21 Apr 2021 10:21:33 +0200 Subject: [PATCH 07/68] Rename observe() to shortcut Signed-off-by: Marcus Eide --- plugins/shortcuts/src/Shortcuts.tsx | 2 +- plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts | 2 +- plugins/shortcuts/src/api/LocalStoredShortcuts.ts | 2 +- plugins/shortcuts/src/api/ShortcutApi.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/shortcuts/src/Shortcuts.tsx b/plugins/shortcuts/src/Shortcuts.tsx index 4d65861bd9..0e8804cd39 100644 --- a/plugins/shortcuts/src/Shortcuts.tsx +++ b/plugins/shortcuts/src/Shortcuts.tsx @@ -25,7 +25,7 @@ import { shortcutsApiRef } from './api'; export const Shortcuts = () => { const shortcutApi = useApi(shortcutsApiRef); const shortcuts = useObservable( - useMemo(() => shortcutApi.observe(), [shortcutApi]), + useMemo(() => shortcutApi.shortcut$(), [shortcutApi]), ); const [anchorEl, setAnchorEl] = React.useState(); const loading = Boolean(!shortcuts); diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts index 6fd45d73e5..94f63f846a 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts @@ -29,7 +29,7 @@ describe('LocalStoredShortcuts', () => { const shortcut: Shortcut = { id: 'id', title: 'title', url: '/url' }; await shortcutApi.add(shortcut); - shortcutApi.observe().subscribe(data => { + shortcutApi.shortcut$().subscribe(data => { expect(data).toEqual( expect.arrayContaining([{ ...shortcut, id: expect.anything() }]), ); diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts index 2f84c0cf76..c606fb5f9a 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts @@ -27,7 +27,7 @@ import { Shortcut } from '../types'; export class LocalStoredShortcuts implements ShortcutApi { constructor(private readonly storageApi: StorageApi) {} - observe() { + shortcut$() { return this.observable; } diff --git a/plugins/shortcuts/src/api/ShortcutApi.ts b/plugins/shortcuts/src/api/ShortcutApi.ts index b2b1f772e5..bd6eb0f196 100644 --- a/plugins/shortcuts/src/api/ShortcutApi.ts +++ b/plugins/shortcuts/src/api/ShortcutApi.ts @@ -27,7 +27,7 @@ export interface ShortcutApi { /** * Returns an Observable that will subscribe to changes. */ - observe(): Observable; + shortcut$(): Observable; /** * Generates a unique id for the shortcut and then saves it. From 55dce861535d58dba8670f64ec8559f0346a7a1c Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 21 Apr 2021 13:54:41 +0200 Subject: [PATCH 08/68] Keep wordboundary when getting the icon text Signed-off-by: Marcus Eide --- plugins/shortcuts/src/ShortcutItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/shortcuts/src/ShortcutItem.tsx b/plugins/shortcuts/src/ShortcutItem.tsx index a52194828c..3aec4bb3b4 100644 --- a/plugins/shortcuts/src/ShortcutItem.tsx +++ b/plugins/shortcuts/src/ShortcutItem.tsx @@ -36,7 +36,7 @@ const getIconText = (title: string) => title[0].toUpperCase() + title[1].toLowerCase() : // If there's more than one word, take the first character of the first two words title - .replace(/\W+/g, ' ') + .replace(/\B\W/g, '') .split(' ') .map(s => s[0]) .join('') From 303d548d745ce57076585905e67aabae419145e9 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 21 Apr 2021 13:57:49 +0200 Subject: [PATCH 09/68] Use all available vertical space in the sidebar and overflow with scroll if needed Signed-off-by: Marcus Eide --- plugins/shortcuts/src/Shortcuts.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/shortcuts/src/Shortcuts.tsx b/plugins/shortcuts/src/Shortcuts.tsx index 0e8804cd39..2c49245e76 100644 --- a/plugins/shortcuts/src/Shortcuts.tsx +++ b/plugins/shortcuts/src/Shortcuts.tsx @@ -17,12 +17,21 @@ import React, { useMemo } from 'react'; import { useObservable } from 'react-use'; import { Progress, SidebarItem, useApi } from '@backstage/core'; +import { makeStyles } from '@material-ui/core'; import PlayListAddIcon from '@material-ui/icons/PlaylistAdd'; import { ShortcutItem } from './ShortcutItem'; import { AddShortcut } from './AddShortcut'; import { shortcutsApiRef } from './api'; +const useStyles = makeStyles({ + root: { + flex: '1 1 auto', + overflow: 'scroll', + }, +}); + export const Shortcuts = () => { + const classes = useStyles(); const shortcutApi = useApi(shortcutsApiRef); const shortcuts = useObservable( useMemo(() => shortcutApi.shortcut$(), [shortcutApi]), @@ -39,7 +48,7 @@ export const Shortcuts = () => { }; return ( - <> +
{ /> )) )} - +
); }; From aac78e0a7bbc0b0a8a83be7c322bf2bc77790d63 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 21 Apr 2021 14:37:40 +0200 Subject: [PATCH 10/68] Sort on title Signed-off-by: Marcus Eide --- plugins/shortcuts/src/api/LocalStoredShortcuts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts index c606fb5f9a..377f8e9190 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts @@ -84,7 +84,7 @@ export class LocalStoredShortcuts implements ShortcutApi { private get() { return ( (this.storageApi.get('items') as Shortcut[])?.sort((a, b) => - a.id >= b.id ? 1 : -1, + a.title >= b.title ? 1 : -1, ) ?? [] ); } From a097131d0d739355dee5883a5ead2ab7daea57d5 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 21 Apr 2021 15:28:08 +0200 Subject: [PATCH 11/68] Use Observable from @backstage/core Signed-off-by: Marcus Eide --- plugins/shortcuts/package.json | 1 - plugins/shortcuts/src/api/ShortcutApi.ts | 3 +-- yarn.lock | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 99e824e835..5795dcd922 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -41,7 +41,6 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", - "@types/zen-observable": "^0.8.2", "cross-fetch": "^3.0.6", "msw": "^0.21.2" }, diff --git a/plugins/shortcuts/src/api/ShortcutApi.ts b/plugins/shortcuts/src/api/ShortcutApi.ts index bd6eb0f196..826988d01f 100644 --- a/plugins/shortcuts/src/api/ShortcutApi.ts +++ b/plugins/shortcuts/src/api/ShortcutApi.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import Observable from 'zen-observable'; -import { createApiRef } from '@backstage/core'; +import { createApiRef, Observable } from '@backstage/core'; import { Shortcut } from '../types'; export const shortcutsApiRef = createApiRef({ diff --git a/yarn.lock b/yarn.lock index 3c563eee9e..f56a6931ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6937,7 +6937,7 @@ resolved "https://registry.npmjs.org/@types/yup/-/yup-0.29.8.tgz#83db15735987db9fe5a38772a0fb9500e3c5bf39" integrity sha512-MBSp62AjB1KrSOI3gX9GekddXU5YYQAVA93+aSl78biBqoSzxg876aQY2KJK5Gnfbpqq7O2cadVX5kPAtBqIXw== -"@types/zen-observable@^0.8.0", "@types/zen-observable@^0.8.2": +"@types/zen-observable@^0.8.0": version "0.8.2" resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== From 947e45cefd04b06d7dead488f651925a19014a8e Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 21 Apr 2021 16:04:51 +0200 Subject: [PATCH 12/68] Add back @types/zen-observable Signed-off-by: Marcus Eide --- plugins/shortcuts/package.json | 1 + yarn.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 5795dcd922..bcac92fcb0 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -24,6 +24,7 @@ "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", + "@types/zen-observable": "^0.8.2", "react": "^16.13.1", "react-dom": "^16.13.1", "react-hook-form": "^7.1.1", diff --git a/yarn.lock b/yarn.lock index f56a6931ea..3c563eee9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6937,7 +6937,7 @@ resolved "https://registry.npmjs.org/@types/yup/-/yup-0.29.8.tgz#83db15735987db9fe5a38772a0fb9500e3c5bf39" integrity sha512-MBSp62AjB1KrSOI3gX9GekddXU5YYQAVA93+aSl78biBqoSzxg876aQY2KJK5Gnfbpqq7O2cadVX5kPAtBqIXw== -"@types/zen-observable@^0.8.0": +"@types/zen-observable@^0.8.0", "@types/zen-observable@^0.8.2": version "0.8.2" resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== From 15067b0566f6f81cf9253e978a104909a05d4ca0 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 22 Apr 2021 09:53:55 +0200 Subject: [PATCH 13/68] Add back @material-ui/lab to pass diff check Signed-off-by: Marcus Eide --- plugins/shortcuts/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index bcac92fcb0..b3d8d7f4c1 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -24,6 +24,7 @@ "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", "@types/zen-observable": "^0.8.2", "react": "^16.13.1", "react-dom": "^16.13.1", From f8eb7cacc7c130a3e3a1df5e5efed29ca018eea5 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 22 Apr 2021 10:33:40 +0200 Subject: [PATCH 14/68] Provide implementation of the api in the app instead Signed-off-by: Marcus Eide --- packages/app/src/apis.ts | 14 ++++++++++++++ plugins/shortcuts/src/plugin.ts | 19 +------------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index ccc576e727..6e643193db 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -20,6 +20,7 @@ import { createApiFactory, errorApiRef, githubAuthApiRef, + WebStorage, } from '@backstage/core'; import { ScmIntegrationsApi, @@ -33,6 +34,10 @@ import { graphQlBrowseApiRef, GraphQLEndpoints, } from '@backstage/plugin-graphiql'; +import { + LocalStoredShortcuts, + shortcutsApiRef, +} from '@backstage/plugin-shortcuts'; export const apis: AnyApiFactory[] = [ createApiFactory({ @@ -61,4 +66,13 @@ export const apis: AnyApiFactory[] = [ }), createApiFactory(costInsightsApiRef, new ExampleCostInsightsClient()), + + createApiFactory({ + api: shortcutsApiRef, + deps: { errorApi: errorApiRef }, + factory: ({ errorApi }) => + new LocalStoredShortcuts( + WebStorage.create({ namespace: '@backstage/shortcuts', errorApi }), + ), + }), ]; diff --git a/plugins/shortcuts/src/plugin.ts b/plugins/shortcuts/src/plugin.ts index d33af76f33..b81062505a 100644 --- a/plugins/shortcuts/src/plugin.ts +++ b/plugins/shortcuts/src/plugin.ts @@ -14,27 +14,10 @@ * limitations under the License. */ -import { - createApiFactory, - createComponentExtension, - createPlugin, - errorApiRef, - WebStorage, -} from '@backstage/core'; -import { shortcutsApiRef, LocalStoredShortcuts } from './api'; +import { createComponentExtension, createPlugin } from '@backstage/core'; export const shortcutsPlugin = createPlugin({ id: 'shortcuts', - apis: [ - createApiFactory({ - api: shortcutsApiRef, - deps: { errorApi: errorApiRef }, - factory: ({ errorApi }) => - new LocalStoredShortcuts( - WebStorage.create({ namespace: '@backstage/shortcuts', errorApi }), - ), - }), - ], }); export const Shortcuts = shortcutsPlugin.provide( From e15241ea1d8f392bee2c9ff7161e21fdb55d28f1 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 22 Apr 2021 10:40:10 +0200 Subject: [PATCH 15/68] Update README Signed-off-by: Marcus Eide --- plugins/shortcuts/README.md | 40 ++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/plugins/shortcuts/README.md b/plugins/shortcuts/README.md index 423f03948c..2848ece0e8 100644 --- a/plugins/shortcuts/README.md +++ b/plugins/shortcuts/README.md @@ -1,13 +1,39 @@ # shortcuts -Welcome to the shortcuts plugin! +`shortcuts-plugin` allows a user to have easy access to pages within a Backstage app by storing them as "shortcuts" in the Sidebar. -_This plugin was created through the Backstage CLI_ +## Usage -## Getting started +Add the `` component within your ``: -Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/shortcuts](http://localhost:3000/shortcuts). +```ts +import { Shortcuts } from '@backstage/plugin-shortcuts'; -You can also serve the plugin in isolation by running `yarn start` in the plugin directory. -This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. -It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. + + {/* ... */} + + + +; +``` + +The plugin exports a `shortcutApiRef` and an implementation of the `ShortcutApi` that uses `localStorage` for storage, that you can use to get started quickly. To use it add it to your app's `apis.ts`: + +```ts +import { + LocalStoredShortcuts, + shortcutsApiRef, +} from '@backstage/plugin-shortcuts'; + +export const apis = [ + // ... + createApiFactory({ + api: shortcutsApiRef, + deps: { errorApi: errorApiRef }, + factory: ({ errorApi }) => + new LocalStoredShortcuts( + WebStorage.create({ namespace: '@backstage/shortcuts', errorApi }), + ), + }), +]; +``` From a4c5361c0ed8daf7e1b81fb56a842e9c585c003e Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 30 Apr 2021 09:42:08 +0200 Subject: [PATCH 16/68] Use storageApi.forBucket and add back a default implementation in the plugin Signed-off-by: Marcus Eide --- packages/app/src/apis.ts | 14 -------------- plugins/shortcuts/README.md | 17 +++++++---------- plugins/shortcuts/src/plugin.ts | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 6e643193db..ccc576e727 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -20,7 +20,6 @@ import { createApiFactory, errorApiRef, githubAuthApiRef, - WebStorage, } from '@backstage/core'; import { ScmIntegrationsApi, @@ -34,10 +33,6 @@ import { graphQlBrowseApiRef, GraphQLEndpoints, } from '@backstage/plugin-graphiql'; -import { - LocalStoredShortcuts, - shortcutsApiRef, -} from '@backstage/plugin-shortcuts'; export const apis: AnyApiFactory[] = [ createApiFactory({ @@ -66,13 +61,4 @@ export const apis: AnyApiFactory[] = [ }), createApiFactory(costInsightsApiRef, new ExampleCostInsightsClient()), - - createApiFactory({ - api: shortcutsApiRef, - deps: { errorApi: errorApiRef }, - factory: ({ errorApi }) => - new LocalStoredShortcuts( - WebStorage.create({ namespace: '@backstage/shortcuts', errorApi }), - ), - }), ]; diff --git a/plugins/shortcuts/README.md b/plugins/shortcuts/README.md index 2848ece0e8..dfe8577190 100644 --- a/plugins/shortcuts/README.md +++ b/plugins/shortcuts/README.md @@ -17,23 +17,20 @@ import { Shortcuts } from '@backstage/plugin-shortcuts'; ; ``` -The plugin exports a `shortcutApiRef` and an implementation of the `ShortcutApi` that uses `localStorage` for storage, that you can use to get started quickly. To use it add it to your app's `apis.ts`: +The plugin exports a `shortcutApiRef` but the plugin includes a default implementation of the `ShortcutApi` that uses `localStorage` to store each users shortcuts. + +To overwrite the default implementation add it to the App's `apis.ts`: ```ts -import { - LocalStoredShortcuts, - shortcutsApiRef, -} from '@backstage/plugin-shortcuts'; +import { shortcutsApiRef } from '@backstage/plugin-shortcuts'; +import { CustomShortcutsImpl } from '...'; export const apis = [ // ... createApiFactory({ api: shortcutsApiRef, - deps: { errorApi: errorApiRef }, - factory: ({ errorApi }) => - new LocalStoredShortcuts( - WebStorage.create({ namespace: '@backstage/shortcuts', errorApi }), - ), + deps: {}, + factory: () => new CustomShortcutsImpl(), }), ]; ``` diff --git a/plugins/shortcuts/src/plugin.ts b/plugins/shortcuts/src/plugin.ts index b81062505a..e2cd819fc6 100644 --- a/plugins/shortcuts/src/plugin.ts +++ b/plugins/shortcuts/src/plugin.ts @@ -14,10 +14,24 @@ * limitations under the License. */ -import { createComponentExtension, createPlugin } from '@backstage/core'; +import { + createApiFactory, + createComponentExtension, + createPlugin, + storageApiRef, +} from '@backstage/core'; +import { LocalStoredShortcuts, shortcutsApiRef } from './api'; export const shortcutsPlugin = createPlugin({ id: 'shortcuts', + apis: [ + createApiFactory({ + api: shortcutsApiRef, + deps: { storageApi: storageApiRef }, + factory: ({ storageApi }) => + new LocalStoredShortcuts(storageApi.forBucket('shortcuts')), + }), + ], }); export const Shortcuts = shortcutsPlugin.provide( From de6c739401316dcbba4a38239a1dbd6b41507ced Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 30 Apr 2021 09:53:30 +0200 Subject: [PATCH 17/68] Remove by id only Signed-off-by: Marcus Eide --- plugins/shortcuts/src/EditShortcut.test.tsx | 6 +----- plugins/shortcuts/src/EditShortcut.tsx | 2 +- plugins/shortcuts/src/api/LocalStoredShortcuts.ts | 4 ++-- plugins/shortcuts/src/api/ShortcutApi.ts | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/plugins/shortcuts/src/EditShortcut.test.tsx b/plugins/shortcuts/src/EditShortcut.test.tsx index bf2396e194..20ef433c37 100644 --- a/plugins/shortcuts/src/EditShortcut.test.tsx +++ b/plugins/shortcuts/src/EditShortcut.test.tsx @@ -86,11 +86,7 @@ describe('EditShortcut', () => { fireEvent.click(screen.getByText('Remove')); await waitFor(() => { - expect(spy).toBeCalledWith({ - id: 'id', - title: 'some title', - url: '/some-url', - }); + expect(spy).toBeCalledWith('id'); }); }); diff --git a/plugins/shortcuts/src/EditShortcut.tsx b/plugins/shortcuts/src/EditShortcut.tsx index 8b98406703..efe8726992 100644 --- a/plugins/shortcuts/src/EditShortcut.tsx +++ b/plugins/shortcuts/src/EditShortcut.tsx @@ -78,7 +78,7 @@ export const EditShortcut = ({ shortcut, onClose, anchorEl, api }: Props) => { const handleRemove = async () => { try { - await api.remove(shortcut); + await api.remove(shortcut.id); alertApi.post({ message: `Removed shortcut '${shortcut.title}' from your sidebar`, severity: 'success', diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts index 377f8e9190..ee8735edb4 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts @@ -39,8 +39,8 @@ export class LocalStoredShortcuts implements ShortcutApi { this.notify(); } - async remove(shortcut: Shortcut) { - const shortcuts = this.get().filter(s => s.id !== shortcut.id); + async remove(id: string) { + const shortcuts = this.get().filter(s => s.id !== id); await this.storageApi.set('items', shortcuts); this.notify(); diff --git a/plugins/shortcuts/src/api/ShortcutApi.ts b/plugins/shortcuts/src/api/ShortcutApi.ts index 826988d01f..74b6aaf777 100644 --- a/plugins/shortcuts/src/api/ShortcutApi.ts +++ b/plugins/shortcuts/src/api/ShortcutApi.ts @@ -36,7 +36,7 @@ export interface ShortcutApi { /** * Removes the shortcut. */ - remove(shortcut: Shortcut): Promise; + remove(id: string): Promise; /** * Finds an existing shortcut that matches the ID of the From de2afb8621494b0f0f9e8fd438792d2eb30bf8b0 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 30 Apr 2021 13:52:36 +0200 Subject: [PATCH 18/68] Use :hover instead of event listeners to display edit button Signed-off-by: Marcus Eide --- plugins/shortcuts/src/ShortcutItem.test.tsx | 20 ----------- plugins/shortcuts/src/ShortcutItem.tsx | 38 +++++++++++---------- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/plugins/shortcuts/src/ShortcutItem.test.tsx b/plugins/shortcuts/src/ShortcutItem.test.tsx index 219771909d..d32ec3d392 100644 --- a/plugins/shortcuts/src/ShortcutItem.test.tsx +++ b/plugins/shortcuts/src/ShortcutItem.test.tsx @@ -81,26 +81,6 @@ describe('ShortcutItem', () => { }); }); - it('displays the edit icon on hover', async () => { - render( - wrapInTestApp( - - - , - ), - ); - - fireEvent.mouseOver(screen.getByText('ST')); - await waitFor(() => { - expect(screen.getByTestId('edit')).toBeInTheDocument(); - }); - - fireEvent.mouseOut(screen.getByText('ST')); - await waitFor(() => { - expect(screen.queryByTestId('edit')).not.toBeInTheDocument(); - }); - }); - it('gets the color based on the theme', async () => { const { rerender } = render( wrapInTestApp(), diff --git a/plugins/shortcuts/src/ShortcutItem.tsx b/plugins/shortcuts/src/ShortcutItem.tsx index 3aec4bb3b4..f9868182f0 100644 --- a/plugins/shortcuts/src/ShortcutItem.tsx +++ b/plugins/shortcuts/src/ShortcutItem.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useState } from 'react'; +import React from 'react'; import { SidebarItem } from '@backstage/core'; import { IconButton, makeStyles } from '@material-ui/core'; import EditIcon from '@material-ui/icons/Edit'; @@ -24,6 +24,14 @@ import { ShortcutApi } from './api'; import { Shortcut } from './types'; const useStyles = makeStyles({ + root: { + '&:hover #edit': { + visibility: 'visible', + }, + }, + button: { + visibility: 'hidden', + }, icon: { color: 'white', fontSize: 16, @@ -50,7 +58,6 @@ type Props = { export const ShortcutItem = ({ shortcut, api }: Props) => { const classes = useStyles(); - const [displayEdit, setDisplayEdit] = useState(false); const [anchorEl, setAnchorEl] = React.useState(); const handleClick = (event: React.MouseEvent) => { @@ -60,32 +67,27 @@ export const ShortcutItem = ({ shortcut, api }: Props) => { const handleClose = () => { setAnchorEl(undefined); - setDisplayEdit(false); - }; - - const handleMouseEnter = () => { - setDisplayEdit(true); - }; - - const handleMouseLeave = () => { - setDisplayEdit(false); }; const text = getIconText(shortcut.title); const color = api.getColor(shortcut.url); return ( -
+ <> } > - {displayEdit && ( - - - - )} + + + { api={api} shortcut={shortcut} /> -
+ ); }; From cea80105ec1627d72148e6ed8a2b1da17851af6d Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 30 Apr 2021 15:25:51 +0200 Subject: [PATCH 19/68] Fix test and unused import Signed-off-by: Marcus Eide --- plugins/shortcuts/src/ShortcutItem.test.tsx | 2 +- plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/shortcuts/src/ShortcutItem.test.tsx b/plugins/shortcuts/src/ShortcutItem.test.tsx index d32ec3d392..ffeec0cdc4 100644 --- a/plugins/shortcuts/src/ShortcutItem.test.tsx +++ b/plugins/shortcuts/src/ShortcutItem.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import { ShortcutItem } from './ShortcutItem'; import { Shortcut } from './types'; import { SidebarContext } from '@backstage/core'; diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts index 94f63f846a..7ca457fd39 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts @@ -69,7 +69,7 @@ describe('LocalStoredShortcuts', () => { const shortcut: Shortcut = { id: 'someid', title: 'title', url: '/url' }; const spy = jest.spyOn(storageApi, 'set'); - await shortcutApi.remove(shortcut); + await shortcutApi.remove(shortcut.id); expect(spy).toHaveBeenCalledWith('items', []); }); From 3796235816251ef9d3bc7907f5fae877e30d52ef Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 30 Apr 2021 17:18:07 +0200 Subject: [PATCH 20/68] Update deps versions Signed-off-by: Marcus Eide --- plugins/shortcuts/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index b3d8d7f4c1..0bc80fa180 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.5", - "@backstage/theme": "^0.2.5", + "@backstage/core": "^0.7.7", + "@backstage/theme": "^0.2.6", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,7 +35,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.6.8", + "@backstage/cli": "^0.6.9", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", From defaa15e55a49f13a2adfd029c41e7c516c6e334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 25 Apr 2021 23:21:55 +0200 Subject: [PATCH 21/68] Add "Organization" tab with a diagram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Gomez --- .../components/ExplorePage/ExploreTabs.tsx | 4 + .../OrganizationDiagram.tsx | 207 ++++++++++++++++++ .../OrganizationExplorerContent.tsx | 30 +++ .../OrganizationExplorerContent/index.ts | 16 ++ 4 files changed, 257 insertions(+) create mode 100644 plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx create mode 100644 plugins/explore/src/components/OrganizationExplorerContent/OrganizationExplorerContent.tsx create mode 100644 plugins/explore/src/components/OrganizationExplorerContent/index.ts diff --git a/plugins/explore/src/components/ExplorePage/ExploreTabs.tsx b/plugins/explore/src/components/ExplorePage/ExploreTabs.tsx index 0cf28f9f1c..24fbf0039c 100644 --- a/plugins/explore/src/components/ExplorePage/ExploreTabs.tsx +++ b/plugins/explore/src/components/ExplorePage/ExploreTabs.tsx @@ -16,6 +16,7 @@ import { TabbedLayout } from '@backstage/core'; import React from 'react'; import { DomainExplorerContent } from '../DomainExplorerContent'; +import { OrganizationExplorerContent } from '../OrganizationExplorerContent'; import { ToolExplorerContent } from '../ToolExplorerContent'; export const ExploreTabs = () => ( @@ -23,6 +24,9 @@ export const ExploreTabs = () => ( + + + diff --git a/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx b/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx new file mode 100644 index 0000000000..4e0d3710f3 --- /dev/null +++ b/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx @@ -0,0 +1,207 @@ +/* + * Copyright 2021 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 { + Entity, + RELATION_CHILD_OF, + stringifyEntityRef, + ENTITY_DEFAULT_NAMESPACE, + parseEntityRef, +} from '@backstage/catalog-model'; +import { + catalogApiRef, + entityRouteRef, + getEntityRelations, +} from '@backstage/plugin-catalog-react'; +import { + DependencyGraph, + DependencyGraphTypes, + Progress, + useApi, + ResponseErrorPanel, + Link, + useRouteRef, + configApiRef, +} from '@backstage/core'; +import { makeStyles, Typography } from '@material-ui/core'; +import ZoomOutMap from '@material-ui/icons/ZoomOutMap'; +import React from 'react'; +import { useAsync } from 'react-use'; +import { BackstageTheme } from '@backstage/theme'; + +const useStyles = makeStyles((theme: BackstageTheme) => ({ + organizationNode: { + fill: 'coral', + stroke: theme.palette.border, + }, + groupNode: { + fill: 'yellowgreen', + stroke: theme.palette.border, + }, +})); + +// Simplifies the diagram output by hiding the default namespace and kind +function readableEntityName( + ref: + | Entity + | { + kind: string; + namespace?: string; + name: string; + }, +): string { + return stringifyEntityRef(ref) + .toLocaleLowerCase('en-US') + .replace(`:${ENTITY_DEFAULT_NAMESPACE}/`, ':') + .split(':')[1]; +} + +function RenderNode(props: DependencyGraphTypes.RenderNodeProps) { + const classes = useStyles(); + const catalogEntityRoute = useRouteRef(entityRouteRef); + + if (props.node.id === 'root') { + return ( + + + + {props.node.name} + + + ); + } + + const ref = parseEntityRef(props.node.id); + + return ( + + + + + {props.node.name} + + + + ); +} + +/** + * Dynamically generates a diagram of an organization. + */ +export function OrganizationDiagram() { + const nodes = new Array<{ id: string; kind: string; name: string }>(); + const edges = new Array<{ from: string; to: string; label: string }>(); + + const configApi = useApi(configApiRef); + const catalogApi = useApi(catalogApiRef); + const organizationName = + configApi.getOptionalString('organization.name') ?? 'Backstage'; + const { loading, error, value: catalogResponse } = useAsync(() => { + return catalogApi.getEntities({ + filter: { + kind: ['Group'], + }, + }); + }, [catalogApi]); + + if (loading) { + return ; + } else if (error) { + return ; + } + + // the root of this diagram is the organization + nodes.push({ + id: 'root', + kind: 'Organization', + name: organizationName, + }); + + for (const catalogItem of catalogResponse?.items || []) { + const currentItemId = stringifyEntityRef(catalogItem); + + nodes.push({ + id: stringifyEntityRef(catalogItem), + kind: catalogItem.kind, + name: readableEntityName(catalogItem), + }); + + // Edge to parent + const catalogItemRelations_childOf = getEntityRelations( + catalogItem, + RELATION_CHILD_OF, + ); + + // if no parent is found, link the node to the root + if (catalogItemRelations_childOf.length === 0) { + edges.push({ + from: currentItemId, + to: 'root', + label: '', + }); + } + + catalogItemRelations_childOf.forEach(relation => { + edges.push({ + from: currentItemId, + to: stringifyEntityRef(relation), + label: '', + }); + }); + } + + return ( + <> + + + Use pinch & zoom + to move around the diagram. + + + ); +} diff --git a/plugins/explore/src/components/OrganizationExplorerContent/OrganizationExplorerContent.tsx b/plugins/explore/src/components/OrganizationExplorerContent/OrganizationExplorerContent.tsx new file mode 100644 index 0000000000..4e05cc2c6c --- /dev/null +++ b/plugins/explore/src/components/OrganizationExplorerContent/OrganizationExplorerContent.tsx @@ -0,0 +1,30 @@ +/* + * Copyright 2021 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 { Content, ContentHeader, SupportButton } from '@backstage/core'; +import React from 'react'; +import { OrganizationDiagram } from './OrganizationDiagram'; + +export const OrganizationExplorerContent = () => { + return ( + + + Explore your organization. + + + + + ); +}; diff --git a/plugins/explore/src/components/OrganizationExplorerContent/index.ts b/plugins/explore/src/components/OrganizationExplorerContent/index.ts new file mode 100644 index 0000000000..87b08eba52 --- /dev/null +++ b/plugins/explore/src/components/OrganizationExplorerContent/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2021 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 { OrganizationExplorerContent } from './OrganizationExplorerContent'; From f37cf4599c6c1e1feafd0d814104a0b2e2f75600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Tue, 4 May 2021 23:17:45 +0200 Subject: [PATCH 22/68] Use formatEntityRefTitle to format node titles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Gomez --- .../OrganizationDiagram.tsx | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx b/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx index 4e0d3710f3..e3a6826835 100644 --- a/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx +++ b/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx @@ -15,16 +15,15 @@ */ import { - Entity, RELATION_CHILD_OF, stringifyEntityRef, - ENTITY_DEFAULT_NAMESPACE, parseEntityRef, } from '@backstage/catalog-model'; import { catalogApiRef, entityRouteRef, getEntityRelations, + formatEntityRefTitle, } from '@backstage/plugin-catalog-react'; import { DependencyGraph, @@ -53,22 +52,6 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ }, })); -// Simplifies the diagram output by hiding the default namespace and kind -function readableEntityName( - ref: - | Entity - | { - kind: string; - namespace?: string; - name: string; - }, -): string { - return stringifyEntityRef(ref) - .toLocaleLowerCase('en-US') - .replace(`:${ENTITY_DEFAULT_NAMESPACE}/`, ':') - .split(':')[1]; -} - function RenderNode(props: DependencyGraphTypes.RenderNodeProps) { const classes = useStyles(); const catalogEntityRoute = useRouteRef(entityRouteRef); @@ -159,7 +142,7 @@ export function OrganizationDiagram() { nodes.push({ id: stringifyEntityRef(catalogItem), kind: catalogItem.kind, - name: readableEntityName(catalogItem), + name: formatEntityRefTitle(catalogItem, { defaultKind: 'Group' }), }); // Edge to parent From 0b033d07b7775b9cb792d7246870213ae4228d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Tue, 4 May 2021 23:19:30 +0200 Subject: [PATCH 23/68] Add changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Gomez --- .changeset/breezy-carrots-hunt.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/breezy-carrots-hunt.md diff --git a/.changeset/breezy-carrots-hunt.md b/.changeset/breezy-carrots-hunt.md new file mode 100644 index 0000000000..0075009a2d --- /dev/null +++ b/.changeset/breezy-carrots-hunt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-explore': patch +--- + +Add "Organization" tab with a diagram From 889d89b6ec2fece63ecb07b04fd1a54bbfb73653 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Wed, 5 May 2021 11:34:27 +0200 Subject: [PATCH 24/68] Fix setting the state with `useQueryParamState` makes inputs lose their focus Signed-off-by: Oliver Sand --- .changeset/short-bobcats-mate.md | 6 ++++++ packages/core/src/hooks/useQueryParamState.ts | 14 +++++++++----- .../ApiExplorerTable/ApiExplorerTable.tsx | 1 - 3 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 .changeset/short-bobcats-mate.md diff --git a/.changeset/short-bobcats-mate.md b/.changeset/short-bobcats-mate.md new file mode 100644 index 0000000000..22aaf11d77 --- /dev/null +++ b/.changeset/short-bobcats-mate.md @@ -0,0 +1,6 @@ +--- +'@backstage/core': patch +'@backstage/plugin-api-docs': patch +--- + +Fix setting the state with `useQueryParamState` makes inputs lose their focus. diff --git a/packages/core/src/hooks/useQueryParamState.ts b/packages/core/src/hooks/useQueryParamState.ts index da303234f5..0a61771eba 100644 --- a/packages/core/src/hooks/useQueryParamState.ts +++ b/packages/core/src/hooks/useQueryParamState.ts @@ -17,7 +17,7 @@ import { isEqual } from 'lodash'; import qs from 'qs'; import { useEffect, useState } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { useDebounce } from 'react-use'; function stringify(queryParams: any): string { @@ -58,9 +58,7 @@ type SetQueryParams = (params: T) => void; export function useQueryParamState( stateName: string, - debounceTime: number = 100, ): [T | undefined, SetQueryParams] { - const navigate = useNavigate(); const location = useLocation(); const [queryParamState, setQueryParamState] = useState( extractState(location.search, stateName), @@ -83,10 +81,16 @@ export function useQueryParamState( ); if (location.search !== queryString) { - navigate({ ...location, search: `?${queryString}` }, { replace: true }); + // We fallback to the history API, as navigate from react-router causes + // input elements to loose focus. + history.replaceState( + undefined, + document.title, + `${location.pathname}?${queryString}`, + ); } }, - debounceTime, + 100, [queryParamState], ); diff --git a/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx b/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx index 39c2caaf1b..5b9808e26d 100644 --- a/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx +++ b/plugins/api-docs/src/components/ApiExplorerTable/ApiExplorerTable.tsx @@ -156,7 +156,6 @@ export const ApiExplorerTable = ({ }: ExplorerTableProps) => { const [queryParamState, setQueryParamState] = useQueryParamState( 'apiTable', - 500, ); if (error) { From d67645ca05676d89134af065c39dcda0a178b5c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Wed, 5 May 2021 14:22:33 +0200 Subject: [PATCH 25/68] Rename Organization diagram to Groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Gomez --- .../explore/src/components/ExplorePage/ExploreTabs.tsx | 6 +++--- .../GroupsDiagram.tsx} | 4 ++-- .../GroupsExplorerContent.tsx} | 10 +++++----- .../index.ts | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) rename plugins/explore/src/components/{OrganizationExplorerContent/OrganizationDiagram.tsx => GroupsExplorerContent/GroupsDiagram.tsx} (97%) rename plugins/explore/src/components/{OrganizationExplorerContent/OrganizationExplorerContent.tsx => GroupsExplorerContent/GroupsExplorerContent.tsx} (75%) rename plugins/explore/src/components/{OrganizationExplorerContent => GroupsExplorerContent}/index.ts (88%) diff --git a/plugins/explore/src/components/ExplorePage/ExploreTabs.tsx b/plugins/explore/src/components/ExplorePage/ExploreTabs.tsx index 24fbf0039c..0415dd97ae 100644 --- a/plugins/explore/src/components/ExplorePage/ExploreTabs.tsx +++ b/plugins/explore/src/components/ExplorePage/ExploreTabs.tsx @@ -16,7 +16,7 @@ import { TabbedLayout } from '@backstage/core'; import React from 'react'; import { DomainExplorerContent } from '../DomainExplorerContent'; -import { OrganizationExplorerContent } from '../OrganizationExplorerContent'; +import { GroupsExplorerContent } from '../GroupsExplorerContent'; import { ToolExplorerContent } from '../ToolExplorerContent'; export const ExploreTabs = () => ( @@ -24,8 +24,8 @@ export const ExploreTabs = () => ( - - + + diff --git a/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx similarity index 97% rename from plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx rename to plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index e3a6826835..99157b878f 100644 --- a/plugins/explore/src/components/OrganizationExplorerContent/OrganizationDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -105,9 +105,9 @@ function RenderNode(props: DependencyGraphTypes.RenderNodeProps) { } /** - * Dynamically generates a diagram of an organization. + * Dynamically generates a diagram of groups registered in the catalog. */ -export function OrganizationDiagram() { +export function GroupsDiagram() { const nodes = new Array<{ id: string; kind: string; name: string }>(); const edges = new Array<{ from: string; to: string; label: string }>(); diff --git a/plugins/explore/src/components/OrganizationExplorerContent/OrganizationExplorerContent.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.tsx similarity index 75% rename from plugins/explore/src/components/OrganizationExplorerContent/OrganizationExplorerContent.tsx rename to plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.tsx index 4e05cc2c6c..c4d46206e4 100644 --- a/plugins/explore/src/components/OrganizationExplorerContent/OrganizationExplorerContent.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.tsx @@ -15,16 +15,16 @@ */ import { Content, ContentHeader, SupportButton } from '@backstage/core'; import React from 'react'; -import { OrganizationDiagram } from './OrganizationDiagram'; +import { GroupsDiagram } from './GroupsDiagram'; -export const OrganizationExplorerContent = () => { +export const GroupsExplorerContent = () => { return ( - - Explore your organization. + + Explore your groups. - + ); }; diff --git a/plugins/explore/src/components/OrganizationExplorerContent/index.ts b/plugins/explore/src/components/GroupsExplorerContent/index.ts similarity index 88% rename from plugins/explore/src/components/OrganizationExplorerContent/index.ts rename to plugins/explore/src/components/GroupsExplorerContent/index.ts index 87b08eba52..4d88f40dde 100644 --- a/plugins/explore/src/components/OrganizationExplorerContent/index.ts +++ b/plugins/explore/src/components/GroupsExplorerContent/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { OrganizationExplorerContent } from './OrganizationExplorerContent'; +export { GroupsExplorerContent } from './GroupsExplorerContent'; From 90ba3a3205363dec2cb1f25688263437d3c79c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Wed, 5 May 2021 16:56:45 +0200 Subject: [PATCH 26/68] Test the GroupsExplorerContent and GroupsDiagram components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Gomez --- .../GroupsDiagram.test.tsx | 69 ++++++++++++ .../GroupsExplorerContent.test.tsx | 103 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.test.tsx create mode 100644 plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.test.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.test.tsx new file mode 100644 index 0000000000..022387faca --- /dev/null +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.test.tsx @@ -0,0 +1,69 @@ +/* + * 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 { ApiProvider, ApiRegistry } from '@backstage/core'; +import { + catalogApiRef, + CatalogApi, + entityRouteRef, +} from '@backstage/plugin-catalog-react'; +import { Entity } from '@backstage/catalog-model'; +import { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { GroupsDiagram } from './GroupsDiagram'; + +describe('', () => { + beforeAll(() => { + Object.defineProperty(window.SVGElement.prototype, 'getBBox', { + value: () => ({ width: 100, height: 100 }), + configurable: true, + }); + }); + + it('shows groups', async () => { + const catalogApi: Partial = { + getEntities: () => + Promise.resolve({ + items: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + name: 'group-a', + namespace: 'my-namespace', + }, + spec: { + type: 'organization', + }, + }, + ] as Entity[], + }), + }; + + const { getByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + expect(getByText('my-namespace/group-a')).toBeInTheDocument(); + }); +}); diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx new file mode 100644 index 0000000000..38c4678d5a --- /dev/null +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx @@ -0,0 +1,103 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; +import React from 'react'; +import { GroupsExplorerContent } from '../GroupsExplorerContent'; + +describe('', () => { + const catalogApi: jest.Mocked = { + addLocation: jest.fn(_a => new Promise(() => {})), + getEntities: jest.fn(), + getOriginLocationByEntity: jest.fn(), + getLocationByEntity: jest.fn(), + getLocationById: jest.fn(), + removeLocationById: jest.fn(), + removeEntityByUid: jest.fn(), + getEntityByName: jest.fn(), + }; + + const Wrapper = ({ children }: { children?: React.ReactNode }) => ( + + {children} + + ); + + beforeEach(() => { + jest.resetAllMocks(); + + Object.defineProperty(window.SVGElement.prototype, 'getBBox', { + value: () => ({ width: 100, height: 100 }), + configurable: true, + }); + }); + + it('renders a groups diagram', async () => { + const entities: Entity[] = [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + name: 'group-a', + namespace: 'my-namespace', + }, + spec: { + type: 'organization', + }, + }, + ]; + catalogApi.getEntities.mockResolvedValue({ items: entities }); + + const { getByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + await waitFor(() => { + expect(getByText('my-namespace/group-a')).toBeInTheDocument(); + }); + }); + + it('renders a friendly error if it cannot collect domains', async () => { + const catalogError = new Error('Network timeout'); + catalogApi.getEntities.mockRejectedValueOnce(catalogError); + + const { getByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + await waitFor(() => + expect(getByText(/Warning: Network timeout/)).toBeInTheDocument(), + ); + }); +}); From e133f76cd153b8d117cd11fa1c1dc51c4a6a1252 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 May 2021 04:12:19 +0000 Subject: [PATCH 27/68] chore(deps): bump ts-node from 8.10.2 to 9.1.1 Bumps [ts-node](https://github.com/TypeStrong/ts-node) from 8.10.2 to 9.1.1. - [Release notes](https://github.com/TypeStrong/ts-node/releases) - [Commits](https://github.com/TypeStrong/ts-node/compare/v8.10.2...v9.1.1) Signed-off-by: dependabot[bot] --- packages/cli/package.json | 2 +- packages/create-app/package.json | 2 +- packages/docgen/package.json | 2 +- packages/e2e-test/package.json | 2 +- plugins/catalog-graphql/package.json | 2 +- yarn.lock | 11 ----------- 6 files changed, 5 insertions(+), 16 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 46e99b762b..1673c62513 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -143,7 +143,7 @@ "del": "^5.1.0", "mock-fs": "^4.13.0", "nodemon": "^2.0.2", - "ts-node": "^8.6.2" + "ts-node": "^9.1.1" }, "files": [ "asset-types", diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 071a0a5ebd..8129dbf5d4 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -41,7 +41,7 @@ "@types/inquirer": "^7.3.1", "@types/react-dev-utils": "^9.0.4", "@types/recursive-readdir": "^2.2.0", - "ts-node": "^8.6.2" + "ts-node": "^9.1.1" }, "peerDependencies": { "@backstage/backend-common": "*", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index bb00620967..a7d7615e19 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -30,7 +30,7 @@ "commander": "^6.1.0", "fs-extra": "^9.0.0", "github-slugger": "^1.3.0", - "ts-node": "^8.6.2", + "ts-node": "^9.1.1", "typescript": "^4.0.3" }, "devDependencies": { diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 6cd740c7b7..77a3e3960a 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -34,7 +34,7 @@ "handlebars": "^4.7.3", "pgtools": "^0.3.0", "tree-kill": "^1.2.2", - "ts-node": "^8.6.2", + "ts-node": "^9.1.1", "zombie": "^6.1.4" }, "nodemonConfig": { diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index 3ab6d64dc8..18aac98aa5 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -52,7 +52,7 @@ "eslint-plugin-graphql": "^4.0.0", "msw": "^0.21.2", "supertest": "^6.1.3", - "ts-node": "^8.10.2" + "ts-node": "^9.1.1" }, "files": [ "dist" diff --git a/yarn.lock b/yarn.lock index 23cf3b2fd2..266171ae3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25582,17 +25582,6 @@ ts-log@^2.2.3: resolved "https://registry.npmjs.org/ts-log/-/ts-log-2.2.3.tgz#4da5640fe25a9fb52642cd32391c886721318efb" integrity sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w== -ts-node@^8.10.2, ts-node@^8.6.2: - version "8.10.2" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" - integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== - dependencies: - arg "^4.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.17" - yn "3.1.1" - ts-node@^9, ts-node@^9.1.1: version "9.1.1" resolved "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" From 7c9d8c3b187626e156367112dcc523b494c5845e Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 6 May 2021 10:17:47 +0200 Subject: [PATCH 28/68] Update README Signed-off-by: Marcus Eide --- plugins/shortcuts/README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/shortcuts/README.md b/plugins/shortcuts/README.md index dfe8577190..36bd9e62f8 100644 --- a/plugins/shortcuts/README.md +++ b/plugins/shortcuts/README.md @@ -1,12 +1,26 @@ # shortcuts -`shortcuts-plugin` allows a user to have easy access to pages within a Backstage app by storing them as "shortcuts" in the Sidebar. +The shortcuts plugin allows a user to have easy access to pages within a Backstage app by storing them as "shortcuts" in the Sidebar. ## Usage +Install the package: + +```bash +yarn add @backstage/plugin-shortcuts +``` + +Add it to your App's `plugins.ts` file: + +```ts +// ... +export { shortcutsPlugin } from '@backstage/plugin-shortcuts'; +``` + Add the `` component within your ``: ```ts +import { Sidebar, SidebarDivider, SidebarSpace } from '@backstage/core'; import { Shortcuts } from '@backstage/plugin-shortcuts'; @@ -17,7 +31,7 @@ import { Shortcuts } from '@backstage/plugin-shortcuts'; ; ``` -The plugin exports a `shortcutApiRef` but the plugin includes a default implementation of the `ShortcutApi` that uses `localStorage` to store each users shortcuts. +The plugin exports a `shortcutApiRef` but the plugin includes a default implementation of the `ShortcutApi` that uses `localStorage` to store each user's shortcuts. To overwrite the default implementation add it to the App's `apis.ts`: From 5d2d8e505c535cb5922106d0666cf8666fc42517 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 6 May 2021 10:25:38 +0200 Subject: [PATCH 29/68] Use renderInTestApp in test where possible Signed-off-by: Marcus Eide --- plugins/shortcuts/src/AddShortcut.test.tsx | 38 +++++++---------- plugins/shortcuts/src/EditShortcut.test.tsx | 36 +++++++---------- plugins/shortcuts/src/ShortcutForm.test.tsx | 20 +++++---- plugins/shortcuts/src/ShortcutItem.test.tsx | 45 +++++++++------------ plugins/shortcuts/src/Shortcuts.test.tsx | 18 ++++----- 5 files changed, 66 insertions(+), 91 deletions(-) diff --git a/plugins/shortcuts/src/AddShortcut.test.tsx b/plugins/shortcuts/src/AddShortcut.test.tsx index cee5d8cf28..8001cdc293 100644 --- a/plugins/shortcuts/src/AddShortcut.test.tsx +++ b/plugins/shortcuts/src/AddShortcut.test.tsx @@ -15,10 +15,10 @@ */ import React from 'react'; -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { screen, fireEvent, waitFor } from '@testing-library/react'; import { AddShortcut } from './AddShortcut'; import { LocalStoredShortcuts } from './api'; -import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; +import { MockStorageApi, renderInTestApp } from '@backstage/test-utils'; import { AlertDisplay } from '@backstage/core'; describe('AddShortcut', () => { @@ -36,26 +36,22 @@ describe('AddShortcut', () => { }); it('displays the title', async () => { - render(wrapInTestApp()); + await renderInTestApp(); - await waitFor(() => { - expect(screen.getByText('Add Shortcut')).toBeInTheDocument(); - }); + expect(screen.getByText('Add Shortcut')).toBeInTheDocument(); }); it('closes the popup', async () => { - render(wrapInTestApp()); + await renderInTestApp(); fireEvent.click(screen.getByText('Cancel')); - await waitFor(() => { - expect(props.onClose).toHaveBeenCalledTimes(1); - }); + expect(props.onClose).toHaveBeenCalledTimes(1); }); it('saves the input', async () => { const spy = jest.spyOn(api, 'add'); - render(wrapInTestApp()); + await renderInTestApp(); const urlInput = screen.getByPlaceholderText('Enter a URL'); const titleInput = screen.getByPlaceholderText('Enter a display name'); @@ -74,11 +70,9 @@ describe('AddShortcut', () => { it('pastes the values', async () => { const spy = jest.spyOn(api, 'add'); - render( - wrapInTestApp(, { - routeEntries: ['/some-initial-url'], - }), - ); + await renderInTestApp(, { + routeEntries: ['/some-initial-url'], + }); fireEvent.click(screen.getByText('Use current page')); fireEvent.click(screen.getByText('Save')); @@ -93,13 +87,11 @@ describe('AddShortcut', () => { it('displays errors', async () => { jest.spyOn(api, 'add').mockRejectedValueOnce(new Error('some add error')); - render( - wrapInTestApp( - <> - - - , - ), + await renderInTestApp( + <> + + + , ); fireEvent.click(screen.getByText('Use current page')); diff --git a/plugins/shortcuts/src/EditShortcut.test.tsx b/plugins/shortcuts/src/EditShortcut.test.tsx index 20ef433c37..eaab6cf609 100644 --- a/plugins/shortcuts/src/EditShortcut.test.tsx +++ b/plugins/shortcuts/src/EditShortcut.test.tsx @@ -15,11 +15,11 @@ */ import React from 'react'; -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { screen, fireEvent, waitFor } from '@testing-library/react'; import { EditShortcut } from './EditShortcut'; import { Shortcut } from './types'; import { LocalStoredShortcuts } from './api'; -import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; +import { MockStorageApi, renderInTestApp } from '@backstage/test-utils'; import { AlertDisplay } from '@backstage/core'; describe('EditShortcut', () => { @@ -42,26 +42,22 @@ describe('EditShortcut', () => { }); it('displays the title', async () => { - render(wrapInTestApp()); + await renderInTestApp(); - await waitFor(() => { - expect(screen.getByText('Edit Shortcut')).toBeInTheDocument(); - }); + expect(screen.getByText('Edit Shortcut')).toBeInTheDocument(); }); it('closes the popup', async () => { - render(wrapInTestApp()); + await renderInTestApp(); fireEvent.click(screen.getByText('Cancel')); - await waitFor(() => { - expect(props.onClose).toHaveBeenCalledTimes(1); - }); + expect(props.onClose).toHaveBeenCalledTimes(1); }); it('updates the shortcut', async () => { const spy = jest.spyOn(api, 'update'); - render(wrapInTestApp()); + await renderInTestApp(); const urlInput = screen.getByPlaceholderText('Enter a URL'); const titleInput = screen.getByPlaceholderText('Enter a display name'); @@ -82,12 +78,10 @@ describe('EditShortcut', () => { it('removes the shortcut', async () => { const spy = jest.spyOn(api, 'remove'); - render(wrapInTestApp()); + await renderInTestApp(); fireEvent.click(screen.getByText('Remove')); - await waitFor(() => { - expect(spy).toBeCalledWith('id'); - }); + expect(spy).toBeCalledWith('id'); }); it('displays errors', async () => { @@ -99,13 +93,11 @@ describe('EditShortcut', () => { .spyOn(api, 'remove') .mockRejectedValueOnce(new Error('some remove error')); - render( - wrapInTestApp( - <> - - - , - ), + await renderInTestApp( + <> + + + , ); fireEvent.click(screen.getByText('Save')); diff --git a/plugins/shortcuts/src/ShortcutForm.test.tsx b/plugins/shortcuts/src/ShortcutForm.test.tsx index 4586c484d0..23a36126c6 100644 --- a/plugins/shortcuts/src/ShortcutForm.test.tsx +++ b/plugins/shortcuts/src/ShortcutForm.test.tsx @@ -14,9 +14,9 @@ * limitations under the License. */ import React from 'react'; -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { screen, fireEvent, waitFor } from '@testing-library/react'; import { ShortcutForm } from './ShortcutForm'; -import { wrapInTestApp } from '@backstage/test-utils'; +import { renderInTestApp } from '@backstage/test-utils'; describe('ShortcutForm', () => { const props = { @@ -25,7 +25,7 @@ describe('ShortcutForm', () => { }; it('displays validation messages', async () => { - render(wrapInTestApp()); + await renderInTestApp(); const urlInput = screen.getByPlaceholderText('Enter a URL'); const titleInput = screen.getByPlaceholderText('Enter a display name'); @@ -44,13 +44,11 @@ describe('ShortcutForm', () => { }); it('calls the save handler', async () => { - render( - wrapInTestApp( - , - ), + await renderInTestApp( + , ); fireEvent.click(screen.getByText('Save')); @@ -63,7 +61,7 @@ describe('ShortcutForm', () => { }); it('calls the close handler', async () => { - render(wrapInTestApp()); + await renderInTestApp(); fireEvent.click(screen.getByText('Cancel')); await waitFor(() => { diff --git a/plugins/shortcuts/src/ShortcutItem.test.tsx b/plugins/shortcuts/src/ShortcutItem.test.tsx index ffeec0cdc4..080f847952 100644 --- a/plugins/shortcuts/src/ShortcutItem.test.tsx +++ b/plugins/shortcuts/src/ShortcutItem.test.tsx @@ -15,12 +15,16 @@ */ import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import { ShortcutItem } from './ShortcutItem'; import { Shortcut } from './types'; import { SidebarContext } from '@backstage/core'; import { LocalStoredShortcuts } from './api'; -import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; +import { + MockStorageApi, + renderInTestApp, + wrapInTestApp, +} from '@backstage/test-utils'; import { pageTheme } from '@backstage/theme'; describe('ShortcutItem', () => { @@ -32,17 +36,13 @@ describe('ShortcutItem', () => { const api = new LocalStoredShortcuts(MockStorageApi.create()); it('displays the shortcut', async () => { - render( - wrapInTestApp( - - - , - ), + await renderInTestApp( + + + , ); - await waitFor(() => { - expect(screen.getByText('ST')).toBeInTheDocument(); - expect(screen.getByText('some title')).toBeInTheDocument(); - }); + expect(screen.getByText('ST')).toBeInTheDocument(); + expect(screen.getByText('some title')).toBeInTheDocument(); }); it('calculates the shortcut text correctly', async () => { @@ -62,13 +62,10 @@ describe('ShortcutItem', () => { title: 'more | title words', }; - const { rerender } = render( - wrapInTestApp(), + const { rerender } = await renderInTestApp( + , ); - - await waitFor(() => { - expect(screen.getByText('On')).toBeInTheDocument(); - }); + expect(screen.getByText('On')).toBeInTheDocument(); rerender(wrapInTestApp()); await waitFor(() => { @@ -82,15 +79,13 @@ describe('ShortcutItem', () => { }); it('gets the color based on the theme', async () => { - const { rerender } = render( - wrapInTestApp(), + const { rerender } = await renderInTestApp( + , ); - await waitFor(() => { - expect(document.querySelector('circle')?.getAttribute('fill')).toEqual( - pageTheme.tool.colors[0], - ); - }); + expect(document.querySelector('circle')?.getAttribute('fill')).toEqual( + pageTheme.tool.colors[0], + ); const newShortcut: Shortcut = { id: 'id', diff --git a/plugins/shortcuts/src/Shortcuts.test.tsx b/plugins/shortcuts/src/Shortcuts.test.tsx index 088a3705d9..b697f9ed27 100644 --- a/plugins/shortcuts/src/Shortcuts.test.tsx +++ b/plugins/shortcuts/src/Shortcuts.test.tsx @@ -16,8 +16,8 @@ import React from 'react'; import { SidebarContext, ApiProvider, ApiRegistry } from '@backstage/core'; -import { wrapInTestApp, MockStorageApi } from '@backstage/test-utils'; -import { render, screen, waitFor } from '@testing-library/react'; +import { MockStorageApi, renderInTestApp } from '@backstage/test-utils'; +import { screen, waitFor } from '@testing-library/react'; import { Shortcuts } from './Shortcuts'; import { LocalStoredShortcuts, shortcutsApiRef } from './api'; @@ -27,14 +27,12 @@ const apis = ApiRegistry.from([ describe('Shortcuts', () => { it('displays an add button', async () => { - render( - wrapInTestApp( - - - - - , - ), + await renderInTestApp( + + + + + , ); await waitFor(() => !screen.queryByTestId('progress')); expect(screen.getByText('Add Shortcuts')).toBeInTheDocument(); From 93ebf0ef2426dced9ff1974815f56c8b77e31309 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 6 May 2021 10:26:11 +0200 Subject: [PATCH 30/68] Change exports Signed-off-by: Marcus Eide --- plugins/shortcuts/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/shortcuts/src/index.ts b/plugins/shortcuts/src/index.ts index 851178710f..70f874e005 100644 --- a/plugins/shortcuts/src/index.ts +++ b/plugins/shortcuts/src/index.ts @@ -15,4 +15,4 @@ */ export { shortcutsPlugin, Shortcuts } from './plugin'; export * from './api'; -export * from './types'; +export type { Shortcut } from './types'; From a3b102c827cd7f34525f8f3bfc7004959b4167be Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Wed, 5 May 2021 17:20:03 +0200 Subject: [PATCH 31/68] Use `useSearchParams` in `useQueryParamState` Signed-off-by: Oliver Sand --- packages/core/src/hooks/useQueryParamState.ts | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/core/src/hooks/useQueryParamState.ts b/packages/core/src/hooks/useQueryParamState.ts index 0a61771eba..023d69bf4f 100644 --- a/packages/core/src/hooks/useQueryParamState.ts +++ b/packages/core/src/hooks/useQueryParamState.ts @@ -17,7 +17,7 @@ import { isEqual } from 'lodash'; import qs from 'qs'; import { useEffect, useState } from 'react'; -import { useLocation } from 'react-router-dom'; +import { useSearchParams } from 'react-router-dom'; import { useDebounce } from 'react-use'; function stringify(queryParams: any): string { @@ -59,39 +59,34 @@ type SetQueryParams = (params: T) => void; export function useQueryParamState( stateName: string, ): [T | undefined, SetQueryParams] { - const location = useLocation(); + const [searchParams, setSearchParams] = useSearchParams(); + const searchParamsString = searchParams.toString(); const [queryParamState, setQueryParamState] = useState( - extractState(location.search, stateName), + extractState(searchParamsString, stateName), ); useEffect(() => { - const newState = extractState(location.search, stateName); + const newState = extractState(searchParamsString, stateName); setQueryParamState(oldState => isEqual(newState, oldState) ? oldState : newState, ); - }, [location, stateName]); + }, [searchParamsString, setQueryParamState, stateName]); useDebounce( () => { const queryString = joinQueryString( - location.search, + searchParamsString, stateName, queryParamState, ); - if (location.search !== queryString) { - // We fallback to the history API, as navigate from react-router causes - // input elements to loose focus. - history.replaceState( - undefined, - document.title, - `${location.pathname}?${queryString}`, - ); + if (searchParamsString !== queryString) { + setSearchParams(queryString, { replace: true }); } }, 100, - [queryParamState], + [setSearchParams, queryParamState, searchParamsString, stateName], ); return [queryParamState, setQueryParamState]; From 42e133b6de6451c7819096c34c72319c7826382d Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 6 May 2021 10:55:48 +0200 Subject: [PATCH 32/68] Fix table toolbar rerendering to often and losing focus Signed-off-by: Oliver Sand --- .changeset/short-bobcats-mate.md | 3 +- packages/core/src/components/Table/Table.tsx | 136 +++++++++++-------- 2 files changed, 81 insertions(+), 58 deletions(-) diff --git a/.changeset/short-bobcats-mate.md b/.changeset/short-bobcats-mate.md index 22aaf11d77..d63e7058a9 100644 --- a/.changeset/short-bobcats-mate.md +++ b/.changeset/short-bobcats-mate.md @@ -3,4 +3,5 @@ '@backstage/plugin-api-docs': patch --- -Fix setting the state with `useQueryParamState` makes inputs lose their focus. +Fix state persisted in the URL make search input in the table toolbar lose their +focus. diff --git a/packages/core/src/components/Table/Table.tsx b/packages/core/src/components/Table/Table.tsx index 2251b8304e..1dd09e7ab8 100644 --- a/packages/core/src/components/Table/Table.tsx +++ b/packages/core/src/components/Table/Table.tsx @@ -20,6 +20,7 @@ import { makeStyles, Typography, useTheme, + withStyles, } from '@material-ui/core'; // Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51 import AddBox from '@material-ui/icons/AddBox'; @@ -49,6 +50,7 @@ import MTable, { } from 'material-table'; import React, { forwardRef, + MutableRefObject, ReactNode, useCallback, useEffect, @@ -100,19 +102,20 @@ function extractValueByField(data: any, field: string): any | undefined { return value; } -const useHeaderStyles = makeStyles(theme => ({ +const StyledMTableHeader = withStyles(theme => ({ header: { padding: theme.spacing(1, 2, 1, 2.5), borderTop: `1px solid ${theme.palette.grey.A100}`, borderBottom: `1px solid ${theme.palette.grey.A100}`, - color: theme.palette.textSubtle, + // withStyles hasn't a generic overload for theme + color: (theme as BackstageTheme).palette.textSubtle, fontWeight: theme.typography.fontWeightBold, position: 'static', wordBreak: 'normal', }, -})); +}))(MTableHeader); -const useToolbarStyles = makeStyles(theme => ({ +const StyledMTableToolbar = withStyles(theme => ({ root: { padding: theme.spacing(3, 0, 2.5, 2.5), }, @@ -124,7 +127,7 @@ const useToolbarStyles = makeStyles(theme => ({ searchField: { paddingRight: theme.spacing(2), }, -})); +}))(MTableToolbar); const useFilterStyles = makeStyles(() => ({ root: { @@ -208,6 +211,59 @@ export interface TableProps onStateChange?: (state: TableState) => any; } +export function TableToolbar(toolbarProps: { + toolbarRef: MutableRefObject; + setSearch: (value: string) => void; + onSearchChanged: (value: string) => void; + toggleFilters: () => void; + hasFilters: boolean; + selectedFiltersLength: number; +}) { + const { + toolbarRef, + setSearch, + hasFilters, + selectedFiltersLength, + toggleFilters, + } = toolbarProps; + const filtersClasses = useFilterStyles(); + const onSearchChanged = useCallback( + (searchText: string) => { + toolbarProps.onSearchChanged(searchText); + setSearch(searchText); + }, + [toolbarProps, setSearch], + ); + + if (hasFilters) { + return ( +
+
+ + + + + Filters ({selectedFiltersLength}) + +
+ +
+ ); + } + + return ( + + ); +} + export function Table({ columns, options, @@ -219,10 +275,7 @@ export function Table({ onStateChange, ...props }: TableProps) { - const headerClasses = useHeaderStyles(); - const toolbarClasses = useToolbarStyles(); const tableClasses = useTableStyles(); - const filtersClasses = useFilterStyles(); const { data, ...propsWithoutData } = props; @@ -230,9 +283,12 @@ export function Table({ const calculatedInitialState = { ...defaultInitialState, ...initialState }; - const [filtersOpen, toggleFilters] = useState( + const [filtersOpen, setFiltersOpen] = useState( calculatedInitialState.filtersOpen, ); + const toggleFilters = useCallback(() => setFiltersOpen(v => !v), [ + setFiltersOpen, + ]); const [selectedFiltersLength, setSelectedFiltersLength] = useState(0); const [tableData, setTableData] = useState(data as any[]); const [selectedFilters, setSelectedFilters] = useState( @@ -379,64 +435,32 @@ export function Table({ })); }; + const hasFilters = !!filters?.length; const Toolbar = useCallback( toolbarProps => { - const onSearchChanged = (searchText: string) => { - toolbarProps.onSearchChanged(searchText); - setSearch(searchText); - }; - - if (filters?.length) { - return ( -
-
- toggleFilters(el => !el)} - aria-label="filter list" - > - - - - Filters ({selectedFiltersLength}) - -
- -
- ); - } - return ( - ); }, - [ - filters?.length, - selectedFiltersLength, - toggleFilters, - toolbarClasses, - filtersClasses, - setSearch, - toolbarRef, - ], + [toggleFilters, hasFilters, selectedFiltersLength, setSearch, toolbarRef], ); + const hasNoRows = typeof data !== 'function' && data.length === 0; + const columnCount = columns.length; const Body = useCallback( bodyProps => { - if (emptyContent && typeof data !== 'function' && data.length === 0) { + if (emptyContent && hasNoRows) { return ( - {emptyContent} + {emptyContent} ); @@ -444,7 +468,7 @@ export function Table({ return ; }, - [data, emptyContent, columns], + [hasNoRows, emptyContent, columnCount], ); return ( @@ -458,9 +482,7 @@ export function Table({ )} components={{ - Header: headerProps => ( - - ), + Header: StyledMTableHeader, Toolbar, Body, }} From 1582d4c7be4cdd55efe23de146d162904b6b06e6 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 6 May 2021 14:31:19 +0200 Subject: [PATCH 33/68] Correct backstage/core version Signed-off-by: Marcus Eide --- plugins/shortcuts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 0bc80fa180..c65d51143f 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.6", "@backstage/theme": "^0.2.6", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", From 81ef1d57bf5a23103fed4160c767e52f3e13c55d Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 6 May 2021 17:50:30 +0200 Subject: [PATCH 34/68] Show error on task page if task does not exist Signed-off-by: Oliver Sand --- .changeset/grumpy-goats-refuse.md | 5 +++++ plugins/scaffolder/package.json | 1 + plugins/scaffolder/src/api.ts | 11 +++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .changeset/grumpy-goats-refuse.md diff --git a/.changeset/grumpy-goats-refuse.md b/.changeset/grumpy-goats-refuse.md new file mode 100644 index 0000000000..8dc2e390fc --- /dev/null +++ b/.changeset/grumpy-goats-refuse.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Show error on task page if task does not exist. diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index f2f69c029b..9cf7c01d02 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -33,6 +33,7 @@ "@backstage/catalog-client": "^0.3.11", "@backstage/catalog-model": "^0.7.8", "@backstage/config": "^0.1.5", + "@backstage/errors": "^0.1.1", "@backstage/core": "^0.7.8", "@backstage/integration": "^0.5.2", "@backstage/integration-react": "^0.1.1", diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 0c5eb2b268..6add205bb6 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -22,6 +22,7 @@ import { IdentityApi, Observable, } from '@backstage/core'; +import { ResponseError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import ObservableImpl from 'zen-observable'; import { ListActionsResponse, ScaffolderTask, Status } from './types'; @@ -173,9 +174,15 @@ export class ScaffolderClient implements ScaffolderApi { const token = await this.identityApi.getIdToken(); const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder'); const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`; - return fetch(url, { + const response = await fetch(url, { headers: token ? { Authorization: `Bearer ${token}` } : {}, - }).then(x => x.json()); + }); + + if (!response.ok) { + throw ResponseError.fromResponse(response); + } + + return await response.json(); } streamLogs({ From 15e248a107d9c7de2f88a6b8363785c8f65707c5 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 6 May 2021 18:22:26 +0200 Subject: [PATCH 35/68] Migrate all scaffolder API calls to `ResponseError` Signed-off-by: Oliver Sand --- plugins/scaffolder/src/api.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 6add205bb6..a603df5fc6 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -129,9 +129,7 @@ export class ScaffolderClient implements ScaffolderApi { }); if (!response.ok) { - throw new Error( - `Failed to fetch template parameter schema, ${await response.text()}`, - ); + throw ResponseError.fromResponse(response); } const schema: TemplateParameterSchema = await response.json(); @@ -161,9 +159,7 @@ export class ScaffolderClient implements ScaffolderApi { }); if (response.status !== 201) { - const status = `${response.status} ${response.statusText}`; - const body = await response.text(); - throw new Error(`Backend request failed, ${status} ${body.trim()}`); + throw ResponseError.fromResponse(response); } const { id } = (await response.json()) as { id: string }; @@ -241,6 +237,11 @@ export class ScaffolderClient implements ScaffolderApi { async listActions(): Promise { const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder'); const response = await fetch(`${baseUrl}/v2/actions`); + + if (!response.ok) { + throw ResponseError.fromResponse(response); + } + return await response.json(); } } From d6ee1830543304d339583ce73f895d75f3ce8e11 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 6 May 2021 18:28:41 +0200 Subject: [PATCH 36/68] Restore for `scaffold()` Signed-off-by: Oliver Sand --- plugins/scaffolder/src/api.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index a603df5fc6..c7b1594071 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -159,7 +159,9 @@ export class ScaffolderClient implements ScaffolderApi { }); if (response.status !== 201) { - throw ResponseError.fromResponse(response); + const status = `${response.status} ${response.statusText}`; + const body = await response.text(); + throw new Error(`Backend request failed, ${status} ${body.trim()}`); } const { id } = (await response.json()) as { id: string }; From dd3b98138fe637d5b42d0a00c8f0e1ec6cfb5330 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 May 2021 18:50:26 +0000 Subject: [PATCH 37/68] chore(deps): bump property-expr from 2.0.2 to 2.0.4 Bumps [property-expr](https://github.com/jquense/expr) from 2.0.2 to 2.0.4. - [Release notes](https://github.com/jquense/expr/releases) - [Changelog](https://github.com/jquense/expr/blob/master/CHANGELOG.md) - [Commits](https://github.com/jquense/expr/compare/v2.0.2...v2.0.4) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a471cbb1e2..8cdd10db6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21585,9 +21585,9 @@ prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, react-is "^16.8.1" property-expr@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.2.tgz#fff2a43919135553a3bc2fdd94bdb841965b2330" - integrity sha512-bc/5ggaYZxNkFKj374aLbEDqVADdYaLcFo8XBkishUWbaAdjlphaBFns9TvRA2pUseVL/wMFmui9X3IdNDU37g== + version "2.0.4" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" + integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== property-information@^5.0.0: version "5.4.0" From e9458540de82885d0a2e0e5bb5983dc100b8c6a0 Mon Sep 17 00:00:00 2001 From: Bret Hubbard Date: Thu, 6 May 2021 16:07:27 -0600 Subject: [PATCH 38/68] Improve error messages when schema validation fails Signed-off-by: Bret Hubbard --- .../src/kinds/ApiEntityV1alpha1.test.ts | 12 ++++++++++++ packages/catalog-model/src/kinds/util.ts | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts index a4d7d904cd..249243bed3 100644 --- a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts @@ -168,4 +168,16 @@ components: (entity as any).spec.system = ''; await expect(validator.check(entity)).rejects.toThrow(/system/); }); + + it('rejects additional properties', async () => { + (entity as any).annotations = 'Test'; + await expect(validator.check(entity)).rejects.toThrow( + /additional properties/, + ); + }); + + it('rejects with useful error message', async () => { + (entity as any).annotations = 'Test'; + await expect(validator.check(entity)).rejects.toThrow(/annotations/); + }); }); diff --git a/packages/catalog-model/src/kinds/util.ts b/packages/catalog-model/src/kinds/util.ts index 4002f2806c..1e9e7b7521 100644 --- a/packages/catalog-model/src/kinds/util.ts +++ b/packages/catalog-model/src/kinds/util.ts @@ -66,7 +66,13 @@ export function ajvCompiledJsonSchemaValidator( } throw new TypeError( - `Malformed ${kind}, ${error.dataPath || ''} ${error.message}`, + `Malformed ${kind}, ${error.dataPath || ''} ${error.message}${ + error.params + ? ` - ${Object.entries(error.params) + .map(([key, val]) => `${key}: ${val}`) + .join(', ')}` + : '' + }`, ); }, }; From 10c008a3a68c810d382cff06349d6d753a44cc04 Mon Sep 17 00:00:00 2001 From: Julian Pearce Date: Fri, 7 May 2021 03:24:00 +0100 Subject: [PATCH 39/68] resolves #5586 Signed-off-by: Julian Pearce --- .changeset/cyan-beans-marry.md | 5 +++++ .../catalog-model/src/kinds/TemplateEntityV1beta2.test.ts | 2 +- packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts | 2 +- .../src/schema/kinds/Template.v1beta2.schema.json | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 .changeset/cyan-beans-marry.md diff --git a/.changeset/cyan-beans-marry.md b/.changeset/cyan-beans-marry.md new file mode 100644 index 0000000000..99d7ad47ce --- /dev/null +++ b/.changeset/cyan-beans-marry.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': patch +--- + +Renamed parameters to input in template schema diff --git a/packages/catalog-model/src/kinds/TemplateEntityV1beta2.test.ts b/packages/catalog-model/src/kinds/TemplateEntityV1beta2.test.ts index 60f3de9e59..958a486687 100644 --- a/packages/catalog-model/src/kinds/TemplateEntityV1beta2.test.ts +++ b/packages/catalog-model/src/kinds/TemplateEntityV1beta2.test.ts @@ -51,7 +51,7 @@ describe('templateEntityV1beta2Validator', () => { id: 'fetch', name: 'Fetch', action: 'fetch:plan', - parameters: { + input: { url: './template', }, }, diff --git a/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts b/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts index c78e7f85d5..1f4e3bcd90 100644 --- a/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts +++ b/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts @@ -38,7 +38,7 @@ export interface TemplateEntityV1beta2 extends Entity { id?: string; name?: string; action: string; - parameters?: JsonObject; + input?: JsonObject; }>; output?: { [name: string]: string }; }; diff --git a/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json b/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json index 1e0b7d0ded..b88b344626 100644 --- a/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json +++ b/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json @@ -125,7 +125,7 @@ "type": "string", "description": "The name of the action to execute." }, - "parameters": { + "input": { "type": "object", "description": "A templated object describing the inputs to the action." } From e38d2c73e1a07bcd4a9abc3e57c0362aa60dd8c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 May 2021 04:12:08 +0000 Subject: [PATCH 40/68] chore(deps-dev): bump @graphql-codegen/typescript-resolvers Bumps [@graphql-codegen/typescript-resolvers](https://github.com/dotansimha/graphql-code-generator/tree/HEAD/packages/plugins/typescript/resolvers) from 1.18.2 to 1.19.1. - [Release notes](https://github.com/dotansimha/graphql-code-generator/releases) - [Changelog](https://github.com/dotansimha/graphql-code-generator/blob/master/packages/plugins/typescript/resolvers/CHANGELOG.md) - [Commits](https://github.com/dotansimha/graphql-code-generator/commits/@graphql-codegen/typescript-resolvers@1.19.1/packages/plugins/typescript/resolvers) Signed-off-by: dependabot[bot] --- yarn.lock | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8cdd10db6e..a435ee5769 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2351,18 +2351,18 @@ tslib "~2.2.0" "@graphql-codegen/typescript-resolvers@^1.17.7": - version "1.18.2" - resolved "https://registry.npmjs.org/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-1.18.2.tgz#7513b92df7c5a0d3c27342c591ada7340696cf8f" - integrity sha512-aWfRR5y1gXCPUNK7zaUiSbmceqidvZ38mNHIBvXmZArRigyz1QZgN26kKpXjJjtFbPvROPnlGsYcZBReybvZXA== + version "1.19.1" + resolved "https://registry.npmjs.org/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-1.19.1.tgz#56677ec56c1ca7174d22a2f236e3fb7f6503e708" + integrity sha512-KdCVfg2u2RMbHu7eV9SOh5rmfnEQaMsQ0k8741bMbBmCESLnrWltujF2RT1OPN7WCn7xJejBtrFg/3UgT0fNug== dependencies: - "@graphql-codegen/plugin-helpers" "^1.18.2" - "@graphql-codegen/typescript" "^1.21.0" - "@graphql-codegen/visitor-plugin-common" "^1.18.3" + "@graphql-codegen/plugin-helpers" "^1.18.5" + "@graphql-codegen/typescript" "^1.22.0" + "@graphql-codegen/visitor-plugin-common" "^1.20.0" "@graphql-tools/utils" "^7.0.0" auto-bind "~4.0.0" - tslib "~2.1.0" + tslib "~2.2.0" -"@graphql-codegen/typescript@^1.17.7", "@graphql-codegen/typescript@^1.21.0": +"@graphql-codegen/typescript@^1.17.7", "@graphql-codegen/typescript@^1.22.0": version "1.22.0" resolved "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-1.22.0.tgz#d05be3a971e5d75a076a43e123b6330f4366a6ab" integrity sha512-YzN/3MBYHrP110m8JgUWQIHt7Ivi3JXiq0RT5XNx/F9mVOSbZz6Ezbaji8YJA3y04Gl2f6ZgtdGazWANUvcOcg== @@ -2372,7 +2372,7 @@ auto-bind "~4.0.0" tslib "~2.2.0" -"@graphql-codegen/visitor-plugin-common@^1.18.3", "@graphql-codegen/visitor-plugin-common@^1.20.0": +"@graphql-codegen/visitor-plugin-common@^1.20.0": version "1.20.0" resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.20.0.tgz#38d829eab7370c79aa5229190788f94adcae8f76" integrity sha512-AYrpy8NA3DpvhDLqYGerQRv44S+YAMPKtwT8x9GNVjzP0gVfmqi3gG1bDWbP5sm6kOZKvDC0kTxGePuBSZerxw== @@ -2664,16 +2664,7 @@ camel-case "4.1.1" tslib "~2.0.1" -"@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.1.0": - version "7.2.6" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.2.6.tgz#5d974cbdec5ddf4d7fdc593816335512ee5fe4de" - integrity sha512-/kY7Nb+cCHi/MvU3tjz3KrXzuJNWMlnn7EoWazLmpDvl6b2Qt69hlVoPd5zQtKlGib35zZw9NZ5zs5qTAw8Y9g== - dependencies: - "@ardatan/aggregate-error" "0.0.6" - camel-case "4.1.2" - tslib "~2.1.0" - -"@graphql-tools/utils@^7.1.2", "@graphql-tools/utils@^7.7.0": +"@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.1.0", "@graphql-tools/utils@^7.1.2", "@graphql-tools/utils@^7.7.0": version "7.7.1" resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.7.1.tgz#81f32cb4819b34b3a378d51ab2cd60935977f0b4" integrity sha512-SFT4/dTfrwWer1wSOLU+jqgv3oa/xTR8q+MiNbE9nCH2FXyMsqIOaXKm9wHfKIWFWHozqBdcnwFkQZrdD7H2TQ== From 3be844496c9c267cd57d8c26687a0c72dcfac6a1 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 7 May 2021 10:09:22 +0200 Subject: [PATCH 41/68] chore(deps): add changeset for ts-node bump Signed-off-by: blam --- .changeset/calm-insects-melt.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/calm-insects-melt.md diff --git a/.changeset/calm-insects-melt.md b/.changeset/calm-insects-melt.md new file mode 100644 index 0000000000..4b17764c33 --- /dev/null +++ b/.changeset/calm-insects-melt.md @@ -0,0 +1,7 @@ +--- +'@backstage/cli': patch +'@backstage/create-app': patch +'@backstage/plugin-catalog-graphql': patch +--- + +chore: bump `ts-node` versions to 9.1.1 From bcf4c2f91d6c65505d961f4830c3952a42d74929 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 7 May 2021 11:20:29 +0200 Subject: [PATCH 42/68] More package versions Signed-off-by: Marcus Eide --- plugins/shortcuts/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index c65d51143f..7c60f54485 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.6", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,7 +35,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", From 0b51bcb0084ff6566e96ceb96ad7562ce1a90bff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 May 2021 09:20:46 +0000 Subject: [PATCH 43/68] chore(deps): bump react-use from 15.3.8 to 17.2.4 Bumps [react-use](https://github.com/streamich/react-use) from 15.3.8 to 17.2.4. - [Release notes](https://github.com/streamich/react-use/releases) - [Changelog](https://github.com/streamich/react-use/blob/master/CHANGELOG.md) - [Commits](https://github.com/streamich/react-use/compare/v15.3.8...v17.2.4) Signed-off-by: dependabot[bot] --- packages/app/package.json | 2 +- packages/core-api/package.json | 2 +- packages/core/package.json | 2 +- packages/integration-react/package.json | 2 +- plugins/api-docs/package.json | 2 +- plugins/badges/package.json | 2 +- plugins/bitrise/package.json | 2 +- plugins/catalog-import/package.json | 2 +- plugins/catalog-react/package.json | 2 +- plugins/catalog/package.json | 2 +- plugins/circleci/package.json | 2 +- plugins/cloudbuild/package.json | 2 +- plugins/code-coverage/package.json | 2 +- plugins/config-schema/package.json | 2 +- plugins/cost-insights/package.json | 2 +- plugins/explore/package.json | 2 +- plugins/fossa/package.json | 2 +- plugins/gcp-projects/package.json | 2 +- plugins/github-actions/package.json | 2 +- plugins/github-deployments/package.json | 2 +- plugins/gitops-profiles/package.json | 2 +- plugins/graphiql/package.json | 2 +- plugins/jenkins/package.json | 2 +- plugins/kafka/package.json | 2 +- plugins/kubernetes/package.json | 2 +- plugins/lighthouse/package.json | 2 +- plugins/newrelic/package.json | 2 +- plugins/org/package.json | 2 +- plugins/pagerduty/package.json | 2 +- plugins/register-component/package.json | 2 +- plugins/rollbar/package.json | 2 +- plugins/scaffolder/package.json | 2 +- plugins/search/package.json | 2 +- plugins/sentry/package.json | 2 +- plugins/sonarqube/package.json | 2 +- plugins/splunk-on-call/package.json | 2 +- plugins/tech-radar/package.json | 2 +- plugins/techdocs/package.json | 2 +- plugins/todo/package.json | 2 +- plugins/user-settings/package.json | 2 +- plugins/welcome/package.json | 2 +- yarn.lock | 75 ++++--------------------- 42 files changed, 51 insertions(+), 106 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 8ddf249366..e05e520ab5 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -51,7 +51,7 @@ "react-hot-loader": "^4.12.21", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "zen-observable": "^0.8.15" }, "devDependencies": { diff --git a/packages/core-api/package.json b/packages/core-api/package.json index a55df67343..13d7efe77b 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -38,7 +38,7 @@ "prop-types": "^15.7.2", "react": "^16.12.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "zen-observable": "^0.8.15" }, "devDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 4da981d064..f8425c3289 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -65,7 +65,7 @@ "react-sparklines": "^1.7.0", "react-syntax-highlighter": "^15.4.3", "react-text-truncate": "^0.16.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "remark-gfm": "^1.0.0", "zen-observable": "^0.8.15" }, diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index 9f33a25ac8..7833952e8e 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -29,7 +29,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.3", diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index e8e8bb8774..9cccb07d56 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -45,7 +45,7 @@ "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "swagger-ui-react": "^3.37.2" }, "devDependencies": { diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 7078b9627e..24660899e2 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -31,7 +31,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index cd0564dc41..45b32a11d1 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -33,7 +33,7 @@ "qs": "^6.9.6", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "recharts": "^1.8.5" }, "devDependencies": { diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 2277604dc2..c5fcdd4d37 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -49,7 +49,7 @@ "react-hook-form": "^6.15.4", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "yaml": "^1.10.0" }, "devDependencies": { diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 1d886478b8..a9092ef75a 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -37,7 +37,7 @@ "react": "^16.13.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 8277c2dcbc..f1a0e6979d 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -49,7 +49,7 @@ "react-helmet": "6.1.0", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index b87fb4bbbf..98af012a49 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -47,7 +47,7 @@ "react-lazylog": "^4.5.2", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 98735f1c42..325dfb4e06 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -44,7 +44,7 @@ "react-lazylog": "^4.5.3", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 241b61b296..9b227c1df1 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -35,7 +35,7 @@ "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "recharts": "^1.8.5" }, "devDependencies": { diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 7877668065..caa03b9ffb 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -31,7 +31,7 @@ "zen-observable": "^0.8.15", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 5d780080d1..3a5601041c 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -49,7 +49,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "recharts": "^1.8.5", "regression": "^2.0.1", "yup": "^0.29.3" diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 35a4a97349..2a25a9db41 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -42,7 +42,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index ee35b94c0b..95762e52d7 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -44,7 +44,7 @@ "p-limit": "^3.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 96102332d9..fd6048ab2a 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -38,7 +38,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "^6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 61de1a1f7d..95608c3c49 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -47,7 +47,7 @@ "react-lazylog": "^4.5.3", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index e67df926ea..84a0fe1660 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -31,7 +31,7 @@ "luxon": "^1.26.0", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 866ee571c0..c00dbde482 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -39,7 +39,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index d03a5f39e9..ae3c0dfe8f 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -40,7 +40,7 @@ "graphql": "15.5.0", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 45bd3c4bf8..c4ba600d7e 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -44,7 +44,7 @@ "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index ce3e7561b1..e405460262 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -30,7 +30,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 8028c3a339..19865a6fcc 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -46,7 +46,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index a6ae0102bf..f70e29f69b 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -43,7 +43,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index dfe5217f9d..20ee5521f4 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -38,7 +38,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/org/package.json b/plugins/org/package.json index 2b802510f4..012514f1c4 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -32,7 +32,7 @@ "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 0005b9aa3b..53f0ddbb23 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -43,7 +43,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 5052fd3342..3efb61b982 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -42,7 +42,7 @@ "react-hook-form": "^6.15.4", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 5563ad0e40..8b559e0bf1 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -44,7 +44,7 @@ "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-sparklines": "^1.7.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 9cf7c01d02..1992028157 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -55,7 +55,7 @@ "react-lazylog": "^4.5.2", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "use-immer": "^0.5.1", "zen-observable": "^0.8.15" }, diff --git a/plugins/search/package.json b/plugins/search/package.json index b135d184f6..f503b9a78e 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -41,7 +41,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index a5b1ac7d68..6784debcf9 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -42,7 +42,7 @@ "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", "react-sparklines": "^1.7.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "timeago.js": "^4.0.2" }, "devDependencies": { diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 648f945e03..df5304d746 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -44,7 +44,7 @@ "rc-progress": "^3.0.0", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 63fb68c0b2..020fbc37cd 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -42,7 +42,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 2a2619bbc4..5e33024f87 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -40,7 +40,7 @@ "prop-types": "^15.7.2", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 380eae2737..20fca364da 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -47,7 +47,7 @@ "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "sanitize-html": "^2.3.2" }, "devDependencies": { diff --git a/plugins/todo/package.json b/plugins/todo/package.json index aac15e9ad6..b7564f7080 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -36,7 +36,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 3349319442..32e232cad4 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -38,7 +38,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 90cdb3dc0f..72ec24bf71 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -38,7 +38,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.6.10", diff --git a/yarn.lock b/yarn.lock index a435ee5769..2cf1551fed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8874,11 +8874,6 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -bowser@^1.7.3: - version "1.9.4" - resolved "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" - integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== - boxen@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -10811,14 +10806,6 @@ css-tree@1.0.0-alpha.37: mdn-data "2.0.4" source-map "^0.6.1" -css-tree@^1.0.0-alpha.28: - version "1.0.0-alpha.39" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" - integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== - dependencies: - mdn-data "2.0.6" - source-map "^0.6.1" - css-tree@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz#9ae393b5dafd7dae8a622475caec78d3d8fbd7b5" @@ -10973,7 +10960,7 @@ cssstyle@^2.2.0: dependencies: cssom "~0.3.6" -csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.5, csstype@^2.5.7, csstype@^2.6.7: +csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.7, csstype@^2.6.7: version "2.6.9" resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== @@ -13195,11 +13182,6 @@ fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: resolved "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== -fastest-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028" - integrity sha1-kSLUBtTJ2YvqZEpraFPVh0uHsCg= - fastest-stable-stringify@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" @@ -15427,14 +15409,6 @@ init-package-json@^2.0.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^3.0.0" -inline-style-prefixer@^4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-4.0.2.tgz#d390957d26f281255fe101da863158ac6eb60911" - integrity sha512-N8nVhwfYga9MiV9jWlwfdj1UDIaZlBFu4cJSJkIr7tZX7sHpHhGR5su1qdpW+7KPL8ISTvCIkcaFi/JdBknvPg== - dependencies: - bowser "^1.7.3" - css-in-js-utils "^2.0.0" - inline-style-prefixer@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-6.0.0.tgz#f73d5dbf2855733d6b153a4d24b7b47a73e9770b" @@ -18352,11 +18326,6 @@ mdn-data@2.0.4: resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== -mdn-data@2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" - integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== - mdurl@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -19064,21 +19033,7 @@ nan@2.14.1, nan@^2.12.1, nan@^2.14.0: resolved "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== -nano-css@^5.1.0, nano-css@^5.2.1: - version "5.3.0" - resolved "https://registry.npmjs.org/nano-css/-/nano-css-5.3.0.tgz#9d3cd29788d48b6a07f52aa4aec7cf4da427b6b5" - integrity sha512-uM/9NGK9/E9/sTpbIZ/bQ9xOLOIHZwrrb/CRlbDHBU/GFS7Gshl24v/WJhwsVViWkpOXUmiZ66XO7fSB4Wd92Q== - dependencies: - css-tree "^1.0.0-alpha.28" - csstype "^2.5.5" - fastest-stable-stringify "^1.0.1" - inline-style-prefixer "^4.0.0" - rtl-css-js "^1.9.0" - sourcemap-codec "^1.4.1" - stacktrace-js "^2.0.0" - stylis "3.5.0" - -nano-css@^5.3.1: +nano-css@^5.1.0, nano-css@^5.2.1, nano-css@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/nano-css/-/nano-css-5.3.1.tgz#b709383e07ad3be61f64edffacb9d98250b87a1f" integrity sha512-ENPIyNzANQRyYVvb62ajDd7PAyIgS2LIUnT9ewih4yrXSZX4hKoUwssy8WjUH++kEOA5wUTMgNnV7ko5n34kUA== @@ -22426,10 +22381,10 @@ react-use@^15.3.3, react-use@^15.3.6: ts-easing "^0.2.0" tslib "^2.0.0" -react-use@^17.2.1: - version "17.2.1" - resolved "https://registry.npmjs.org/react-use/-/react-use-17.2.1.tgz#c81e12544115ed049c7deba1e3bb3d977dfee9b8" - integrity sha512-9r51/at7/Nr/nEP4CsHz+pl800EAqhIY9R6O68m68kaWc8slDAfx1UrIedQqpsb4ImddFYb+6hF1i5Vj4u4Cnw== +react-use@^17.2.1, react-use@^17.2.4: + version "17.2.4" + resolved "https://registry.npmjs.org/react-use/-/react-use-17.2.4.tgz#1f89be3db0a8237c79253db0a15e12bbe3cfeff1" + integrity sha512-vQGpsAM0F5UIlshw5UI8ULGPS4yn5rm7/qvn3T1Gnkrz7YRMEEMh+ynKcmRloOyiIeLvKWiQjMiwRGtdbgs5qQ== dependencies: "@types/js-cookie" "^2.2.6" "@xobotyi/scrollbar-width" "^1.9.5" @@ -23337,7 +23292,7 @@ rsvp@^4.8.4: resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -rtl-css-js@^1.14.0, rtl-css-js@^1.9.0: +rtl-css-js@^1.14.0: version "1.14.0" resolved "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.14.0.tgz#daa4f192a92509e292a0519f4b255e6e3c076b7d" integrity sha512-Dl5xDTeN3e7scU1cWX8c9b6/Nqz3u/HgR4gePc1kWXYiQWVQbKCEyK6+Hxve9LbcJ5EieHy1J9nJCN3grTtGwg== @@ -23494,12 +23449,7 @@ schema-utils@^3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -screenfull@^5.0.0: - version "5.0.2" - resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.0.2.tgz#b9acdcf1ec676a948674df5cd0ff66b902b0bed7" - integrity sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ== - -screenfull@^5.1.0: +screenfull@^5.0.0, screenfull@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.1.0.tgz#85c13c70f4ead4c1b8a935c70010dfdcd2c0e5c8" integrity sha512-dYaNuOdzr+kc6J6CFcBrzkLCfyGcMg+gWkJ8us93IQ7y1cevhQAugFsaCdMHb6lw8KV3xPzSxzH7zM1dQap9mA== @@ -24069,7 +24019,7 @@ source-map@^0.7.3: resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== -sourcemap-codec@^1.4.1, sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8: +sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== @@ -24302,7 +24252,7 @@ stacktrace-gps@^3.0.4: source-map "0.5.6" stackframe "^1.1.1" -stacktrace-js@^2.0.0, stacktrace-js@^2.0.2: +stacktrace-js@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== @@ -24722,11 +24672,6 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -stylis@3.5.0: - version "3.5.0" - resolved "https://registry.npmjs.org/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" - integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw== - stylis@^4.0.6: version "4.0.7" resolved "https://registry.npmjs.org/stylis/-/stylis-4.0.7.tgz#412a90c28079417f3d27c028035095e4232d2904" From 4d9a337a4a4d7a35f7673f17afd8174abc291241 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 7 May 2021 11:54:12 +0200 Subject: [PATCH 44/68] Restore `debounceTime` and mark it as deprecated Signed-off-by: Oliver Sand --- packages/core/src/hooks/useQueryParamState.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/hooks/useQueryParamState.ts b/packages/core/src/hooks/useQueryParamState.ts index 023d69bf4f..944e73bf39 100644 --- a/packages/core/src/hooks/useQueryParamState.ts +++ b/packages/core/src/hooks/useQueryParamState.ts @@ -58,6 +58,8 @@ type SetQueryParams = (params: T) => void; export function useQueryParamState( stateName: string, + /** @depracated Don't configure a custom debouceTime */ + debounceTime: number = 250, ): [T | undefined, SetQueryParams] { const [searchParams, setSearchParams] = useSearchParams(); const searchParamsString = searchParams.toString(); @@ -85,7 +87,7 @@ export function useQueryParamState( setSearchParams(queryString, { replace: true }); } }, - 100, + debounceTime, [setSearchParams, queryParamState, searchParamsString, stateName], ); From add75e7a5b074b1af1063bdacf369d64ee4e697f Mon Sep 17 00:00:00 2001 From: Julian Pearce Date: Fri, 7 May 2021 11:42:03 +0100 Subject: [PATCH 45/68] generate API report Signed-off-by: Julian Pearce --- packages/catalog-model/api-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index c37c86b01f..2b56879f48 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -514,7 +514,7 @@ export interface TemplateEntityV1beta2 extends Entity { id?: string; name?: string; action: string; - parameters?: JsonObject; + input?: JsonObject; }>; output?: { [name: string]: string; From 9eabe20696e1c824616501df35fc7c5a4b63280b Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 7 May 2021 13:27:05 +0200 Subject: [PATCH 46/68] chore: new plugins should use later versions of react-use Signed-off-by: blam --- packages/cli/templates/default-plugin/package.json.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 49eda9e0eb..1b4f8ea6ab 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -31,7 +31,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^{{version '@backstage/cli'}}", From 675a569a99fffcf313e3d08148a754ec51027276 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 7 May 2021 13:27:29 +0200 Subject: [PATCH 47/68] chore(deps): added changeset for dep bump Signed-off-by: blam --- .changeset/ten-paws-ring.md | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .changeset/ten-paws-ring.md diff --git a/.changeset/ten-paws-ring.md b/.changeset/ten-paws-ring.md new file mode 100644 index 0000000000..8ed2158f6c --- /dev/null +++ b/.changeset/ten-paws-ring.md @@ -0,0 +1,44 @@ +--- +'@backstage/core': patch +'@backstage/core-api': patch +'@backstage/integration-react': patch +'@backstage/plugin-api-docs': patch +'@backstage/plugin-badges': patch +'@backstage/plugin-bitrise': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-circleci': patch +'@backstage/plugin-cloudbuild': patch +'@backstage/plugin-code-coverage': patch +'@backstage/plugin-config-schema': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-explore': patch +'@backstage/plugin-fossa': patch +'@backstage/plugin-gcp-projects': patch +'@backstage/plugin-github-actions': patch +'@backstage/plugin-github-deployments': patch +'@backstage/plugin-gitops-profiles': patch +'@backstage/plugin-graphiql': patch +'@backstage/plugin-jenkins': patch +'@backstage/plugin-kafka': patch +'@backstage/plugin-kubernetes': patch +'@backstage/plugin-lighthouse': patch +'@backstage/plugin-newrelic': patch +'@backstage/plugin-org': patch +'@backstage/plugin-pagerduty': patch +'@backstage/plugin-register-component': patch +'@backstage/plugin-rollbar': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-search': patch +'@backstage/plugin-sentry': patch +'@backstage/plugin-sonarqube': patch +'@backstage/plugin-splunk-on-call': patch +'@backstage/plugin-tech-radar': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-todo': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-welcome': patch +--- + +chore: bump `react-use` dependency in all packages From e3fc89df6edc43c8b2f7094a6fb6846c24ba65fc Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 7 May 2021 13:28:41 +0200 Subject: [PATCH 48/68] chore: added changeset for cli changes Signed-off-by: blam --- .changeset/ninety-pots-guess.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ninety-pots-guess.md diff --git a/.changeset/ninety-pots-guess.md b/.changeset/ninety-pots-guess.md new file mode 100644 index 0000000000..4b9a770c29 --- /dev/null +++ b/.changeset/ninety-pots-guess.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +update plugins created to use react-use 17.2.4 From 3e3d5572fe3062c9592670321d1d79deffd989a0 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 7 May 2021 13:36:59 +0200 Subject: [PATCH 49/68] catalog/next: Fix postgres type error when deleting entries Signed-off-by: Johan Haals --- .../src/next/database/DefaultProcessingDatabase.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts b/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts index dc5e6036f5..140c93929d 100644 --- a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts +++ b/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts @@ -185,7 +185,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase { ), -- All the nodes that can be reached upwards from the descendants ancestors(root_id, via_entity_ref, to_entity_ref) AS ( - SELECT NULL, entity_ref, entity_ref + SELECT CAST(NULL as INT), entity_ref, entity_ref FROM descendants UNION SELECT @@ -235,7 +235,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase { .withRecursive('ancestors', function ancestors(outer) { return outer .select({ - root_id: tx.raw('NULL', []), + root_id: tx.raw('CAST(NULL as INT)', []), via_entity_ref: 'entity_ref', to_entity_ref: 'entity_ref', }) From 6f57e2df67494f596dcdaed3f401a2e5487ae4c0 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 7 May 2021 15:09:12 +0200 Subject: [PATCH 50/68] Fix typo Signed-off-by: Oliver Sand --- packages/core/src/hooks/useQueryParamState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/hooks/useQueryParamState.ts b/packages/core/src/hooks/useQueryParamState.ts index 944e73bf39..173993a466 100644 --- a/packages/core/src/hooks/useQueryParamState.ts +++ b/packages/core/src/hooks/useQueryParamState.ts @@ -58,7 +58,7 @@ type SetQueryParams = (params: T) => void; export function useQueryParamState( stateName: string, - /** @depracated Don't configure a custom debouceTime */ + /** @deprecated Don't configure a custom debouceTime */ debounceTime: number = 250, ): [T | undefined, SetQueryParams] { const [searchParams, setSearchParams] = useSearchParams(); From 1abb1d1e7baf0fbb3cb2c09d8b8a1655037ed4d8 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 7 May 2021 15:24:13 +0200 Subject: [PATCH 51/68] Add support for skipTLSVerify when using the GkeClusterLocator Signed-off-by: Marcus Eide --- .../src/cluster-locator/GkeClusterLocator.ts | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts index 7bfd62ef8d..b2bdc3bca8 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts @@ -18,28 +18,28 @@ import { Config } from '@backstage/config'; import * as container from '@google-cloud/container'; import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; -export class GkeClusterLocator implements KubernetesClustersSupplier { - private readonly projectId: string; - private readonly region: string | undefined; - private readonly client: container.v1.ClusterManagerClient; +type GkeClusterLocatorOptions = { + projectId: string; + region?: string; + skipTLSVerify?: boolean; +}; +export class GkeClusterLocator implements KubernetesClustersSupplier { constructor( - projectId: string, - client: container.v1.ClusterManagerClient, - region?: string, - ) { - this.projectId = projectId; - this.region = region; - this.client = client; - } + private readonly options: GkeClusterLocatorOptions, + private readonly client: container.v1.ClusterManagerClient, + ) {} static fromConfigWithClient( config: Config, client: container.v1.ClusterManagerClient, ): GkeClusterLocator { - const projectId = config.getString('projectId'); - const region = config.getOptionalString('region'); - return new GkeClusterLocator(projectId, client, region); + const options = { + projectId: config.getString('projectId'), + region: config.getOptionalString('region') ?? '-', + skipTLSVerify: config.getOptionalBoolean('skipTLSVerify') ?? false, + }; + return new GkeClusterLocator(options, client); } static fromConfig(config: Config): GkeClusterLocator { @@ -50,9 +50,9 @@ export class GkeClusterLocator implements KubernetesClustersSupplier { } async getClusters(): Promise { - const region = this.region ?? '-'; + const { projectId, region, skipTLSVerify } = this.options; const request = { - parent: `projects/${this.projectId}/locations/${region}`, + parent: `projects/${projectId}/locations/${region}`, }; try { @@ -62,10 +62,11 @@ export class GkeClusterLocator implements KubernetesClustersSupplier { name: r.name ?? 'unknown', url: `https://${r.endpoint ?? ''}`, authProvider: 'google', + skipTLSVerify, })); } catch (e) { throw new Error( - `There was an error retrieving clusters from GKE for projectId=${this.projectId} region=${region} : [${e.message}]`, + `There was an error retrieving clusters from GKE for projectId=${projectId} region=${region} : [${e.message}]`, ); } } From 2723253d7e0e35cfe01ebc648f1bf84edc239b26 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 7 May 2021 15:24:52 +0200 Subject: [PATCH 52/68] Update documentation Signed-off-by: Marcus Eide --- docs/features/kubernetes/configuration.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 5dbad9cf4a..45ef28ea2f 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -33,6 +33,7 @@ kubernetes: - type: 'gke' projectId: 'gke-clusters' region: 'europe-west1' + skipTLSVerify: true ``` ### `serviceLocatorMethod` @@ -129,6 +130,11 @@ The Google Cloud project to look for Kubernetes clusters in. The Google Cloud region to look for Kubernetes clusters in. Defaults to all regions. +##### `skipTLSVerify` + +This determines whether or not the Kubernetes client verifies the TLS +certificate presented by the API server. Defaults to `false`. + ### `customResources` (optional) Configures which [custom resources][3] to look for when returning an entity's From 7ee8ad08fd2e503b5444083505bf1eb0cb9fe329 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 7 May 2021 15:25:25 +0200 Subject: [PATCH 53/68] Fix tests Signed-off-by: Marcus Eide --- .../src/cluster-locator/GkeClusterLocator.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts index cf555bd8c9..f45368479c 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts @@ -106,6 +106,7 @@ describe('GkeClusterLocator', () => { authProvider: 'google', name: 'some-cluster', url: 'https://1.2.3.4', + skipTLSVerify: false, }, ]); expect(mockedListClusters).toBeCalledTimes(1); @@ -141,6 +142,7 @@ describe('GkeClusterLocator', () => { authProvider: 'google', name: 'some-cluster', url: 'https://1.2.3.4', + skipTLSVerify: false, }, ]); expect(mockedListClusters).toBeCalledTimes(1); @@ -181,11 +183,13 @@ describe('GkeClusterLocator', () => { authProvider: 'google', name: 'some-cluster', url: 'https://1.2.3.4', + skipTLSVerify: false, }, { authProvider: 'google', name: 'some-other-cluster', url: 'https://6.7.8.9', + skipTLSVerify: false, }, ]); expect(mockedListClusters).toBeCalledTimes(1); From bc551b5f465585558042d40b9c6db6fb60cb2cb9 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 7 May 2021 15:39:39 +0200 Subject: [PATCH 54/68] Use config schema inline instead Signed-off-by: Marcus Eide --- plugins/kubernetes-backend/schema.d.ts | 37 ++++++++++++++-- plugins/kubernetes-backend/src/types/types.ts | 44 ------------------- 2 files changed, 33 insertions(+), 48 deletions(-) diff --git a/plugins/kubernetes-backend/schema.d.ts b/plugins/kubernetes-backend/schema.d.ts index fa5bc52abf..71dbace6c7 100644 --- a/plugins/kubernetes-backend/schema.d.ts +++ b/plugins/kubernetes-backend/schema.d.ts @@ -14,14 +14,43 @@ * limitations under the License. */ -import { ClusterLocatorMethod, CustomResource } from './src/types'; - export interface Config { kubernetes?: { serviceLocatorMethod: { type: 'multiTenant'; }; - clusterLocatorMethods: ClusterLocatorMethod[]; - customResources?: CustomResource[]; + clusterLocatorMethods: Array< + | { + /** @visibility frontend */ + type: 'gke'; + /** @visibility frontend */ + projectId: string; + /** @visibility frontend */ + region?: string; + /** @visibility frontend */ + skipTLSVerify?: boolean; + } + | { + /** @visibility frontend */ + type: 'config'; + clusters: Array<{ + /** @visibility frontend */ + url: string; + /** @visibility frontend */ + name: string; + /** @visibility secret */ + serviceAccountToken?: string; + /** @visibility frontend */ + authProvider: 'aws' | 'google' | 'serviceAccount'; + /** @visibility frontend */ + skipTLSVerify?: boolean; + }>; + } + >; + customResources?: Array<{ + group: string; + apiVersion: string; + plural: string; + }>; }; } diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 0be0b447af..ae254dfdae 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -19,50 +19,6 @@ import type { KubernetesFetchError, } from '@backstage/plugin-kubernetes-common'; -export type ClusterLocatorMethod = - | ConfigClusterLocatorMethod - | GKEClusterLocatorMethod; - -export interface ConfigClusterLocatorMethod { - /** - * @visibility frontend - */ - type: 'config'; - clusters: { - /** - * @visibility frontend - */ - url: string; - /** - * @visibility frontend - */ - name: string; - /** - * @visibility secret - */ - serviceAccountToken: string | undefined; - /** - * @visibility frontend - */ - authProvider: 'aws' | 'google' | 'serviceAccount'; - }[]; -} - -export interface GKEClusterLocatorMethod { - /** - * @visibility frontend - */ - type: 'gke'; - /** - * @visibility frontend - */ - projectId: string; - /** - * @visibility frontend - */ - region?: string; -} - export interface CustomResource { group: string; apiVersion: string; From f9f9d633d206c719c7cdfeb263bc365279aeb209 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 7 May 2021 15:41:25 +0200 Subject: [PATCH 55/68] Add changeset Signed-off-by: Marcus Eide --- .changeset/wild-moles-care.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wild-moles-care.md diff --git a/.changeset/wild-moles-care.md b/.changeset/wild-moles-care.md new file mode 100644 index 0000000000..35b886c2c1 --- /dev/null +++ b/.changeset/wild-moles-care.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +Add possibility to configure TLS verification for `gke` type clusters From 3b269181b68625f283269cdca9200da3a3937b3b Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 7 May 2021 16:01:34 +0200 Subject: [PATCH 56/68] Update README with tsx code block Signed-off-by: Marcus Eide --- plugins/shortcuts/README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/shortcuts/README.md b/plugins/shortcuts/README.md index 36bd9e62f8..162afe009b 100644 --- a/plugins/shortcuts/README.md +++ b/plugins/shortcuts/README.md @@ -19,16 +19,18 @@ export { shortcutsPlugin } from '@backstage/plugin-shortcuts'; Add the `` component within your ``: -```ts +```tsx import { Sidebar, SidebarDivider, SidebarSpace } from '@backstage/core'; import { Shortcuts } from '@backstage/plugin-shortcuts'; - - {/* ... */} - - - -; +export const SidebarComponent = () => ( + + {/* ... */} + + + + +); ``` The plugin exports a `shortcutApiRef` but the plugin includes a default implementation of the `ShortcutApi` that uses `localStorage` to store each user's shortcuts. From 85978cb28a1be07a760f8ca8c510182c44cee5d4 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 7 May 2021 16:38:00 +0200 Subject: [PATCH 57/68] chore: trying to fix the version packages PR as there's some horrible formatting in there somehow Signed-off-by: blam --- packages/create-app/CHANGELOG.md | 37 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 968c798625..8c3dc14245 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -933,26 +933,25 @@ Update imports and remove the usage of the deprecated `app.getRoutes()`. - ```diff - -import { Router as DocsRouter } from '@backstage/plugin-techdocs'; - +import { TechdocsPage } from '@backstage/plugin-techdocs'; - import { CatalogImportPage } from '@backstage/plugin-catalog-import'; - -import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; - -import { SearchPage as SearchRouter } from '@backstage/plugin-search'; - -import { Router as SettingsRouter } from '@backstage/plugin-user-settings'; - +import { TechRadarPage } from '@backstage/plugin-tech-radar'; - +import { SearchPage } from '@backstage/plugin-search'; - +import { UserSettingsPage } from '@backstage/plugin-user-settings'; - +import { ApiExplorerPage } from '@backstage/plugin-api-docs'; - import { EntityPage } from './components/catalog/EntityPage'; - import { scaffolderPlugin, ScaffolderPage } from '@backstage/plugin-scaffolder'; - ``` +```diff +- import { Router as DocsRouter } from '@backstage/plugin-techdocs'; ++ import { TechdocsPage } from '@backstage/plugin-techdocs'; + import { CatalogImportPage } from '@backstage/plugin-catalog-import'; +- import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; +- import { SearchPage as SearchRouter } from '@backstage/plugin-search'; +- import { Router as SettingsRouter } from '@backstage/plugin-user-settings'; ++ import { TechRadarPage } from '@backstage/plugin-tech-radar'; ++ import { SearchPage } from '@backstage/plugin-search'; ++ import { UserSettingsPage } from '@backstage/plugin-user-settings'; ++ import { ApiExplorerPage } from '@backstage/plugin-api-docs'; + import { EntityPage } from './components/catalog/EntityPage'; + import { scaffolderPlugin, ScaffolderPage } from '@backstage/plugin-scaffolder'; -const AppProvider = app.getProvider(); -const AppRouter = app.getRouter(); --const deprecatedAppRoutes = app.getRoutes(); -```` + const AppProvider = app.getProvider(); + const AppRouter = app.getRouter(); +- const deprecatedAppRoutes = app.getRoutes(); +``` As well as update or add the following routes: @@ -975,7 +974,7 @@ As well as update or add the following routes: - {deprecatedAppRoutes} + } /> + } /> -```` +``` If you have added additional plugins with registered routes or are using `Router` components from other plugins, these should be migrated to use the `*Page` components as well. See [this commit](https://github.com/backstage/backstage/commit/abd655e42d4ed416b70848ffdb1c4b99d189f13b) for more examples of how to migrate. From 16be1d093f0637a07f7dceee080e68b2dc28317b Mon Sep 17 00:00:00 2001 From: Bret Hubbard Date: Thu, 6 May 2021 17:29:32 -0600 Subject: [PATCH 58/68] Add changeset Signed-off-by: Bret Hubbard --- .changeset/smart-sheep-itch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/smart-sheep-itch.md diff --git a/.changeset/smart-sheep-itch.md b/.changeset/smart-sheep-itch.md new file mode 100644 index 0000000000..12a0ff5ad0 --- /dev/null +++ b/.changeset/smart-sheep-itch.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': patch +--- + +Improve error messages when schema validation fails From afb968c02170669d05d1a8b55f9d86436770c113 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 7 May 2021 17:09:41 +0200 Subject: [PATCH 59/68] Update react-use version Signed-off-by: Marcus Eide --- plugins/shortcuts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 7c60f54485..f87fe0e683 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -30,7 +30,7 @@ "react-dom": "^16.13.1", "react-hook-form": "^7.1.1", "react-router": "6.0.0-beta.0", - "react-use": "^15.3.3", + "react-use": "^17.2.4", "uuid": "^8.3.2", "zen-observable": "^0.8.15" }, From fd39d4662704e43866c26aae20247799a08bbaa8 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 7 May 2021 17:19:28 +0200 Subject: [PATCH 60/68] Move jest-when to the dev dependencies Signed-off-by: Dominik Henneke --- .changeset/sour-kids-compete.md | 5 +++++ plugins/scaffolder-backend/package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/sour-kids-compete.md diff --git a/.changeset/sour-kids-compete.md b/.changeset/sour-kids-compete.md new file mode 100644 index 0000000000..ce94305c48 --- /dev/null +++ b/.changeset/sour-kids-compete.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Move `jest-when` to the dev dependencies diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 9cd951f7d8..7452d94c63 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -53,7 +53,6 @@ "handlebars": "^4.7.6", "helmet": "^4.0.0", "isomorphic-git": "^1.8.0", - "jest-when": "^3.1.0", "jsonschema": "^1.2.6", "knex": "^0.95.1", "lodash": "^4.17.21", @@ -70,6 +69,7 @@ "@types/fs-extra": "^9.0.1", "@types/mock-fs": "^4.13.0", "@types/supertest": "^2.0.8", + "jest-when": "^3.1.0", "mock-fs": "^4.13.0", "msw": "^0.21.2", "supertest": "^6.1.3", From f6dbdb6021b9d88b6dc54fe7325c09cde8f4724e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 May 2021 21:08:39 +0000 Subject: [PATCH 61/68] chore(deps): bump ua-parser-js from 0.7.21 to 0.7.28 Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.21 to 0.7.28. - [Release notes](https://github.com/faisalman/ua-parser-js/releases) - [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.21...0.7.28) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8bf1bc1ca5..77607f0637 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25727,9 +25727,9 @@ typescript@^4.0.3, typescript@^4.1.3, typescript@~4.1.3: integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== ua-parser-js@^0.7.18: - version "0.7.21" - resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" - integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== + version "0.7.28" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" + integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" From 80f07624cd20976cc0ba3802c9213ac02a7873dd Mon Sep 17 00:00:00 2001 From: Shashank Bairy R Date: Sun, 9 May 2021 00:02:50 +0530 Subject: [PATCH 62/68] Add context for versions:bump about version change and update tests Signed-off-by: Shashank Bairy R --- packages/cli/src/commands/versions/bump.test.ts | 2 +- packages/cli/src/commands/versions/bump.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index 31803d4827..e5128fc995 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -136,7 +136,7 @@ describe('bump', () => { 'bumping @backstage/theme in b to ^2.0.0', 'Running yarn install to install new versions', '⚠️ The following packages may have breaking changes:', - ' @backstage/theme', + ' @backstage/theme : 1.0.0 ~> 2.0.0', ' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md', 'Version bump complete!', ]); diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index ceb2f7f988..642b93f242 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -195,8 +195,14 @@ export default async () => { ); console.log(); - for (const name of Array.from(breakingUpdates.keys()).sort()) { - console.log(` ${chalk.yellow(name)}`); + for (const [name, { from, to }] of [ + ...breakingUpdates.entries(), + ].sort()) { + console.log( + ` ${chalk.yellow(name)} : ${chalk.yellow(from)} ~> ${chalk.yellow( + to, + )}`, + ); let path; if (name.startsWith('@backstage/plugin-')) { From 2cd70e1646109d9a42f5de8f42bc691fc975873e Mon Sep 17 00:00:00 2001 From: Shashank Bairy R Date: Sun, 9 May 2021 00:08:36 +0530 Subject: [PATCH 63/68] Add changeset Signed-off-by: Shashank Bairy R --- .changeset/beige-cheetahs-jog.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/beige-cheetahs-jog.md diff --git a/.changeset/beige-cheetahs-jog.md b/.changeset/beige-cheetahs-jog.md new file mode 100644 index 0000000000..87ef3ef44c --- /dev/null +++ b/.changeset/beige-cheetahs-jog.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Add context for versions:bump on what version it was bumped to. Updated tests for the same. From d40316a7fd4a44d929b8a868eaa6e35c8ac0b458 Mon Sep 17 00:00:00 2001 From: Shashank Bairy R Date: Sun, 9 May 2021 11:58:40 +0530 Subject: [PATCH 64/68] use Array.from instead of spread operator Signed-off-by: Shashank Bairy R --- packages/cli/src/commands/versions/bump.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index 642b93f242..d046283aab 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -195,9 +195,9 @@ export default async () => { ); console.log(); - for (const [name, { from, to }] of [ - ...breakingUpdates.entries(), - ].sort()) { + for (const [name, { from, to }] of Array.from( + breakingUpdates.entries(), + ).sort()) { console.log( ` ${chalk.yellow(name)} : ${chalk.yellow(from)} ~> ${chalk.yellow( to, From eeb6e5ddaa53b6a628a0bb7f75d6f91eedd722e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 04:09:18 +0000 Subject: [PATCH 65/68] chore(deps-dev): bump prettier from 2.2.1 to 2.3.0 in /microsite Bumps [prettier](https://github.com/prettier/prettier) from 2.2.1 to 2.3.0. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.2.1...2.3.0) Signed-off-by: dependabot[bot] --- microsite/package.json | 2 +- microsite/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/microsite/package.json b/microsite/package.json index 07899fa703..595debcc65 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -18,7 +18,7 @@ "@spotify/prettier-config": "^10.0.0", "docusaurus": "^2.0.0-alpha.70", "js-yaml": "^4.1.0", - "prettier": "^2.2.1" + "prettier": "^2.3.0" }, "prettier": "@spotify/prettier-config" } diff --git a/microsite/yarn.lock b/microsite/yarn.lock index bed7897abc..4033968a2e 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -5197,10 +5197,10 @@ prepend-http@^2.0.0: resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" - integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +prettier@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" + integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== prismjs@^1.22.0: version "1.23.0" From 5f343551dc490e80a7ed5575d52d4b35f348f16a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 04:14:03 +0000 Subject: [PATCH 66/68] chore(deps): bump sanitize-html from 2.3.2 to 2.3.3 Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html) from 2.3.2 to 2.3.3. - [Release notes](https://github.com/apostrophecms/sanitize-html/releases) - [Changelog](https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md) - [Commits](https://github.com/apostrophecms/sanitize-html/compare/2.3.2...2.3.3) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 77607f0637..da32525e2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23385,9 +23385,9 @@ sane@^4.0.3: walker "~1.0.5" sanitize-html@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.3.2.tgz#a1954aea877a096c408aca7b0c260bef6e4fc402" - integrity sha512-p7neuskvC8pSurUjdVmbWPXmc9A4+QpOXIL+4gwFC+av5h+lYCXFT8uEneqsFQg/wEA1IH+cKQA60AaQI6p3cg== + version "2.3.3" + resolved "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.3.3.tgz#3db382c9a621cce4c46d90f10c64f1e9da9e8353" + integrity sha512-DCFXPt7Di0c6JUnlT90eIgrjs6TsJl/8HYU3KLdmrVclFN4O0heTcVbJiMa23OKVr6aR051XYtsgd8EWwEBwUA== dependencies: deepmerge "^4.2.2" escape-string-regexp "^4.0.0" From f39dbc7403ecb0b0fd1e64a788f91735ba8a874b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 04:20:29 +0000 Subject: [PATCH 67/68] chore(deps): bump @google-cloud/storage from 5.8.0 to 5.8.5 Bumps [@google-cloud/storage](https://github.com/googleapis/nodejs-storage) from 5.8.0 to 5.8.5. - [Release notes](https://github.com/googleapis/nodejs-storage/releases) - [Changelog](https://github.com/googleapis/nodejs-storage/blob/master/CHANGELOG.md) - [Commits](https://github.com/googleapis/nodejs-storage/compare/v5.8.0...v5.8.5) Signed-off-by: dependabot[bot] --- yarn.lock | 57 +++++++++++++++---------------------------------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/yarn.lock b/yarn.lock index 77607f0637..edc9287a9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2219,10 +2219,10 @@ through2 "^3.0.0" xdg-basedir "^3.0.0" -"@google-cloud/storage@^5.6.0": - version "5.8.1" - resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.1.tgz#00e627723614bcf97e6e29f9a59ec39339171847" - integrity sha512-qP8gCJ2myyMN3JMJN12d82Oo8VBSDO8vO4/x56dtQZX9+WISqcagurntnJVyFX885tIOtS97bsyv8qR1xv6HMg== +"@google-cloud/storage@^5.6.0", "@google-cloud/storage@^5.8.0": + version "5.8.5" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.5.tgz#2cf1e2e0ef8ca552abc4450301fef3fea4900ef6" + integrity sha512-i0gB9CRwQeOBYP7xuvn14M40LhHCwMjceBjxE4CTvsqL519sVY5yVKxLiAedHWGwUZHJNRa7Q2CmNfkdRwVNPg== dependencies: "@google-cloud/common" "^3.6.0" "@google-cloud/paginator" "^3.0.0" @@ -2230,38 +2230,11 @@ arrify "^2.0.0" async-retry "^1.3.1" compressible "^2.0.12" - date-and-time "^0.14.2" + date-and-time "^1.0.0" duplexify "^4.0.0" extend "^3.0.2" gaxios "^4.0.0" - gcs-resumable-upload "^3.1.3" - get-stream "^6.0.0" - hash-stream-validation "^0.2.2" - mime "^2.2.0" - mime-types "^2.0.8" - onetime "^5.1.0" - p-limit "^3.0.1" - pumpify "^2.0.0" - snakeize "^0.1.0" - stream-events "^1.0.1" - xdg-basedir "^4.0.0" - -"@google-cloud/storage@^5.8.0": - version "5.8.0" - resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.0.tgz#1f580e276f1d453790b382156421d1bcc4bd3f4b" - integrity sha512-WOShvBPOfkDXUzXMO+3j8Bzus+PFI9r1Ey9dLG2Zf458/PVuFTtaRWntd9ZiDG8g90zl2LmnA1JkDCreGUKr5g== - dependencies: - "@google-cloud/common" "^3.6.0" - "@google-cloud/paginator" "^3.0.0" - "@google-cloud/promisify" "^2.0.0" - arrify "^2.0.0" - async-retry "^1.3.1" - compressible "^2.0.12" - date-and-time "^0.14.2" - duplexify "^4.0.0" - extend "^3.0.2" - gaxios "^4.0.0" - gcs-resumable-upload "^3.1.3" + gcs-resumable-upload "^3.1.4" get-stream "^6.0.0" hash-stream-validation "^0.2.2" mime "^2.2.0" @@ -11262,16 +11235,16 @@ dataloader@2.0.0: resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== -date-and-time@^0.14.2: - version "0.14.2" - resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.14.2.tgz#a4266c3dead460f6c231fe9674e585908dac354e" - integrity sha512-EFTCh9zRSEpGPmJaexg7HTuzZHh6cnJj1ui7IGCFNXzd2QdpsNh05Db5TF3xzJm30YN+A8/6xHSuRcQqoc3kFA== - date-and-time@^0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.6.3.tgz#2daee52df67c28bd93bce862756ac86b68cf4237" integrity sha512-lcWy3AXDRJOD7MplwZMmNSRM//kZtJaLz4n6D1P5z9wEmZGBKhJRBIr1Xs9KNQJmdXPblvgffynYji4iylUTcA== +date-and-time@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-1.0.0.tgz#0062394bdf6f44e961f0db00511cb19cdf3cc0a5" + integrity sha512-477D7ypIiqlXBkxhU7YtG9wWZJEQ+RUpujt2quTfgf4+E8g5fNUkB0QIL0bVyP5/TKBg8y55Hfa1R/c4bt3dEw== + date-fns@^1.27.2: version "1.30.1" resolved "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" @@ -13836,10 +13809,10 @@ gcs-resumable-upload@^1.0.0: pumpify "^1.5.1" stream-events "^1.0.4" -gcs-resumable-upload@^3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.3.tgz#1e38e1339600b85812e6430a5ab455453c64cce3" - integrity sha512-LjVrv6YVH0XqBr/iBW0JgRA1ndxhK6zfEFFJR4im51QVTj/4sInOXimY2evDZuSZ75D3bHxTaQAdXRukMc1y+w== +gcs-resumable-upload@^3.1.4: + version "3.1.4" + resolved "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.4.tgz#2e591889efb02247af26868de300b398346b17b5" + integrity sha512-5dyDfHrrVcIskiw/cPssVD4HRiwoHjhk1Nd6h5W3pQ/qffDvhfy4oNCr1f3ZXFPwTnxkCbibsB+73oOM+NvmJQ== dependencies: abort-controller "^3.0.0" configstore "^5.0.0" From 3e3c3659a02cea0a2b7bfa588a6cdf9fdec3e876 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 04:21:58 +0000 Subject: [PATCH 68/68] chore(deps-dev): bump storybook-dark-mode from 1.0.3 to 1.0.8 Bumps [storybook-dark-mode](https://github.com/hipstersmoothie/storybook-dark-mode) from 1.0.3 to 1.0.8. - [Release notes](https://github.com/hipstersmoothie/storybook-dark-mode/releases) - [Changelog](https://github.com/hipstersmoothie/storybook-dark-mode/blob/master/CHANGELOG.md) - [Commits](https://github.com/hipstersmoothie/storybook-dark-mode/compare/v1.0.3...v1.0.8) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 77607f0637..ba7d945c4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24313,9 +24313,9 @@ store2@^2.7.1: integrity sha512-tWEpK0snS2RPUq1i3R6OahfJNjWCQYNxq0+by1amCSuw0mXtymJpzmZIeYpA1UAa+7B0grCpNYIbDcd7AgTbFg== storybook-dark-mode@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/storybook-dark-mode/-/storybook-dark-mode-1.0.3.tgz#8d58a874b6107ff1a7f29ebb0e6726b8036eee08" - integrity sha512-mjHLrv/dwtqKmbOoQ2CMtGKDttWSnUybutujsIPxLcEC77EujjWiRBFv46LtXAZEyZLm8sGFUz0s6HJJfJ3tSw== + version "1.0.8" + resolved "https://registry.npmjs.org/storybook-dark-mode/-/storybook-dark-mode-1.0.8.tgz#bbd64b382fd62d38685fdd769e2cac4e32ec293d" + integrity sha512-uY6yTSd1vYE0YwlON50u+iIoNF/fmMj59ww1cpd/naUcmOmCjwawViKFG5YjichwdR/yJ5ybWRUF0tnRQfaSiw== dependencies: fast-deep-equal "^3.0.0" memoizerific "^1.11.3"