Merge pull request #21323 from backstage/mob/sign-in-page

frontend-app-api: implement sign-in page
This commit is contained in:
Patrik Oldsberg
2023-11-23 14:58:23 +01:00
committed by GitHub
17 changed files with 418 additions and 50 deletions
@@ -68,6 +68,7 @@ import { default as React_2 } from 'react';
import { ReactNode } from 'react';
import { SessionApi } from '@backstage/core-plugin-api';
import { SessionState } from '@backstage/core-plugin-api';
import { SignInPageProps } from '@backstage/core-plugin-api';
import { StorageApi } from '@backstage/core-plugin-api';
import { storageApiRef } from '@backstage/core-plugin-api';
import { StorageValueSnapshot } from '@backstage/core-plugin-api';
@@ -455,6 +456,25 @@ export function createSchemaFromZod<TOutput, TInput>(
schemaCreator: (zImpl: typeof z) => ZodSchema<TOutput, ZodTypeDef, TInput>,
): PortableSchema<TOutput>;
// @public (undocumented)
export function createSignInPageExtension<
TConfig extends {},
TInputs extends AnyExtensionInputMap,
>(options: {
id: string;
attachTo?: {
id: string;
input: string;
};
configSchema?: PortableSchema<TConfig>;
disabled?: boolean;
inputs?: TInputs;
loader: (options: {
config: TConfig;
inputs: Expand<ExtensionInputValues<TInputs>>;
}) => Promise<ComponentType<SignInPageProps>>;
}): Extension<TConfig>;
// @public
export function createSubRouteRef<
Path extends string,
@@ -0,0 +1,47 @@
/*
* Copyright 2023 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 { createExtensionTester } from '@backstage/frontend-test-utils';
import { screen } from '@testing-library/react';
import { createSignInPageExtension } from './createSignInPageExtension';
import { coreExtensionData, createExtension } from '../wiring';
describe('createSignInPageExtension', () => {
it('renders a sign-in page', async () => {
const SignInPage = createSignInPageExtension({
id: 'test',
loader: async () => () => <div data-testid="sign-in-page" />,
});
createExtensionTester(
createExtension({
id: 'dummy',
attachTo: { id: 'ignored', input: 'ignored' },
output: {
element: coreExtensionData.reactElement,
},
factory: () => ({ element: <div /> }),
}),
)
.add(SignInPage)
.render();
await expect(
screen.findByTestId('sign-in-page'),
).resolves.toBeInTheDocument();
});
});
@@ -0,0 +1,79 @@
/*
* Copyright 2023 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, { ComponentType, lazy } from 'react';
import { ExtensionBoundary } from '../components';
import { PortableSchema } from '../schema';
import {
createExtension,
Extension,
ExtensionInputValues,
AnyExtensionInputMap,
createExtensionDataRef,
} from '../wiring';
import { Expand } from '../types';
import { SignInPageProps } from '@backstage/core-plugin-api';
/** @internal */
export const signInPageComponentDataRef =
createExtensionDataRef<ComponentType<SignInPageProps>>('core.signInPage');
/**
*
* @public
*/
export function createSignInPageExtension<
TConfig extends {},
TInputs extends AnyExtensionInputMap,
>(options: {
id: string;
attachTo?: { id: string; input: string };
configSchema?: PortableSchema<TConfig>;
disabled?: boolean;
inputs?: TInputs;
loader: (options: {
config: TConfig;
inputs: Expand<ExtensionInputValues<TInputs>>;
}) => Promise<ComponentType<SignInPageProps>>;
}): Extension<TConfig> {
const { id } = options;
return createExtension({
id,
attachTo: options.attachTo ?? { id: 'core.router', input: 'signInPage' },
configSchema: options.configSchema,
inputs: options.inputs,
disabled: options.disabled,
output: {
component: signInPageComponentDataRef,
},
factory({ config, inputs, source }) {
const ExtensionComponent = lazy(() =>
options
.loader({ config, inputs })
.then(component => ({ default: component })),
);
return {
component: props => (
<ExtensionBoundary id={id} source={source} routable>
<ExtensionComponent {...props} />
</ExtensionBoundary>
),
};
},
});
}
@@ -17,4 +17,5 @@
export { createApiExtension } from './createApiExtension';
export { createPageExtension } from './createPageExtension';
export { createNavItemExtension } from './createNavItemExtension';
export { createSignInPageExtension } from './createSignInPageExtension';
export { createThemeExtension } from './createThemeExtension';
@@ -133,7 +133,7 @@ describe('createPlugin', () => {
await renderWithEffects(
createTestAppRoot({
features: [plugin],
config: { app: { extensions: [{ 'core.layout': false }] } },
config: { app: { extensions: [{ 'core.router': false }] } },
}),
);
@@ -161,7 +161,7 @@ describe('createPlugin', () => {
config: {
app: {
extensions: [
{ 'core.layout': false },
{ 'core.router': false },
{
'plugin.catalog.page': {
config: { name: 'CatalogRenamed' },