From 016c574b5137d20f0fac516afb178351e6ea27b5 Mon Sep 17 00:00:00 2001 From: Yousif Al-Raheem Date: Mon, 7 Mar 2022 18:31:16 +0100 Subject: [PATCH] enable adding extra setting tabs Signed-off-by: Yousif Al-Raheem --- .changeset/three-rivers-remain.md | 6 ++ packages/app/src/App.tsx | 13 +++- .../advancedSettings/AdvancedSettings.tsx | 63 +++++++++++++++++++ .../src/components/advancedSettings/index.ts | 17 +++++ plugins/user-settings/README.md | 28 +++++++++ .../src/components/SettingsPage.test.tsx | 43 +++++++++++++ .../src/components/SettingsPage.tsx | 17 ++++- plugins/user-settings/src/components/index.ts | 1 + 8 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 .changeset/three-rivers-remain.md create mode 100644 packages/app/src/components/advancedSettings/AdvancedSettings.tsx create mode 100644 packages/app/src/components/advancedSettings/index.ts create mode 100644 plugins/user-settings/src/components/SettingsPage.test.tsx 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';