From fcebd611b8d59fbe30d703380360e9537619130a Mon Sep 17 00:00:00 2001 From: JulianWielga Date: Fri, 22 Nov 2024 18:23:44 +0100 Subject: [PATCH] Toolbar additional parameters (#7168) * ToolbarPanelConfig additionalParams (cherry picked from commit 893c34c7470aa9fc663fdde600dd222797de7cbf) * allow additionalParams in RemoteComponent * add RemoteModuleDialog * deps * fixed missing params * fixed missing params * label translation * allow RemoteModuleDialog buttons customization * revert unnecessary changes * test update * test update * fixed theme & styles * review * formatting * Update NussknackerRemoteDebug.run.xml * Update NussknackerRemoteDebug.run.xml * Update NussknackerRemoteDebug.run.xml * formatting --- designer/client/package-lock.json | 15 +++-- designer/client/package.json | 2 +- .../client/src/components/RemoteComponent.tsx | 38 ++++++++++-- .../src/components/RemoteModuleDialog.tsx | 61 +++++++++++++++++++ .../fields/StyledSettingsComponnets.tsx | 4 +- .../toolbarComponents/DefaultToolbarPanel.tsx | 13 ++-- .../toolbarWrapper/ToolbarWrapper.tsx | 7 ++- .../src/components/toolbarSettings/types.ts | 1 + .../toolbars/creator/CreatorPanel.tsx | 50 +++++++++++++-- .../components/toolbars/creator/ToolBox.tsx | 24 +++++--- .../creator/ToolboxComponentGroup.tsx | 9 ++- .../RemoteAuthStrategy/RemoteAuthStrategy.tsx | 26 ++------ .../RemoteAuthStrategy/externalAuthModule.ts | 4 +- .../client/src/containers/theme/nuTheme.tsx | 33 ++++++---- .../src/windowManager/ContentGetter.tsx | 3 + .../src/windowManager/WindowContent.tsx | 2 +- .../client/src/windowManager/WindowKind.tsx | 1 + .../scenariotoolbar/ToolbarPanelConfig.scala | 4 +- .../ui/process/ScenarioToolbarService.scala | 11 ++-- .../ui/api/ProcessesResourcesSpec.scala | 9 +-- ...riesScenarioToolbarsConfigParserSpec.scala | 32 ++++++++-- .../ConfigScenarioToolbarServiceSpec.scala | 54 +++++++++------- 22 files changed, 293 insertions(+), 110 deletions(-) create mode 100644 designer/client/src/components/RemoteModuleDialog.tsx diff --git a/designer/client/package-lock.json b/designer/client/package-lock.json index 1b7539f899a..f46693d865d 100644 --- a/designer/client/package-lock.json +++ b/designer/client/package-lock.json @@ -21,7 +21,7 @@ "@mui/icons-material": "5.15.7", "@mui/lab": "5.0.0-alpha.165", "@mui/material": "5.15.7", - "@touk/federated-component": "1.0.0", + "@touk/federated-component": "1.1.0", "@touk/window-manager": "1.9.0", "ace-builds": "1.34.2", "axios": "1.7.5", @@ -6109,10 +6109,9 @@ } }, "node_modules/@touk/federated-component": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@touk/federated-component/-/federated-component-1.0.0.tgz", - "integrity": "sha512-ibliSr5T1pbM8S8M0NqUGJlBQJSmBeDTuO1fcPRQ97XO+Ai+9tQ00qUaCr0tI43cdTXLlkunXW+K5IlM4Krx+Q==", - "hasInstallScript": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@touk/federated-component/-/federated-component-1.1.0.tgz", + "integrity": "sha512-pYHL8d4RWxONtW3+PJT/1AzaS9cmtyKLd9VXhEM+dIL0DMIe2IkvB12W/yZUBeB1mW8bWlG/vpthL0lD8yV44Q==", "peerDependencies": { "react": "^17 || ^18", "react-dom": "^17 || ^18" @@ -32692,9 +32691,9 @@ "dev": true }, "@touk/federated-component": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@touk/federated-component/-/federated-component-1.0.0.tgz", - "integrity": "sha512-ibliSr5T1pbM8S8M0NqUGJlBQJSmBeDTuO1fcPRQ97XO+Ai+9tQ00qUaCr0tI43cdTXLlkunXW+K5IlM4Krx+Q==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@touk/federated-component/-/federated-component-1.1.0.tgz", + "integrity": "sha512-pYHL8d4RWxONtW3+PJT/1AzaS9cmtyKLd9VXhEM+dIL0DMIe2IkvB12W/yZUBeB1mW8bWlG/vpthL0lD8yV44Q==" }, "@touk/federated-types": { "version": "1.1.0", diff --git a/designer/client/package.json b/designer/client/package.json index 89dd719feb2..9bba27fe7e0 100644 --- a/designer/client/package.json +++ b/designer/client/package.json @@ -14,7 +14,7 @@ "@mui/icons-material": "5.15.7", "@mui/lab": "5.0.0-alpha.165", "@mui/material": "5.15.7", - "@touk/federated-component": "1.0.0", + "@touk/federated-component": "1.1.0", "@touk/window-manager": "1.9.0", "ace-builds": "1.34.2", "axios": "1.7.5", diff --git a/designer/client/src/components/RemoteComponent.tsx b/designer/client/src/components/RemoteComponent.tsx index 6c28c975a2b..ddca29f0e1b 100644 --- a/designer/client/src/components/RemoteComponent.tsx +++ b/designer/client/src/components/RemoteComponent.tsx @@ -1,8 +1,9 @@ -import React from "react"; +import React, { useMemo } from "react"; import LoaderSpinner from "./spinner/Spinner"; import { FederatedComponent, FederatedComponentProps, getFederatedComponentLoader } from "@touk/federated-component"; import { NuThemeProvider } from "../containers/theme/nuThemeProvider"; import SystemUtils from "../common/SystemUtils"; +import { useWindows, WindowKind } from "../windowManager"; export const loadExternalReactModule = getFederatedComponentLoader({ Wrapper: NuThemeProvider }); export const loadExternalReactModuleWithAuth = getFederatedComponentLoader({ @@ -13,6 +14,35 @@ export const loadExternalReactModuleWithAuth = getFederatedComponentLoader({ window["loadExternalReactModule"] = loadExternalReactModule; window["loadExternalReactModuleWithAuth"] = loadExternalReactModuleWithAuth; -export const RemoteComponent =

>(props: FederatedComponentProps

) => ( - {...props} fallback={} buildHash={__BUILD_HASH__} /> -); +export type RemoteToolbarContentProps = { + openRemoteModuleWindow:

>(props: P & { url?: string; title?: string }) => void; +}; + +function RemoteComponentRender

, T = unknown>(props: FederatedComponentProps

, ref: React.ForwardedRef) { + const { open } = useWindows(); + const sharedContext = useMemo( + () => ({ + openRemoteModuleWindow: ({ title, ...props }) => + open({ + kind: WindowKind.remote, + title, + meta: props, + }), + }), + [open], + ); + + return ( + + ref={ref} + {...sharedContext} + {...props} + fallback={} + buildHash={__BUILD_HASH__} + /> + ); +} + +export const RemoteComponent = React.forwardRef(RemoteComponentRender) as

, T = unknown>( + props: FederatedComponentProps

& React.RefAttributes, +) => React.ReactElement; diff --git a/designer/client/src/components/RemoteModuleDialog.tsx b/designer/client/src/components/RemoteModuleDialog.tsx new file mode 100644 index 00000000000..4d44d822dab --- /dev/null +++ b/designer/client/src/components/RemoteModuleDialog.tsx @@ -0,0 +1,61 @@ +import { ModuleUrl } from "@touk/federated-component"; +import { WindowContentProps } from "@touk/window-manager"; +import type { FooterButtonProps } from "@touk/window-manager/cjs/components/window/footer"; +import React, { useCallback, useMemo, useRef } from "react"; +import { useTranslation } from "react-i18next"; +import { WindowContent, WindowKind } from "../windowManager"; +import { LoadingButtonTypes } from "../windowManager/LoadingButton"; +import { RemoteComponent } from "./RemoteComponent"; + +export type RemoteModuleDialogProps = NonNullable; +export type RemoteModuleDialogRef = NonNullable<{ + closeAction?: () => Promise; + adjustButtons: (buttons: { closeButton: FooterButtonProps; confirmButton: FooterButtonProps }) => FooterButtonProps[]; +}>; + +export function RemoteModuleDialog

>({ + close, + ...props +}: WindowContentProps): JSX.Element { + const { + data: { meta: passProps }, + } = props; + + const ref = useRef(); + + const closeAction = useCallback(async () => { + await Promise.all([ref.current?.closeAction?.()]); + close(); + }, [close]); + + const { t } = useTranslation(); + + const closeButton = useMemo( + () => ({ + title: t("dialog.button.cancel", "cancel"), + action: closeAction, + className: LoadingButtonTypes.secondaryButton, + }), + [closeAction, t], + ); + + const confirmButton = useMemo( + () => ({ + title: t("dialog.button.ok", "OK"), + action: closeAction, + }), + [closeAction, t], + ); + + return ( + + ref={ref} {...passProps} /> + + ); +} + +export default RemoteModuleDialog; diff --git a/designer/client/src/components/graph/node-modal/fragment-input-definition/settings/variants/fields/StyledSettingsComponnets.tsx b/designer/client/src/components/graph/node-modal/fragment-input-definition/settings/variants/fields/StyledSettingsComponnets.tsx index 741d527aeab..c1212443023 100644 --- a/designer/client/src/components/graph/node-modal/fragment-input-definition/settings/variants/fields/StyledSettingsComponnets.tsx +++ b/designer/client/src/components/graph/node-modal/fragment-input-definition/settings/variants/fields/StyledSettingsComponnets.tsx @@ -16,7 +16,9 @@ export const SettingLabelStyled = styled(FormLabel)(({ theme }) => ({ color: theme.palette.text.secondary, fontSize: "12px", fontWeight: "400", - flexBasis: "30%", + ".MuiFormControl-root &": { + flexBasis: "30%", + }, })); export const ListItemContainer = styled("div")` diff --git a/designer/client/src/components/toolbarComponents/DefaultToolbarPanel.tsx b/designer/client/src/components/toolbarComponents/DefaultToolbarPanel.tsx index 98e723cca39..57286f2cc99 100644 --- a/designer/client/src/components/toolbarComponents/DefaultToolbarPanel.tsx +++ b/designer/client/src/components/toolbarComponents/DefaultToolbarPanel.tsx @@ -1,10 +1,9 @@ -import React, { PropsWithChildren, ReactElement, useMemo } from "react"; +import React, { PropsWithChildren, ReactElement } from "react"; import { useTranslation } from "react-i18next"; -import { splitUrl } from "@touk/federated-component"; -import { ToolbarButtons } from "./toolbarButtons"; +import { RemoteComponent } from "../RemoteComponent"; import { ToolbarConfig } from "../toolbarSettings/types"; +import { ToolbarButtons } from "./toolbarButtons"; import { ToolbarWrapper } from "./toolbarWrapper/ToolbarWrapper"; -import { RemoteComponent } from "../RemoteComponent"; export type ToolbarPanelProps = PropsWithChildren>; @@ -22,8 +21,6 @@ export function DefaultToolbarPanel(props: ToolbarPanelProps): ReactElement { } function RemoteToolbarContent(props: ToolbarPanelProps): ReactElement { - const { componentUrl, ...passProps } = props; - const [url, scope] = useMemo(() => splitUrl(componentUrl), [componentUrl]); - - return ; + const { componentUrl, additionalParams, ...passProps } = props; + return ; } diff --git a/designer/client/src/components/toolbarComponents/toolbarWrapper/ToolbarWrapper.tsx b/designer/client/src/components/toolbarComponents/toolbarWrapper/ToolbarWrapper.tsx index e78e5e1b95c..e3209976ae4 100644 --- a/designer/client/src/components/toolbarComponents/toolbarWrapper/ToolbarWrapper.tsx +++ b/designer/client/src/components/toolbarComponents/toolbarWrapper/ToolbarWrapper.tsx @@ -25,7 +25,7 @@ export const TOOLBAR_WRAPPER_CLASSNAME = "toolbar-wrapper"; export function ToolbarWrapper(props: ToolbarWrapperProps): React.JSX.Element | null { const theme = useTheme(); - const { title, children, id, onClose, onExpand, onCollapse, color = theme.palette.background.paper, disableCollapse } = props; + const { title, children, id, onClose, onExpand, onCollapse, color, disableCollapse } = props; const handlerProps = useDragHandler(); const dispatch = useDispatch(); @@ -59,7 +59,7 @@ export function ToolbarWrapper(props: ToolbarWrapperProps): React.JSX.Element | borderRadius: theme.spacing(0.5), }} expanded={!isCollapsedLocal} - color={color} + color={color || theme.palette.background.paper} width={SIDEBAR_WIDTH} data-testid={id} {...(isCollapsible ? {} : handlerProps)} @@ -67,7 +67,7 @@ export function ToolbarWrapper(props: ToolbarWrapperProps): React.JSX.Element | {(isCollapsible || onClose) && ( { if (e.key === "Enter") { @@ -82,6 +82,7 @@ export function ToolbarWrapper(props: ToolbarWrapperProps): React.JSX.Element | textTransform={"uppercase"} variant={"overline"} sx={{ + color: color ? "inherit" : undefined, "::after": { // force line height for empty content: "' '", diff --git a/designer/client/src/components/toolbarSettings/types.ts b/designer/client/src/components/toolbarSettings/types.ts index 130c3b2f7dc..05346232a32 100644 --- a/designer/client/src/components/toolbarSettings/types.ts +++ b/designer/client/src/components/toolbarSettings/types.ts @@ -11,6 +11,7 @@ export interface ToolbarConfig { color?: string; buttonsVariant?: ButtonsVariant; disableCollapse?: boolean; + additionalParams?: Record; } export type ToolbarsConfig = Partial>; diff --git a/designer/client/src/components/toolbars/creator/CreatorPanel.tsx b/designer/client/src/components/toolbars/creator/CreatorPanel.tsx index 58f7deffbdf..53eae31ce26 100644 --- a/designer/client/src/components/toolbars/creator/CreatorPanel.tsx +++ b/designer/client/src/components/toolbars/creator/CreatorPanel.tsx @@ -1,14 +1,35 @@ +import { ModuleUrl } from "@touk/federated-component"; import { isEmpty } from "lodash"; import React, { useCallback, useState } from "react"; +import { ErrorBoundary } from "react-error-boundary"; import { useTranslation } from "react-i18next"; +import { EventTrackingSelector, getEventTrackingProps } from "../../../containers/event-tracking"; +import { RemoteComponent } from "../../RemoteComponent"; import { SearchIcon } from "../../table/SearchFilter"; +import { SearchInputWithIcon } from "../../themed/SearchInput"; import { ToolbarPanelProps } from "../../toolbarComponents/DefaultToolbarPanel"; import { ToolbarWrapper } from "../../toolbarComponents/toolbarWrapper/ToolbarWrapper"; import ToolBox from "./ToolBox"; -import { SearchInputWithIcon } from "../../themed/SearchInput"; -import { EventTrackingSelector, getEventTrackingProps } from "../../../containers/event-tracking"; -export function CreatorPanel(props: ToolbarPanelProps): JSX.Element { +type CreatorPanelProps = ToolbarPanelProps & { + additionalParams?: { + addGroupElement?: ModuleUrl; + }; +}; + +const AddGroupElement =

>(props: P) => { + const { t } = useTranslation(); + return props.url ? ( + + + + ) : null; +}; + +export function CreatorPanel({ additionalParams, ...props }: CreatorPanelProps): JSX.Element { const { t } = useTranslation(); const [filter, setFilter] = useState(""); const clearFilter = useCallback(() => setFilter(""), []); @@ -24,7 +45,28 @@ export function CreatorPanel(props: ToolbarPanelProps): JSX.Element { > - + ( + + )} + addTreeElement={({ name }) => ( + + )} + /> ); } diff --git a/designer/client/src/components/toolbars/creator/ToolBox.tsx b/designer/client/src/components/toolbars/creator/ToolBox.tsx index 787c16d4776..def6637eb36 100644 --- a/designer/client/src/components/toolbars/creator/ToolBox.tsx +++ b/designer/client/src/components/toolbars/creator/ToolBox.tsx @@ -1,16 +1,15 @@ +import { lighten, styled } from "@mui/material"; +import { getLuminance } from "@mui/system/colorManipulator"; import React, { useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { useSelector } from "react-redux"; import "react-treeview/react-treeview.css"; import { filterComponentsByLabel } from "../../../common/ProcessDefinitionUtils"; +import { blendDarken, blendLighten } from "../../../containers/theme/helpers"; import { getProcessDefinitionData } from "../../../reducers/selectors/settings"; import { ComponentGroup } from "../../../types"; -import { ToolboxComponentGroup } from "./ToolboxComponentGroup"; import Tool from "./Tool"; -import { useTranslation } from "react-i18next"; -import { lighten, styled } from "@mui/material"; - -import { blendDarken, blendLighten } from "../../../containers/theme/helpers"; -import { getLuminance } from "@mui/system/colorManipulator"; +import { ToolboxComponentGroup } from "./ToolboxComponentGroup"; const StyledToolbox = styled("div")(({ theme }) => ({ fontSize: "14px", @@ -78,6 +77,7 @@ const StyledToolbox = styled("div")(({ theme }) => ({ padding: theme.spacing(0.75, 0.5, 0.75, 4), border: "none", borderRight: 0, + userSelect: "none", "&.disabled": { opacity: 0.4, cursor: "not-allowed !important", @@ -88,7 +88,7 @@ const StyledToolbox = styled("div")(({ theme }) => ({ cursor: "grabbing", }, - "&:hover": { + "&:hover, &:focus-within": { backgroundColor: theme.palette.action.hover, color: lighten(theme.palette.text.primary, 0.2), }, @@ -104,7 +104,13 @@ const StyledToolbox = styled("div")(({ theme }) => ({ }, })); -export default function ToolBox(props: { filter: string }): JSX.Element { +type ToolBoxProps = { + filter: string; + addTreeElement?: (group: ComponentGroup) => React.ReactElement | null; + addGroupLabelElement?: (group: ComponentGroup) => React.ReactElement | null; +}; + +export default function ToolBox(props: ToolBoxProps): JSX.Element { const processDefinitionData = useSelector(getProcessDefinitionData); const { t } = useTranslation(); @@ -126,6 +132,8 @@ export default function ToolBox(props: { filter: string }): JSX.Element { componentGroup={componentGroup} highlights={filters} flatten={groups.length === 1} + addTreeElement={props.addTreeElement?.(componentGroup)} + addGroupLabelElement={props.addGroupLabelElement?.(componentGroup)} /> )) ) : ( diff --git a/designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx b/designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx index 0480c3ae9e0..4397aee00e1 100644 --- a/designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx +++ b/designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx @@ -28,10 +28,12 @@ interface Props { componentGroup: ComponentGroup; highlights?: string[]; flatten?: boolean; + addTreeElement?: React.ReactElement | null; + addGroupLabelElement?: React.ReactElement | null; } export function ToolboxComponentGroup(props: Props): JSX.Element { - const { componentGroup, highlights = [], flatten } = props; + const { componentGroup, highlights = [], flatten, addGroupLabelElement, addTreeElement } = props; const dispatch = useDispatch(); const closedComponentGroups = useSelector(getClosedComponentGroups); const { name } = componentGroup; @@ -53,6 +55,7 @@ export function ToolboxComponentGroup(props: Props): JSX.Element { {name} + {addGroupLabelElement} ), - [highlighted, name, toggle, toggleForceCollapsed], + [addGroupLabelElement, highlighted, name, toggle, toggleForceCollapsed], ); const elements = useMemo( @@ -89,6 +93,7 @@ export function ToolboxComponentGroup(props: Props): JSX.Element { onClick={highlighted ? toggleForceCollapsed : toggle} > {elements} + {addTreeElement} ); } diff --git a/designer/client/src/containers/Auth/strategies/RemoteAuthStrategy/RemoteAuthStrategy.tsx b/designer/client/src/containers/Auth/strategies/RemoteAuthStrategy/RemoteAuthStrategy.tsx index 27f85b7f576..d73cd088d19 100644 --- a/designer/client/src/containers/Auth/strategies/RemoteAuthStrategy/RemoteAuthStrategy.tsx +++ b/designer/client/src/containers/Auth/strategies/RemoteAuthStrategy/RemoteAuthStrategy.tsx @@ -1,4 +1,4 @@ -import { ModuleString, ModuleUrl, splitUrl } from "@touk/federated-component"; +import { ModuleUrl } from "@touk/federated-component"; import React, { FunctionComponent, PropsWithChildren } from "react"; import { PendingPromise } from "../../../../common/PendingPromise"; import SystemUtils from "../../../../common/SystemUtils"; @@ -14,20 +14,11 @@ type RemoteAuthProviderProps = PropsWithChildren<{ onInit: AuthLibCallback; }>; -function createAuthWrapper( - { - url, - scope, - }: { - url: ModuleUrl; - scope: ModuleString; - }, - onInit: AuthLibCallback, -): FunctionComponent { +function createAuthWrapper(url: ModuleUrl, onInit: AuthLibCallback): FunctionComponent { return function Wrapper({ children }: PropsWithChildren) { return ( - url={url} scope={scope} onInit={onInit}> + url={url} onInit={onInit}> {children} @@ -65,15 +56,8 @@ export const RemoteAuthStrategy: StrategyConstructor = class RemoteAuthStrategy constructor(private settings: RemoteAuthenticationSettings) {} - private get urlWithScope(): { - scope: ModuleString; - url: ModuleUrl; - } { - const [url, scope] = splitUrl(this.settings.moduleUrl as ModuleUrl); - return { - url, - scope, - }; + private get urlWithScope(): ModuleUrl { + return this.settings.moduleUrl as ModuleUrl; } private onError?: (error: AuthErrorCodes) => void = () => { diff --git a/designer/client/src/containers/Auth/strategies/RemoteAuthStrategy/externalAuthModule.ts b/designer/client/src/containers/Auth/strategies/RemoteAuthStrategy/externalAuthModule.ts index a26b1db595f..200128009e4 100644 --- a/designer/client/src/containers/Auth/strategies/RemoteAuthStrategy/externalAuthModule.ts +++ b/designer/client/src/containers/Auth/strategies/RemoteAuthStrategy/externalAuthModule.ts @@ -1,4 +1,4 @@ -import { ComponentType, PropsWithChildren } from "react"; +import { ForwardRefExoticComponent, PropsWithChildren, PropsWithoutRef, RefAttributes } from "react"; import { Module } from "@touk/federated-component"; interface RedirectState { @@ -40,7 +40,7 @@ export interface ExternalAuthModule extends Module { /** * provides auth context for hooks */ - default: ComponentType>; + default: ForwardRefExoticComponent> & RefAttributes>; /** * returns auth client from context */ diff --git a/designer/client/src/containers/theme/nuTheme.tsx b/designer/client/src/containers/theme/nuTheme.tsx index 35292c65956..f582563e995 100644 --- a/designer/client/src/containers/theme/nuTheme.tsx +++ b/designer/client/src/containers/theme/nuTheme.tsx @@ -178,28 +178,37 @@ export const nuTheme = (mode: PaletteMode, setMode: Dispatch globalStyles(theme), }, MuiFormControl: { + defaultProps: { + variant: "standard", + }, styleOverrides: { - root: { - display: "flex", - flexDirection: "row", - margin: "16px 0", + root: ({ ownerState }) => { + if (ownerState.variant === "standard") { + return { + display: "flex", + flexDirection: "row", + margin: "16px 0", + ".MuiFormLabel-root": { + display: "flex", + flexBasis: formLabelWidth, + maxWidth: "20em", + overflowWrap: "anywhere", + marginTop: "9px", + }, + }; + } }, }, }, MuiFormLabel: { + defaultProps: { + focused: false, + }, styleOverrides: { root: ({ theme }) => ({ ...theme.typography.body2, - display: "flex", - marginTop: "9px", - flexBasis: formLabelWidth, - maxWidth: "20em", - overflowWrap: "anywhere", }), }, - defaultProps: { - focused: false, - }, }, MuiFormHelperText: { styleOverrides: { diff --git a/designer/client/src/windowManager/ContentGetter.tsx b/designer/client/src/windowManager/ContentGetter.tsx index 05ccc81dd31..8d2fdd8d49e 100644 --- a/designer/client/src/windowManager/ContentGetter.tsx +++ b/designer/client/src/windowManager/ContentGetter.tsx @@ -8,6 +8,7 @@ import { NuThemeProvider } from "../containers/theme/nuThemeProvider"; import { WindowContent } from "./WindowContent"; import { WindowKind } from "./WindowKind"; import AddAttachmentDialog from "../components/modals/AddAttachmentDialog"; +import RemoteModuleDialog from "../components/RemoteModuleDialog"; const AddProcessDialog = loadable(() => import("../components/AddProcessDialog"), { fallback: }); const NodeDetails = loadable(() => import("../components/graph/node-modal/node/NodeDetails"), { @@ -97,6 +98,8 @@ const contentGetter: React.FC> = (props) => { return ; case WindowKind.survey: return ; + case WindowKind.remote: + return ; case WindowKind.scenarioDetails: return ; case WindowKind.addComment: diff --git a/designer/client/src/windowManager/WindowContent.tsx b/designer/client/src/windowManager/WindowContent.tsx index e22f8e812a6..da28eb6d363 100644 --- a/designer/client/src/windowManager/WindowContent.tsx +++ b/designer/client/src/windowManager/WindowContent.tsx @@ -13,7 +13,7 @@ function cleanList>(elements: (T | null | undefin return elements.filter(Boolean) as T[]; } -type WindowContentProps = Omit & +export type WindowContentProps = Omit & PropsWithChildren<{ icon?: ReactElement; subheader?: ReactElement; diff --git a/designer/client/src/windowManager/WindowKind.tsx b/designer/client/src/windowManager/WindowKind.tsx index 984fcfd2fb9..c03f0381fd6 100644 --- a/designer/client/src/windowManager/WindowKind.tsx +++ b/designer/client/src/windowManager/WindowKind.tsx @@ -22,4 +22,5 @@ export enum WindowKind { modifyActivityComment, addAttachment, editProperties, + remote, } diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala index fe0f8b3b614..3245fea184c 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala @@ -52,7 +52,9 @@ final case class ToolbarPanelConfig( title: Option[String], buttonsVariant: Option[ToolbarButtonVariant], buttons: Option[List[ToolbarButtonConfig]], - hidden: Option[ToolbarCondition] + hidden: Option[ToolbarCondition], + // for custom toolbar components + additionalParams: Option[Map[String, String]] ) { if (ToolbarPanelTypeConfig.requiresIdParam(`type`)) { diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioToolbarService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioToolbarService.scala index 0b8d2736e05..9ea0033c591 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioToolbarService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioToolbarService.scala @@ -65,9 +65,10 @@ object ToolbarPanel { `type`: ToolbarPanelType, title: Option[String], buttonsVariant: Option[ToolbarButtonVariant], - buttons: Option[List[ToolbarButton]] + buttons: Option[List[ToolbarButton]], + additionalParams: Option[Map[String, String]] ): ToolbarPanel = - ToolbarPanel(`type`.toString, title, buttonsVariant, buttons) + ToolbarPanel(`type`.toString, title, buttonsVariant, buttons, additionalParams) def fromConfig(config: ToolbarPanelConfig, scenario: ScenarioWithDetailsEntity[_]): ToolbarPanel = ToolbarPanel( @@ -80,7 +81,8 @@ object ToolbarPanel { verifyCondition(button.hidden, scenario) }) .map(button => ToolbarButton.fromConfig(button, scenario)) - ) + ), + config.additionalParams ) } @@ -90,7 +92,8 @@ final case class ToolbarPanel( id: String, title: Option[String], buttonsVariant: Option[ToolbarButtonVariant], - buttons: Option[List[ToolbarButton]] + buttons: Option[List[ToolbarButton]], + additionalParams: Option[Map[String, String]] ) object ToolbarButton { diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ProcessesResourcesSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ProcessesResourcesSpec.scala index 9a3bc3c9388..9a1dd78adb9 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ProcessesResourcesSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ProcessesResourcesSpec.scala @@ -1223,9 +1223,9 @@ class ProcessesResourcesSpec toolbar shouldBe ScenarioToolbarSettings( id = s"${toolbarConfig.uuidCode}-not-archived-scenario", List( - ToolbarPanel(SearchPanel, None, None, None), - ToolbarPanel(TipsPanel, None, None, None), - ToolbarPanel(CreatorPanel, None, None, None) + ToolbarPanel(SearchPanel, None, None, None, None), + ToolbarPanel(TipsPanel, None, None, None, None), + ToolbarPanel(CreatorPanel, None, None, None, None) ), List(), List( @@ -1246,7 +1246,8 @@ class ProcessesResourcesSpec disabled = false ) ) - ) + ), + None ) ), List() diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/CategoriesScenarioToolbarsConfigParserSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/CategoriesScenarioToolbarsConfigParserSpec.scala index 1f2e04a6304..9d9a2c82a1d 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/CategoriesScenarioToolbarsConfigParserSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/CategoriesScenarioToolbarsConfigParserSpec.scala @@ -17,7 +17,7 @@ class CategoriesScenarioToolbarsConfigParserSpec extends AnyFlatSpec with Matche |processToolbarConfig { | defaultConfig { | topLeft: [ - | { type: "tips-panel", hidden: { fragment: true } } + | { type: "tips-panel", hidden: { fragment: true }, additionalParams: { customParam1: "value1" } } | ] | topRight: [ | { @@ -56,7 +56,17 @@ class CategoriesScenarioToolbarsConfigParserSpec extends AnyFlatSpec with Matche it should "properly create scenario toolbar configuration" in { val defaultToolbarConfig = ScenarioToolbarsConfig( None, - List(ToolbarPanelConfig(TipsPanel, None, None, None, None, Some(ToolbarCondition(Some(true), None, None)))), + List( + ToolbarPanelConfig( + TipsPanel, + None, + None, + None, + None, + Some(ToolbarCondition(Some(true), None, None)), + Some(Map("customParam1" -> "value1")) + ) + ), Nil, List( ToolbarPanelConfig( @@ -93,8 +103,9 @@ class CategoriesScenarioToolbarsConfigParserSpec extends AnyFlatSpec with Matche None, None ) - ) + ), ), + None, None ) ), @@ -103,7 +114,17 @@ class CategoriesScenarioToolbarsConfigParserSpec extends AnyFlatSpec with Matche val categoryToolbarConfig = ScenarioToolbarsConfig( Some(UUID.fromString("58f1acff-d864-4d66-9f86-0fa7319f7043")), - List(ToolbarPanelConfig(TipsPanel, None, None, None, None, Some(ToolbarCondition(Some(true), None, None)))), + List( + ToolbarPanelConfig( + TipsPanel, + None, + None, + None, + None, + Some(ToolbarCondition(Some(true), None, None)), + Some(Map("customParam1" -> "value1")) + ) + ), Nil, List( ToolbarPanelConfig( @@ -116,10 +137,11 @@ class CategoriesScenarioToolbarsConfigParserSpec extends AnyFlatSpec with Matche ToolbarButtonConfig(ToolbarButtonConfigType.ProcessSave, None, None, None, None, None, None) ) ), + None, None ) ), - List(ToolbarPanelConfig(ActivitiesPanel, None, None, None, None, None)) + List(ToolbarPanelConfig(ActivitiesPanel, None, None, None, None, None, None)) ) val testingConfigs = Table( diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ConfigScenarioToolbarServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ConfigScenarioToolbarServiceSpec.scala index 2aefd305ba4..b997e1cf699 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ConfigScenarioToolbarServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ConfigScenarioToolbarServiceSpec.scala @@ -198,8 +198,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { ScenarioToolbarSettings( id, List( - ToolbarPanel(CreatorPanel, None, None, None), - ToolbarPanel(ActivitiesPanel, None, None, None), + ToolbarPanel(CreatorPanel, None, None, None, None), + ToolbarPanel(ActivitiesPanel, None, None, None, None), ), Nil, List( @@ -234,7 +234,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { disabled = false ) ) - ) + ), + None ), ToolbarPanel( "buttons2", @@ -244,7 +245,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { List( ToolbarButton(ProcessCancel, None, None, None, None, disabled = false) ) - ) + ), + None ) ), List.empty, @@ -253,8 +255,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { ScenarioToolbarSettings( id, List( - ToolbarPanel(CreatorPanel, None, None, None), - ToolbarPanel(ActivitiesPanel, None, None, None) + ToolbarPanel(CreatorPanel, None, None, None, None), + ToolbarPanel(ActivitiesPanel, None, None, None, None) ), Nil, List( @@ -289,7 +291,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { disabled = false ) ) - ) + ), + None ) ), List.empty, @@ -298,7 +301,7 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { ScenarioToolbarSettings( id, List( - ToolbarPanel(ActivitiesPanel, None, None, None), + ToolbarPanel(ActivitiesPanel, None, None, None, None), ), Nil, List( @@ -333,7 +336,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { disabled = false ) ) - ) + ), + None ), ToolbarPanel( "buttons1", @@ -344,7 +348,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { ToolbarButton(ProcessDeploy, None, None, None, None, disabled = false), ToolbarButton(ProcessPDF, None, None, None, None, disabled = false) ) - ) + ), + None ), ToolbarPanel( "buttons2", @@ -354,7 +359,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { List( ToolbarButton(ProcessCancel, None, None, None, None, disabled = false) ) - ) + ), + None ) ), List.empty, @@ -363,8 +369,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { ScenarioToolbarSettings( id, List( - ToolbarPanel(CreatorPanel, None, None, None), - ToolbarPanel(ActivitiesPanel, None, None, None) + ToolbarPanel(CreatorPanel, None, None, None, None), + ToolbarPanel(ActivitiesPanel, None, None, None, None) ), Nil, List( @@ -399,7 +405,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { disabled = true ) ) - ) + ), + None ), ToolbarPanel( "buttons1", @@ -409,7 +416,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { List( ToolbarButton(ProcessDeploy, None, None, None, None, disabled = false) ) - ) + ), + None ) ), List.empty, @@ -418,7 +426,7 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { ScenarioToolbarSettings( id, List( - ToolbarPanel(TipsPanel, None, None, None) + ToolbarPanel(TipsPanel, None, None, None, None) ), Nil, List( @@ -453,7 +461,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { disabled = false ) ) - ) + ), + None ), ToolbarPanel( "buttons2", @@ -463,7 +472,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { List( ToolbarButton(ProcessCancel, None, None, None, None, disabled = false) ) - ) + ), + None ) ), Nil @@ -472,7 +482,7 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { ScenarioToolbarSettings( id, List( - ToolbarPanel(ActivitiesPanel, None, None, None) + ToolbarPanel(ActivitiesPanel, None, None, None, None) ), Nil, List( @@ -507,7 +517,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { disabled = false ) ) - ) + ), + None ), ToolbarPanel( "buttons2", @@ -517,7 +528,8 @@ class ConfigScenarioToolbarServiceSpec extends AnyFlatSpec with Matchers { List( ToolbarButton(ProcessCancel, None, None, None, None, disabled = false) ) - ) + ), + None ) ), List.empty,