Merge pull request #26005 from backstage/blam/nfs/last-of-the-extensions
NFS: Migrate the last of the `create*Extension` creators
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
'@backstage/plugin-search-react': patch
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead.
|
||||
@@ -6,16 +6,19 @@
|
||||
/// <reference types="react" />
|
||||
|
||||
import { AnyExtensionInputMap } from '@backstage/frontend-plugin-api';
|
||||
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { PortableSchema } from '@backstage/frontend-plugin-api';
|
||||
import { default as React_2 } from 'react';
|
||||
import { ResolvedExtensionInputs } from '@backstage/frontend-plugin-api';
|
||||
import { ResourcePermission } from '@backstage/plugin-permission-common';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
// @alpha (undocumented)
|
||||
// @alpha @deprecated (undocumented)
|
||||
export const catalogExtensionData: {
|
||||
entityContentTitle: ConfigurableExtensionDataRef<
|
||||
string,
|
||||
@@ -92,7 +95,7 @@ export const catalogReactTranslationRef: TranslationRef<
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
// @alpha @deprecated (undocumented)
|
||||
export function createEntityCardExtension<
|
||||
TConfig extends {
|
||||
filter?: string;
|
||||
@@ -125,7 +128,7 @@ export function createEntityCardExtension<
|
||||
string | undefined
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
// @alpha @deprecated (undocumented)
|
||||
export function createEntityContentExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(options: {
|
||||
@@ -164,6 +167,117 @@ export function createEntityContentExtension<
|
||||
string | undefined
|
||||
>;
|
||||
|
||||
// @alpha
|
||||
export const EntityCardBlueprint: ExtensionBlueprint<
|
||||
'entity-card',
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<React_2.JSX.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
{
|
||||
filterFunction: ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{}
|
||||
>;
|
||||
filterExpression: ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha
|
||||
export const EntityContentBlueprint: ExtensionBlueprint<
|
||||
'entity-content',
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
loader: () => Promise<JSX.Element>;
|
||||
defaultPath: string;
|
||||
defaultTitle: string;
|
||||
routeRef?: RouteRef<AnyRouteRefParams> | undefined;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<React_2.JSX.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
RouteRef<AnyRouteRefParams>,
|
||||
'core.routing.ref',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<string, 'catalog.entity-content-title', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
{
|
||||
path: string | undefined;
|
||||
title: string | undefined;
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
title?: string | undefined;
|
||||
path?: string | undefined;
|
||||
},
|
||||
{
|
||||
title: ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-content-title',
|
||||
{}
|
||||
>;
|
||||
filterFunction: ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{}
|
||||
>;
|
||||
filterExpression: ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha
|
||||
export function isOwnerOf(owner: Entity, entity: Entity): boolean;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./alpha": "./src/alpha.tsx",
|
||||
"./alpha": "./src/alpha/index.ts",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
@@ -37,7 +37,7 @@
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"alpha": [
|
||||
"src/alpha.tsx"
|
||||
"src/alpha/index.ts"
|
||||
],
|
||||
"package.json": [
|
||||
"package.json"
|
||||
@@ -85,6 +85,7 @@
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/frontend-test-utils": "workspace:^",
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-common": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2024 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 { EntityCardBlueprint } from './EntityCardBlueprint';
|
||||
import {
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionInput,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { waitFor, screen } from '@testing-library/react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
describe('EntityCardBlueprint', () => {
|
||||
it('should return an extension with sensible defaults', () => {
|
||||
const extension = EntityCardBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
filter: 'has:labels',
|
||||
loader: async () => <div>im a card</div>,
|
||||
},
|
||||
});
|
||||
|
||||
expect(extension).toMatchInlineSnapshot(`
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDefinition",
|
||||
"attachTo": {
|
||||
"id": "entity-content:catalog/overview",
|
||||
"input": "cards",
|
||||
},
|
||||
"configSchema": {
|
||||
"parse": [Function],
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"filter": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
"disabled": false,
|
||||
"factory": [Function],
|
||||
"inputs": {},
|
||||
"kind": "entity-card",
|
||||
"name": "test",
|
||||
"namespace": undefined,
|
||||
"output": [
|
||||
[Function],
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDataRef",
|
||||
"config": {
|
||||
"optional": true,
|
||||
},
|
||||
"id": "catalog.entity-filter-function",
|
||||
"optional": [Function],
|
||||
"toString": [Function],
|
||||
},
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDataRef",
|
||||
"config": {
|
||||
"optional": true,
|
||||
},
|
||||
"id": "catalog.entity-filter-expression",
|
||||
"optional": [Function],
|
||||
"toString": [Function],
|
||||
},
|
||||
],
|
||||
"override": [Function],
|
||||
"toString": [Function],
|
||||
"version": "v2",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should output the correct filter output', () => {
|
||||
const mockFilter = (_entity: Entity) => true;
|
||||
|
||||
expect(
|
||||
createExtensionTester(
|
||||
EntityCardBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
loader: async () => <div>Test!</div>,
|
||||
filter: 'test',
|
||||
},
|
||||
}),
|
||||
).data(EntityCardBlueprint.dataRefs.filterExpression),
|
||||
).toBe('test');
|
||||
|
||||
expect(
|
||||
createExtensionTester(
|
||||
EntityCardBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
loader: async () => <div>Test!</div>,
|
||||
},
|
||||
}),
|
||||
{ config: { filter: 'test' } },
|
||||
).data(EntityCardBlueprint.dataRefs.filterExpression),
|
||||
).toBe('test');
|
||||
|
||||
expect(
|
||||
createExtensionTester(
|
||||
EntityCardBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
filter: mockFilter,
|
||||
loader: async () => <div>Test!</div>,
|
||||
},
|
||||
}),
|
||||
).data(EntityCardBlueprint.dataRefs.filterFunction),
|
||||
).toBe(mockFilter);
|
||||
});
|
||||
|
||||
it('should allow overriding config and inputs', async () => {
|
||||
const extension = EntityCardBlueprint.makeWithOverrides({
|
||||
name: 'test',
|
||||
inputs: {
|
||||
mock: createExtensionInput([coreExtensionData.reactElement]),
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
mock: z => z.string(),
|
||||
},
|
||||
},
|
||||
factory(originalFactory, { inputs, config }) {
|
||||
return originalFactory({
|
||||
loader: async () => (
|
||||
<div data-testid="test">
|
||||
config: {config.mock}
|
||||
<div data-testid="contents">
|
||||
{inputs.mock.map((i, k) => (
|
||||
<div key={k}>{i.get(coreExtensionData.reactElement)}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const mockExtension = createExtension({
|
||||
attachTo: { id: 'entity-card:test', input: 'mock' },
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory() {
|
||||
return [coreExtensionData.reactElement(<div>im a mock</div>)];
|
||||
},
|
||||
});
|
||||
|
||||
createExtensionTester(extension, { config: { mock: 'mock test config' } })
|
||||
.add(mockExtension)
|
||||
.render();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('test')).toHaveTextContent(
|
||||
'config: mock test config',
|
||||
);
|
||||
expect(screen.getByTestId('contents')).toHaveTextContent('im a mock');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2024 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, { lazy } from 'react';
|
||||
import {
|
||||
ExtensionBoundary,
|
||||
coreExtensionData,
|
||||
createExtensionBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { catalogExtensionData } from '../extensions';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* A blueprint for creating cards for the entity pages in the catalog.
|
||||
*/
|
||||
export const EntityCardBlueprint = createExtensionBlueprint({
|
||||
kind: 'entity-card',
|
||||
attachTo: { id: 'entity-content:catalog/overview', input: 'cards' },
|
||||
output: [
|
||||
coreExtensionData.reactElement,
|
||||
catalogExtensionData.entityFilterFunction.optional(),
|
||||
catalogExtensionData.entityFilterExpression.optional(),
|
||||
],
|
||||
dataRefs: {
|
||||
filterFunction: catalogExtensionData.entityFilterFunction,
|
||||
filterExpression: catalogExtensionData.entityFilterExpression,
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
filter: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
*factory(
|
||||
{
|
||||
loader,
|
||||
filter,
|
||||
}: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?:
|
||||
| typeof catalogExtensionData.entityFilterFunction.T
|
||||
| typeof catalogExtensionData.entityFilterExpression.T;
|
||||
},
|
||||
{ node, config },
|
||||
) {
|
||||
const ExtensionComponent = lazy(() =>
|
||||
loader().then(element => ({ default: () => element })),
|
||||
);
|
||||
|
||||
yield coreExtensionData.reactElement(
|
||||
<ExtensionBoundary node={node}>
|
||||
<ExtensionComponent />
|
||||
</ExtensionBoundary>,
|
||||
);
|
||||
|
||||
if (config.filter) {
|
||||
yield catalogExtensionData.entityFilterExpression(config.filter);
|
||||
} else if (typeof filter === 'string') {
|
||||
yield catalogExtensionData.entityFilterExpression(filter);
|
||||
} else if (typeof filter === 'function') {
|
||||
yield catalogExtensionData.entityFilterFunction(filter);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright 2024 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 { EntityContentBlueprint } from './EntityContentBlueprint';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import {
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionInput,
|
||||
createRouteRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { waitFor, screen } from '@testing-library/react';
|
||||
|
||||
describe('EntityContentBlueprint', () => {
|
||||
it('should return an extension with sane defaults', () => {
|
||||
const extension = EntityContentBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
defaultPath: '/test',
|
||||
defaultTitle: 'Test',
|
||||
loader: async () => <div>Test!</div>,
|
||||
},
|
||||
});
|
||||
|
||||
expect(extension).toMatchInlineSnapshot(`
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDefinition",
|
||||
"attachTo": {
|
||||
"id": "page:catalog/entity",
|
||||
"input": "contents",
|
||||
},
|
||||
"configSchema": {
|
||||
"parse": [Function],
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"filter": {
|
||||
"type": "string",
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
"disabled": false,
|
||||
"factory": [Function],
|
||||
"inputs": {},
|
||||
"kind": "entity-content",
|
||||
"name": "test",
|
||||
"namespace": undefined,
|
||||
"output": [
|
||||
[Function],
|
||||
[Function],
|
||||
[Function],
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDataRef",
|
||||
"config": {
|
||||
"optional": true,
|
||||
},
|
||||
"id": "core.routing.ref",
|
||||
"optional": [Function],
|
||||
"toString": [Function],
|
||||
},
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDataRef",
|
||||
"config": {
|
||||
"optional": true,
|
||||
},
|
||||
"id": "catalog.entity-filter-function",
|
||||
"optional": [Function],
|
||||
"toString": [Function],
|
||||
},
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDataRef",
|
||||
"config": {
|
||||
"optional": true,
|
||||
},
|
||||
"id": "catalog.entity-filter-expression",
|
||||
"optional": [Function],
|
||||
"toString": [Function],
|
||||
},
|
||||
],
|
||||
"override": [Function],
|
||||
"toString": [Function],
|
||||
"version": "v2",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should emit the correct defaults', () => {
|
||||
const mockRouteRef = createRouteRef();
|
||||
const extension = EntityContentBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
defaultPath: '/test',
|
||||
defaultTitle: 'Test',
|
||||
routeRef: mockRouteRef,
|
||||
loader: async () => <div>Test!</div>,
|
||||
},
|
||||
});
|
||||
|
||||
const tester = createExtensionTester(extension);
|
||||
|
||||
// todo(blam): route paths are always set to / in the createExtensionTester. This will work eventually.
|
||||
// expect(tester.data(coreExtensionData.routePath)).toBe('/test');
|
||||
|
||||
expect(tester.data(coreExtensionData.routeRef)).toBe(mockRouteRef);
|
||||
expect(tester.data(EntityContentBlueprint.dataRefs.title)).toBe('Test');
|
||||
});
|
||||
|
||||
it('should emit the correct filter output', () => {
|
||||
const mockFilter = (_entity: Entity) => true;
|
||||
|
||||
expect(
|
||||
createExtensionTester(
|
||||
EntityContentBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
defaultPath: '/test',
|
||||
defaultTitle: 'Test',
|
||||
loader: async () => <div>Test!</div>,
|
||||
filter: 'test',
|
||||
},
|
||||
}),
|
||||
).data(EntityContentBlueprint.dataRefs.filterExpression),
|
||||
).toBe('test');
|
||||
|
||||
expect(
|
||||
createExtensionTester(
|
||||
EntityContentBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
defaultPath: '/test',
|
||||
defaultTitle: 'Test',
|
||||
loader: async () => <div>Test!</div>,
|
||||
},
|
||||
}),
|
||||
{ config: { filter: 'test' } },
|
||||
).data(EntityContentBlueprint.dataRefs.filterExpression),
|
||||
).toBe('test');
|
||||
|
||||
expect(
|
||||
createExtensionTester(
|
||||
EntityContentBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
defaultPath: '/test',
|
||||
defaultTitle: 'Test',
|
||||
filter: mockFilter,
|
||||
loader: async () => <div>Test!</div>,
|
||||
},
|
||||
}),
|
||||
).data(EntityContentBlueprint.dataRefs.filterFunction),
|
||||
).toBe(mockFilter);
|
||||
});
|
||||
|
||||
it('should allow overriding config and inputs', async () => {
|
||||
const extension = EntityContentBlueprint.makeWithOverrides({
|
||||
name: 'test',
|
||||
inputs: {
|
||||
mock: createExtensionInput([coreExtensionData.reactElement]),
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
mock: z => z.string(),
|
||||
},
|
||||
},
|
||||
factory(originalFactory, { inputs, config }) {
|
||||
return originalFactory({
|
||||
defaultPath: '/test',
|
||||
defaultTitle: 'Test',
|
||||
loader: async () => (
|
||||
<div data-testid="test">
|
||||
config: {config.mock}
|
||||
<div data-testid="contents">
|
||||
{inputs.mock.map((i, k) => (
|
||||
<div key={k}>{i.get(coreExtensionData.reactElement)}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const mockExtension = createExtension({
|
||||
attachTo: { id: 'entity-content:test', input: 'mock' },
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory() {
|
||||
return [coreExtensionData.reactElement(<div>im a mock</div>)];
|
||||
},
|
||||
});
|
||||
|
||||
createExtensionTester(extension, { config: { mock: 'mock test config' } })
|
||||
.add(mockExtension)
|
||||
.render();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('test')).toHaveTextContent(
|
||||
'config: mock test config',
|
||||
);
|
||||
expect(screen.getByTestId('contents')).toHaveTextContent('im a mock');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2024 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, { lazy } from 'react';
|
||||
import {
|
||||
coreExtensionData,
|
||||
createExtensionBlueprint,
|
||||
ExtensionBoundary,
|
||||
RouteRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { catalogExtensionData } from '../extensions';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* Creates an EntityContent extension.
|
||||
*/
|
||||
export const EntityContentBlueprint = createExtensionBlueprint({
|
||||
kind: 'entity-content',
|
||||
attachTo: { id: 'page:catalog/entity', input: 'contents' },
|
||||
output: [
|
||||
coreExtensionData.reactElement,
|
||||
coreExtensionData.routePath,
|
||||
catalogExtensionData.entityContentTitle,
|
||||
coreExtensionData.routeRef.optional(),
|
||||
catalogExtensionData.entityFilterFunction.optional(),
|
||||
catalogExtensionData.entityFilterExpression.optional(),
|
||||
],
|
||||
dataRefs: {
|
||||
title: catalogExtensionData.entityContentTitle,
|
||||
filterFunction: catalogExtensionData.entityFilterFunction,
|
||||
filterExpression: catalogExtensionData.entityFilterExpression,
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
path: z => z.string().optional(),
|
||||
title: z => z.string().optional(),
|
||||
filter: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
*factory(
|
||||
{
|
||||
loader,
|
||||
defaultPath,
|
||||
defaultTitle,
|
||||
filter,
|
||||
routeRef,
|
||||
}: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
defaultPath: string;
|
||||
defaultTitle: string;
|
||||
routeRef?: RouteRef;
|
||||
filter?:
|
||||
| typeof catalogExtensionData.entityFilterFunction.T
|
||||
| typeof catalogExtensionData.entityFilterExpression.T;
|
||||
},
|
||||
{ node, config },
|
||||
) {
|
||||
const path = config.path ?? defaultPath;
|
||||
const title = config.title ?? defaultTitle;
|
||||
|
||||
const ExtensionComponent = lazy(() =>
|
||||
loader().then(element => ({ default: () => element })),
|
||||
);
|
||||
|
||||
yield coreExtensionData.reactElement(
|
||||
<ExtensionBoundary node={node}>
|
||||
<ExtensionComponent />
|
||||
</ExtensionBoundary>,
|
||||
);
|
||||
|
||||
yield coreExtensionData.routePath(path);
|
||||
|
||||
yield catalogExtensionData.entityContentTitle(title);
|
||||
|
||||
if (routeRef) {
|
||||
yield coreExtensionData.routeRef(routeRef);
|
||||
}
|
||||
|
||||
if (config.filter) {
|
||||
yield catalogExtensionData.entityFilterExpression(config.filter);
|
||||
} else if (typeof filter === 'string') {
|
||||
yield catalogExtensionData.entityFilterExpression(filter);
|
||||
} else if (typeof filter === 'function') {
|
||||
yield catalogExtensionData.entityFilterFunction(filter);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2024 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,8 +13,5 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './alpha/index';
|
||||
export { default } from './alpha/index';
|
||||
export { catalogTranslationRef } from './translation';
|
||||
export * from './translation';
|
||||
export { EntityCardBlueprint } from './EntityCardBlueprint';
|
||||
export { EntityContentBlueprint } from './EntityContentBlueprint';
|
||||
+16
-7
@@ -28,13 +28,16 @@ import {
|
||||
import React, { lazy } from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { Expand } from '../../../packages/frontend-plugin-api/src/types';
|
||||
import { Expand } from '../../../../packages/frontend-plugin-api/src/types';
|
||||
|
||||
export { useEntityPermission } from './hooks/useEntityPermission';
|
||||
export { isOwnerOf } from './utils';
|
||||
export * from './translation';
|
||||
export { useEntityPermission } from '../hooks/useEntityPermission';
|
||||
export { isOwnerOf } from '../utils';
|
||||
export * from '../translation';
|
||||
|
||||
/** @alpha */
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated use `dataRefs` property on either {@link EntityCardBlueprint} or {@link EntityContentBlueprint} instead
|
||||
*/
|
||||
export const catalogExtensionData = {
|
||||
entityContentTitle: createExtensionDataRef<string>().with({
|
||||
id: 'catalog.entity-content-title',
|
||||
@@ -48,7 +51,10 @@ export const catalogExtensionData = {
|
||||
};
|
||||
|
||||
// TODO: Figure out how to merge with provided config schema
|
||||
/** @alpha */
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated use {@link EntityCardBlueprint} instead
|
||||
*/
|
||||
export function createEntityCardExtension<
|
||||
TConfig extends { filter?: string },
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
@@ -110,7 +116,10 @@ export function createEntityCardExtension<
|
||||
});
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated use {@link EntityContentBlueprint} instead
|
||||
*/
|
||||
export function createEntityContentExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(options: {
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 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 './blueprints';
|
||||
export * from './extensions';
|
||||
@@ -15,3 +15,18 @@
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const originalConsoleWarn = console.warn;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn = (...args: any[]) => {
|
||||
const message = args[0];
|
||||
if (
|
||||
typeof message === 'string' &&
|
||||
(message.includes('CSSOM.parse is not a function') ||
|
||||
message.includes('[JSS]'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
originalConsoleWarn(...args);
|
||||
};
|
||||
|
||||
@@ -6,13 +6,35 @@
|
||||
/// <reference types="react" />
|
||||
|
||||
import { AnyExtensionInputMap } from '@backstage/frontend-plugin-api';
|
||||
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
|
||||
import { BackstagePlugin } from '@backstage/frontend-plugin-api';
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { PortableSchema } from '@backstage/frontend-plugin-api';
|
||||
import { default as React_2 } from 'react';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
// @alpha
|
||||
export const CatalogFilterBlueprint: ExtensionBlueprint<
|
||||
'catalog-filter',
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
loader: () => Promise<JSX.Element>;
|
||||
},
|
||||
ConfigurableExtensionDataRef<React_2.JSX.Element, 'core.reactElement', {}>,
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
never
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const catalogTranslationRef: TranslationRef<
|
||||
'catalog',
|
||||
@@ -98,7 +120,7 @@ export const catalogTranslationRef: TranslationRef<
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
// @alpha @deprecated (undocumented)
|
||||
export function createCatalogFilterExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
@@ -141,7 +163,344 @@ const _default: BackstagePlugin<
|
||||
}>;
|
||||
unregisterRedirect: ExternalRouteRef<undefined>;
|
||||
},
|
||||
{}
|
||||
{
|
||||
'entity-card:catalog/about': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'about'
|
||||
>;
|
||||
'entity-card:catalog/links': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'links'
|
||||
>;
|
||||
'entity-card:catalog/labels': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'labels'
|
||||
>;
|
||||
'entity-card:catalog/depends-on-components': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'depends-on-components'
|
||||
>;
|
||||
'entity-card:catalog/depends-on-resources': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'depends-on-resources'
|
||||
>;
|
||||
'entity-card:catalog/has-components': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'has-components'
|
||||
>;
|
||||
'entity-card:catalog/has-resources': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'has-resources'
|
||||
>;
|
||||
'entity-card:catalog/has-subcomponents': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'has-subcomponents'
|
||||
>;
|
||||
'entity-card:catalog/has-subdomains': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'has-subdomains'
|
||||
>;
|
||||
'entity-card:catalog/has-systems': ExtensionDefinition<
|
||||
{
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
filter?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{},
|
||||
'entity-card',
|
||||
undefined,
|
||||
'has-systems'
|
||||
>;
|
||||
'entity-content:catalog/overview': ExtensionDefinition<
|
||||
{
|
||||
[x: string]: any;
|
||||
} & {
|
||||
path: string | undefined;
|
||||
title: string | undefined;
|
||||
filter: string | undefined;
|
||||
},
|
||||
{
|
||||
[x: string]: any;
|
||||
} & {
|
||||
filter?: string | undefined;
|
||||
title?: string | undefined;
|
||||
path?: string | undefined;
|
||||
},
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
RouteRef<AnyRouteRefParams>,
|
||||
'core.routing.ref',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<string, 'catalog.entity-content-title', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{
|
||||
cards: ExtensionInput<
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.entity-filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
}
|
||||
>;
|
||||
},
|
||||
'entity-content',
|
||||
undefined,
|
||||
'overview'
|
||||
>;
|
||||
}
|
||||
>;
|
||||
export default _default;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./alpha": "./src/alpha.ts",
|
||||
"./alpha": "./src/alpha/index.ts",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
@@ -37,7 +37,7 @@
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"alpha": [
|
||||
"src/alpha.ts"
|
||||
"src/alpha/index.ts"
|
||||
],
|
||||
"package.json": [
|
||||
"package.json"
|
||||
@@ -90,6 +90,7 @@
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/dev-utils": "workspace:^",
|
||||
"@backstage/frontend-test-utils": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/dom": "^10.0.0",
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2024 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 { CatalogFilterBlueprint } from './CatalogFilterBlueprint';
|
||||
import {
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionInput,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { waitFor, screen } from '@testing-library/react';
|
||||
|
||||
describe('CatalogFilterBlueprint', () => {
|
||||
it('should create an extension with sane defaults', () => {
|
||||
const extension = CatalogFilterBlueprint.make({
|
||||
params: {
|
||||
loader: async () => <div />,
|
||||
},
|
||||
});
|
||||
expect(extension).toMatchInlineSnapshot(`
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDefinition",
|
||||
"attachTo": {
|
||||
"id": "page:catalog",
|
||||
"input": "filters",
|
||||
},
|
||||
"configSchema": undefined,
|
||||
"disabled": false,
|
||||
"factory": [Function],
|
||||
"inputs": {},
|
||||
"kind": "catalog-filter",
|
||||
"name": undefined,
|
||||
"namespace": undefined,
|
||||
"output": [
|
||||
[Function],
|
||||
],
|
||||
"override": [Function],
|
||||
"toString": [Function],
|
||||
"version": "v2",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should allow overrding of inputs and config', async () => {
|
||||
const extension = CatalogFilterBlueprint.makeWithOverrides({
|
||||
name: 'test-name',
|
||||
inputs: {
|
||||
mock: createExtensionInput([coreExtensionData.reactElement]),
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
test: z => z.string(),
|
||||
},
|
||||
},
|
||||
factory(originalFactory, { config, inputs }) {
|
||||
return originalFactory({
|
||||
loader: async () => (
|
||||
<div data-testid="test">
|
||||
config: {config.test}
|
||||
<div data-testid="contents">
|
||||
{inputs.mock.map((i, k) => (
|
||||
<div key={k}>{i.get(coreExtensionData.reactElement)}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const mockExtension = createExtension({
|
||||
attachTo: { id: 'catalog-filter:test-name', input: 'mock' },
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory() {
|
||||
return [coreExtensionData.reactElement(<div>im a mock</div>)];
|
||||
},
|
||||
});
|
||||
|
||||
createExtensionTester(extension, { config: { test: 'mock test config' } })
|
||||
.add(mockExtension)
|
||||
.render();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('test')).toHaveTextContent(
|
||||
'config: mock test config',
|
||||
);
|
||||
expect(screen.getByTestId('contents')).toHaveTextContent('im a mock');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2024 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, { lazy } from 'react';
|
||||
import {
|
||||
ExtensionBoundary,
|
||||
coreExtensionData,
|
||||
createExtensionBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
/**
|
||||
* Creates Catalog Filter Extensions
|
||||
* @alpha
|
||||
*/
|
||||
export const CatalogFilterBlueprint = createExtensionBlueprint({
|
||||
kind: 'catalog-filter',
|
||||
attachTo: { id: 'page:catalog', input: 'filters' },
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory(params: { loader: () => Promise<JSX.Element> }, { node }) {
|
||||
const ExtensionComponent = lazy(() =>
|
||||
params.loader().then(element => ({ default: () => element })),
|
||||
);
|
||||
return [
|
||||
coreExtensionData.reactElement(
|
||||
<ExtensionBoundary node={node}>
|
||||
<ExtensionComponent />
|
||||
</ExtensionBoundary>,
|
||||
),
|
||||
];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2024 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 { CatalogFilterBlueprint } from './CatalogFilterBlueprint';
|
||||
@@ -23,7 +23,10 @@ import {
|
||||
createExtension,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
/** @alpha */
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated Use {@link CatalogFilterBlueprint} instead
|
||||
*/
|
||||
export function createCatalogFilterExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TConfig,
|
||||
|
||||
@@ -15,96 +15,116 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
|
||||
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
|
||||
import { compatWrapper } from '@backstage/core-compat-api';
|
||||
|
||||
export const catalogAboutEntityCard = createEntityCardExtension({
|
||||
export const catalogAboutEntityCard = EntityCardBlueprint.make({
|
||||
name: 'about',
|
||||
loader: async () =>
|
||||
import('../components/AboutCard').then(m =>
|
||||
compatWrapper(<m.AboutCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
loader: async () =>
|
||||
import('../components/AboutCard').then(m =>
|
||||
compatWrapper(<m.AboutCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogLinksEntityCard = createEntityCardExtension({
|
||||
export const catalogLinksEntityCard = EntityCardBlueprint.make({
|
||||
name: 'links',
|
||||
filter: 'has:links',
|
||||
loader: async () =>
|
||||
import('../components/EntityLinksCard').then(m =>
|
||||
compatWrapper(<m.EntityLinksCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'has:links',
|
||||
loader: async () =>
|
||||
import('../components/EntityLinksCard').then(m =>
|
||||
compatWrapper(<m.EntityLinksCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogLabelsEntityCard = createEntityCardExtension({
|
||||
export const catalogLabelsEntityCard = EntityCardBlueprint.make({
|
||||
name: 'labels',
|
||||
filter: 'has:labels',
|
||||
loader: async () =>
|
||||
import('../components/EntityLabelsCard').then(m =>
|
||||
compatWrapper(<m.EntityLabelsCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'has:labels',
|
||||
loader: async () =>
|
||||
import('../components/EntityLabelsCard').then(m =>
|
||||
compatWrapper(<m.EntityLabelsCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogDependsOnComponentsEntityCard = createEntityCardExtension({
|
||||
export const catalogDependsOnComponentsEntityCard = EntityCardBlueprint.make({
|
||||
name: 'depends-on-components',
|
||||
filter: 'kind:component',
|
||||
loader: async () =>
|
||||
import('../components/DependsOnComponentsCard').then(m =>
|
||||
compatWrapper(<m.DependsOnComponentsCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'kind:component',
|
||||
loader: async () =>
|
||||
import('../components/DependsOnComponentsCard').then(m =>
|
||||
compatWrapper(<m.DependsOnComponentsCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogDependsOnResourcesEntityCard = createEntityCardExtension({
|
||||
export const catalogDependsOnResourcesEntityCard = EntityCardBlueprint.make({
|
||||
name: 'depends-on-resources',
|
||||
filter: 'kind:component',
|
||||
loader: async () =>
|
||||
import('../components/DependsOnResourcesCard').then(m =>
|
||||
compatWrapper(<m.DependsOnResourcesCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'kind:component',
|
||||
loader: async () =>
|
||||
import('../components/DependsOnResourcesCard').then(m =>
|
||||
compatWrapper(<m.DependsOnResourcesCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogHasComponentsEntityCard = createEntityCardExtension({
|
||||
export const catalogHasComponentsEntityCard = EntityCardBlueprint.make({
|
||||
name: 'has-components',
|
||||
filter: 'kind:system',
|
||||
loader: async () =>
|
||||
import('../components/HasComponentsCard').then(m =>
|
||||
compatWrapper(<m.HasComponentsCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'kind:system',
|
||||
loader: async () =>
|
||||
import('../components/HasComponentsCard').then(m =>
|
||||
compatWrapper(<m.HasComponentsCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogHasResourcesEntityCard = createEntityCardExtension({
|
||||
export const catalogHasResourcesEntityCard = EntityCardBlueprint.make({
|
||||
name: 'has-resources',
|
||||
filter: 'kind:system',
|
||||
loader: async () =>
|
||||
import('../components/HasResourcesCard').then(m =>
|
||||
compatWrapper(<m.HasResourcesCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'kind:system',
|
||||
loader: async () =>
|
||||
import('../components/HasResourcesCard').then(m =>
|
||||
compatWrapper(<m.HasResourcesCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogHasSubcomponentsEntityCard = createEntityCardExtension({
|
||||
export const catalogHasSubcomponentsEntityCard = EntityCardBlueprint.make({
|
||||
name: 'has-subcomponents',
|
||||
filter: 'kind:component',
|
||||
loader: async () =>
|
||||
import('../components/HasSubcomponentsCard').then(m =>
|
||||
compatWrapper(<m.HasSubcomponentsCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'kind:component',
|
||||
loader: async () =>
|
||||
import('../components/HasSubcomponentsCard').then(m =>
|
||||
compatWrapper(<m.HasSubcomponentsCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogHasSubdomainsEntityCard = createEntityCardExtension({
|
||||
export const catalogHasSubdomainsEntityCard = EntityCardBlueprint.make({
|
||||
name: 'has-subdomains',
|
||||
filter: 'kind:domain',
|
||||
loader: async () =>
|
||||
import('../components/HasSubdomainsCard').then(m =>
|
||||
compatWrapper(<m.HasSubdomainsCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'kind:domain',
|
||||
loader: async () =>
|
||||
import('../components/HasSubdomainsCard').then(m =>
|
||||
compatWrapper(<m.HasSubdomainsCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const catalogHasSystemsEntityCard = createEntityCardExtension({
|
||||
export const catalogHasSystemsEntityCard = EntityCardBlueprint.make({
|
||||
name: 'has-systems',
|
||||
filter: 'kind:domain',
|
||||
loader: async () =>
|
||||
import('../components/HasSystemsCard').then(m =>
|
||||
compatWrapper(<m.HasSystemsCard variant="gridItem" />),
|
||||
),
|
||||
params: {
|
||||
filter: 'kind:domain',
|
||||
loader: async () =>
|
||||
import('../components/HasSystemsCard').then(m =>
|
||||
compatWrapper(<m.HasSystemsCard variant="gridItem" />),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export default [
|
||||
|
||||
@@ -19,27 +19,38 @@ import {
|
||||
coreExtensionData,
|
||||
createExtensionInput,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
createEntityContentExtension,
|
||||
catalogExtensionData,
|
||||
} from '@backstage/plugin-catalog-react/alpha';
|
||||
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
|
||||
|
||||
export const catalogOverviewEntityContent = createEntityContentExtension({
|
||||
name: 'overview',
|
||||
defaultPath: '/',
|
||||
defaultTitle: 'Overview',
|
||||
disabled: false,
|
||||
inputs: {
|
||||
cards: createExtensionInput({
|
||||
element: coreExtensionData.reactElement,
|
||||
filterFunction: catalogExtensionData.entityFilterFunction.optional(),
|
||||
filterExpression: catalogExtensionData.entityFilterExpression.optional(),
|
||||
}),
|
||||
},
|
||||
loader: async ({ inputs }) =>
|
||||
import('./EntityOverviewPage').then(m => (
|
||||
<m.EntityOverviewPage cards={inputs.cards.map(c => c.output)} />
|
||||
)),
|
||||
});
|
||||
export const catalogOverviewEntityContent =
|
||||
EntityContentBlueprint.makeWithOverrides({
|
||||
name: 'overview',
|
||||
inputs: {
|
||||
cards: createExtensionInput([
|
||||
coreExtensionData.reactElement,
|
||||
EntityContentBlueprint.dataRefs.filterFunction.optional(),
|
||||
EntityContentBlueprint.dataRefs.filterExpression.optional(),
|
||||
]),
|
||||
},
|
||||
factory: (originalFactory, { inputs }) => {
|
||||
return originalFactory({
|
||||
defaultPath: '/',
|
||||
defaultTitle: 'Overview',
|
||||
loader: async () =>
|
||||
import('./EntityOverviewPage').then(m => (
|
||||
<m.EntityOverviewPage
|
||||
cards={inputs.cards.map(c => ({
|
||||
element: c.get(coreExtensionData.reactElement),
|
||||
filterFunction: c.get(
|
||||
EntityContentBlueprint.dataRefs.filterFunction,
|
||||
),
|
||||
filterExpression: c.get(
|
||||
EntityContentBlueprint.dataRefs.filterExpression,
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
)),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default [catalogOverviewEntityContent];
|
||||
|
||||
@@ -16,3 +16,6 @@
|
||||
|
||||
export { default } from './plugin';
|
||||
export { createCatalogFilterExtension } from './createCatalogFilterExtension';
|
||||
|
||||
export * from './blueprints';
|
||||
export * from './translation';
|
||||
|
||||
@@ -64,7 +64,7 @@ import { catalogEntityRefreshPermission } from '@backstage/plugin-catalog-common
|
||||
import { useSourceTemplateCompoundEntityRef } from './hooks';
|
||||
import { taskCreatePermission } from '@backstage/plugin-scaffolder-common/alpha';
|
||||
import { usePermission } from '@backstage/plugin-permission-react';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
|
||||
|
||||
@@ -33,7 +33,7 @@ import React from 'react';
|
||||
import { AboutField } from './AboutField';
|
||||
import { LinksGridList } from '../EntityLinksCard/LinksGridList';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
description: {
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
import React, { ReactNode } from 'react';
|
||||
import { createComponentRouteRef } from '../../routes';
|
||||
import { CatalogTable, CatalogTableRow } from '../CatalogTable';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
import { CatalogTableColumnsFunc } from '../CatalogTable/types';
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import {
|
||||
ResultHighlight,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { HighlightedSearchResultText } from '@backstage/plugin-search-react';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
const useStyles = makeStyles(
|
||||
|
||||
@@ -49,7 +49,7 @@ import { CatalogTableColumnsFunc, CatalogTableRow } from './types';
|
||||
import { PaginatedCatalogTable } from './PaginatedCatalogTable';
|
||||
import { defaultCatalogTableColumnsFunc } from './defaultCatalogTableColumnsFunc';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
|
||||
/**
|
||||
* Props for {@link CatalogTable}.
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import {
|
||||
componentEntityHelpLink,
|
||||
RelatedEntitiesCard,
|
||||
} from '../RelatedEntitiesCard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
componentEntityHelpLink,
|
||||
RelatedEntitiesCard,
|
||||
} from '../RelatedEntitiesCard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
RelatedEntitiesCard,
|
||||
resourceEntityColumns,
|
||||
} from '../RelatedEntitiesCard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -33,7 +33,7 @@ import { catalogEntityDeletePermission } from '@backstage/plugin-catalog-common/
|
||||
import { UnregisterEntity, UnregisterEntityOptions } from './UnregisterEntity';
|
||||
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
|
||||
import useCopyToClipboard from 'react-use/esm/useCopyToClipboard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -19,7 +19,7 @@ import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import CancelIcon from '@material-ui/icons/Cancel';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
type VisibleType = 'visible' | 'hidden' | 'disable';
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { EntityLabelsEmptyState } from './EntityLabelsEmptyState';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -19,7 +19,7 @@ import Typography from '@material-ui/core/Typography';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
import { CodeSnippet } from '@backstage/core-components';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const ENTITY_YAML = `metadata:
|
||||
|
||||
@@ -53,7 +53,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { EntityContextMenu } from '../EntityContextMenu/EntityContextMenu';
|
||||
import { rootRouteRef, unregisterRedirectRouteRef } from '../../routes';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -23,7 +23,7 @@ import { ColumnBreakpoints } from './types';
|
||||
import { IconComponent, useApp } from '@backstage/core-plugin-api';
|
||||
import { InfoCard, InfoCardVariants } from '@backstage/core-components';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
|
||||
/** @public */
|
||||
export interface EntityLinksCardProps {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
import { CodeSnippet } from '@backstage/core-components';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
|
||||
const ENTITY_YAML = `metadata:
|
||||
name: example
|
||||
|
||||
@@ -20,7 +20,7 @@ import Button from '@material-ui/core/Button';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { Illo } from './Illo';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
|
||||
@@ -23,7 +23,7 @@ import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import React, { useState } from 'react';
|
||||
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
interface DeleteEntityDialogProps {
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { DeleteEntityDialog } from './DeleteEntityDialog';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { rootRouteRef } from '../../routes';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ import {
|
||||
import { useApi, ApiHolder } from '@backstage/core-plugin-api';
|
||||
import useAsync from 'react-use/esm/useAsync';
|
||||
import { SerializedError } from '@backstage/errors';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
const errorFilter = (i: EntityStatusItem) =>
|
||||
|
||||
@@ -26,7 +26,7 @@ import useAsync from 'react-use/esm/useAsync';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import { ResponseErrorPanel } from '@backstage/core-components';
|
||||
import { useApi, ApiHolder } from '@backstage/core-plugin-api';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
async function getRelationWarnings(entity: Entity, catalogApi: CatalogApi) {
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
componentEntityHelpLink,
|
||||
RelatedEntitiesCard,
|
||||
} from '../RelatedEntitiesCard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
resourceEntityColumns,
|
||||
resourceEntityHelpLink,
|
||||
} from '../RelatedEntitiesCard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
componentEntityColumns,
|
||||
RelatedEntitiesCard,
|
||||
} from '../RelatedEntitiesCard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
componentEntityColumns,
|
||||
RelatedEntitiesCard,
|
||||
} from '../RelatedEntitiesCard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
systemEntityColumns,
|
||||
systemEntityHelpLink,
|
||||
} from '../RelatedEntitiesCard';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
systemEntityColumns,
|
||||
systemEntityHelpLink,
|
||||
} from './presets';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
} from '@backstage/core-components';
|
||||
|
||||
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -15,3 +15,18 @@
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const originalConsoleWarn = console.warn;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn = (...args: any[]) => {
|
||||
const message = args[0];
|
||||
if (
|
||||
typeof message === 'string' &&
|
||||
(message.includes('CSSOM.parse is not a function') ||
|
||||
message.includes('[JSS]'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
originalConsoleWarn(...args);
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
/// <reference types="react" />
|
||||
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { ListItemProps } from '@material-ui/core/ListItem';
|
||||
import { PortableSchema } from '@backstage/frontend-plugin-api';
|
||||
@@ -18,7 +19,7 @@ export type BaseSearchResultListItemProps<T = {}> = T & {
|
||||
result?: SearchDocument;
|
||||
} & Omit<ListItemProps, 'button'>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
// @alpha @deprecated
|
||||
export function createSearchResultListItemExtension<
|
||||
TConfig extends {
|
||||
noTrack?: boolean;
|
||||
@@ -35,9 +36,9 @@ export function createSearchResultListItemExtension<
|
||||
string | undefined
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
// @alpha @deprecated (undocumented)
|
||||
export namespace createSearchResultListItemExtension {
|
||||
const // (undocumented)
|
||||
const // @deprecated (undocumented)
|
||||
itemDataRef: ConfigurableExtensionDataRef<
|
||||
{
|
||||
predicate?: SearchResultItemExtensionPredicate | undefined;
|
||||
@@ -55,7 +56,7 @@ export type SearchResultItemExtensionComponent = <
|
||||
props: P,
|
||||
) => JSX.Element | null;
|
||||
|
||||
// @alpha (undocumented)
|
||||
// @alpha @deprecated (undocumented)
|
||||
export type SearchResultItemExtensionOptions<
|
||||
TConfig extends {
|
||||
noTrack?: boolean;
|
||||
@@ -79,5 +80,48 @@ export type SearchResultItemExtensionPredicate = (
|
||||
result: SearchResult,
|
||||
) => boolean;
|
||||
|
||||
// @alpha
|
||||
export const SearchResultListItemBlueprint: ExtensionBlueprint<
|
||||
'search-result-list-item',
|
||||
undefined,
|
||||
undefined,
|
||||
SearchResultListItemBlueprintParams,
|
||||
ConfigurableExtensionDataRef<
|
||||
{
|
||||
predicate?: SearchResultItemExtensionPredicate | undefined;
|
||||
component: SearchResultItemExtensionComponent;
|
||||
},
|
||||
'search.search-result-list-item.item',
|
||||
{}
|
||||
>,
|
||||
{},
|
||||
{
|
||||
noTrack: boolean;
|
||||
},
|
||||
{
|
||||
noTrack?: boolean | undefined;
|
||||
},
|
||||
{
|
||||
item: ConfigurableExtensionDataRef<
|
||||
{
|
||||
predicate?: SearchResultItemExtensionPredicate | undefined;
|
||||
component: SearchResultItemExtensionComponent;
|
||||
},
|
||||
'search.search-result-list-item.item',
|
||||
{}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface SearchResultListItemBlueprintParams {
|
||||
component: (options: {
|
||||
config: {
|
||||
noTrack?: boolean;
|
||||
};
|
||||
}) => Promise<SearchResultItemExtensionComponent>;
|
||||
predicate?: SearchResultItemExtensionPredicate;
|
||||
}
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./alpha": "./src/alpha.tsx",
|
||||
"./alpha": "./src/alpha/index.ts",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
@@ -36,7 +36,7 @@
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"alpha": [
|
||||
"src/alpha.tsx"
|
||||
"src/alpha/index.ts"
|
||||
],
|
||||
"package.json": [
|
||||
"package.json"
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2024 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 { SearchResultListItemBlueprint } from './SearchResultListItemBlueprint';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import {
|
||||
PageBlueprint,
|
||||
createExtensionInput,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { searchResultListItemDataRef } from './types';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe('SearchResultListItemBlueprint', () => {
|
||||
it('should return an extension with sane defaults', () => {
|
||||
const extension = SearchResultListItemBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
component: async () => () => <div>Hello</div>,
|
||||
predicate: () => true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(extension).toMatchInlineSnapshot(`
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDefinition",
|
||||
"attachTo": {
|
||||
"id": "page:search",
|
||||
"input": "items",
|
||||
},
|
||||
"configSchema": {
|
||||
"parse": [Function],
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"noTrack": {
|
||||
"default": false,
|
||||
"type": "boolean",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
"disabled": false,
|
||||
"factory": [Function],
|
||||
"inputs": {},
|
||||
"kind": "search-result-list-item",
|
||||
"name": "test",
|
||||
"namespace": undefined,
|
||||
"output": [
|
||||
[Function],
|
||||
],
|
||||
"override": [Function],
|
||||
"toString": [Function],
|
||||
"version": "v2",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('defaults and passes on config properly', async () => {
|
||||
const extension = SearchResultListItemBlueprint.make({
|
||||
name: 'test',
|
||||
params: {
|
||||
component:
|
||||
async ({ config: { noTrack } }) =>
|
||||
() =>
|
||||
<div>noTrack: {String(noTrack)}</div>,
|
||||
},
|
||||
});
|
||||
|
||||
const mockSearchPage = PageBlueprint.makeWithOverrides({
|
||||
namespace: 'search',
|
||||
inputs: {
|
||||
items: createExtensionInput([searchResultListItemDataRef]),
|
||||
},
|
||||
factory(originalFactory, { inputs }) {
|
||||
return originalFactory({
|
||||
defaultPath: '/',
|
||||
loader: async () => {
|
||||
const items = inputs.items.map(i =>
|
||||
i.get(searchResultListItemDataRef),
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
{items.map((item, i) => (
|
||||
<item.component key={i} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
createExtensionTester(mockSearchPage)
|
||||
.add(extension)
|
||||
.render()
|
||||
.findByText('noTrack: false'),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
await expect(
|
||||
createExtensionTester(mockSearchPage)
|
||||
.add(extension, { config: { noTrack: true } })
|
||||
.render()
|
||||
.findByText('noTrack: true'),
|
||||
).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2024 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, { lazy } from 'react';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
ExtensionBoundary,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
SearchResultItemExtensionComponent,
|
||||
SearchResultItemExtensionPredicate,
|
||||
searchResultListItemDataRef,
|
||||
} from './types';
|
||||
import {
|
||||
SearchResultListItemExtension,
|
||||
SearchResultListItemExtensionProps,
|
||||
} from '../../extensions';
|
||||
|
||||
/** @alpha */
|
||||
export interface SearchResultListItemBlueprintParams {
|
||||
/**
|
||||
* The extension component.
|
||||
*/
|
||||
component: (options: {
|
||||
config: { noTrack?: boolean };
|
||||
}) => Promise<SearchResultItemExtensionComponent>;
|
||||
/**
|
||||
* When an extension defines a predicate, it returns true if the result should be rendered by that extension.
|
||||
* Defaults to a predicate that returns true, which means it renders all sorts of results.
|
||||
*/
|
||||
predicate?: SearchResultItemExtensionPredicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* Creates SearchResultListItem extensions
|
||||
*/
|
||||
export const SearchResultListItemBlueprint = createExtensionBlueprint({
|
||||
kind: 'search-result-list-item',
|
||||
attachTo: {
|
||||
id: 'page:search',
|
||||
input: 'items',
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
noTrack: z => z.boolean().default(false),
|
||||
},
|
||||
},
|
||||
output: [searchResultListItemDataRef],
|
||||
dataRefs: {
|
||||
item: searchResultListItemDataRef,
|
||||
},
|
||||
*factory(params: SearchResultListItemBlueprintParams, { config, node }) {
|
||||
const ExtensionComponent = lazy(() =>
|
||||
params.component({ config }).then(component => ({ default: component })),
|
||||
);
|
||||
|
||||
yield searchResultListItemDataRef({
|
||||
predicate: params.predicate,
|
||||
component: (props: SearchResultListItemExtensionProps) => (
|
||||
<ExtensionBoundary node={node}>
|
||||
<SearchResultListItemExtension
|
||||
rank={props.rank}
|
||||
result={props.result}
|
||||
noTrack={config.noTrack}
|
||||
>
|
||||
<ExtensionComponent {...props} />
|
||||
</SearchResultListItemExtension>
|
||||
</ExtensionBoundary>
|
||||
),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2024 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 {
|
||||
SearchResultListItemBlueprint,
|
||||
type SearchResultListItemBlueprintParams,
|
||||
} from './SearchResultListItemBlueprint';
|
||||
export {
|
||||
type BaseSearchResultListItemProps,
|
||||
type SearchResultItemExtensionComponent,
|
||||
type SearchResultItemExtensionPredicate,
|
||||
} from './types';
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2024 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 { ListItemProps } from '@material-ui/core/ListItem';
|
||||
import { SearchDocument, SearchResult } from '@backstage/plugin-search-common';
|
||||
import { createExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
/** @alpha */
|
||||
export type BaseSearchResultListItemProps<T = {}> = T & {
|
||||
rank?: number;
|
||||
result?: SearchDocument;
|
||||
} & Omit<ListItemProps, 'button'>;
|
||||
|
||||
/** @alpha */
|
||||
export type SearchResultItemExtensionComponent = <
|
||||
P extends BaseSearchResultListItemProps,
|
||||
>(
|
||||
props: P,
|
||||
) => JSX.Element | null;
|
||||
|
||||
/** @alpha */
|
||||
export type SearchResultItemExtensionPredicate = (
|
||||
result: SearchResult,
|
||||
) => boolean;
|
||||
|
||||
export const searchResultListItemDataRef = createExtensionDataRef<{
|
||||
predicate?: SearchResultItemExtensionPredicate;
|
||||
component: SearchResultItemExtensionComponent;
|
||||
}>().with({ id: 'search.search-result-list-item.item' });
|
||||
+2
-4
@@ -23,10 +23,8 @@ import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { SearchResult } from '@backstage/plugin-search-common';
|
||||
import { screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import {
|
||||
BaseSearchResultListItemProps,
|
||||
createSearchResultListItemExtension,
|
||||
} from './alpha';
|
||||
import { createSearchResultListItemExtension } from './extensions';
|
||||
import { BaseSearchResultListItemProps } from './blueprints';
|
||||
|
||||
describe('createSearchResultListItemExtension', () => {
|
||||
it('Should use the correct result component', async () => {
|
||||
+24
-32
@@ -15,39 +15,23 @@
|
||||
*/
|
||||
|
||||
import React, { lazy } from 'react';
|
||||
|
||||
import { ListItemProps } from '@material-ui/core/ListItem';
|
||||
|
||||
import {
|
||||
ExtensionBoundary,
|
||||
PortableSchema,
|
||||
createExtension,
|
||||
createExtensionDataRef,
|
||||
createSchemaFromZod,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { SearchDocument, SearchResult } from '@backstage/plugin-search-common';
|
||||
import {
|
||||
SearchResultItemExtensionComponent,
|
||||
SearchResultItemExtensionPredicate,
|
||||
} from './blueprints';
|
||||
import { SearchResultListItemExtension } from '../extensions';
|
||||
import { searchResultListItemDataRef } from './blueprints/types';
|
||||
|
||||
import { SearchResultListItemExtension } from './extensions';
|
||||
|
||||
/** @alpha */
|
||||
export type BaseSearchResultListItemProps<T = {}> = T & {
|
||||
rank?: number;
|
||||
result?: SearchDocument;
|
||||
} & Omit<ListItemProps, 'button'>;
|
||||
|
||||
/** @alpha */
|
||||
export type SearchResultItemExtensionComponent = <
|
||||
P extends BaseSearchResultListItemProps,
|
||||
>(
|
||||
props: P,
|
||||
) => JSX.Element | null;
|
||||
|
||||
/** @alpha */
|
||||
export type SearchResultItemExtensionPredicate = (
|
||||
result: SearchResult,
|
||||
) => boolean;
|
||||
|
||||
/** @alpha */
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated Use {@link SearchResultListItemBlueprint} instead
|
||||
*/
|
||||
export type SearchResultItemExtensionOptions<
|
||||
TConfig extends { noTrack?: boolean },
|
||||
> = {
|
||||
@@ -80,7 +64,12 @@ export type SearchResultItemExtensionOptions<
|
||||
predicate?: SearchResultItemExtensionPredicate;
|
||||
};
|
||||
|
||||
/** @alpha */
|
||||
/**
|
||||
* Creates items for the search result list.
|
||||
*
|
||||
* @alpha
|
||||
* @deprecated Use {@link SearchResultListItemBlueprint} instead
|
||||
*/
|
||||
export function createSearchResultListItemExtension<
|
||||
TConfig extends { noTrack?: boolean },
|
||||
>(options: SearchResultItemExtensionOptions<TConfig>) {
|
||||
@@ -132,10 +121,13 @@ export function createSearchResultListItemExtension<
|
||||
});
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated Use {@link SearchResultListItemBlueprint} instead
|
||||
*/
|
||||
export namespace createSearchResultListItemExtension {
|
||||
export const itemDataRef = createExtensionDataRef<{
|
||||
predicate?: SearchResultItemExtensionPredicate;
|
||||
component: SearchResultItemExtensionComponent;
|
||||
}>().with({ id: 'search.search-result-list-item.item' });
|
||||
/**
|
||||
* @deprecated Use {@link SearchResultListItemBlueprint#dataRefs.item} instead
|
||||
*/
|
||||
export const itemDataRef = searchResultListItemDataRef;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 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 './extensions';
|
||||
export * from './blueprints';
|
||||
@@ -15,3 +15,18 @@
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const originalConsoleWarn = console.warn;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn = (...args: any[]) => {
|
||||
const message = args[0];
|
||||
if (
|
||||
typeof message === 'string' &&
|
||||
(message.includes('CSSOM.parse is not a function') ||
|
||||
message.includes('[JSS]'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
originalConsoleWarn(...args);
|
||||
};
|
||||
|
||||
@@ -5920,6 +5920,7 @@ __metadata:
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-test-utils": "workspace:^"
|
||||
"@backstage/integration-react": "workspace:^"
|
||||
"@backstage/plugin-catalog-common": "workspace:^"
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
@@ -6000,6 +6001,7 @@ __metadata:
|
||||
"@backstage/dev-utils": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-test-utils": "workspace:^"
|
||||
"@backstage/integration-react": "workspace:^"
|
||||
"@backstage/plugin-catalog-common": "workspace:^"
|
||||
"@backstage/plugin-catalog-react": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user