diff --git a/packages/app-experiments/src/Experiment2.tsx b/packages/app-experiments/src/Experiment2.tsx index f188114b50..ef803803c8 100644 --- a/packages/app-experiments/src/Experiment2.tsx +++ b/packages/app-experiments/src/Experiment2.tsx @@ -14,21 +14,16 @@ * limitations under the License. */ -import React, { CSSProperties, ComponentType, createContext } from 'react'; -import { z } from 'zod'; +import React, { ComponentType } from 'react'; import mapValues from 'lodash/mapValues'; -import { Typography } from '@material-ui/core'; /** * App structure * - Root - * - GroceryLayout (fruit + vegetable columns} - * - Apple [grocery, fruit, title, component] - * - Orange [grocery, fruit, title, component] - * - Carrot [grocery, vegetable, component] - * - Tomato [grocery, vegetable, title, component] - * - Banana [grocery, title, component] - * - Metallica [band, title] + * - GroceryLayout (fruit} + * - Apple [grocery, fruit, component] + * - Orange [grocery, fruit, component] + * - Metallica [band] */ interface ExtensionDataRef { @@ -42,94 +37,150 @@ function createExtensionDataRef(id: string) { } const coreExtensionData = { - reactComponent: createExtensionDataRef<{ Component: ComponentType }>( - 'core.reactComponent', - ), - routable: createExtensionDataRef<{ path: string }>('core.routable'), - title: createExtensionDataRef<{ title: string }>('core.title'), + reactComponent: createExtensionDataRef('core.reactComponent'), + + isGrocery: createExtensionDataRef('isGrocery'), + isFruit: createExtensionDataRef('isFruit'), + + isBand: createExtensionDataRef('isBand'), + title: createExtensionDataRef('core.title'), }; -createPlugin({ - id: 'catalog', - extension: [], -}); +type AnyExtensionDataMap = Record>; -function createExtension(options: { id: string }) { +type ExtensionDataBind = { + [K in keyof TData]: (value: TData[K]['T']) => void; +}; + +type ExtensionDataValue = { + [K in keyof TData]: TData[K]['T']; +}; + +interface CreateExtensionOptions< + TData extends AnyExtensionDataMap, + TPoint extends Record, +> { + extensionData: TData; + points?: TPoint; + factory(options: { + bind: ExtensionDataBind; + config?: unknown; + points: { + [pointName in keyof TPoint]: ExtensionDataValue< + TPoint[pointName]['extensionData'] + >[]; + }; + }): void; +} + +function createExtension< + TData extends AnyExtensionDataMap, + TPoint extends Record, +>(options: CreateExtensionOptions) { return options; } -createExtension1({ - // id: 'catalog', - // features: [ - // { - // typeRef: coreExtensionData.routable, - // data: { - // path: 'catalog' - // } - // } - // ], - // init({ bind }) { - // bind(coreExtensionData.routable, { - // path: 'catalog' - // }) - // }, - // factory(bind, config, mountConfig) { - // bind(coreExtensionData.routable, { - // path: 'catalog' - // }) - // }, +const GroceryLayout = createExtension({ + points: { + groceries: { + // requiredData: [ + // coreExtensionData.reactComponent, + // coreExtensionData.isGrocery, + // ], + extensionData: { + Component: coreExtensionData.reactComponent, + isGrocery: coreExtensionData.isGrocery, + }, + }, + }, extensionData: { - // Record> ? - routable: { ref: coreExtensionData.routable, optional: true }, - title: coreExtensionData.title, component: coreExtensionData.reactComponent, }, - configSchema: z.object({ - path: z.string(), - title: z.string().default('Ma title'), - }), - factory(bind, config) { - bind.title({ title: config.title }); - const Component = () =>
hello
; - bind.component({ Component }); - bind.routable({ path: config.path }); + factory({ bind, points }) { + const elements = points.groceries.map(point => ); + /** + * Probably easier to evolve over time + * const elements = points.groceries.map((point) => getExtensionData(point, coreExtensionData.reactComponent)) + **/ + const Component = () =>
yo yo yo, here's my elements: {elements}
; + bind.component(Component); }, }); -function createEntityContentExtension() {} - -const AboutCard = createEntityContentExtension({ - component: () =>
hello
, +const Apple = createExtension({ extensionData: { - // Record> ? - channels: coreExtensionData.notificationChannels, + component: coreExtensionData.reactComponent, + isGrocery: coreExtensionData.isGrocery, + isFruit: coreExtensionData.isFruit, + }, + factory({ bind }) { + const Component = () =>
apple
; + bind.component(Component); + bind.isGrocery(true); + bind.isFruit(true); }, - configSchema: z.object({ - channelName: z.string(), - }), - factory(bind, config) {}, }); -function createEntityCardExtension(options: { configSchema }) { - return createExtension({ - configSchema: options.configSchema( - z.object({ - path: z.string(), - title: z.string(), - }), - ), - factory() {}, +const Metallica = createExtension({ + extensionData: { + component: coreExtensionData.reactComponent, + }, + factory({ bind }) { + const Component = () =>
HEAVY METAL YEAH
; + bind.component(Component); + }, +}); + +const Orange = createExtension({ + extensionData: { + component: coreExtensionData.reactComponent, + isGrocery: coreExtensionData.isGrocery, + isFruit: coreExtensionData.isFruit, + }, + factory({ bind }) { + const Component = () =>
orange
; + bind.component(Component); + bind.isGrocery(true); + bind.isFruit(true); + }, +}); + +function createExtensionInstance( + options: CreateExtensionOptions< + AnyExtensionDataMap, + Record + >, + children: { data: Map, unknown> }[], +) { + const extensionData = new Map, unknown>(); + options.factory({ + bind: mapValues(options.extensionData, ref => { + return (value: unknown) => extensionData.set(ref, value); + }), + points: mapValues(options.points, ({ extensionData: pointData }) => { + const requiredRef = Object.values(pointData); + const matchingChildren = children.filter(child => + requiredRef.every(ref => child.data.has(ref)), + ); + return matchingChildren.map(child => + mapValues(pointData, ref => child.data.get(ref)), + ); + }), }); + return { data: extensionData }; } -const createEntityCardExtension = createExtensionFactory({ - baseConfigSchema: z.object({ - path: z.string(), - title: z.string(), - }), -}); - -const AboutCard = createEntityCardExtension({ - // features: [catalogCard], - configSchema: parent => parent.union(), -}); +export function Experiment2() { + const apple = createExtensionInstance(Apple, []); + const orange = createExtensionInstance(Orange, []); + const metallica = createExtensionInstance(Metallica, []); + const layout = createExtensionInstance(GroceryLayout, [ + apple, + orange, + metallica, + ]); + const Component = layout.data.get( + coreExtensionData.reactComponent, + ) as ComponentType; + return ; +} diff --git a/packages/app-experiments/src/index.tsx b/packages/app-experiments/src/index.tsx index 6f0434c15e..3abd870c17 100644 --- a/packages/app-experiments/src/index.tsx +++ b/packages/app-experiments/src/index.tsx @@ -17,6 +17,6 @@ import '@backstage/cli/asset-types'; import React from 'react'; import ReactDOM from 'react-dom'; -import { Experiment1 } from './Experiment1'; +import { Experiment2 } from './Experiment2'; -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render(, document.getElementById('root'));