Used Layout pattern in SettingsPage, analogous to Explore Plugin

Signed-off-by: Nikita Karpukhin <nikita.karpukhin@scout24.com>
This commit is contained in:
Nikita Karpukhin
2022-10-31 12:53:15 +01:00
parent 405370d5fe
commit 1083a2c47e
8 changed files with 190 additions and 67 deletions
+18 -8
View File
@@ -60,18 +60,18 @@ import { HomepageCompositionRoot } from '@backstage/plugin-home';
import { LighthousePage } from '@backstage/plugin-lighthouse';
import { NewRelicPage } from '@backstage/plugin-newrelic';
import {
ScaffolderFieldExtensions,
ScaffolderPage,
NextScaffolderPage,
scaffolderPlugin,
ScaffolderFieldExtensions,
ScaffolderLayouts,
ScaffolderPage,
scaffolderPlugin,
} from '@backstage/plugin-scaffolder';
import { SearchPage } from '@backstage/plugin-search';
import { TechRadarPage } from '@backstage/plugin-tech-radar';
import {
TechDocsIndexPage,
TechDocsReaderPage,
techdocsPlugin,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
import {
@@ -80,8 +80,10 @@ import {
TextSize,
} from '@backstage/plugin-techdocs-module-addons-contrib';
import {
SettingsLayout,
UserSettingsFeatureFlags,
UserSettingsGeneral,
UserSettingsPage,
UserSettingsTab,
} from '@backstage/plugin-user-settings';
import { AdvancedSettings } from './components/advancedSettings';
import AlarmIcon from '@material-ui/icons/Alarm';
@@ -267,9 +269,17 @@ const routes = (
element={<CostInsightsLabelDataflowInstructionsPage />}
/>
<Route path="/settings" element={<UserSettingsPage />}>
<UserSettingsTab path="/advanced" title="Advanced">
<AdvancedSettings />
</UserSettingsTab>
<SettingsLayout>
<SettingsLayout.Route path="general" title="General">
<UserSettingsGeneral />
</SettingsLayout.Route>
<SettingsLayout.Route path="feature-flags" title="Feature Flags">
<UserSettingsFeatureFlags />
</SettingsLayout.Route>
<SettingsLayout.Route path="advanced" title="Advanced">
<AdvancedSettings />
</SettingsLayout.Route>
</SettingsLayout>
</Route>
<Route path="/azure-pull-requests" element={<AzurePullRequestsPage />} />
<Route path="/apache-airflow" element={<ApacheAirflowPage />} />
@@ -16,16 +16,15 @@
import React from 'react';
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
import { SettingsPage } from './SettingsPage';
import { UserSettingsTab } from './UserSettingsTab';
import { DefaultSettingsPage } from './DefaultSettingsPage';
import { UserSettingsTab } from '../UserSettingsTab';
import { useOutlet } from 'react-router';
jest.mock('react-router', () => ({
...jest.requireActual('react-router'),
useOutlet: jest.fn().mockReturnValue(undefined),
}));
import { useOutlet } from 'react-router';
describe('<SettingsPage />', () => {
beforeEach(() => {
(useOutlet as jest.Mock).mockReset();
@@ -33,7 +32,7 @@ describe('<SettingsPage />', () => {
it('should render the settings page with 3 tabs', async () => {
const { container } = await renderWithEffects(
wrapInTestApp(<SettingsPage />),
wrapInTestApp(<DefaultSettingsPage />),
);
const tabs = container.querySelectorAll('[class*=MuiTabs-root] button');
@@ -48,7 +47,7 @@ describe('<SettingsPage />', () => {
);
(useOutlet as jest.Mock).mockReturnValue(advancedTabRoute);
const { container } = await renderWithEffects(
wrapInTestApp(<SettingsPage />),
wrapInTestApp(<DefaultSettingsPage />),
);
const tabs = container.querySelectorAll('[class*=MuiTabs-root] button');
@@ -0,0 +1,47 @@
/*
* 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 { UserSettingsAuthProviders } from '../AuthProviders';
import { UserSettingsFeatureFlags } from '../FeatureFlags';
import { UserSettingsGeneral } from '../General';
import { SettingsLayout } from '../SettingsLayout';
/**
* @public
*/
export const DefaultSettingsPage = (props: {
providerSettings?: JSX.Element;
}) => {
const { providerSettings } = props;
return (
<SettingsLayout>
<SettingsLayout.Route path="general" title="General">
<UserSettingsGeneral />
</SettingsLayout.Route>
<SettingsLayout.Route
path="auth-providers"
title="Authentication Providers"
>
<UserSettingsAuthProviders providerSettings={providerSettings} />
</SettingsLayout.Route>
<SettingsLayout.Route path="feature-flags" title="Feature Flags">
<UserSettingsFeatureFlags />
</SettingsLayout.Route>
</SettingsLayout>
);
};
@@ -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 { DefaultSettingsPage } from './DefaultSettingsPage';
@@ -0,0 +1,79 @@
/*
* 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 { TabProps } from '@material-ui/core';
import {
Header,
Page,
RoutedTabs,
useSidebarPinState,
} from '@backstage/core-components';
import {
attachComponentData,
useElementFilter,
} from '@backstage/core-plugin-api';
/** @public */
export type SubRoute = {
path: string;
title: string;
children: JSX.Element;
tabProps?: TabProps<React.ElementType, { component?: React.ElementType }>;
};
const dataKey = 'plugin.explore.settingsLayoutRoute';
const Route: (props: SubRoute) => null = () => null;
attachComponentData(Route, dataKey, true);
// This causes all mount points that are discovered within this route to use the path of the route itself
attachComponentData(Route, 'core.gatherMountPoints', true);
/** @public */
export type SettingsLayoutProps = {
title?: string;
subtitle?: string;
children?: React.ReactNode;
};
/**
* @public
*/
export const SettingsLayout = (props: SettingsLayoutProps) => {
const { title, children } = props;
const { isMobile } = useSidebarPinState();
const routes = useElementFilter(children, elements =>
elements
.selectByComponentData({
key: dataKey,
withStrictError:
'Child of SettingsLayout must be an SettingsLayout.Route',
})
.getElements<SubRoute>()
.map(child => child.props),
);
return (
<Page themeId="home">
{!isMobile && <Header title={title ?? 'Settings'} />}
<RoutedTabs routes={routes} />
</Page>
);
};
SettingsLayout.Route = Route;
@@ -0,0 +1,18 @@
/*
* 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 type { SettingsLayoutProps, SubRoute } from './SettingsLayout';
export { SettingsLayout } from './SettingsLayout';
@@ -1,5 +1,5 @@
/*
* Copyright 2020 The Backstage Authors
* 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.
@@ -13,60 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Header,
Page,
TabbedLayout,
useSidebarPinState,
} from '@backstage/core-components';
import React from 'react';
import { useOutlet } from 'react-router';
import { useElementFilter } from '@backstage/core-plugin-api';
import { UserSettingsAuthProviders } from './AuthProviders';
import { UserSettingsFeatureFlags } from './FeatureFlags';
import { UserSettingsGeneral } from './General';
import { USER_SETTINGS_TAB_KEY, UserSettingsTabProps } from './UserSettingsTab';
import React from 'react';
import { DefaultSettingsPage } from './DefaultSettingsPage';
/**
* @public
*/
export const SettingsPage = (props: { providerSettings?: JSX.Element }) => {
const { providerSettings } = props;
const { isMobile } = useSidebarPinState();
export const SettingsPage = () => {
const outlet = useOutlet();
const tabs = useElementFilter(outlet, elements =>
elements
.selectByComponentData({
key: USER_SETTINGS_TAB_KEY,
})
.getElements<UserSettingsTabProps>(),
);
return (
<Page themeId="home">
{!isMobile && <Header title="Settings" />}
<TabbedLayout>
<TabbedLayout.Route path="general" title="General">
<UserSettingsGeneral />
</TabbedLayout.Route>
<TabbedLayout.Route
path="auth-providers"
title="Authentication Providers"
>
<UserSettingsAuthProviders providerSettings={providerSettings} />
</TabbedLayout.Route>
<TabbedLayout.Route path="feature-flags" title="Feature Flags">
<UserSettingsFeatureFlags />
</TabbedLayout.Route>
{tabs.map((child, i) => (
<TabbedLayout.Route key={i} {...child.props}>
{child}
</TabbedLayout.Route>
))}
</TabbedLayout>
</Page>
);
return <>{outlet || <DefaultSettingsPage />}</>;
};
@@ -21,3 +21,4 @@ export * from './General';
export * from './FeatureFlags';
export { useUserProfile } from './useUserProfileInfo';
export * from './UserSettingsTab';
export * from './SettingsLayout';