From e14cab944832d7decd2870014f62585fd7b48652 Mon Sep 17 00:00:00 2001 From: hulutter <> Date: Wed, 24 Apr 2024 09:22:13 +0200 Subject: [PATCH 1/2] feat(client): add id and class properties to each component to enable advanced styling capabilities --- .../src/comps/generators/uiCompBuilder.tsx | 70 +++++++++++++++++-- .../packages/lowcoder/src/i18n/locales/de.ts | 5 +- .../packages/lowcoder/src/i18n/locales/en.ts | 3 + 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx b/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx index a631f1d6d..f2b8fca93 100644 --- a/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx +++ b/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx @@ -1,4 +1,4 @@ -import { BoolCodeControl } from "comps/controls/codeControl"; +import { BoolCodeControl, StringControl } from "comps/controls/codeControl"; import React, { ReactNode, useContext, useRef } from "react"; import { ExternalEditorContext } from "util/context/ExternalEditorContext"; import { Comp, CompParams, MultiBaseComp } from "lowcoder-core"; @@ -22,10 +22,14 @@ import { MethodConfigsType, withMethodExposing, } from "./withMethodExposing"; +import { Section } from "lowcoder-design"; +import { trans } from "i18n"; export type NewChildren>> = ChildrenCompMap & { hidden: InstanceType; + id: InstanceType; + className: InstanceType; }; export function HidableView(props: { @@ -50,12 +54,46 @@ export function HidableView(props: { } } +export function ExtendedComponentView(props: { + children: JSX.Element | React.ReactNode; + id: string; + className: string; +}) { + if (!props.id && !props.className) { + return <>{props.children}; + } + + return ( +
+ {props.children} +
+ ); +} + +export function ExtendedPropertyView< + ChildrenCompMap extends Record>, +>(props: { + children: JSX.Element | React.ReactNode, + childrenMap: NewChildren + } +) { + return ( + <> + {props.children} +
+ {props.childrenMap.id?.propertyView({ label: trans("prop.id") })} + {props.childrenMap.className?.propertyView({ label: trans("prop.className") })} +
+ + ); +} + export function uiChildren< ChildrenCompMap extends Record>, >( childrenMap: ToConstructor ): ToConstructor> { - return { ...childrenMap, hidden: BoolCodeControl } as any; + return { ...childrenMap, hidden: BoolCodeControl, id: StringControl, className: StringControl } as any; } type ViewReturn = ReactNode; @@ -89,10 +127,22 @@ export class UICompBuilder< setPropertyViewFn( propertyViewFn: PropertyViewFnTypeForComp> ) { - this.propertyViewFn = propertyViewFn; + this.propertyViewFn = this.decoratePropertyViewFn(propertyViewFn); return this; } + decoratePropertyViewFn( + propertyViewFn: PropertyViewFnTypeForComp> + ): PropertyViewFnTypeForComp> { + return (childrenMap, dispatch) => { + return ( + + {propertyViewFn(childrenMap, dispatch)} + + ); + }; + } + setExposeStateConfigs( configs: ExposingConfig>[] ) { @@ -113,6 +163,12 @@ export class UICompBuilder< if (this.childrenMap.hasOwnProperty("hidden")) { throw new Error("already has hidden"); } + if (this.childrenMap.hasOwnProperty("id")) { + throw new Error("already has id"); + } + if (this.childrenMap.hasOwnProperty("className")) { + throw new Error("already has className"); + } const newChildrenMap = uiChildren(this.childrenMap); const builder = this; @@ -185,8 +241,10 @@ function UIView(props: { comp: any; viewFn: any }) { //END ADD BY FRED return ( - + + + ); } diff --git a/client/packages/lowcoder/src/i18n/locales/de.ts b/client/packages/lowcoder/src/i18n/locales/de.ts index 240173dd2..ae670a0a9 100644 --- a/client/packages/lowcoder/src/i18n/locales/de.ts +++ b/client/packages/lowcoder/src/i18n/locales/de.ts @@ -182,7 +182,10 @@ export const de = { "showBody": "Körper zeigen", "showFooter": "Fußzeile anzeigen", "maskClosable": "Zum Schließen auf \"Draußen\" klicken", - "showMask": "Maske zeigen" + "showMask": "Maske zeigen", + "component": "Komponente", + "id": "ID", + "className": "Klasse", }, "autoHeightProp": { "auto": "Auto", diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 6319fdd1f..6377b4eb3 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -211,6 +211,9 @@ export const en = { "baseURL": "Lowcoder API Base URL", "horizontal": "Horizontal", "minHorizontalWidth": "Minimum Horizontal Width", + "component": "Component", + "id": "ID", + "className": "Class", }, "autoHeightProp": { "auto": "Auto", From 9c8b97bf349b5d706a2d9369a53e4cdffa1774d9 Mon Sep 17 00:00:00 2001 From: hulutter <> Date: Wed, 24 Apr 2024 10:20:38 +0200 Subject: [PATCH 2/2] feat(client): remove id property as it might interfere with other features and add data-testid to enable test automation --- .../src/comps/generators/uiCompBuilder.tsx | 55 ++++++++++--------- .../packages/lowcoder/src/i18n/locales/de.ts | 2 +- .../packages/lowcoder/src/i18n/locales/en.ts | 2 +- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx b/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx index f2b8fca93..4ddf32d11 100644 --- a/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx +++ b/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx @@ -28,8 +28,8 @@ import { trans } from "i18n"; export type NewChildren>> = ChildrenCompMap & { hidden: InstanceType; - id: InstanceType; className: InstanceType; + dataTestId: InstanceType; }; export function HidableView(props: { @@ -56,15 +56,15 @@ export function HidableView(props: { export function ExtendedComponentView(props: { children: JSX.Element | React.ReactNode; - id: string; className: string; + dataTestId: string; }) { - if (!props.id && !props.className) { + if (!props.className && !props.dataTestId) { return <>{props.children}; - } - + } + return ( -
+
{props.children}
); @@ -73,17 +73,17 @@ export function ExtendedComponentView(props: { export function ExtendedPropertyView< ChildrenCompMap extends Record>, >(props: { - children: JSX.Element | React.ReactNode, - childrenMap: NewChildren - } + children: JSX.Element | React.ReactNode, + childrenMap: NewChildren +} ) { return ( <> - {props.children} -
- {props.childrenMap.id?.propertyView({ label: trans("prop.id") })} - {props.childrenMap.className?.propertyView({ label: trans("prop.className") })} -
+ {props.children} +
+ {props.childrenMap.className?.propertyView({ label: trans("prop.className") })} + {props.childrenMap.dataTestId?.propertyView({ label: trans("prop.dataTestId") })} +
); } @@ -93,7 +93,12 @@ export function uiChildren< >( childrenMap: ToConstructor ): ToConstructor> { - return { ...childrenMap, hidden: BoolCodeControl, id: StringControl, className: StringControl } as any; + return { + ...childrenMap, + hidden: BoolCodeControl, + className: StringControl, + dataTestId: StringControl + } as any; } type ViewReturn = ReactNode; @@ -160,14 +165,11 @@ export class UICompBuilder< } build() { - if (this.childrenMap.hasOwnProperty("hidden")) { - throw new Error("already has hidden"); - } - if (this.childrenMap.hasOwnProperty("id")) { - throw new Error("already has id"); - } - if (this.childrenMap.hasOwnProperty("className")) { - throw new Error("already has className"); + const reservedProps = ["hidden", "className", "dataTestId"]; + for (const reservedProp of reservedProps) { + if (this.childrenMap.hasOwnProperty(reservedProp)) { + throw new Error(`Property »${reservedProp}« is reserved and must not be implemented in components!`); + } } const newChildrenMap = uiChildren(this.childrenMap); const builder = this; @@ -178,7 +180,7 @@ export class UICompBuilder< ToNodeType> > { ref: React.RefObject = React.createRef(); - + override parseChildrenFromValue( params: CompParams>> ): NewChildren { @@ -241,7 +243,10 @@ function UIView(props: { comp: any; viewFn: any }) { //END ADD BY FRED return ( - + diff --git a/client/packages/lowcoder/src/i18n/locales/de.ts b/client/packages/lowcoder/src/i18n/locales/de.ts index ae670a0a9..514b0f2be 100644 --- a/client/packages/lowcoder/src/i18n/locales/de.ts +++ b/client/packages/lowcoder/src/i18n/locales/de.ts @@ -184,8 +184,8 @@ export const de = { "maskClosable": "Zum Schließen auf \"Draußen\" klicken", "showMask": "Maske zeigen", "component": "Komponente", - "id": "ID", "className": "Klasse", + "dataTestId": "Test ID", }, "autoHeightProp": { "auto": "Auto", diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 6377b4eb3..6b4510dd1 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -212,8 +212,8 @@ export const en = { "horizontal": "Horizontal", "minHorizontalWidth": "Minimum Horizontal Width", "component": "Component", - "id": "ID", "className": "Class", + "dataTestId": "Test ID", }, "autoHeightProp": { "auto": "Auto",