chore: tidying up
Signed-off-by: blam <ben@blam.sh> Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -26,7 +26,6 @@ import {
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionInput,
|
||||
createExtensionKind,
|
||||
createExtensionOverrides,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { getComponentData } from '@backstage/core-plugin-api';
|
||||
@@ -129,80 +128,21 @@ export function convertLegacyApp(
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const CoreNavOverride = CurrentCoreNav.override({
|
||||
// namespace: 'app',
|
||||
// name: 'nav',
|
||||
// attachTo: { id: 'app/layout', input: 'nav' },
|
||||
// output: {},
|
||||
const CoreNavOverride = createExtension({
|
||||
namespace: 'app',
|
||||
name: 'nav',
|
||||
attachTo: { id: 'app/layout', input: 'nav' },
|
||||
output: {},
|
||||
factory: () => ({}),
|
||||
disabled: true,
|
||||
});
|
||||
|
||||
createExtensionOverride({
|
||||
extension: CurrentCoreNav,
|
||||
factory: () => null,
|
||||
});
|
||||
|
||||
const collectedRoutes = collectLegacyRoutes(routesEl);
|
||||
|
||||
return [
|
||||
...collectedRoutes,
|
||||
createExtensionOverrides({
|
||||
extensions: [CoreNavOverride, CoreNavOverride],
|
||||
extensions: [CoreLayoutOverride, CoreNavOverride],
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
const EntityCardExtension = createExtensionKind({
|
||||
kind: 'entity-card',
|
||||
attachTo: { id: 'entity-card', input: 'default' },
|
||||
inputs: {
|
||||
loader: createExtensionInput({
|
||||
element: coreExtensionData.reactElement,
|
||||
}),
|
||||
},
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory({ inputs }, props: { title: string }) {
|
||||
console.log(inputs.loader);
|
||||
return {
|
||||
element: React.createElement('h1'),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const GithubCard = EntityCardExtension.new({
|
||||
props: {
|
||||
title: 'GitHub Card',
|
||||
},
|
||||
factory({ inputs: { loader } }) {
|
||||
console.log(loader);
|
||||
return {
|
||||
element: React.createElement('h2'),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
GithubCard.override({
|
||||
attachTo: { id: 'entity-card', input: 'github' },
|
||||
inputs: {
|
||||
loader: createExtensionInput(
|
||||
{
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
{ singleton: false },
|
||||
),
|
||||
loader2: createExtensionInput({
|
||||
element: coreExtensionData.reactElement,
|
||||
}),
|
||||
},
|
||||
factory({ originalFactory, inputs }) {
|
||||
inputs.loader2;
|
||||
inputs.loader;
|
||||
return originalFactory({
|
||||
inputsOverride: inputs,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -529,9 +529,91 @@ export function createExtensionKind<
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig,
|
||||
>(
|
||||
options: ExtensionKindOptions<TOptions, TInputs, TOutput, TConfig>,
|
||||
options: CreateExtensionKindOptions<TOptions, TInputs, TOutput, TConfig>,
|
||||
): ExtensionKind<TOptions, TInputs, TOutput, TConfig>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CreateExtensionKindInstanceOptions<
|
||||
TOptions,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig,
|
||||
> {
|
||||
// (undocumented)
|
||||
attachTo?: {
|
||||
id: string;
|
||||
input: string;
|
||||
};
|
||||
// (undocumented)
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
factory?(
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
originalFactory(
|
||||
context?: {
|
||||
node?: AppNode;
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
options?: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
},
|
||||
options: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
// (undocumented)
|
||||
inputs?: TInputs;
|
||||
// (undocumented)
|
||||
name?: string;
|
||||
// (undocumented)
|
||||
namespace?: string;
|
||||
// (undocumented)
|
||||
options: TOptions;
|
||||
// (undocumented)
|
||||
output?: TOutput;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CreateExtensionKindOptions<
|
||||
TOptions,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig,
|
||||
> {
|
||||
// (undocumented)
|
||||
attachTo: {
|
||||
id: string;
|
||||
input: string;
|
||||
};
|
||||
// (undocumented)
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
factory(
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
options: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
// (undocumented)
|
||||
inputs?: TInputs;
|
||||
// (undocumented)
|
||||
kind: string;
|
||||
// (undocumented)
|
||||
name?: string;
|
||||
// (undocumented)
|
||||
namespace?: string;
|
||||
// (undocumented)
|
||||
output: TOutput;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CreateExtensionOptions<
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
@@ -548,7 +630,7 @@ export interface CreateExtensionOptions<
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
factory(context: {
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
@@ -565,6 +647,51 @@ export interface CreateExtensionOptions<
|
||||
output: TOutput;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CreateExtensionOverrideKindInstanceOptions<
|
||||
TOptions,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig,
|
||||
> {
|
||||
// (undocumented)
|
||||
attachTo?: {
|
||||
id: string;
|
||||
input: string;
|
||||
};
|
||||
// (undocumented)
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
factory?(
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
originalFactory(
|
||||
context?: {
|
||||
node?: AppNode;
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
options?: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
},
|
||||
options: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
// (undocumented)
|
||||
inputs?: TInputs;
|
||||
// (undocumented)
|
||||
name?: string;
|
||||
// (undocumented)
|
||||
namespace?: string;
|
||||
// (undocumented)
|
||||
options?: TOptions;
|
||||
// (undocumented)
|
||||
output?: TOutput;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function createExtensionOverrides(
|
||||
options: ExtensionOverridesOptions,
|
||||
@@ -937,75 +1064,26 @@ export class ExtensionKind<
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig,
|
||||
>(
|
||||
options: ExtensionKindOptions<TOptions, TInputs, TOutput, TConfig>,
|
||||
options: CreateExtensionKindOptions<TOptions, TInputs, TOutput, TConfig>,
|
||||
): ExtensionKind<TOptions, TInputs, TOutput, TConfig>;
|
||||
// (undocumented)
|
||||
new(args: {
|
||||
namespace?: string;
|
||||
name?: string;
|
||||
attachTo?: {
|
||||
id: string;
|
||||
input: string;
|
||||
};
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
output?: TOutput;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
options: TOptions;
|
||||
factory?(
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
orignalFactory(
|
||||
context?: {
|
||||
node?: AppNode;
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
options?: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
},
|
||||
options: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
}): ExtensionDefinition<TConfig>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ExtensionKindOptions<
|
||||
TOptions,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig,
|
||||
> {
|
||||
// (undocumented)
|
||||
attachTo: {
|
||||
id: string;
|
||||
input: string;
|
||||
new(
|
||||
instanceProperties: CreateExtensionKindInstanceOptions<
|
||||
TOptions,
|
||||
TInputs,
|
||||
TOutput,
|
||||
TConfig
|
||||
>,
|
||||
): ExtensionDefinition<TConfig> & {
|
||||
override: (
|
||||
overrides: CreateExtensionOverrideKindInstanceOptions<
|
||||
TOptions,
|
||||
TInputs,
|
||||
TOutput,
|
||||
TConfig
|
||||
>,
|
||||
) => ExtensionDefinition<TConfig>;
|
||||
};
|
||||
// (undocumented)
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
factory(
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
options: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
// (undocumented)
|
||||
inputs?: TInputs;
|
||||
// (undocumented)
|
||||
kind: string;
|
||||
// (undocumented)
|
||||
name?: string;
|
||||
// (undocumented)
|
||||
namespace?: string;
|
||||
// (undocumented)
|
||||
output: TOutput;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -291,37 +291,4 @@ describe('createExtension', () => {
|
||||
'ExtensionDefinition{namespace=test,attachTo=root@default}',
|
||||
);
|
||||
});
|
||||
|
||||
describe('override', () => {
|
||||
it('should create an extension override', () => {
|
||||
const extension = createExtension({
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'root', input: 'default' },
|
||||
output: {
|
||||
foo: stringData,
|
||||
},
|
||||
factory() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const override = extension.override({
|
||||
attachTo: {
|
||||
id: 'root',
|
||||
input: 'default2',
|
||||
},
|
||||
factory() {
|
||||
return {
|
||||
foo: 'baz',
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
expect(String(override)).toBe(
|
||||
'ExtensionDefinition{namespace=test,attachTo=root@default2}',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,7 +91,7 @@ export interface CreateExtensionOptions<
|
||||
inputs?: TInputs;
|
||||
output: TOutput;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
factory(context: {
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
@@ -99,33 +99,7 @@ export interface CreateExtensionOptions<
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionDefinitionOverrides<
|
||||
TOriginalConfig,
|
||||
TOriginalInputs extends AnyExtensionInputMap,
|
||||
TOverrideInputs extends AnyExtensionInputMap,
|
||||
> {
|
||||
readonly disabled?: boolean;
|
||||
readonly attachTo?: { id: string; input: string };
|
||||
// TODO: inputs (only adding to the list? or redefine arity and superset of old type on existing input?)
|
||||
// TODO: config (any merging needed on there?)
|
||||
inputs?: TOverrideInputs;
|
||||
factory?(options: {
|
||||
node: AppNode;
|
||||
config: TOriginalConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TOriginalInputs & TOverrideInputs>>;
|
||||
originalFactory: (originalFactoryOptions?: {
|
||||
inputsOverride?: ResolvedExtensionInputs<
|
||||
TOriginalInputs & TOverrideInputs
|
||||
>;
|
||||
}) => ExtensionDataValues<any>;
|
||||
}): ExtensionDataValues<any>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionDefinition<
|
||||
TConfig,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
> {
|
||||
export interface ExtensionDefinition<TConfig> {
|
||||
$$type: '@backstage/ExtensionDefinition';
|
||||
readonly kind?: string;
|
||||
readonly namespace?: string;
|
||||
@@ -133,20 +107,15 @@ export interface ExtensionDefinition<
|
||||
readonly attachTo: { id: string; input: string };
|
||||
readonly disabled: boolean;
|
||||
readonly configSchema?: PortableSchema<TConfig>;
|
||||
override<TOverrideInputs extends AnyExtensionInputMap>(
|
||||
overrides: ExtensionDefinitionOverrides<TConfig, TInputs, TOverrideInputs>,
|
||||
): ExtensionDefinition<TConfig, TInputs>;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface InternalExtensionDefinition<
|
||||
TConfig,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
> extends ExtensionDefinition<TConfig, TInputs> {
|
||||
export interface InternalExtensionDefinition<TConfig>
|
||||
extends ExtensionDefinition<TConfig> {
|
||||
readonly version: 'v1';
|
||||
readonly inputs: TInputs;
|
||||
readonly inputs: AnyExtensionInputMap;
|
||||
readonly output: AnyExtensionDataMap;
|
||||
factory(context: {
|
||||
factory(options: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: ResolvedExtensionInputs<any>;
|
||||
@@ -154,13 +123,10 @@ export interface InternalExtensionDefinition<
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function toInternalExtensionDefinition<
|
||||
TConfig,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(
|
||||
overrides: ExtensionDefinition<TConfig, TInputs>,
|
||||
): InternalExtensionDefinition<TConfig, TInputs> {
|
||||
const internal = overrides as InternalExtensionDefinition<TConfig, TInputs>;
|
||||
export function toInternalExtensionDefinition<TConfig>(
|
||||
overrides: ExtensionDefinition<TConfig>,
|
||||
): InternalExtensionDefinition<TConfig> {
|
||||
const internal = overrides as InternalExtensionDefinition<TConfig>;
|
||||
if (internal.$$type !== '@backstage/ExtensionDefinition') {
|
||||
throw new Error(
|
||||
`Invalid extension definition instance, bad type '${internal.$$type}'`,
|
||||
@@ -181,69 +147,38 @@ export function createExtension<
|
||||
TConfig = never,
|
||||
>(
|
||||
options: CreateExtensionOptions<TOutput, TInputs, TConfig>,
|
||||
): ExtensionDefinition<TConfig, TInputs> {
|
||||
const {
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
attachTo,
|
||||
disabled = false,
|
||||
inputs = {} as TInputs,
|
||||
output,
|
||||
configSchema,
|
||||
factory,
|
||||
} = options;
|
||||
|
||||
): ExtensionDefinition<TConfig> {
|
||||
return {
|
||||
$$type: '@backstage/ExtensionDefinition',
|
||||
version: 'v1',
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
attachTo,
|
||||
disabled,
|
||||
inputs,
|
||||
output,
|
||||
configSchema,
|
||||
factory({ inputs: factoryInputs, ...rest }) {
|
||||
kind: options.kind,
|
||||
namespace: options.namespace,
|
||||
name: options.name,
|
||||
attachTo: options.attachTo,
|
||||
disabled: options.disabled ?? false,
|
||||
inputs: options.inputs ?? {},
|
||||
output: options.output,
|
||||
configSchema: options.configSchema,
|
||||
factory({ inputs, ...rest }) {
|
||||
// TODO: Simplify this, but TS wouldn't infer the input type for some reason
|
||||
return factory({
|
||||
inputs: factoryInputs as Expand<ResolvedExtensionInputs<TInputs>>,
|
||||
return options.factory({
|
||||
inputs: inputs as Expand<ResolvedExtensionInputs<TInputs>>,
|
||||
...rest,
|
||||
});
|
||||
},
|
||||
toString() {
|
||||
const parts: string[] = [];
|
||||
if (kind) {
|
||||
parts.push(`kind=${kind}`);
|
||||
if (options.kind) {
|
||||
parts.push(`kind=${options.kind}`);
|
||||
}
|
||||
if (namespace) {
|
||||
parts.push(`namespace=${namespace}`);
|
||||
if (options.namespace) {
|
||||
parts.push(`namespace=${options.namespace}`);
|
||||
}
|
||||
if (name) {
|
||||
parts.push(`name=${name}`);
|
||||
if (options.name) {
|
||||
parts.push(`name=${options.name}`);
|
||||
}
|
||||
parts.push(`attachTo=${attachTo.id}@${attachTo.input}`);
|
||||
parts.push(`attachTo=${options.attachTo.id}@${options.attachTo.input}`);
|
||||
return `ExtensionDefinition{${parts.join(',')}}`;
|
||||
},
|
||||
override<TOverrideInputs extends AnyExtensionInputMap>(
|
||||
overrides: ExtensionDefinitionOverrides<
|
||||
TConfig,
|
||||
TInputs,
|
||||
TOverrideInputs
|
||||
>,
|
||||
): ExtensionDefinition<TConfig, TInputs> {
|
||||
return createExtension({
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
attachTo: overrides.attachTo ?? attachTo,
|
||||
disabled: overrides.disabled ?? disabled,
|
||||
inputs: { ...inputs, ...overrides.inputs },
|
||||
output,
|
||||
configSchema,
|
||||
factory,
|
||||
});
|
||||
},
|
||||
} as InternalExtensionDefinition<TConfig, TInputs>;
|
||||
} as InternalExtensionDefinition<TConfig>;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ describe('createExtensionKind', () => {
|
||||
},
|
||||
factory: expect.any(Function),
|
||||
toString: expect.any(Function),
|
||||
override: expect.any(Function),
|
||||
version: 'v1',
|
||||
});
|
||||
|
||||
@@ -102,4 +103,167 @@ describe('createExtensionKind', () => {
|
||||
const { container } = createExtensionTester(extension).render();
|
||||
expect(container.querySelector('h2')).toHaveTextContent('Hello, world!');
|
||||
});
|
||||
|
||||
it('should allow calling of the default value from override', () => {
|
||||
const TestExtension = createExtensionKind({
|
||||
kind: 'test-extension',
|
||||
attachTo: { id: 'test', input: 'default' },
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory(_, options: { text: string }) {
|
||||
return {
|
||||
element: <h1>{options.text}</h1>,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const extension = TestExtension.new({
|
||||
name: 'my-extension',
|
||||
options: {
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
factory({ originalFactory }, options: { text: string }) {
|
||||
const { element } = originalFactory();
|
||||
return {
|
||||
element: (
|
||||
<h2>
|
||||
{options.text}
|
||||
{element}
|
||||
</h2>
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
expect(extension).toBeDefined();
|
||||
|
||||
const { container } = createExtensionTester(extension).render();
|
||||
|
||||
expect(container.querySelector('h2 h1')).toHaveTextContent('Hello, world!');
|
||||
});
|
||||
|
||||
it('should allow overriding options of the default value from override', () => {
|
||||
const TestExtension = createExtensionKind({
|
||||
kind: 'test-extension',
|
||||
attachTo: { id: 'test', input: 'default' },
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory(_, options: { text: string }) {
|
||||
return {
|
||||
element: <h1>{options.text}</h1>,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const extension = TestExtension.new({
|
||||
name: 'my-extension',
|
||||
options: {
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
factory({ originalFactory }, options: { text: string }) {
|
||||
const { element } = originalFactory(undefined, { text: 'nothing!' });
|
||||
return {
|
||||
element: (
|
||||
<h2>
|
||||
{options.text}
|
||||
{element}
|
||||
</h2>
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
expect(extension).toBeDefined();
|
||||
|
||||
const { container } = createExtensionTester(extension).render();
|
||||
|
||||
expect(container.querySelector('h2 h1')).toHaveTextContent('nothing!');
|
||||
});
|
||||
|
||||
describe('override', () => {
|
||||
it('should allow overriding of the default factory', () => {
|
||||
const TestExtension = createExtensionKind({
|
||||
kind: 'test-extension',
|
||||
attachTo: { id: 'test', input: 'default' },
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory(_, options: { text: string }) {
|
||||
return {
|
||||
element: <h1>{options.text}</h1>,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const extension = TestExtension.new({
|
||||
name: 'my-extension',
|
||||
options: {
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
factory(_, options: { text: string }) {
|
||||
return {
|
||||
element: <h2>{options.text}</h2>,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const overridden = extension.override({
|
||||
factory({ originalFactory }, { text }) {
|
||||
return {
|
||||
element: (
|
||||
<div>
|
||||
{originalFactory().element}
|
||||
<h3>{text}</h3>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const { container } = createExtensionTester(overridden).render();
|
||||
expect(container.querySelector('h2')).toHaveTextContent('Hello, world!');
|
||||
expect(container.querySelector('h3')).toHaveTextContent('Hello, world!');
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow calling the kind factory if another factory is not defined in the instance', () => {
|
||||
const TestExtension = createExtensionKind({
|
||||
kind: 'test-extension',
|
||||
attachTo: { id: 'test', input: 'default' },
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory(_, options: { text: string }) {
|
||||
return {
|
||||
element: <h1>{options.text}</h1>,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const extension = TestExtension.new({
|
||||
name: 'my-extension',
|
||||
options: {
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
});
|
||||
|
||||
const overridden = extension.override({
|
||||
factory({ originalFactory }, { text }) {
|
||||
return {
|
||||
element: (
|
||||
<div>
|
||||
{originalFactory().element}
|
||||
<h3>{text}</h3>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const { container } = createExtensionTester(overridden).render();
|
||||
expect(container.querySelector('h1')).toHaveTextContent('Hello, world!');
|
||||
expect(container.querySelector('h3')).toHaveTextContent('Hello, world!');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AppNode } from '../apis';
|
||||
import { PortableSchema } from '../schema';
|
||||
import { Expand } from '../types';
|
||||
@@ -52,6 +53,76 @@ export interface CreateExtensionKindOptions<
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CreateExtensionKindInstanceOptions<
|
||||
TOptions,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig,
|
||||
> {
|
||||
namespace?: string;
|
||||
name?: string;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
output?: TOutput;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
options: TOptions;
|
||||
factory?(
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
originalFactory(
|
||||
context?: {
|
||||
node?: AppNode;
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
options?: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
},
|
||||
options: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CreateExtensionOverrideKindInstanceOptions<
|
||||
TOptions,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfig,
|
||||
> {
|
||||
namespace?: string;
|
||||
name?: string;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
output?: TOutput;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
options?: TOptions;
|
||||
factory?(
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
originalFactory(
|
||||
context?: {
|
||||
node?: AppNode;
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
options?: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
},
|
||||
options: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: should we export an interface instead of a concrete class?
|
||||
* @public
|
||||
@@ -74,7 +145,7 @@ export class ExtensionKind<
|
||||
}
|
||||
|
||||
private constructor(
|
||||
private readonly options: CreateExtensionKindOptions<
|
||||
private readonly kindProperties: CreateExtensionKindOptions<
|
||||
TOptions,
|
||||
TInputs,
|
||||
TOutput,
|
||||
@@ -82,49 +153,41 @@ export class ExtensionKind<
|
||||
>,
|
||||
) {}
|
||||
|
||||
public new(args: {
|
||||
namespace?: string;
|
||||
name?: string;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
output?: TOutput;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
options: TOptions;
|
||||
factory?(
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
orignalFactory(
|
||||
context?: {
|
||||
node?: AppNode;
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
options?: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
},
|
||||
options: TOptions,
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
}): ExtensionDefinition<TConfig, TInputs> {
|
||||
return createExtension({
|
||||
kind: this.options.kind,
|
||||
namespace: args.namespace ?? this.options.namespace,
|
||||
name: args.name ?? this.options.name,
|
||||
attachTo: args.attachTo ?? this.options.attachTo,
|
||||
disabled: args.disabled ?? this.options.disabled,
|
||||
inputs: args.inputs ?? this.options.inputs,
|
||||
output: args.output ?? this.options.output,
|
||||
configSchema: args.configSchema ?? this.options.configSchema, // TODO: some config merging or smth
|
||||
public new(
|
||||
instanceProperties: CreateExtensionKindInstanceOptions<
|
||||
TOptions,
|
||||
TInputs,
|
||||
TOutput,
|
||||
TConfig
|
||||
>,
|
||||
): ExtensionDefinition<TConfig> & {
|
||||
override: (
|
||||
overrides: CreateExtensionOverrideKindInstanceOptions<
|
||||
TOptions,
|
||||
TInputs,
|
||||
TOutput,
|
||||
TConfig
|
||||
>,
|
||||
) => ExtensionDefinition<TConfig>;
|
||||
} {
|
||||
const extension = createExtension({
|
||||
kind: this.kindProperties.kind,
|
||||
namespace: instanceProperties.namespace ?? this.kindProperties.namespace,
|
||||
name: instanceProperties.name ?? this.kindProperties.name,
|
||||
attachTo: instanceProperties.attachTo ?? this.kindProperties.attachTo,
|
||||
disabled: instanceProperties.disabled ?? this.kindProperties.disabled,
|
||||
inputs: instanceProperties.inputs ?? this.kindProperties.inputs,
|
||||
output: instanceProperties.output ?? this.kindProperties.output,
|
||||
configSchema:
|
||||
instanceProperties.configSchema ?? this.kindProperties.configSchema, // TODO: some config merging or smth
|
||||
factory: ({ node, config, inputs }) => {
|
||||
if (args.factory) {
|
||||
return args.factory(
|
||||
if (instanceProperties.factory) {
|
||||
return instanceProperties.factory(
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
orignalFactory: (
|
||||
originalFactory: (
|
||||
innerContext?: {
|
||||
node?: AppNode;
|
||||
config?: TConfig;
|
||||
@@ -132,29 +195,123 @@ export class ExtensionKind<
|
||||
},
|
||||
innerOptions?: TOptions,
|
||||
) =>
|
||||
this.options.factory(
|
||||
this.kindProperties.factory(
|
||||
{
|
||||
node: innerContext?.node ?? node,
|
||||
config: innerContext?.config ?? config,
|
||||
inputs: innerContext?.inputs ?? inputs,
|
||||
},
|
||||
innerOptions ?? args.options,
|
||||
innerOptions ?? instanceProperties.options,
|
||||
),
|
||||
},
|
||||
args.options,
|
||||
instanceProperties.options,
|
||||
);
|
||||
}
|
||||
|
||||
return this.options.factory(
|
||||
return this.kindProperties.factory(
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
},
|
||||
args.options,
|
||||
instanceProperties.options,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
...extension,
|
||||
override: overrides => {
|
||||
return createExtension({
|
||||
kind: this.kindProperties.kind,
|
||||
namespace:
|
||||
overrides.namespace ??
|
||||
instanceProperties.namespace ??
|
||||
this.kindProperties.namespace,
|
||||
name:
|
||||
overrides.name ??
|
||||
instanceProperties.name ??
|
||||
this.kindProperties.name,
|
||||
attachTo:
|
||||
overrides.attachTo ??
|
||||
instanceProperties.attachTo ??
|
||||
this.kindProperties.attachTo,
|
||||
disabled:
|
||||
overrides.disabled ??
|
||||
instanceProperties.disabled ??
|
||||
this.kindProperties.disabled,
|
||||
inputs:
|
||||
overrides.inputs ??
|
||||
instanceProperties.inputs ??
|
||||
this.kindProperties.inputs,
|
||||
output:
|
||||
overrides.output ??
|
||||
instanceProperties.output ??
|
||||
this.kindProperties.output,
|
||||
configSchema:
|
||||
overrides.configSchema ??
|
||||
instanceProperties.configSchema ??
|
||||
this.kindProperties.configSchema, // TODO: some config merging or smth
|
||||
factory: ({ node, config, inputs }) => {
|
||||
if (overrides.factory) {
|
||||
return overrides.factory(
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
originalFactory: (
|
||||
innerContext?: {
|
||||
node?: AppNode;
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
innerOptions?: TOptions,
|
||||
) =>
|
||||
instanceProperties.factory?.(
|
||||
{
|
||||
node: innerContext?.node ?? node,
|
||||
config: innerContext?.config ?? config,
|
||||
inputs: innerContext?.inputs ?? inputs,
|
||||
originalFactory: this.kindProperties.factory,
|
||||
},
|
||||
innerOptions ?? instanceProperties.options,
|
||||
) ??
|
||||
this.kindProperties.factory(
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
},
|
||||
innerOptions ?? instanceProperties.options,
|
||||
),
|
||||
},
|
||||
instanceProperties.options,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
instanceProperties.factory?.(
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
originalFactory: this.kindProperties.factory,
|
||||
},
|
||||
instanceProperties.options,
|
||||
) ??
|
||||
this.kindProperties.factory(
|
||||
{
|
||||
node,
|
||||
config,
|
||||
inputs,
|
||||
},
|
||||
instanceProperties.options,
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,9 @@ export {
|
||||
type FrontendFeature,
|
||||
} from './types';
|
||||
export {
|
||||
type CreateExtensionKindOptions as ExtensionKindOptions,
|
||||
type CreateExtensionKindOptions,
|
||||
type CreateExtensionKindInstanceOptions,
|
||||
type CreateExtensionOverrideKindInstanceOptions,
|
||||
ExtensionKind,
|
||||
createExtensionKind,
|
||||
} from './createExtensionKind';
|
||||
|
||||
Reference in New Issue
Block a user