core-plugin-api: switch to using a plain string key for attaching component data
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-plugin-api': patch
|
||||
---
|
||||
|
||||
Migrated component data attachment method to have better compatibility with component proxies such as `react-hot-loader`.
|
||||
@@ -17,7 +17,7 @@
|
||||
import React from 'react';
|
||||
import { attachComponentData, getComponentData } from './componentData';
|
||||
|
||||
describe('elementData', () => {
|
||||
describe('componentData', () => {
|
||||
it('should attach a single piece of data', () => {
|
||||
const data = { foo: 'bar' };
|
||||
const Component = () => null;
|
||||
@@ -59,4 +59,51 @@ describe('elementData', () => {
|
||||
'Attempted to attach duplicate data "my-data" to component "MyComponent"',
|
||||
);
|
||||
});
|
||||
|
||||
describe('works across versions', () => {
|
||||
it('should should be able to get data from newer versions', () => {
|
||||
const data = { foo: 'bar' };
|
||||
const Component = () => null;
|
||||
attachComponentData(Component, 'my-data', data);
|
||||
|
||||
const element = <Component />;
|
||||
expect((element as any).type.__backstage_data.map.get('my-data')).toBe(
|
||||
data,
|
||||
);
|
||||
});
|
||||
|
||||
it('should should be able to attach data for newer versions', () => {
|
||||
const data = { foo: 'bar' };
|
||||
const Component = () => null;
|
||||
(Component as any).__backstage_data = {
|
||||
map: new Map([['my-data', data]]),
|
||||
};
|
||||
|
||||
const element = <Component />;
|
||||
expect(getComponentData(element, 'my-data')).toBe(data);
|
||||
});
|
||||
|
||||
it('should be able to get data from older versions', () => {
|
||||
const data = { foo: 'bar' };
|
||||
const Component = () => null;
|
||||
attachComponentData(Component, 'my-data', data);
|
||||
|
||||
const element = <Component />;
|
||||
const container = (global as any)[
|
||||
'__@backstage/component-data-store__'
|
||||
].get(element.type);
|
||||
expect(container.map.get('my-data')).toBe(data);
|
||||
});
|
||||
|
||||
it('should should be able to attach data for older versions', () => {
|
||||
const data = { foo: 'bar' };
|
||||
const Component = () => null;
|
||||
(global as any)['__@backstage/component-data-store__'].set(Component, {
|
||||
map: new Map([['my-data', data]]),
|
||||
});
|
||||
|
||||
const element = <Component />;
|
||||
expect(getComponentData(element, 'my-data')).toBe(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,24 +21,42 @@ type DataContainer = {
|
||||
map: Map<string, unknown>;
|
||||
};
|
||||
|
||||
type MaybeComponentNode = ReactNode & {
|
||||
type?: ComponentType<any>;
|
||||
};
|
||||
|
||||
// The store is bridged across versions using the global object
|
||||
// This method of storing the component data was deprecated in September 2021, it
|
||||
// will be removed in the future for the reasons described below.
|
||||
const globalStore = getOrCreateGlobalSingleton(
|
||||
'component-data-store',
|
||||
() => new WeakMap<ComponentType<any>, DataContainer>(),
|
||||
);
|
||||
|
||||
// This key is used to attach component data to the component type (function or class)
|
||||
// itself. This method is used because it has better compatibility component wrappers
|
||||
// like react-hot-loader, as opposed to the WeakMap method or using a symbol.
|
||||
const componentDataKey = '__backstage_data';
|
||||
|
||||
type ComponentWithData = ComponentType<any> & {
|
||||
[componentDataKey]?: DataContainer;
|
||||
};
|
||||
|
||||
type MaybeComponentNode = ReactNode & {
|
||||
type?: ComponentWithData;
|
||||
};
|
||||
|
||||
export function attachComponentData<P>(
|
||||
component: ComponentType<P>,
|
||||
type: string,
|
||||
data: unknown,
|
||||
) {
|
||||
let container = globalStore.get(component);
|
||||
const dataComponent = component as ComponentWithData;
|
||||
|
||||
let container = dataComponent[componentDataKey] ?? globalStore.get(component);
|
||||
if (!container) {
|
||||
container = { map: new Map() };
|
||||
Object.defineProperty(dataComponent, componentDataKey, {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: container,
|
||||
});
|
||||
globalStore.set(component, container);
|
||||
}
|
||||
|
||||
@@ -65,7 +83,7 @@ export function getComponentData<T>(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const container = globalStore.get(component);
|
||||
const container = component[componentDataKey] ?? globalStore.get(component);
|
||||
if (!container) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user