@backstage/dev-utils 1.0.38-next.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @backstage/dev-utils
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 10b1452: Allow using translations in `createDevApp`
8
+
9
+ ### Patch Changes
10
+
11
+ - 836127c: Updated dependency `@testing-library/react` to `^16.0.0`.
12
+ - Updated dependencies
13
+ - @backstage/core-components@0.15.0
14
+ - @backstage/plugin-catalog-react@1.13.0
15
+ - @backstage/core-app-api@1.15.0
16
+ - @backstage/integration-react@1.1.31
17
+ - @backstage/catalog-model@1.7.0
18
+ - @backstage/app-defaults@1.5.11
19
+ - @backstage/core-plugin-api@1.9.4
20
+ - @backstage/theme@0.5.7
21
+
22
+ ## 1.1.0-next.2
23
+
24
+ ### Minor Changes
25
+
26
+ - 10b1452: Allow using translations in `createDevApp`
27
+
28
+ ### Patch Changes
29
+
30
+ - 836127c: Updated dependency `@testing-library/react` to `^16.0.0`.
31
+ - Updated dependencies
32
+ - @backstage/core-components@0.14.11-next.1
33
+ - @backstage/plugin-catalog-react@1.13.0-next.2
34
+ - @backstage/core-app-api@1.14.3-next.0
35
+ - @backstage/integration-react@1.1.31-next.0
36
+ - @backstage/app-defaults@1.5.11-next.1
37
+ - @backstage/core-plugin-api@1.9.4-next.0
38
+ - @backstage/theme@0.5.7-next.0
39
+ - @backstage/catalog-model@1.6.0
40
+
3
41
  ## 1.0.38-next.1
4
42
 
5
43
  ### Patch Changes
@@ -0,0 +1,86 @@
1
+ import React, { useState } from 'react';
2
+ import { appLanguageApiRef } from '@backstage/core-plugin-api/alpha';
3
+ import TranslateIcon from '@material-ui/icons/Translate';
4
+ import ListItemText from '@material-ui/core/ListItemText';
5
+ import { useApi } from '@backstage/core-plugin-api';
6
+ import useObservable from 'react-use/esm/useObservable';
7
+ import { SidebarItem } from '@backstage/core-components';
8
+ import Menu from '@material-ui/core/Menu';
9
+ import MenuItem from '@material-ui/core/MenuItem';
10
+
11
+ const SidebarLanguageSwitcher = () => {
12
+ const languageApi = useApi(appLanguageApiRef);
13
+ const [languageObservable] = useState(() => languageApi.language$());
14
+ const { language: currentLanguage } = useObservable(
15
+ languageObservable,
16
+ languageApi.getLanguage()
17
+ );
18
+ const [anchorEl, setAnchorEl] = useState();
19
+ const { languages } = languageApi.getAvailableLanguages();
20
+ if (languages.length <= 1) {
21
+ return null;
22
+ }
23
+ const open = Boolean(anchorEl);
24
+ const handleClose = () => {
25
+ setAnchorEl(void 0);
26
+ };
27
+ const handleOpen = (event) => {
28
+ setAnchorEl(event.currentTarget);
29
+ };
30
+ const handleSetLanguage = (newLanguage) => {
31
+ languageApi.setLanguage(newLanguage);
32
+ setAnchorEl(void 0);
33
+ };
34
+ const getLanguageDisplayName = (language) => {
35
+ try {
36
+ const names = new Intl.DisplayNames([language], {
37
+ type: "language"
38
+ });
39
+ return names.of(language) || language;
40
+ } catch (err) {
41
+ return language;
42
+ }
43
+ };
44
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
45
+ SidebarItem,
46
+ {
47
+ icon: TranslateIcon,
48
+ text: "Language",
49
+ id: "language-button",
50
+ "aria-haspopup": "listbox",
51
+ "aria-controls": "language-menu",
52
+ "aria-label": "switch language",
53
+ "aria-expanded": open ? "true" : void 0,
54
+ onClick: handleOpen
55
+ }
56
+ ), /* @__PURE__ */ React.createElement(
57
+ Menu,
58
+ {
59
+ id: "language-menu",
60
+ anchorEl,
61
+ open,
62
+ onClose: handleClose,
63
+ MenuListProps: {
64
+ "aria-labelledby": "language-button",
65
+ role: "listbox"
66
+ }
67
+ },
68
+ /* @__PURE__ */ React.createElement(MenuItem, { disabled: true }, "Choose language"),
69
+ languages.map((lang) => {
70
+ const active = currentLanguage === lang;
71
+ return /* @__PURE__ */ React.createElement(
72
+ MenuItem,
73
+ {
74
+ key: lang,
75
+ selected: active,
76
+ "aria-selected": active,
77
+ onClick: () => handleSetLanguage(lang)
78
+ },
79
+ /* @__PURE__ */ React.createElement(ListItemText, null, getLanguageDisplayName(lang))
80
+ );
81
+ })
82
+ ));
83
+ };
84
+
85
+ export { SidebarLanguageSwitcher };
86
+ //# sourceMappingURL=SidebarLanguageSwitcher.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SidebarLanguageSwitcher.esm.js","sources":["../../../src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useState } from 'react';\nimport { appLanguageApiRef } from '@backstage/core-plugin-api/alpha';\nimport TranslateIcon from '@material-ui/icons/Translate';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport { useApi } from '@backstage/core-plugin-api';\nimport useObservable from 'react-use/esm/useObservable';\nimport { SidebarItem } from '@backstage/core-components';\nimport Menu from '@material-ui/core/Menu';\nimport MenuItem from '@material-ui/core/MenuItem';\n\n/** @public */\nexport const SidebarLanguageSwitcher = () => {\n const languageApi = useApi(appLanguageApiRef);\n\n const [languageObservable] = useState(() => languageApi.language$());\n const { language: currentLanguage } = useObservable(\n languageObservable,\n languageApi.getLanguage(),\n );\n const [anchorEl, setAnchorEl] = useState<Element | undefined>();\n\n const { languages } = languageApi.getAvailableLanguages();\n\n if (languages.length <= 1) {\n return null;\n }\n\n const open = Boolean(anchorEl);\n\n const handleClose = () => {\n setAnchorEl(undefined);\n };\n\n const handleOpen = (event: React.MouseEvent) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleSetLanguage = (newLanguage: string | undefined) => {\n languageApi.setLanguage(newLanguage);\n setAnchorEl(undefined);\n };\n\n const getLanguageDisplayName = (language: string) => {\n try {\n const names = new Intl.DisplayNames([language], {\n type: 'language',\n });\n return names.of(language) || language;\n } catch (err) {\n return language;\n }\n };\n\n return (\n <>\n <SidebarItem\n icon={TranslateIcon}\n text=\"Language\"\n id=\"language-button\"\n aria-haspopup=\"listbox\"\n aria-controls=\"language-menu\"\n aria-label=\"switch language\"\n aria-expanded={open ? 'true' : undefined}\n onClick={handleOpen}\n />\n <Menu\n id=\"language-menu\"\n anchorEl={anchorEl}\n open={open}\n onClose={handleClose}\n MenuListProps={{\n 'aria-labelledby': 'language-button',\n role: 'listbox',\n }}\n >\n <MenuItem disabled>Choose language</MenuItem>\n {languages.map(lang => {\n const active = currentLanguage === lang;\n return (\n <MenuItem\n key={lang}\n selected={active}\n aria-selected={active}\n onClick={() => handleSetLanguage(lang)}\n >\n <ListItemText>{getLanguageDisplayName(lang)}</ListItemText>\n </MenuItem>\n );\n })}\n </Menu>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ import { SidebarThemeSwitcher } from './SidebarThemeSwitcher.esm.js';
11
11
  import 'react-dom';
12
12
  import '../components/EntityGridItem/EntityGridItem.esm.js';
13
13
  import { SidebarSignOutButton } from '../components/SidebarSignOutButton/SidebarSignOutButton.esm.js';
14
+ import { SidebarLanguageSwitcher } from '../components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.esm.js';
14
15
 
15
16
  let ReactDOMPromise;
16
17
  if (process.env.HAS_REACT_DOM_CLIENT) {
@@ -33,8 +34,11 @@ class DevAppBuilder {
33
34
  routes = new Array();
34
35
  sidebarItems = new Array();
35
36
  signInProviders = new Array();
37
+ translationResources = new Array();
36
38
  defaultPage;
37
39
  themes;
40
+ languages;
41
+ defaultLanguage;
38
42
  /**
39
43
  * Register one or more plugins to render in the dev app
40
44
  */
@@ -118,6 +122,27 @@ class DevAppBuilder {
118
122
  this.signInProviders.push(provider);
119
123
  return this;
120
124
  }
125
+ /**
126
+ * Set available languages to be shown in the dev app
127
+ */
128
+ setAvailableLanguages(languages) {
129
+ this.languages = languages;
130
+ return this;
131
+ }
132
+ /**
133
+ * Add translation resource to the dev app
134
+ */
135
+ addTranslationResource(resource) {
136
+ this.translationResources.push(resource);
137
+ return this;
138
+ }
139
+ /**
140
+ * Set default language for the dev app
141
+ */
142
+ setDefaultLanguage(language) {
143
+ this.defaultLanguage = language;
144
+ return this;
145
+ }
121
146
  /**
122
147
  * Build a DevApp component using the resources registered so far
123
148
  */
@@ -160,9 +185,14 @@ class DevAppBuilder {
160
185
  }
161
186
  bind(plugin.externalRoutes, targets);
162
187
  }
188
+ },
189
+ __experimentalTranslations: {
190
+ defaultLanguage: this.defaultLanguage,
191
+ availableLanguages: this.languages,
192
+ resources: this.translationResources
163
193
  }
164
194
  });
165
- const DevApp = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(AlertDisplay, null), /* @__PURE__ */ React.createElement(OAuthRequestDialog, null), this.rootChildren, /* @__PURE__ */ React.createElement(AppRouter, null, /* @__PURE__ */ React.createElement(SidebarPage, null, /* @__PURE__ */ React.createElement(Sidebar, null, /* @__PURE__ */ React.createElement(SidebarSpacer, null), this.sidebarItems, /* @__PURE__ */ React.createElement(SidebarSpace, null), /* @__PURE__ */ React.createElement(SidebarDivider, null), /* @__PURE__ */ React.createElement(SidebarThemeSwitcher, null), /* @__PURE__ */ React.createElement(SidebarSignOutButton, null)), /* @__PURE__ */ React.createElement(FlatRoutes, null, this.routes, /* @__PURE__ */ React.createElement(Route, { path: "/_external_route", element: /* @__PURE__ */ React.createElement(FakePage, null) })))));
195
+ const DevApp = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(AlertDisplay, null), /* @__PURE__ */ React.createElement(OAuthRequestDialog, null), this.rootChildren, /* @__PURE__ */ React.createElement(AppRouter, null, /* @__PURE__ */ React.createElement(SidebarPage, null, /* @__PURE__ */ React.createElement(Sidebar, null, /* @__PURE__ */ React.createElement(SidebarSpacer, null), this.sidebarItems, /* @__PURE__ */ React.createElement(SidebarSpace, null), /* @__PURE__ */ React.createElement(SidebarDivider, null), /* @__PURE__ */ React.createElement(SidebarThemeSwitcher, null), /* @__PURE__ */ React.createElement(SidebarLanguageSwitcher, null), /* @__PURE__ */ React.createElement(SidebarSignOutButton, null)), /* @__PURE__ */ React.createElement(FlatRoutes, null, this.routes, /* @__PURE__ */ React.createElement(Route, { path: "/_external_route", element: /* @__PURE__ */ React.createElement(FakePage, null) })))));
166
196
  return app.createRoot(DevApp);
167
197
  }
168
198
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"render.esm.js","sources":["../../src/devApp/render.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createApp } from '@backstage/app-defaults';\nimport { AppRouter, FlatRoutes } from '@backstage/core-app-api';\nimport {\n AlertDisplay,\n OAuthRequestDialog,\n Sidebar,\n SidebarDivider,\n SidebarItem,\n SidebarPage,\n SidebarSpace,\n SidebarSpacer,\n SignInPage,\n SignInProviderConfig,\n} from '@backstage/core-components';\nimport {\n AnyApiFactory,\n ApiFactory,\n AppTheme,\n attachComponentData,\n BackstagePlugin,\n configApiRef,\n createApiFactory,\n createRouteRef,\n IconComponent,\n RouteRef,\n} from '@backstage/core-plugin-api';\nimport {\n ScmIntegrationsApi,\n scmIntegrationsApiRef,\n} from '@backstage/integration-react';\nimport Box from '@material-ui/core/Box';\nimport BookmarkIcon from '@material-ui/icons/Bookmark';\nimport React, { ComponentType, PropsWithChildren, ReactNode } from 'react';\nimport { createRoutesFromChildren, Route } from 'react-router-dom';\nimport { SidebarThemeSwitcher } from './SidebarThemeSwitcher';\nimport 'react-dom';\nimport { SidebarSignOutButton } from '../components';\n\nlet ReactDOMPromise: Promise<\n typeof import('react-dom') | typeof import('react-dom/client')\n>;\nif (process.env.HAS_REACT_DOM_CLIENT) {\n ReactDOMPromise = import('react-dom/client');\n} else {\n ReactDOMPromise = import('react-dom');\n}\n\nexport function isReactRouterBeta(): boolean {\n const [obj] = createRoutesFromChildren(<Route index element={<div />} />);\n return !obj.index;\n}\n\nconst MaybeGatheringRoute: (props: {\n path: string;\n element: JSX.Element;\n children?: ReactNode;\n}) => JSX.Element = ({ element }) => element;\n\nif (isReactRouterBeta()) {\n attachComponentData(MaybeGatheringRoute, 'core.gatherMountPoints', true);\n}\n\n/** @public */\nexport type DevAppPageOptions = {\n path?: string;\n element: JSX.Element;\n children?: JSX.Element;\n title?: string;\n icon?: IconComponent;\n};\n\n/**\n * DevApp builder that is similar to the App builder API, but creates an App\n * with the purpose of developing one or more plugins inside it.\n *\n * @public\n */\nexport class DevAppBuilder {\n private readonly plugins = new Array<BackstagePlugin>();\n private readonly apis = new Array<AnyApiFactory>();\n private readonly rootChildren = new Array<ReactNode>();\n private readonly routes = new Array<JSX.Element>();\n private readonly sidebarItems = new Array<JSX.Element>();\n private readonly signInProviders = new Array<SignInProviderConfig>();\n\n private defaultPage?: string;\n private themes?: Array<AppTheme>;\n\n /**\n * Register one or more plugins to render in the dev app\n */\n registerPlugin(...plugins: BackstagePlugin[]): DevAppBuilder {\n this.plugins.push(...plugins);\n return this;\n }\n\n /**\n * Register an API factory to add to the app\n */\n registerApi<\n Api,\n Impl extends Api,\n Deps extends { [name in string]: unknown },\n >(factory: ApiFactory<Api, Impl, Deps>): DevAppBuilder {\n this.apis.push(factory);\n return this;\n }\n\n /**\n * Adds a React node to place just inside the App Provider.\n *\n * Useful for adding more global components like the AlertDisplay.\n */\n addRootChild(node: ReactNode): DevAppBuilder {\n this.rootChildren.push(node);\n return this;\n }\n\n /**\n * Adds a new sidebar item to the dev app.\n *\n * Useful for adding only sidebar items without a corresponding page.\n */\n addSidebarItem(sidebarItem: JSX.Element): DevAppBuilder {\n this.sidebarItems.push(sidebarItem);\n return this;\n }\n\n /**\n * Adds a page component along with accompanying sidebar item.\n *\n * If no path is provided one will be generated.\n * If no title is provided, no sidebar item will be created.\n */\n addPage(opts: DevAppPageOptions): DevAppBuilder {\n const path = opts.path ?? `/page-${this.routes.length + 1}`;\n\n if (!this.defaultPage || path === '/') {\n this.defaultPage = path;\n }\n\n if (opts.title) {\n this.sidebarItems.push(\n <SidebarItem\n key={path}\n to={path}\n text={opts.title}\n icon={opts.icon ?? BookmarkIcon}\n />,\n );\n }\n\n this.routes.push(\n <MaybeGatheringRoute\n key={path}\n path={path}\n element={opts.element}\n children={opts.children}\n />,\n );\n return this;\n }\n\n /**\n * Adds an array of themes to override the default theme.\n */\n addThemes(themes: AppTheme[]) {\n this.themes = themes;\n return this;\n }\n\n /**\n * Adds new sign in provider for the dev app\n */\n addSignInProvider(provider: SignInProviderConfig) {\n this.signInProviders.push(provider);\n return this;\n }\n\n /**\n * Build a DevApp component using the resources registered so far\n */\n build(): ComponentType<PropsWithChildren<{}>> {\n const fakeRouteRef = createRouteRef({ id: 'fake' });\n const FakePage = () => <Box p={3}>Page belonging to another plugin.</Box>;\n attachComponentData(FakePage, 'core.mountPoint', fakeRouteRef);\n\n const apis = [...this.apis];\n if (!apis.some(api => api.api.id === scmIntegrationsApiRef.id)) {\n apis.push(\n createApiFactory({\n api: scmIntegrationsApiRef,\n deps: { configApi: configApiRef },\n factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),\n }),\n );\n }\n\n const app = createApp({\n apis,\n plugins: this.plugins,\n themes: this.themes,\n components: {\n SignInPage: props => {\n return (\n <SignInPage\n {...props}\n providers={['guest', ...this.signInProviders]}\n title=\"Select a sign-in method\"\n align=\"center\"\n />\n );\n },\n },\n bindRoutes: ({ bind }) => {\n for (const plugin of this.plugins ?? []) {\n const targets: Record<string, RouteRef<any>> = {};\n for (const routeKey of Object.keys(plugin.externalRoutes)) {\n targets[routeKey] = fakeRouteRef;\n }\n bind(plugin.externalRoutes, targets);\n }\n },\n });\n\n const DevApp = (\n <>\n <AlertDisplay />\n <OAuthRequestDialog />\n {this.rootChildren}\n <AppRouter>\n <SidebarPage>\n <Sidebar>\n <SidebarSpacer />\n {this.sidebarItems}\n <SidebarSpace />\n <SidebarDivider />\n <SidebarThemeSwitcher />\n <SidebarSignOutButton />\n </Sidebar>\n <FlatRoutes>\n {this.routes}\n <Route path=\"/_external_route\" element={<FakePage />} />\n </FlatRoutes>\n </SidebarPage>\n </AppRouter>\n </>\n );\n\n return app.createRoot(DevApp);\n }\n\n /**\n * Build and render directory to #root element, with react hot loading.\n */\n render(): void {\n const DevApp = this.build();\n\n if (\n window.location.pathname === '/' &&\n this.defaultPage &&\n this.defaultPage !== '/'\n ) {\n window.location.pathname = this.defaultPage;\n }\n\n ReactDOMPromise.then(ReactDOM => {\n if ('createRoot' in ReactDOM) {\n ReactDOM.createRoot(document.getElementById('root')!).render(\n <DevApp />,\n );\n } else {\n ReactDOM.render(<DevApp />, document.getElementById('root'));\n }\n });\n }\n}\n\n// TODO(rugvip): Figure out patterns for how to allow in-house apps to build upon\n// this to provide their own plugin dev wrappers.\n\n/**\n * Creates a dev app for rendering one or more plugins and exposing the touch points of the plugin.\n *\n * @public\n */\nexport function createDevApp() {\n return new DevAppBuilder();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render.esm.js","sources":["../../src/devApp/render.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createApp } from '@backstage/app-defaults';\nimport { AppRouter, FlatRoutes } from '@backstage/core-app-api';\nimport {\n AlertDisplay,\n OAuthRequestDialog,\n Sidebar,\n SidebarDivider,\n SidebarItem,\n SidebarPage,\n SidebarSpace,\n SidebarSpacer,\n SignInPage,\n SignInProviderConfig,\n} from '@backstage/core-components';\nimport {\n AnyApiFactory,\n ApiFactory,\n AppTheme,\n attachComponentData,\n BackstagePlugin,\n configApiRef,\n createApiFactory,\n createRouteRef,\n IconComponent,\n RouteRef,\n} from '@backstage/core-plugin-api';\nimport { TranslationResource } from '@backstage/core-plugin-api/alpha';\nimport {\n ScmIntegrationsApi,\n scmIntegrationsApiRef,\n} from '@backstage/integration-react';\nimport Box from '@material-ui/core/Box';\nimport BookmarkIcon from '@material-ui/icons/Bookmark';\nimport React, { ComponentType, PropsWithChildren, ReactNode } from 'react';\nimport { createRoutesFromChildren, Route } from 'react-router-dom';\nimport { SidebarThemeSwitcher } from './SidebarThemeSwitcher';\nimport 'react-dom';\nimport { SidebarLanguageSwitcher, SidebarSignOutButton } from '../components';\n\nlet ReactDOMPromise: Promise<\n typeof import('react-dom') | typeof import('react-dom/client')\n>;\nif (process.env.HAS_REACT_DOM_CLIENT) {\n ReactDOMPromise = import('react-dom/client');\n} else {\n ReactDOMPromise = import('react-dom');\n}\n\nexport function isReactRouterBeta(): boolean {\n const [obj] = createRoutesFromChildren(<Route index element={<div />} />);\n return !obj.index;\n}\n\nconst MaybeGatheringRoute: (props: {\n path: string;\n element: JSX.Element;\n children?: ReactNode;\n}) => JSX.Element = ({ element }) => element;\n\nif (isReactRouterBeta()) {\n attachComponentData(MaybeGatheringRoute, 'core.gatherMountPoints', true);\n}\n\n/** @public */\nexport type DevAppPageOptions = {\n path?: string;\n element: JSX.Element;\n children?: JSX.Element;\n title?: string;\n icon?: IconComponent;\n};\n\n/**\n * DevApp builder that is similar to the App builder API, but creates an App\n * with the purpose of developing one or more plugins inside it.\n *\n * @public\n */\nexport class DevAppBuilder {\n private readonly plugins = new Array<BackstagePlugin>();\n private readonly apis = new Array<AnyApiFactory>();\n private readonly rootChildren = new Array<ReactNode>();\n private readonly routes = new Array<JSX.Element>();\n private readonly sidebarItems = new Array<JSX.Element>();\n private readonly signInProviders = new Array<SignInProviderConfig>();\n private readonly translationResources = new Array<TranslationResource>();\n\n private defaultPage?: string;\n private themes?: Array<AppTheme>;\n private languages?: string[];\n private defaultLanguage?: string;\n\n /**\n * Register one or more plugins to render in the dev app\n */\n registerPlugin(...plugins: BackstagePlugin[]): DevAppBuilder {\n this.plugins.push(...plugins);\n return this;\n }\n\n /**\n * Register an API factory to add to the app\n */\n registerApi<\n Api,\n Impl extends Api,\n Deps extends { [name in string]: unknown },\n >(factory: ApiFactory<Api, Impl, Deps>): DevAppBuilder {\n this.apis.push(factory);\n return this;\n }\n\n /**\n * Adds a React node to place just inside the App Provider.\n *\n * Useful for adding more global components like the AlertDisplay.\n */\n addRootChild(node: ReactNode): DevAppBuilder {\n this.rootChildren.push(node);\n return this;\n }\n\n /**\n * Adds a new sidebar item to the dev app.\n *\n * Useful for adding only sidebar items without a corresponding page.\n */\n addSidebarItem(sidebarItem: JSX.Element): DevAppBuilder {\n this.sidebarItems.push(sidebarItem);\n return this;\n }\n\n /**\n * Adds a page component along with accompanying sidebar item.\n *\n * If no path is provided one will be generated.\n * If no title is provided, no sidebar item will be created.\n */\n addPage(opts: DevAppPageOptions): DevAppBuilder {\n const path = opts.path ?? `/page-${this.routes.length + 1}`;\n\n if (!this.defaultPage || path === '/') {\n this.defaultPage = path;\n }\n\n if (opts.title) {\n this.sidebarItems.push(\n <SidebarItem\n key={path}\n to={path}\n text={opts.title}\n icon={opts.icon ?? BookmarkIcon}\n />,\n );\n }\n\n this.routes.push(\n <MaybeGatheringRoute\n key={path}\n path={path}\n element={opts.element}\n children={opts.children}\n />,\n );\n return this;\n }\n\n /**\n * Adds an array of themes to override the default theme.\n */\n addThemes(themes: AppTheme[]) {\n this.themes = themes;\n return this;\n }\n\n /**\n * Adds new sign in provider for the dev app\n */\n addSignInProvider(provider: SignInProviderConfig) {\n this.signInProviders.push(provider);\n return this;\n }\n\n /**\n * Set available languages to be shown in the dev app\n */\n setAvailableLanguages(languages: string[]) {\n this.languages = languages;\n return this;\n }\n\n /**\n * Add translation resource to the dev app\n */\n addTranslationResource(resource: TranslationResource) {\n this.translationResources.push(resource);\n return this;\n }\n\n /**\n * Set default language for the dev app\n */\n setDefaultLanguage(language: string) {\n this.defaultLanguage = language;\n return this;\n }\n\n /**\n * Build a DevApp component using the resources registered so far\n */\n build(): ComponentType<PropsWithChildren<{}>> {\n const fakeRouteRef = createRouteRef({ id: 'fake' });\n const FakePage = () => <Box p={3}>Page belonging to another plugin.</Box>;\n attachComponentData(FakePage, 'core.mountPoint', fakeRouteRef);\n\n const apis = [...this.apis];\n if (!apis.some(api => api.api.id === scmIntegrationsApiRef.id)) {\n apis.push(\n createApiFactory({\n api: scmIntegrationsApiRef,\n deps: { configApi: configApiRef },\n factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),\n }),\n );\n }\n\n const app = createApp({\n apis,\n plugins: this.plugins,\n themes: this.themes,\n components: {\n SignInPage: props => {\n return (\n <SignInPage\n {...props}\n providers={['guest', ...this.signInProviders]}\n title=\"Select a sign-in method\"\n align=\"center\"\n />\n );\n },\n },\n bindRoutes: ({ bind }) => {\n for (const plugin of this.plugins ?? []) {\n const targets: Record<string, RouteRef<any>> = {};\n for (const routeKey of Object.keys(plugin.externalRoutes)) {\n targets[routeKey] = fakeRouteRef;\n }\n bind(plugin.externalRoutes, targets);\n }\n },\n __experimentalTranslations: {\n defaultLanguage: this.defaultLanguage,\n availableLanguages: this.languages,\n resources: this.translationResources,\n },\n });\n\n const DevApp = (\n <>\n <AlertDisplay />\n <OAuthRequestDialog />\n {this.rootChildren}\n <AppRouter>\n <SidebarPage>\n <Sidebar>\n <SidebarSpacer />\n {this.sidebarItems}\n <SidebarSpace />\n <SidebarDivider />\n <SidebarThemeSwitcher />\n <SidebarLanguageSwitcher />\n <SidebarSignOutButton />\n </Sidebar>\n <FlatRoutes>\n {this.routes}\n <Route path=\"/_external_route\" element={<FakePage />} />\n </FlatRoutes>\n </SidebarPage>\n </AppRouter>\n </>\n );\n\n return app.createRoot(DevApp);\n }\n\n /**\n * Build and render directory to #root element, with react hot loading.\n */\n render(): void {\n const DevApp = this.build();\n\n if (\n window.location.pathname === '/' &&\n this.defaultPage &&\n this.defaultPage !== '/'\n ) {\n window.location.pathname = this.defaultPage;\n }\n\n ReactDOMPromise.then(ReactDOM => {\n if ('createRoot' in ReactDOM) {\n ReactDOM.createRoot(document.getElementById('root')!).render(\n <DevApp />,\n );\n } else {\n ReactDOM.render(<DevApp />, document.getElementById('root'));\n }\n });\n }\n}\n\n// TODO(rugvip): Figure out patterns for how to allow in-house apps to build upon\n// this to provide their own plugin dev wrappers.\n\n/**\n * Creates a dev app for rendering one or more plugins and exposing the touch points of the plugin.\n *\n * @public\n */\nexport function createDevApp() {\n return new DevAppBuilder();\n}\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { GridProps } from '@material-ui/core/Grid';
4
4
  import React, { ReactNode, ComponentType, PropsWithChildren } from 'react';
5
5
  import { IconComponent, BackstagePlugin, ApiFactory, AppTheme } from '@backstage/core-plugin-api';
6
6
  import { SignInProviderConfig } from '@backstage/core-components';
7
+ import { TranslationResource } from '@backstage/core-plugin-api/alpha';
7
8
 
8
9
  /** @public */
9
10
  declare const EntityGridItem: (props: Omit<GridProps, 'item' | 'container'> & {
@@ -20,6 +21,9 @@ declare const SidebarSignOutButton: (props: {
20
21
  text?: string;
21
22
  }) => React.JSX.Element;
22
23
 
24
+ /** @public */
25
+ declare const SidebarLanguageSwitcher: () => React.JSX.Element | null;
26
+
23
27
  /** @public */
24
28
  type DevAppPageOptions = {
25
29
  path?: string;
@@ -41,8 +45,11 @@ declare class DevAppBuilder {
41
45
  private readonly routes;
42
46
  private readonly sidebarItems;
43
47
  private readonly signInProviders;
48
+ private readonly translationResources;
44
49
  private defaultPage?;
45
50
  private themes?;
51
+ private languages?;
52
+ private defaultLanguage?;
46
53
  /**
47
54
  * Register one or more plugins to render in the dev app
48
55
  */
@@ -80,6 +87,18 @@ declare class DevAppBuilder {
80
87
  * Adds new sign in provider for the dev app
81
88
  */
82
89
  addSignInProvider(provider: SignInProviderConfig): this;
90
+ /**
91
+ * Set available languages to be shown in the dev app
92
+ */
93
+ setAvailableLanguages(languages: string[]): this;
94
+ /**
95
+ * Add translation resource to the dev app
96
+ */
97
+ addTranslationResource(resource: TranslationResource): this;
98
+ /**
99
+ * Set default language for the dev app
100
+ */
101
+ setDefaultLanguage(language: string): this;
83
102
  /**
84
103
  * Build a DevApp component using the resources registered so far
85
104
  */
@@ -96,4 +115,4 @@ declare class DevAppBuilder {
96
115
  */
97
116
  declare function createDevApp(): DevAppBuilder;
98
117
 
99
- export { DevAppBuilder, type DevAppPageOptions, EntityGridItem, SidebarSignOutButton, createDevApp };
118
+ export { DevAppBuilder, type DevAppPageOptions, EntityGridItem, SidebarLanguageSwitcher, SidebarSignOutButton, createDevApp };
package/dist/index.esm.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { EntityGridItem } from './components/EntityGridItem/EntityGridItem.esm.js';
2
2
  export { SidebarSignOutButton } from './components/SidebarSignOutButton/SidebarSignOutButton.esm.js';
3
+ export { SidebarLanguageSwitcher } from './components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.esm.js';
3
4
  export { createDevApp } from './devApp/render.esm.js';
4
5
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/dev-utils",
3
- "version": "1.0.38-next.1",
3
+ "version": "1.1.0",
4
4
  "description": "Utilities for developing Backstage plugins.",
5
5
  "backstage": {
6
6
  "role": "web-library"
@@ -36,25 +36,25 @@
36
36
  "test": "backstage-cli package test"
37
37
  },
38
38
  "dependencies": {
39
- "@backstage/app-defaults": "^1.5.11-next.0",
40
- "@backstage/catalog-model": "^1.6.0",
41
- "@backstage/core-app-api": "^1.14.2",
42
- "@backstage/core-components": "^0.14.11-next.0",
43
- "@backstage/core-plugin-api": "^1.9.3",
44
- "@backstage/integration-react": "^1.1.30",
45
- "@backstage/plugin-catalog-react": "^1.12.4-next.1",
46
- "@backstage/theme": "^0.5.6",
39
+ "@backstage/app-defaults": "^1.5.11",
40
+ "@backstage/catalog-model": "^1.7.0",
41
+ "@backstage/core-app-api": "^1.15.0",
42
+ "@backstage/core-components": "^0.15.0",
43
+ "@backstage/core-plugin-api": "^1.9.4",
44
+ "@backstage/integration-react": "^1.1.31",
45
+ "@backstage/plugin-catalog-react": "^1.13.0",
46
+ "@backstage/theme": "^0.5.7",
47
47
  "@material-ui/core": "^4.12.2",
48
48
  "@material-ui/icons": "^4.9.1",
49
49
  "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0",
50
50
  "react-use": "^17.2.4"
51
51
  },
52
52
  "devDependencies": {
53
- "@backstage/cli": "^0.27.1-next.1",
54
- "@backstage/test-utils": "^1.6.0-next.0",
53
+ "@backstage/cli": "^0.27.1",
54
+ "@backstage/test-utils": "^1.6.0",
55
55
  "@testing-library/dom": "^10.0.0",
56
56
  "@testing-library/jest-dom": "^6.0.0",
57
- "@testing-library/react": "^15.0.0",
57
+ "@testing-library/react": "^16.0.0",
58
58
  "@testing-library/user-event": "^14.0.0",
59
59
  "zen-observable": "^0.10.0"
60
60
  },