From 9046113414d4f9a7e10d44de90d91189274c0f45 Mon Sep 17 00:00:00 2001 From: Michael Beckemeyer Date: Mon, 18 Sep 2023 16:01:03 +0200 Subject: [PATCH] Add test for `theme` parameter. Also removes the (unused) props of the application root component to avoid confusion. --- src/packages/runtime/CustomElement.ts | 10 ++-- .../ReactIntegration.test.ts | 60 +++++++++++++++---- .../react-integration/ReactIntegration.tsx | 11 ++-- 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/packages/runtime/CustomElement.ts b/src/packages/runtime/CustomElement.ts index 33993182..d15d1890 100644 --- a/src/packages/runtime/CustomElement.ts +++ b/src/packages/runtime/CustomElement.ts @@ -72,10 +72,9 @@ export interface CustomElementOptions { openShadowRoot?: boolean; /** - * Chakra theming object + * Chakra theming object. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - theme?: Record; + theme?: Record; } /** @@ -321,6 +320,7 @@ class ElementState { this.reactIntegration = new ReactIntegration({ rootNode: container, container: shadowRoot, + theme: options.theme, serviceLayer, packages }); @@ -332,9 +332,7 @@ class ElementState { } private render() { - this.reactIntegration?.render(this.options.component ?? emptyComponent, { - theme: this.options.theme - }); + this.reactIntegration?.render(this.options.component ?? emptyComponent); } private initStyles() { diff --git a/src/packages/runtime/react-integration/ReactIntegration.test.ts b/src/packages/runtime/react-integration/ReactIntegration.test.ts index 46aca5b5..c98cd51f 100644 --- a/src/packages/runtime/react-integration/ReactIntegration.test.ts +++ b/src/packages/runtime/react-integration/ReactIntegration.test.ts @@ -6,17 +6,18 @@ import { createElement } from "react"; import { beforeEach, expect, it } from "vitest"; import { usePropertiesInternal, useServiceInternal, useServicesInternal } from "./hooks"; -import { findByText } from "@testing-library/dom"; +import { findByTestId, findByText } from "@testing-library/dom"; +import { act } from "@testing-library/react"; import { Service, ServiceConstructor } from "../Service"; // eslint-disable-next-line import/no-relative-packages import { UIWithProperties, UIWithService, UIWithServices } from "./test-data/test-package/UI"; import { ServiceLayer } from "../service-layer/ServiceLayer"; import { ReactIntegration } from "./ReactIntegration"; -import { act } from "react-dom/test-utils"; import { PackageRepr } from "../service-layer/PackageRepr"; import { createConstructorFactory, ServiceRepr } from "../service-layer/ServiceRepr"; import { InterfaceSpec, ReferenceSpec } from "../service-layer/InterfaceSpec"; import { createEmptyI18n, PackageIntl } from "../i18n"; +import { useTheme } from "@open-pioneer/chakra-integration"; interface TestProvider { value: string; @@ -46,7 +47,7 @@ it("should allow access to service via react hook", async () => { }); act(() => { - integration.render(TestComponent, {}); + integration.render(TestComponent); }); const node = await findByText(wrapper, "Hello TEST"); @@ -65,7 +66,7 @@ it("should get error when using undefined service", async () => { expect(() => { act(() => { - integration.render(TestComponent, {}); + integration.render(TestComponent); }); }).toThrowErrorMatchingSnapshot(); }); @@ -92,7 +93,7 @@ it("should allow access to service with qualifier via react hook", async () => { }); act(() => { - integration.render(TestComponent, {}); + integration.render(TestComponent); }); const node = await findByText(wrapper, "Hello TEST"); @@ -122,7 +123,7 @@ it("should deny access to service when the qualifier does not match", async () = expect(() => { act(() => { - integration.render(TestComponent, {}); + integration.render(TestComponent); }); }).toThrowErrorMatchingSnapshot(); }); @@ -165,7 +166,7 @@ it("should allow access to all services via react hook", async () => { }); act(() => { - integration.render(TestComponent, {}); + integration.render(TestComponent); }); const node = await findByText(wrapper, /^Joined Values:/); @@ -188,7 +189,7 @@ it("should deny access to all services if declaration is missing", async () => { expect(() => { act(() => { - integration.render(TestComponent, {}); + integration.render(TestComponent); }); }).toThrowErrorMatchingSnapshot(); }); @@ -206,7 +207,7 @@ it("should be able to read properties from react component", async () => { }); act(() => { - integration.render(TestComponent, {}); + integration.render(TestComponent); }); const node = await findByText(wrapper, "Hello USER"); @@ -230,7 +231,7 @@ it("should provide the autogenerated useService hook", async () => { }); act(() => { - integration.render(UIWithService, {}); + integration.render(UIWithService); }); const node = await findByText(wrapper, /^Test-UI:/); @@ -261,7 +262,7 @@ it("should provide the autogenerated useServices hook", async () => { }); act(() => { - integration.render(UIWithServices, {}); + integration.render(UIWithServices); }); const node = await findByText(wrapper, /^Test-UI:/); @@ -278,7 +279,7 @@ it("should provide the autogenerated useProperties hook", async () => { }); act(() => { - integration.render(UIWithProperties, {}); + integration.render(UIWithProperties); }); const node = await findByText(wrapper, /^Test-UI:/); @@ -297,11 +298,42 @@ it("should throw error when requesting properties from an unknown package", asyn expect(() => { act(() => { - integration.render(TestComponent, {}); + integration.render(TestComponent); }); }).toThrowErrorMatchingSnapshot(); }); +it("should apply the configured chakra theme", async () => { + const testTheme = { + colors: { + dummyColor: "#123456" + } + }; + const { integration, wrapper } = createIntegration({ + disablePackage: true, + theme: testTheme + }); + + function TestComponent() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const theme = useTheme() as any; + return createElement( + "div", + { + "data-testid": "test-div" + }, + `Color: ${theme.colors.dummyColor}` + ); + } + + act(() => { + integration.render(TestComponent); + }); + + const node = await findByTestId(wrapper, "test-div"); + expect(node.textContent).toBe("Color: #123456"); +}); + interface ServiceSpec { name: string; interfaces: InterfaceSpec[]; @@ -320,6 +352,7 @@ function createIntegration(options?: { packageUiReferences?: ReferenceSpec[]; i18n?: PackageIntl; services?: ServiceSpec[]; + theme?: Record; }): TestIntegration { const wrapper = document.createElement("div"); const packages = new Map(); @@ -354,6 +387,7 @@ function createIntegration(options?: { const integration = new ReactIntegration({ container: wrapper, rootNode: wrapper, + theme: options?.theme, packages, serviceLayer }); diff --git a/src/packages/runtime/react-integration/ReactIntegration.tsx b/src/packages/runtime/react-integration/ReactIntegration.tsx index 647dc6a4..cd40406e 100644 --- a/src/packages/runtime/react-integration/ReactIntegration.tsx +++ b/src/packages/runtime/react-integration/ReactIntegration.tsx @@ -16,10 +16,12 @@ export interface ReactIntegrationOptions { serviceLayer: ServiceLayer; rootNode: HTMLDivElement; container: Node; + theme: Record | undefined; } export class ReactIntegration { private containerNode: Node; + private theme: Record | undefined; private packages: Map; private serviceLayer: ServiceLayer; private root: Root; @@ -27,6 +29,7 @@ export class ReactIntegration { constructor(options: ReactIntegrationOptions) { this.containerNode = options.container; + this.theme = options.theme; this.packages = options.packages; this.serviceLayer = options.serviceLayer; this.root = createRoot(options.rootNode); @@ -85,18 +88,16 @@ export class ReactIntegration { }; } - render(Component: ComponentType, props: Record) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const customTheme = props.theme as Record; + render(Component: ComponentType) { this.root.render( - +