diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 667b14366f..81d08f58a9 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -33,6 +33,7 @@ import { Router as GraphiQLRouter } from '@backstage/plugin-graphiql';
import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar';
import { Router as LighthouseRouter } from '@backstage/plugin-lighthouse';
import { Router as RegisterComponentRouter } from '@backstage/plugin-register-component';
+import { Router as SettingsRouter } from '@backstage/plugin-user-settings';
import { Route, Routes, Navigate } from 'react-router';
import { EntityPage } from './components/catalog/EntityPage';
@@ -81,6 +82,7 @@ const AppRoutes = () => (
path="/register-component"
element={}
/>
+ } />
{...deprecatedAppRoutes}
);
diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx
index 9f5387cea8..e947e5c91d 100644
--- a/packages/app/src/components/Root/Root.tsx
+++ b/packages/app/src/components/Root/Root.tsx
@@ -38,7 +38,7 @@ import {
} from '@backstage/core';
import { NavLink } from 'react-router-dom';
import { graphiQLRouteRef } from '@backstage/plugin-graphiql';
-import { UserSettings } from '@backstage/plugin-user-settings';
+import { Settings as SidebarSettings } from '@backstage/plugin-user-settings';
const useSidebarLogoStyles = makeStyles({
root: {
@@ -102,7 +102,7 @@ const Root: FC<{}> = ({ children }) => (
/>
-
+
{children}
diff --git a/packages/core/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core/src/layout/Sidebar/Sidebar.stories.tsx
index 14bf6e69ed..ec5746d428 100644
--- a/packages/core/src/layout/Sidebar/Sidebar.stories.tsx
+++ b/packages/core/src/layout/Sidebar/Sidebar.stories.tsx
@@ -56,7 +56,6 @@ const handleSearch = (input: string) => {
export const SampleSidebar = () => (
- {/* */}
@@ -92,7 +91,6 @@ const apis = ApiRegistry.from([
export const WithUserSettingsPlugin = () => (
- {/* */}
diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json
index 1c5d489747..c744f757c5 100644
--- a/plugins/user-settings/package.json
+++ b/plugins/user-settings/package.json
@@ -28,6 +28,7 @@
"@material-ui/lab": "4.0.0-alpha.45",
"react": "^16.13.1",
"react-dom": "^16.13.1",
+ "react-router": "6.0.0-beta.0",
"react-use": "^15.3.3"
},
"devDependencies": {
diff --git a/plugins/user-settings/src/components/AuthProviderList.tsx b/plugins/user-settings/src/components/AuthProviders.tsx
similarity index 81%
rename from plugins/user-settings/src/components/AuthProviderList.tsx
rename to plugins/user-settings/src/components/AuthProviders.tsx
index 9a2096212d..917e70b067 100644
--- a/plugins/user-settings/src/components/AuthProviderList.tsx
+++ b/plugins/user-settings/src/components/AuthProviders.tsx
@@ -16,11 +16,14 @@
import React from 'react';
import { List } from '@material-ui/core';
+import { InfoCard } from '@backstage/core';
type Props = {
providers: React.ReactNode;
};
-export const AuthProvidersList = ({ providers }: Props) => (
- {providers}
+export const AuthProviders = ({ providers }: Props) => (
+
+ {providers}
+
);
diff --git a/plugins/user-settings/src/components/DefaultProviderSettings.test.tsx b/plugins/user-settings/src/components/DefaultProviderSettings.test.tsx
index 8f6e4b72dc..bc4e0da363 100644
--- a/plugins/user-settings/src/components/DefaultProviderSettings.test.tsx
+++ b/plugins/user-settings/src/components/DefaultProviderSettings.test.tsx
@@ -23,7 +23,7 @@ import {
} from '@backstage/core';
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
import { fireEvent } from '@testing-library/react';
-import * as React from 'react';
+import React from 'react';
import { DefaultProviderSettings } from './DefaultProviderSettings';
const mockSignInHandler = jest.fn().mockReturnValue('');
diff --git a/plugins/user-settings/src/components/FeatureFlagsList.tsx b/plugins/user-settings/src/components/FeatureFlags.tsx
similarity index 78%
rename from plugins/user-settings/src/components/FeatureFlagsList.tsx
rename to plugins/user-settings/src/components/FeatureFlags.tsx
index 2636d0e66f..d3f21a7c6e 100644
--- a/plugins/user-settings/src/components/FeatureFlagsList.tsx
+++ b/plugins/user-settings/src/components/FeatureFlags.tsx
@@ -22,12 +22,13 @@ import {
FeatureFlagName,
FeatureFlagState,
FeatureFlagsRegistryItem,
+ InfoCard,
} from '@backstage/core';
import { FlagItem } from './FeatureFlagsItem';
type Props = { featureFlags: FeatureFlagsRegistryItem[] };
-export const FeatureFlagsList = ({ featureFlags }: Props) => {
+export const FeatureFlags = ({ featureFlags }: Props) => {
const featureFlagApi = useApi(featureFlagsApiRef);
const initialFlagState = featureFlags.reduce(
(result, featureFlag: FeatureFlagsRegistryItem) => {
@@ -57,19 +58,21 @@ export const FeatureFlagsList = ({ featureFlags }: Props) => {
);
return (
-
- {featureFlags.map(featureFlag => {
- const enabled = Boolean(state[featureFlag.name]);
+
+
+ {featureFlags.map(featureFlag => {
+ const enabled = Boolean(state[featureFlag.name]);
- return (
-
- );
- })}
-
+ return (
+
+ );
+ })}
+
+
);
};
diff --git a/plugins/user-settings/src/components/AppSettingsList.tsx b/plugins/user-settings/src/components/General.tsx
similarity index 63%
rename from plugins/user-settings/src/components/AppSettingsList.tsx
rename to plugins/user-settings/src/components/General.tsx
index 3f2b455520..00ac17dfe1 100644
--- a/plugins/user-settings/src/components/AppSettingsList.tsx
+++ b/plugins/user-settings/src/components/General.tsx
@@ -13,14 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { InfoCard } from '@backstage/core';
+import { Grid, List } from '@material-ui/core';
import React from 'react';
-import { List } from '@material-ui/core';
-import { ThemeToggle } from './ThemeToggle';
import { PinButton } from './PinButton';
+import { Profile } from './Profile';
+import { ThemeToggle } from './ThemeToggle';
-export const AppSettingsList = () => (
-
-
-
-
+export const General = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
);
diff --git a/plugins/user-settings/src/components/PinButton.test.tsx b/plugins/user-settings/src/components/PinButton.test.tsx
index a800193c06..76af8ce8d4 100644
--- a/plugins/user-settings/src/components/PinButton.test.tsx
+++ b/plugins/user-settings/src/components/PinButton.test.tsx
@@ -17,7 +17,7 @@
import { SidebarPinStateContext } from '@backstage/core';
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
import { fireEvent } from '@testing-library/react';
-import * as React from 'react';
+import React from 'react';
import { PinButton } from './PinButton';
describe('', () => {
diff --git a/plugins/user-settings/src/components/Profile.tsx b/plugins/user-settings/src/components/Profile.tsx
new file mode 100644
index 0000000000..5bb9485f8d
--- /dev/null
+++ b/plugins/user-settings/src/components/Profile.tsx
@@ -0,0 +1,54 @@
+/*
+ * 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 { InfoCard } from '@backstage/core';
+import { Grid, Typography } from '@material-ui/core';
+import React from 'react';
+import { SignInAvatar } from './SignInAvatar';
+import { UserSettingsMenu } from './UserSettingsMenu';
+import { useUserProfile } from './useUserProfileInfo';
+
+export const Profile = () => {
+ const { profile, displayName } = useUserProfile();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {displayName}
+
+
+ {profile.email}
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/plugins/user-settings/src/components/UserSettings.test.tsx b/plugins/user-settings/src/components/Settings.tsx
similarity index 55%
rename from plugins/user-settings/src/components/UserSettings.test.tsx
rename to plugins/user-settings/src/components/Settings.tsx
index 1d43f4093e..ae141457df 100644
--- a/plugins/user-settings/src/components/UserSettings.test.tsx
+++ b/plugins/user-settings/src/components/Settings.tsx
@@ -14,13 +14,21 @@
* limitations under the License.
*/
-import * as React from 'react';
-import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
-import { UserSettings } from './UserSettings';
+import React from 'react';
+import { SidebarItem } from '@backstage/core';
+import { SignInAvatar } from './SignInAvatar';
+import { useUserProfile } from './useUserProfileInfo';
+import { settingsRouteRef } from '../plugin';
-describe('', () => {
- it('renders', async () => {
- const rendered = await renderWithEffects(wrapInTestApp());
- expect(rendered.getByText('G')).toBeInTheDocument();
- });
-});
+export const Settings = () => {
+ const { displayName } = useUserProfile();
+ const SidebarAvatar = () => ;
+
+ return (
+
+ );
+};
diff --git a/plugins/user-settings/src/components/SettingsDialog.test.tsx b/plugins/user-settings/src/components/SettingsDialog.test.tsx
deleted file mode 100644
index fb32e9cfdb..0000000000
--- a/plugins/user-settings/src/components/SettingsDialog.test.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2020 Spotify AB
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {
- ApiProvider,
- ApiRegistry,
- FeatureFlags,
- featureFlagsApiRef,
-} from '@backstage/core';
-import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
-import { fireEvent } from '@testing-library/react';
-import React from 'react';
-import { SettingsDialog } from './SettingsDialog';
-
-const apiRegistry = ApiRegistry.from([
- [featureFlagsApiRef, new FeatureFlags()],
-]);
-
-describe('', () => {
- const mockFn = jest.fn();
-
- it('displays the users name and email, and the tabs and titles and updates the position', async () => {
- const rendered = await renderWithEffects(
- wrapInTestApp(
-
-
- ,
- ),
- );
-
- expect(rendered.getByText('Guest')).toBeInTheDocument();
- expect(rendered.getByText('guest@example.com')).toBeInTheDocument();
- expect(rendered.getByText('App Settings')).toBeInTheDocument();
- expect(rendered.getByText('Additional Settings')).toBeInTheDocument();
- expect(rendered.getByText('Auth Providers')).toBeInTheDocument();
- expect(rendered.getByText('Feature Flags')).toBeInTheDocument();
-
- const flagButton = rendered.getByText('Feature Flags');
- expect(mockFn).toBeCalledTimes(1);
- fireEvent.click(flagButton);
- expect(mockFn).toBeCalledTimes(2);
- });
-});
diff --git a/plugins/user-settings/src/components/SettingsDialog.tsx b/plugins/user-settings/src/components/SettingsDialog.tsx
deleted file mode 100644
index c6d9cfa8c5..0000000000
--- a/plugins/user-settings/src/components/SettingsDialog.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2020 Spotify AB
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, { ChangeEvent, useEffect, useState } from 'react';
-import { Card, CardContent, CardHeader, makeStyles } from '@material-ui/core';
-import { AppSettingsList } from './AppSettingsList';
-import { AuthProvidersList } from './AuthProviderList';
-import { FeatureFlagsList } from './FeatureFlagsList';
-import { SignInAvatar } from './SignInAvatar';
-import { UserSettingsMenu } from './UserSettingsMenu';
-import { useUserProfile } from './useUserProfileInfo';
-import {
- useApi,
- featureFlagsApiRef,
- TabbedCard,
- CardTab,
- InfoCard,
-} from '@backstage/core';
-import { DefaultProviderSettings } from './DefaultProviderSettings';
-import { ProviderSettings } from './UserSettings';
-import { Alert } from '@material-ui/lab';
-
-const useStyles = makeStyles({
- root: {
- minWidth: 400,
- maxWidth: 500,
- },
-});
-
-type Props = {
- providerSettings?: ProviderSettings;
- updatePosition: () => void;
-};
-
-export const SettingsDialog = ({ providerSettings, updatePosition }: Props) => {
- const classes = useStyles();
- const { profile, displayName } = useUserProfile();
- const featureFlagsApi = useApi(featureFlagsApiRef);
- const featureFlags = featureFlagsApi.getRegisteredFlags();
- const [selectedTab, setSelectedTab] = useState('auth');
-
- const providers = providerSettings ?? ;
- const ref = React.useRef(updatePosition);
-
- const handleChange = (
- _ev: ChangeEvent<{}>,
- newSelectedTab: string | number,
- ) => {
- setSelectedTab(newSelectedTab);
- };
-
- // Update the position of the popover to handle different heights
- useEffect(() => {
- ref.current?.();
- }, [selectedTab]);
-
- return (
-
- }
- action={}
- title={displayName}
- subheader={profile.email}
- />
-
-
-
-
-
-
-
-
-
- {featureFlags.length > 0 ? (
-
- ) : (
- // TODO(marcuseide): Replace with empty state component?
- No registered Feature Flags found
- )}
-
-
-
-
- );
-};
diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx
new file mode 100644
index 0000000000..de76866653
--- /dev/null
+++ b/plugins/user-settings/src/components/SettingsPage.tsx
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, { useState } from 'react';
+import {
+ Content,
+ featureFlagsApiRef,
+ Header,
+ HeaderTabs,
+ Page,
+ pageTheme,
+ useApi,
+} from '@backstage/core';
+import { General } from './General';
+import { AuthProviders } from './AuthProviders';
+import { DefaultProviderSettings } from './DefaultProviderSettings';
+import { FeatureFlags } from './FeatureFlags';
+
+type Props = {
+ providerSettings?: JSX.Element;
+};
+
+export const SettingsPage = ({ providerSettings }: Props) => {
+ const [activeTab, setActiveTab] = useState(0);
+ const onTabChange = (index: number) => {
+ setActiveTab(index);
+ };
+ const featureFlagsApi = useApi(featureFlagsApiRef);
+ const featureFlags = featureFlagsApi.getRegisteredFlags();
+ const providers = providerSettings ?? ;
+
+ const tabs = [
+ { id: 'general', label: 'General' },
+ { id: 'auth-providers', label: 'Authentication Providers' },
+ { id: 'feature-flags', label: 'Feature Flags' },
+ ];
+
+ const content = [
+ ,
+ ,
+ ,
+ ];
+
+ return (
+
+
+
+ {content[activeTab]}
+
+ );
+};
diff --git a/plugins/user-settings/src/components/SignInAvatar.tsx b/plugins/user-settings/src/components/SignInAvatar.tsx
index 11a5e6ed99..72c636ffe7 100644
--- a/plugins/user-settings/src/components/SignInAvatar.tsx
+++ b/plugins/user-settings/src/components/SignInAvatar.tsx
@@ -20,23 +20,21 @@ import { makeStyles, Avatar } from '@material-ui/core';
import { useUserProfile } from './useUserProfileInfo';
import { sidebarConfig } from '@backstage/core';
-const useStyles = makeStyles({
+const useStyles = makeStyles(theme => ({
avatar: {
width: ({ size }) => size,
height: ({ size }) => size,
+ fontSize: ({ size }) => size * 0.7,
+ border: `1px solid ${theme.palette.textSubtle}`,
},
-});
+}));
type Props = { size?: number };
export const SignInAvatar = ({ size }: Props) => {
const { iconSize } = sidebarConfig;
const classes = useStyles(size ? { size } : { size: iconSize });
- const { profile, displayName } = useUserProfile();
+ const { profile } = useUserProfile();
- return (
-
- {displayName[0]}
-
- );
+ return ;
};
diff --git a/plugins/user-settings/src/components/ThemeToggle.test.tsx b/plugins/user-settings/src/components/ThemeToggle.test.tsx
index df36a985f6..79d9210826 100644
--- a/plugins/user-settings/src/components/ThemeToggle.test.tsx
+++ b/plugins/user-settings/src/components/ThemeToggle.test.tsx
@@ -23,7 +23,7 @@ import {
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
import { lightTheme } from '@backstage/theme';
import { fireEvent } from '@testing-library/react';
-import * as React from 'react';
+import React from 'react';
import { ThemeToggle } from './ThemeToggle';
const mockTheme = {
diff --git a/plugins/user-settings/src/components/UserSettings.tsx b/plugins/user-settings/src/components/UserSettings.tsx
deleted file mode 100644
index 34d7fa02be..0000000000
--- a/plugins/user-settings/src/components/UserSettings.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2020 Spotify AB
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React, { useEffect, useContext, useCallback } from 'react';
-import { Popover, PopoverActions } from '@material-ui/core';
-import { SignInAvatar } from './SignInAvatar';
-import { SettingsDialog } from './SettingsDialog';
-import { SidebarItem, SidebarContext } from '@backstage/core';
-import { useUserProfile } from './useUserProfileInfo';
-
-export type ProviderSettings = JSX.Element;
-
-type Props = {
- /**
- * Available auth providers. Providers will be rendered as children
- * of a MUI List.
- *
- * If undefined, providers that are configured in app-config.yaml
- * will be displayed instead.
- */
- providerSettings?: ProviderSettings;
-};
-
-export const UserSettings = ({ providerSettings }: Props) => {
- const { isOpen: sidebarOpen } = useContext(SidebarContext);
- const { displayName } = useUserProfile();
- const [open, setOpen] = React.useState(false);
- const [anchorEl, setAnchorEl] = React.useState(
- undefined,
- );
- const popoverActionRef = React.useRef(null);
-
- const updatePosition = useCallback(() => {
- popoverActionRef?.current?.updatePosition();
- }, []);
-
- const handleOpen = (event?: React.MouseEvent) => {
- setAnchorEl(event?.currentTarget ?? undefined);
- setOpen(true);
- };
-
- const handleClose = () => {
- setAnchorEl(undefined);
- setOpen(false);
- };
-
- useEffect(() => {
- if (!sidebarOpen && open) setOpen(false);
- }, [open, sidebarOpen]);
-
- const SidebarAvatar = () => ;
-
- return (
- <>
-
-
-
-
- >
- );
-};
diff --git a/plugins/user-settings/src/components/UserSettingsMenu.test.tsx b/plugins/user-settings/src/components/UserSettingsMenu.test.tsx
index b1ad059611..c86d702e30 100644
--- a/plugins/user-settings/src/components/UserSettingsMenu.test.tsx
+++ b/plugins/user-settings/src/components/UserSettingsMenu.test.tsx
@@ -16,7 +16,7 @@
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
import { fireEvent } from '@testing-library/react';
-import * as React from 'react';
+import React from 'react';
import { UserSettingsMenu } from './UserSettingsMenu';
describe('', () => {
diff --git a/plugins/user-settings/src/index.ts b/plugins/user-settings/src/index.ts
index 311ae69a51..113930902a 100644
--- a/plugins/user-settings/src/index.ts
+++ b/plugins/user-settings/src/index.ts
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export { plugin } from './plugin';
-export { UserSettings } from './components/UserSettings';
+export { Settings } from './components/Settings';
+export { SettingsPage as Router } from './components/SettingsPage';
diff --git a/plugins/user-settings/src/plugin.ts b/plugins/user-settings/src/plugin.ts
index 4b556180f2..2f896c8197 100644
--- a/plugins/user-settings/src/plugin.ts
+++ b/plugins/user-settings/src/plugin.ts
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { createPlugin } from '@backstage/core';
+import { createPlugin, createRouteRef } from '@backstage/core';
+
+export const settingsRouteRef = createRouteRef({
+ path: '/settings',
+ title: 'Settings',
+});
export const plugin = createPlugin({
id: 'user-settings',