diff --git a/.changeset/three-rivers-remain.md b/.changeset/three-rivers-remain.md
new file mode 100644
index 0000000000..3bd4b43ff7
--- /dev/null
+++ b/.changeset/three-rivers-remain.md
@@ -0,0 +1,6 @@
+---
+'example-app': minor
+'@backstage/plugin-user-settings': minor
+---
+
+Added the ability to render extra setting tabs
diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 374d637b2e..9151e55e51 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -69,7 +69,7 @@ import {
techdocsPlugin,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
-import { UserSettingsPage } from '@backstage/plugin-user-settings';
+import { UserSettingsPage, SettingsTab } from '@backstage/plugin-user-settings';
import AlarmIcon from '@material-ui/icons/Alarm';
import React from 'react';
import { hot } from 'react-hot-loader/root';
@@ -82,7 +82,7 @@ import { LowerCaseValuePickerFieldExtension } from './components/scaffolder/cust
import { searchPage } from './components/search/SearchPage';
import { providers } from './identityProviders';
import * as plugins from './plugins';
-
+import { AdvancedSettings } from './components/advancedSettings';
import { techDocsPage } from './components/techdocs/TechDocsPage';
import { ApacheAirflowPage } from '@backstage/plugin-apache-airflow';
import { PermissionedRoute } from '@backstage/plugin-permission-react';
@@ -127,6 +127,10 @@ const app = createApp({
const AppProvider = app.getProvider();
const AppRouter = app.getRouter();
+const extraSettingTabs: SettingsTab[] = [
+ { title: 'Advanced', content: },
+];
+
const routes = (
@@ -215,7 +219,10 @@ const routes = (
path="/cost-insights/labeling-jobs"
element={}
/>
- } />
+ }
+ />
} />
} />
diff --git a/packages/app/src/components/advancedSettings/AdvancedSettings.tsx b/packages/app/src/components/advancedSettings/AdvancedSettings.tsx
new file mode 100644
index 0000000000..324605c1e8
--- /dev/null
+++ b/packages/app/src/components/advancedSettings/AdvancedSettings.tsx
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * 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 { InfoCard } from '@backstage/core-components';
+import {
+ List,
+ Grid,
+ ListItem,
+ ListItemText,
+ ListItemSecondaryAction,
+ Switch,
+} from '@material-ui/core';
+import { useLocalStorage } from 'react-use';
+
+export function AdvancedSettings() {
+ const [value, setValue] = useLocalStorage<'on' | 'off'>(
+ 'advanced-option',
+ 'off',
+ );
+
+ const toggleValue = (ev: React.ChangeEvent) => {
+ setValue(ev.currentTarget.checked ? 'on' : 'off');
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/app/src/components/advancedSettings/index.ts b/packages/app/src/components/advancedSettings/index.ts
new file mode 100644
index 0000000000..9d5e946e99
--- /dev/null
+++ b/packages/app/src/components/advancedSettings/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export * from './AdvancedSettings';
diff --git a/plugins/user-settings/README.md b/plugins/user-settings/README.md
index db0b01681f..ed6c648648 100644
--- a/plugins/user-settings/README.md
+++ b/plugins/user-settings/README.md
@@ -63,3 +63,31 @@ const AppRoutes = () => (
```
> **Note that the list of providers expects to be rendered within a MUI [``](https://material-ui.com/components/lists/)**
+
+**Tabs**
+By default, the plugin renders 3 tabs of settings; GENERAL, AUTHENTICATION PROVIDERS, and FEATURE FLAGS.
+
+If you want to add more options for your users, use the `tabs` prop:
+
+```tsx
+import { UserSettingsPage, SettingsTab } from '@backstage/plugin-user-settings';
+
+const extraSettingTabs: SettingsTab[] = [
+ { title: 'Advanced', content: },
+];
+
+const AppRoutes = () => (
+
+ }
+ />
+
+);
+```
+
+To standardize the UI of all setting tabs,
+make sure you use a similar component structure as the other tabs.
+You can take a look at
+[the example extra tab](https://github.com/backstage/backstage/blob/master/packages/app/src/components/advancedSettings/AdvancedSettings.tsx)
+we have created in Backstage's demo app.
diff --git a/plugins/user-settings/src/components/SettingsPage.test.tsx b/plugins/user-settings/src/components/SettingsPage.test.tsx
new file mode 100644
index 0000000000..846f120a6b
--- /dev/null
+++ b/plugins/user-settings/src/components/SettingsPage.test.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * 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 { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
+import { SettingsPage, SettingsTab } from './SettingsPage';
+
+describe('', () => {
+ it('should render the settings page with 3 tabs', async () => {
+ const { container } = await renderWithEffects(
+ wrapInTestApp(),
+ );
+
+ const tabs = container.querySelectorAll('[class*=MuiTabs-root] button');
+ expect(tabs).toHaveLength(3);
+ });
+
+ it('should render the settings page with 4 tabs when extra tabs are provided', async () => {
+ const extraTabs: SettingsTab[] = [
+ { title: 'Advanced', content: advanced content
},
+ ];
+ const { container } = await renderWithEffects(
+ wrapInTestApp(),
+ );
+
+ const tabs = container.querySelectorAll('[class*=MuiTabs-root] button');
+ expect(tabs).toHaveLength(4);
+ expect(tabs[3].textContent).toEqual(extraTabs[0].title);
+ });
+});
diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx
index 88b46fc372..a8ef1bda8f 100644
--- a/plugins/user-settings/src/components/SettingsPage.tsx
+++ b/plugins/user-settings/src/components/SettingsPage.tsx
@@ -25,11 +25,17 @@ import { UserSettingsAuthProviders } from './AuthProviders';
import { UserSettingsFeatureFlags } from './FeatureFlags';
import { UserSettingsGeneral } from './General';
+export interface SettingsTab {
+ title: string;
+ content: React.ReactElement;
+}
+
type Props = {
providerSettings?: JSX.Element;
+ tabs?: SettingsTab[];
};
-export const SettingsPage = ({ providerSettings }: Props) => {
+export const SettingsPage = ({ providerSettings, tabs }: Props) => {
const { isMobile } = useContext(SidebarPinStateContext);
return (
@@ -48,6 +54,15 @@ export const SettingsPage = ({ providerSettings }: Props) => {
+ {tabs?.map(({ title, content }) => {
+ // Hyphenated the title
+ const path = title.toLocaleLowerCase().replace(/\s/, '-');
+ return (
+
+ {content}
+
+ );
+ })}
);
diff --git a/plugins/user-settings/src/components/index.ts b/plugins/user-settings/src/components/index.ts
index cb058f9c5e..a1dae855d8 100644
--- a/plugins/user-settings/src/components/index.ts
+++ b/plugins/user-settings/src/components/index.ts
@@ -15,6 +15,7 @@
*/
export { Settings } from './Settings';
export { SettingsPage as Router } from './SettingsPage';
+export type { SettingsTab } from './SettingsPage';
export * from './AuthProviders';
export * from './General';
export * from './FeatureFlags';