From e6f74caee2e100782dde27cbf4cf871e80316e52 Mon Sep 17 00:00:00 2001 From: haschek Date: Tue, 21 Oct 2025 11:45:06 +0000 Subject: [PATCH 01/70] Prepare release v25.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 37941a86..389e4f49 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@eccenca/gui-elements", "description": "GUI elements based on other libraries, usable in React application, written in Typescript.", - "version": "24.4.1", + "version": "25.0.0", "license": "Apache-2.0", "homepage": "https://github.com/eccenca/gui-elements", "bugs": "https://github.com/eccenca/gui-elements/issues", From 8d365d881a008bb2e1d2954522ebd91f0c4e6c16 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Tue, 21 Oct 2025 14:19:07 +0200 Subject: [PATCH 02/70] improve changelog messages --- CHANGELOG.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05d5d038..bd55d8d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,9 +43,9 @@ This is a major release, and it might be not compatible with your current usage - `eccgui-color-var`: returns a var of a custom property used for palette color - `eccgui-color-mix`: mix 2 colors in `srgb`, works with all types of color values and CSS custom properties - `eccgui-color-rgba`: like `rgba()` but it works also for CSS custom properties -- `colorCalculateDistance()` +- `utils.colorCalculateDistance()` - function to calculate the difference between 2 colors using the simple CIE76 formula -- `textToColorHash()` +- `utils.textToColorHash()` - function to calculate a color from a text string - new icons - `artefact-task-sqlupdatequeryoperator` @@ -53,6 +53,7 @@ This is a major release, and it might be not compatible with your current usage ### Removed +- support for React Flow v10 was completely removed - removed direct replacements for legacy components (imported via `@eccenca/gui-elements/src/legacy-replacements` or `LegacyReplacements`) - ``, ` + + } + /> + + ); +}; + +const MiddleModal = () => { + return ( + + } /> + + ); +}; + +/** Shows the current stack of open modals. */ +const TrackingContent = ({ more }: { more: React.JSX.Element }) => { + const modalContext = React.useContext(ModalContext); + + return ( + + + Tracking content + + +
    + {(modalContext.openModalStack ?? []).map((modalId, idx) => ( +
  • + {idx + 1}. {modalId} +
  • + ))} +
+ {more} +
+
+ ); +}; + +export const NestedModalWithContext = ContextTemplate.bind({}); +NestedModalWithContext.args = { + ...Default.args, + size: "regular", + children: [], +}; From 17a0bd147dcaab861d6ac630653c5e282d05f2e8 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Wed, 29 Oct 2025 16:40:02 +0100 Subject: [PATCH 21/70] align Button intent states visually with intent states of other components --- CHANGELOG.md | 4 ++ src/components/Button/Button.stories.tsx | 8 +++- src/components/Button/Button.tsx | 19 ++++++--- src/components/Button/button.scss | 40 ++++++++++++------- .../Icon/stories/IconButton.stories.tsx | 3 +- 5 files changed, 52 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4b8fe8e..5cc96ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ This is a major release, and it might be not compatible with your current usage - indentity, semantic, layout, extra - managed via CSS custom properties - see `README.md` for inf about usage +- ` - From abf9c0501bc883c78cea8a00dd09907da7320d65 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 30 Oct 2025 09:08:15 +0100 Subject: [PATCH 24/70] fix property descriptions --- src/components/Button/Button.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index d4722759..7110d1c3 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -17,17 +17,17 @@ import Tooltip, { TooltipProps } from "./../Tooltip/Tooltip"; interface AdditionalButtonProps { /** * Always use this when the button triggers an affirmative action, e.g. confirm a process. - * The button is displayed with primary color scheme. + * The button is displayed with accent color intent. */ affirmative?: boolean; /** * Always use this when the button triggers an disruptive action, e.g. delete or remove. - * The button is displayed with primary color scheme. + * The button is displayed with danger color intent. */ disruptive?: boolean; /** * Use this when a button is important enough to highlight it in a set of other buttons. - * The button is displayed with primary color scheme. + * The button is displayed with accent color intent. */ elevated?: boolean; /** From a0bac9696de97902d355cd58e61bc885dc53969d Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 30 Oct 2025 09:08:45 +0100 Subject: [PATCH 25/70] align Spinner intent display with other components --- CHANGELOG.md | 9 +++++++-- src/components/Spinner/Spinner.tsx | 16 +++++++++++++--- .../Spinner/Stories/spinner.stories.tsx | 2 +- src/components/Spinner/spinner.scss | 6 +++++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef0490c8..dd928713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,10 @@ This is a major release, and it might be not compatible with your current usage - managed via CSS custom properties - see `README.md` for inf about usage - ` @@ -71,8 +84,23 @@ const InnerModal = () => { }; const MiddleModal = () => { + const [portalElement, setPortalElement] = React.useState(); + + React.useEffect(() => { + setPortalElement(document.getElementById("modalPortal")); + }, []); + return ( - + + Middle modal with constant modal ID "middleModal". + } /> ); @@ -105,5 +133,12 @@ export const NestedModalWithContext = ContextTemplate.bind({}); NestedModalWithContext.args = { ...Default.args, size: "regular", - children: [], + usePortal: false, + children: [ +
+ Root modal with automatically generated unique modal ID. + + +
, + ], }; From c3525ec7ac3c1aea54cd2165eac998ba5c98e2b0 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 30 Oct 2025 18:44:43 +0100 Subject: [PATCH 32/70] fix export of ModalContext --- src/components/Dialog/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Dialog/index.ts b/src/components/Dialog/index.ts index 6db73324..1f831946 100644 --- a/src/components/Dialog/index.ts +++ b/src/components/Dialog/index.ts @@ -1,3 +1,4 @@ export * from "./Modal"; export * from "./SimpleDialog"; export * from "./AlertDialog"; +export * from "./ModalContext"; From 1d4f034e9cd0c8bdefa24b6526b452d3ba6840fb Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 30 Oct 2025 18:46:24 +0100 Subject: [PATCH 33/70] improve display of ModalContext example --- .../Dialog/stories/Modal.stories.tsx | 153 +++++++++--------- 1 file changed, 78 insertions(+), 75 deletions(-) diff --git a/src/components/Dialog/stories/Modal.stories.tsx b/src/components/Dialog/stories/Modal.stories.tsx index fa4914fe..d445d674 100644 --- a/src/components/Dialog/stories/Modal.stories.tsx +++ b/src/components/Dialog/stories/Modal.stories.tsx @@ -1,12 +1,20 @@ import React from "react"; -import { OverlaysProvider } from "@blueprintjs/core"; +import { Classes, OverlaysProvider } from "@blueprintjs/core"; import { Meta, StoryFn } from "@storybook/react"; import { fn } from "@storybook/test"; import { SimpleCard } from "../../Card/stories/Card.stories"; -import { ModalContext, useModalContext } from "../ModalContext"; -import { Button, Card, CardContent, CardHeader, CardTitle, Modal, Spacing } from "./../../../../index"; +import { + Button, + Card, + CardContent, + Modal, + ModalContext, + ModalSize, + Spacing, + useModalContext, +} from "./../../../../index"; export default { title: "Components/Dialog/Modal", @@ -26,20 +34,6 @@ const Template: StoryFn = (args) => ( ); -const ContextTemplate: StoryFn = (args) => { - const { setModalOpen, openModalStack } = useModalContext(); - - return ( - - -
- -
-
-
- ); -}; - export const Default = Template.bind({}); Default.args = { isOpen: true, @@ -49,96 +43,105 @@ Default.args = { onClosing: fn(), }; -/** Nested modals with modal context for tracking open/close states. */ -const InnerModal = () => { +const ContextTemplate = ({ children }: React.HTMLAttributes) => { + const { setModalOpen, openModalStack } = useModalContext(); + + return ( + +
+ {children} +
+
+ ); +}; + +const ModalContent = ({ children }: React.HTMLAttributes) => { + return ( + + {children} + + ); +}; + +/** Component for nested modals. */ +const ExampleModal = ({ + id, + size, + children, +}: { + id?: string; + size: ModalSize; + children?: React.HTMLAttributes["children"]; +}) => { const [isOpen, setIsOpen] = React.useState(true); const [portalElement, setPortalElement] = React.useState(); React.useEffect(() => { - setPortalElement(document.getElementById("modalPortal")); + setPortalElement(document.getElementById("modalPortal")!); }, []); return ( { + // workaround, Blueprint attach a class to body tht prevents scrolling, probably it is attached to the wrong portal + document.body.classList.remove(Classes.OVERLAY_OPEN); + }} > - Inner modal with constant modal ID "innerModal". - - - - - - } - /> + + Modal with constant modal ID "{id}". + + + + {children} + + + ); }; -const MiddleModal = () => { - const [portalElement, setPortalElement] = React.useState(); - - React.useEffect(() => { - setPortalElement(document.getElementById("modalPortal")); - }, []); +const InnerModal = () => { + return ; +}; +const MiddleModal = () => { return ( - - Middle modal with constant modal ID "middleModal". - - } /> - + + + ); }; /** Shows the current stack of open modals. */ -const TrackingContent = ({ more }: { more: React.JSX.Element }) => { +const TrackingContent = () => { const modalContext = React.useContext(ModalContext); return ( - - - Tracking content - - -
    - {(modalContext.openModalStack ?? []).map((modalId, idx) => ( -
  • - {idx + 1}. {modalId} -
  • - ))} -
- {more} -
-
+
    + {(modalContext.openModalStack ?? []).map((modalId, idx) => ( +
  • + {idx + 1}. {modalId} +
  • + ))} +
); }; +//* export const NestedModalWithContext = ContextTemplate.bind({}); NestedModalWithContext.args = { - ...Default.args, - size: "regular", - usePortal: false, children: [ -
- Root modal with automatically generated unique modal ID. - + -
, +
, ], }; +// */ From 96f58c4575a9b7b12ad6332ca264a201074c3a18 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Mon, 3 Nov 2025 09:44:09 +0100 Subject: [PATCH 34/70] add simple example as comment --- .../Dialog/stories/Modal.stories.tsx | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/components/Dialog/stories/Modal.stories.tsx b/src/components/Dialog/stories/Modal.stories.tsx index d445d674..b42efc55 100644 --- a/src/components/Dialog/stories/Modal.stories.tsx +++ b/src/components/Dialog/stories/Modal.stories.tsx @@ -135,7 +135,37 @@ const TrackingContent = () => { ); }; -//* +/** + * `ModalContext` can be used as provider to track a stack of modals. + * + * ```(Javascript) + * const ContextTemplate = () => { + * const { setModalOpen, openModalStack } = useModalContext(); + * return ( + * + * + * + * + * + * ); + * }; + * + * const OtherModal = () => { + * const modalContext = React.useContext(ModalContext); + * return ( + * + *
    + * {(modalContext.openModalStack ?? []).map((modalId, idx) => ( + *
  • + * {idx + 1}. {modalId} + *
  • + * ))} + *
+ *
+ * ); + * }; + * ``` + */ export const NestedModalWithContext = ContextTemplate.bind({}); NestedModalWithContext.args = { children: [ @@ -144,4 +174,3 @@ NestedModalWithContext.args = { , ], }; -// */ From 3a74d9f4b225a20fb4ba640b9c9e92c29dd214d7 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Mon, 3 Nov 2025 11:33:09 +0100 Subject: [PATCH 35/70] swap colors --- .../configuration/_colors-graph.scss | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cmem/react-flow/configuration/_colors-graph.scss b/src/cmem/react-flow/configuration/_colors-graph.scss index e43165b3..01693c8c 100644 --- a/src/cmem/react-flow/configuration/_colors-graph.scss +++ b/src/cmem/react-flow/configuration/_colors-graph.scss @@ -1,20 +1,20 @@ .#{$eccgui}-configuration--colors__react-flow-graph { - --#{$eccgui}-graph-node: #{eccgui-color-var("layout", "purple", 700)}; - --#{$eccgui}-graph-node-bright: #{eccgui-color-var("layout", "purple", 100)}; - --#{$eccgui}-class-node: #{eccgui-color-var("layout", "magenta", 900)}; - --#{$eccgui}-class-node-bright: #{eccgui-color-var("layout", "magenta", 100)}; - --#{$eccgui}-instance-node: #{eccgui-color-var("layout", "magenta", 500)}; - --#{$eccgui}-instance-node-bright: #{eccgui-color-var("layout", "magenta", 100)}; + --#{$eccgui}-graph-node: #{eccgui-color-var("layout", "magenta", 900)}; + --#{$eccgui}-graph-node-bright: #{eccgui-color-var("layout", "magenta", 100)}; + --#{$eccgui}-class-node: #{eccgui-color-var("layout", "purple", 700)}; + --#{$eccgui}-class-node-bright: #{eccgui-color-var("layout", "purple", 100)}; + --#{$eccgui}-instance-node: #{eccgui-color-var("layout", "purple", 500)}; + --#{$eccgui}-instance-node-bright: #{eccgui-color-var("layout", "purple", 100)}; --#{$eccgui}-property-node: #{eccgui-color-var("layout", "teal", 700)}; --#{$eccgui}-property-node-bright: #{eccgui-color-var("layout", "teal", 100)}; --#{$eccgui}-implicit-edge: #{eccgui-color-var("identity", "text", 700)}; --#{$eccgui}-implicit-edge-bright: #{eccgui-color-var("identity", "text", 100)}; - --#{$eccgui}-import-edge: #{eccgui-color-var("layout", "purple", 700)}; - --#{$eccgui}-import-edge-bright: #{eccgui-color-var("layout", "purple", 100)}; - --#{$eccgui}-subclass-edge: #{eccgui-color-var("layout", "magenta", 900)}; - --#{$eccgui}-subclass-edge-bright: #{eccgui-color-var("layout", "magenta", 100)}; + --#{$eccgui}-import-edge: #{eccgui-color-var("layout", "magenta", 900)}; + --#{$eccgui}-import-edge-bright: #{eccgui-color-var("layout", "magenta", 100)}; + --#{$eccgui}-subclass-edge: #{eccgui-color-var("layout", "purple", 700)}; + --#{$eccgui}-subclass-edge-bright: #{eccgui-color-var("layout", "purple", 100)}; --#{$eccgui}-subproperty-edge: #{eccgui-color-var("layout", "teal", 700)}; --#{$eccgui}-subproperty-edge-bright: #{eccgui-color-var("layout", "teal", 100)}; - --#{$eccgui}-rdftype-edge: #{eccgui-color-var("layout", "magenta", 500)}; - --#{$eccgui}-rdftype-edge-bright: #{eccgui-color-var("layout", "magenta", 100)}; + --#{$eccgui}-rdftype-edge: #{eccgui-color-var("layout", "purple", 500)}; + --#{$eccgui}-rdftype-edge-bright: #{eccgui-color-var("layout", "purple", 100)}; } From 14673485bff4e89c33d4fff493fdefedb3942713 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Mon, 3 Nov 2025 15:22:30 +0100 Subject: [PATCH 36/70] display icons in custom edge labels --- src/extensions/react-flow/_react-flow_v12.scss | 2 +- src/extensions/react-flow/edges/EdgeLabel.tsx | 2 +- src/extensions/react-flow/edges/_edges.scss | 4 ++++ .../react-flow/edges/stories/EdgeDefault.stories.tsx | 10 +++++----- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/extensions/react-flow/_react-flow_v12.scss b/src/extensions/react-flow/_react-flow_v12.scss index 51de22ce..857acd99 100644 --- a/src/extensions/react-flow/_react-flow_v12.scss +++ b/src/extensions/react-flow/_react-flow_v12.scss @@ -67,7 +67,7 @@ position: absolute; } -.react-flow .react-flow__edges svg { +.react-flow .react-flow__edges svg:not(.#{$eccgui}-icon) { position: absolute; overflow: visible; pointer-events: none; diff --git a/src/extensions/react-flow/edges/EdgeLabel.tsx b/src/extensions/react-flow/edges/EdgeLabel.tsx index 1faf5fa0..767146a2 100644 --- a/src/extensions/react-flow/edges/EdgeLabel.tsx +++ b/src/extensions/react-flow/edges/EdgeLabel.tsx @@ -28,7 +28,7 @@ export interface EdgeLabelProps extends React.HTMLAttributes { */ fullWidth?: boolean; /** - * Label is diaplayed without a box that comes with borders and background color. + * Label is displayed without a box that comes with borders and background color. */ loose?: boolean; /** diff --git a/src/extensions/react-flow/edges/_edges.scss b/src/extensions/react-flow/edges/_edges.scss index 25783eed..f134be25 100644 --- a/src/extensions/react-flow/edges/_edges.scss +++ b/src/extensions/react-flow/edges/_edges.scss @@ -271,6 +271,10 @@ path.react-flow__edge-path-highlight { // Custom label +.#{$eccgui}-graphviz__edge-labelobject { + overflow: visible; +} + .#{$eccgui}-graphviz__edge-label { --#{$eccgui}-reactflow-edge-label-color: currentcolor; --#{$eccgui}-reactflow-edge-label-color-background: #{$card-background-color}; diff --git a/src/extensions/react-flow/edges/stories/EdgeDefault.stories.tsx b/src/extensions/react-flow/edges/stories/EdgeDefault.stories.tsx index 64dd1593..1fc450b7 100644 --- a/src/extensions/react-flow/edges/stories/EdgeDefault.stories.tsx +++ b/src/extensions/react-flow/edges/stories/EdgeDefault.stories.tsx @@ -2,13 +2,13 @@ import React, { useCallback, useEffect, useState } from "react"; import { ArrowHeadType, Elements, getMarkerEnd, ReactFlowProvider } from "react-flow-renderer"; import { Meta, StoryFn } from "@storybook/react"; -import { EdgeLabel, EdgeLabelObject, ReactFlow } from "./../../../../../index"; -import { EdgeDefault, EdgeDefaultDataProps as EdgeData } from "./../EdgeDefault"; +import { EdgeDefaultProps, EdgeLabel, EdgeLabelObject, ReactFlowExtended } from "./../../../../../index"; import { edgeTypes } from "./../../../../cmem/react-flow/configuration/workflow"; +import { EdgeDefault, EdgeDefaultDataProps as EdgeData } from "./../EdgeDefault"; const EdgeDefaultDataProps = (data: EdgeData) => { // this is only a mock to get it as sub element in the table - return <>; + return <>{data.intent}; }; export default { @@ -23,7 +23,7 @@ export default { }, } as Meta; -const EdgeDefaultExample = (args: any) => { +const EdgeDefaultExample = (args: EdgeDefaultProps) => { const [reactflowInstance, setReactflowInstance] = useState(null); const [elements, setElements] = useState([] as Elements); @@ -66,7 +66,7 @@ const EdgeDefaultExample = (args: any) => { return ( - Date: Mon, 3 Nov 2025 16:45:07 +0100 Subject: [PATCH 37/70] do not use random id to prevent changes in the visual storybook tests --- src/components/Dialog/stories/Modal.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Dialog/stories/Modal.stories.tsx b/src/components/Dialog/stories/Modal.stories.tsx index b42efc55..eb29f437 100644 --- a/src/components/Dialog/stories/Modal.stories.tsx +++ b/src/components/Dialog/stories/Modal.stories.tsx @@ -169,7 +169,7 @@ const TrackingContent = () => { export const NestedModalWithContext = ContextTemplate.bind({}); NestedModalWithContext.args = { children: [ - + , ], From f5f381f2b0a216d3d7a7e8cd993555cdfa4af0cf Mon Sep 17 00:00:00 2001 From: arausly Date: Tue, 4 Nov 2025 09:51:08 +0100 Subject: [PATCH 38/70] add text reducer to replace extra characters --- .../StringPreviewContentBlobToggler.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx b/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx index f9c08126..72e00002 100644 --- a/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx +++ b/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx @@ -1,6 +1,7 @@ import React from "react"; import { ContentBlobToggler, ContentBlobTogglerProps, Markdown } from "./.."; +import { TextReducer } from "../../components"; export interface StringPreviewContentBlobTogglerProps extends Omit { @@ -39,14 +40,15 @@ export function StringPreviewContentBlobToggler({ noTogglerContentSuffix, }: StringPreviewContentBlobTogglerProps) { const previewMaybeFirstLine = firstNonEmptyLineOnly ? firstNonEmptyLine(content) : content; - const previewString = previewMaxLength ? previewMaybeFirstLine.substr(0, previewMaxLength) : previewMaybeFirstLine; - const enableToggler = previewString !== content; + const enableToggler = (previewMaxLength ?? Infinity) < content.length; let previewContent = renderPreviewAsMarkdown ? ( - - {previewString} - + + + {previewMaybeFirstLine} + + ) : ( - previewString + previewMaybeFirstLine ); if (!enableToggler && noTogglerContentSuffix) { previewContent = ( From 4487c8e8e8bb61f5842fb3523edb77fd072785e3 Mon Sep 17 00:00:00 2001 From: arausly Date: Tue, 4 Nov 2025 15:33:37 +0100 Subject: [PATCH 39/70] modified textreducer --- .../StringPreviewContentBlobToggler.tsx | 16 ++--- src/components/TextReducer/TextReducer.tsx | 45 +----------- src/components/TextReducer/text-utils.tsx | 68 +++++++++++++++++++ src/components/index.ts | 1 + 4 files changed, 78 insertions(+), 52 deletions(-) create mode 100644 src/components/TextReducer/text-utils.tsx diff --git a/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx b/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx index 72e00002..bb219663 100644 --- a/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx +++ b/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx @@ -1,7 +1,6 @@ import React from "react"; import { ContentBlobToggler, ContentBlobTogglerProps, Markdown } from "./.."; -import { TextReducer } from "../../components"; export interface StringPreviewContentBlobTogglerProps extends Omit { @@ -40,15 +39,14 @@ export function StringPreviewContentBlobToggler({ noTogglerContentSuffix, }: StringPreviewContentBlobTogglerProps) { const previewMaybeFirstLine = firstNonEmptyLineOnly ? firstNonEmptyLine(content) : content; - const enableToggler = (previewMaxLength ?? Infinity) < content.length; + const previewString = previewMaxLength ? previewMaybeFirstLine.substr(0, previewMaxLength) : previewMaybeFirstLine; + const enableToggler = previewString !== content; let previewContent = renderPreviewAsMarkdown ? ( - - - {previewMaybeFirstLine} - - + + {previewString} + ) : ( - previewMaybeFirstLine + previewString ); if (!enableToggler && noTogglerContentSuffix) { previewContent = ( @@ -85,4 +83,4 @@ function firstNonEmptyLine(preview: string) { export const stringPreviewContentBlobTogglerUtils = { firstNonEmptyLine, -}; +}; \ No newline at end of file diff --git a/src/components/TextReducer/TextReducer.tsx b/src/components/TextReducer/TextReducer.tsx index d02fc02b..2190d6be 100644 --- a/src/components/TextReducer/TextReducer.tsx +++ b/src/components/TextReducer/TextReducer.tsx @@ -1,10 +1,9 @@ import React from "react"; -import { renderToString } from "react-dom/server"; -import * as ReactIs from "react-is"; import { CLASSPREFIX as eccgui } from "../../configuration/constants"; import { OverflowText, OverflowTextProps } from "./../Typography"; +import { reduceToText } from "./text-utils"; export interface TextReducerProps extends Pick, "children"> { /** @@ -38,47 +37,7 @@ export const TextReducer = ({ useOverflowTextWrapper, overflowTextProps, }: TextReducerProps) => { - const nodesCount = 0; - - const onlyText = (children: React.ReactNode | React.ReactNode[], maxNodes?: number): string => { - if (typeof maxNodes !== "undefined" && nodesCount >= maxNodes) { - return ""; - } - - if (children instanceof Array) { - return children - .slice(0, maxNodes) - .map((child: React.ReactNode) => { - return onlyText(child, maxNodes); - }) - .join(" "); - } - - return React.Children.toArray(children) - .slice(0, maxNodes) - .map((child) => { - if (ReactIs.isFragment(child)) { - return onlyText(child.props?.children, maxNodes); - } - if (typeof child === "string") { - return child; - } - if (typeof child === "number") { - return child.toString(); - } - if (ReactIs.isElement(child)) { - // for some reasons `renderToString` returns empty string if not wrappe in a `span` - return renderToString({child}); - } - return ""; - }) - .join(" ") - .replaceAll("\n", " "); - }; - - const shrinkedContent = onlyText(children, maxNodes) - .replaceAll(/<[^\s][^>]*>/g, "") - .slice(0, maxLength); + const shrinkedContent = reduceToText(children, {maxLength, maxNodes}) return useOverflowTextWrapper ? ( { + if (typeof maxNodes !== "undefined" && nodeCount >= maxNodes) return ""; + + return React.Children.toArray(nodes) + .slice(0, maxNodes) + .map((child) => { + if (typeof maxNodes !== "undefined" && nodeCount >= maxNodes) return ""; + + if (ReactIs.isFragment(child)) return onlyText(child.props?.children); + if (typeof child === "string" || typeof child === "number") { + nodeCount++; + return child.toString(); + } + if (ReactIs.isElement(child)) { + nodeCount++; + return renderToString({child}); + } + return ""; + }) + .join(" "); + }; + + let text = + typeof content === "string" ? content : onlyText(content); + + // Basic HTML cleanup + text = text.replace(/<[^\s][^>]*>/g, "").replace(/\n/g, " "); + + // Optional Markdown simplification + if (markdown) { + text = text + // bold / italic + .replace(/\*\*(.*?)\*\*/g, "$1") + .replace(/__(.*?)__/g, "$1") + .replace(/\*(.*?)\*/g, "$1") + .replace(/_(.*?)_/g, "$1") + // inline code + .replace(/`([^`]*)`/g, "$1") + // links: [text](url) → text + .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") + // headings (# Title) + .replace(/^#+\s*/gm, "") + // lists + .replace(/^\s*[-*+]\s+/gm, "") + // blockquotes + .replace(/^\s*>\s*/gm, "") + // images ![alt](url) → alt + .replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1"); + } + + if (typeof maxLength === "number") { + text = text.slice(0, maxLength); + } + + return text.trim(); +} diff --git a/src/components/index.ts b/src/components/index.ts index 6f7fdf95..c43bc8c6 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -43,6 +43,7 @@ export * from "./Tabs"; export * from "./Tag"; export * from "./TextField"; export * from "./TextReducer/TextReducer"; +export * from "./TextReducer/text-utils"; export * from "./Toolbar"; export * from "./Tooltip/Tooltip"; export * from "./Tree/Tree"; From b497b1df77d0eb02d3c542b04fc104772a9be594 Mon Sep 17 00:00:00 2001 From: arausly Date: Wed, 5 Nov 2025 15:00:34 +0100 Subject: [PATCH 40/70] moved utility and made refactors --- CHANGELOG.md | 2 + src/common/index.ts | 2 + .../utils/reduceToText.tsx} | 54 +++++++++---------- src/components/TextReducer/TextReducer.tsx | 2 +- src/components/index.ts | 1 - 5 files changed, 32 insertions(+), 29 deletions(-) rename src/{components/TextReducer/text-utils.tsx => common/utils/reduceToText.tsx} (57%) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbed2749..94aa26ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,8 @@ This is a major release, and it might be not compatible with your current usage - `intent` display was aligned with other components, `intent="primary"` is now `intent="accent"`, in most cases it may be better to use `affirmative={true}` or `elevated={true}` instead of primary/accent intent - `` - `intent` display was aligned with other components, `intent="primary"` is now `intent="accent"`, in most cases it may be better to use `elevated={true}` instead of using intent +- `` + - extracted utility for shrinking content to plain text. The utility, `reduceToText`, can be used independently from the component. ### Deprecated diff --git a/src/common/index.ts b/src/common/index.ts index aca74bd2..161a07f9 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -6,6 +6,7 @@ import getColorConfiguration from "./utils/getColorConfiguration"; import { getScrollParent } from "./utils/getScrollParent"; import { getGlobalVar, setGlobalVar } from "./utils/globalVars"; import { openInNewTab } from "./utils/openInNewTab"; +import { reduceToText } from "./utils/reduceToText" export type { IntentTypes as IntentBaseTypes } from "./Intent"; export const utils = { @@ -19,4 +20,5 @@ export const utils = { getScrollParent, getEnabledColorsFromPalette, textToColorHash, + reduceToText }; diff --git a/src/components/TextReducer/text-utils.tsx b/src/common/utils/reduceToText.tsx similarity index 57% rename from src/components/TextReducer/text-utils.tsx rename to src/common/utils/reduceToText.tsx index 71d8d728..46aa17d6 100644 --- a/src/components/TextReducer/text-utils.tsx +++ b/src/common/utils/reduceToText.tsx @@ -2,11 +2,33 @@ import React from "react"; import { renderToString } from "react-dom/server"; import * as ReactIs from "react-is"; -export function reduceToText( - input: React.ReactNode | React.ReactNode[] | string, - options?: { maxNodes?: number; maxLength?: number; markdown?: boolean } -): string { - const { maxNodes, maxLength, markdown } = options || {}; + +interface ReduceToTextFuncType { + ( + /** + * Component or text to reduce HTML markup content to plain text. + */ + input: React.ReactNode | React.ReactNode[] | string, + options?: { + /** + * Maximum number of nodes that are used from the HTML content. + * An HTML element with multiple sub elements is count as only 1 node. + */ + maxNodes?: number; + /** + * Set maximum string length of returned content. + */ + maxLength?: number; + } + ): string +} + + +export const reduceToText: ReduceToTextFuncType = ( + input, + options +) => { + const { maxNodes, maxLength} = options || {}; let content: React.ReactNode | React.ReactNode[] = input; let nodeCount = 0; @@ -38,28 +60,6 @@ export function reduceToText( // Basic HTML cleanup text = text.replace(/<[^\s][^>]*>/g, "").replace(/\n/g, " "); - // Optional Markdown simplification - if (markdown) { - text = text - // bold / italic - .replace(/\*\*(.*?)\*\*/g, "$1") - .replace(/__(.*?)__/g, "$1") - .replace(/\*(.*?)\*/g, "$1") - .replace(/_(.*?)_/g, "$1") - // inline code - .replace(/`([^`]*)`/g, "$1") - // links: [text](url) → text - .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") - // headings (# Title) - .replace(/^#+\s*/gm, "") - // lists - .replace(/^\s*[-*+]\s+/gm, "") - // blockquotes - .replace(/^\s*>\s*/gm, "") - // images ![alt](url) → alt - .replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1"); - } - if (typeof maxLength === "number") { text = text.slice(0, maxLength); } diff --git a/src/components/TextReducer/TextReducer.tsx b/src/components/TextReducer/TextReducer.tsx index 2190d6be..1c2115b2 100644 --- a/src/components/TextReducer/TextReducer.tsx +++ b/src/components/TextReducer/TextReducer.tsx @@ -3,7 +3,7 @@ import React from "react"; import { CLASSPREFIX as eccgui } from "../../configuration/constants"; import { OverflowText, OverflowTextProps } from "./../Typography"; -import { reduceToText } from "./text-utils"; +import { reduceToText } from "../../common/utils/reduceToText"; export interface TextReducerProps extends Pick, "children"> { /** diff --git a/src/components/index.ts b/src/components/index.ts index c43bc8c6..6f7fdf95 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -43,7 +43,6 @@ export * from "./Tabs"; export * from "./Tag"; export * from "./TextField"; export * from "./TextReducer/TextReducer"; -export * from "./TextReducer/text-utils"; export * from "./Toolbar"; export * from "./Tooltip/Tooltip"; export * from "./Tree/Tree"; From 40d263cf0450c86bea84be75a2167913c72d55ae Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Wed, 5 Nov 2025 16:20:47 +0100 Subject: [PATCH 41/70] Refactor modal context - Make modalStack a function in order to not trigger re-render. - If a push notification feature is needed, think about registering for updates. --- src/components/Dialog/ModalContext.tsx | 18 +++++++++++++----- .../Dialog/stories/Modal.stories.tsx | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/Dialog/ModalContext.tsx b/src/components/Dialog/ModalContext.tsx index 54ad38e1..19bf55fe 100644 --- a/src/components/Dialog/ModalContext.tsx +++ b/src/components/Dialog/ModalContext.tsx @@ -5,13 +5,13 @@ export interface ModalContextProps { setModalOpen: (modalId: string, isOpen: boolean) => void; /** The currently opened modals ordered by when they have been opened. Oldest coming first. */ - openModalStack: string[] | undefined; + openModalStack(): string[] | undefined; } /** Can be provided in the application to react to modal related changes. */ export const ModalContext = React.createContext({ setModalOpen: () => {}, - openModalStack: [], + openModalStack: () => [], }); /** Default implementation for modal context props. @@ -19,10 +19,14 @@ export const ModalContext = React.createContext({ **/ export const useModalContext = (): ModalContextProps => { // A stack of modal IDs. These should reflect a stacked opening of modals on top of each other. - const [openModalStack, setOpenModalStack] = React.useState([]); + const currentOpenModalStack = React.useRef([]); + + const setOpenModalStack = ((stackUpdateFunction: (old: string[]) => string[]) => { + currentOpenModalStack.current = stackUpdateFunction([...currentOpenModalStack.current]) + }) const setModalOpen = React.useCallback((modalId: string, isOpen: boolean) => { - setOpenModalStack((old) => { + setOpenModalStack(old => { if (isOpen) { return [...old, modalId]; } else { @@ -41,8 +45,12 @@ export const useModalContext = (): ModalContextProps => { }); }, []); + const openModalStack = React.useCallback(() => { + return currentOpenModalStack.current.length ? [...currentOpenModalStack.current] : undefined + }, []) + return { - openModalStack: openModalStack.length ? [...openModalStack] : undefined, + openModalStack, setModalOpen, }; }; diff --git a/src/components/Dialog/stories/Modal.stories.tsx b/src/components/Dialog/stories/Modal.stories.tsx index b42efc55..9a51a7a5 100644 --- a/src/components/Dialog/stories/Modal.stories.tsx +++ b/src/components/Dialog/stories/Modal.stories.tsx @@ -126,7 +126,7 @@ const TrackingContent = () => { return (
    - {(modalContext.openModalStack ?? []).map((modalId, idx) => ( + {(modalContext.openModalStack() ?? []).map((modalId, idx) => (
  • {idx + 1}. {modalId}
  • From bcc7defe45e1d7564ed6d9a8149ea0a58b4ee6e5 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 6 Nov 2025 15:33:16 +0100 Subject: [PATCH 42/70] improve changelog info --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94aa26ec..5c2a0bfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,8 @@ This is a major release, and it might be not compatible with your current usage - function to calculate the difference between 2 colors using the simple CIE76 formula - `utils.textToColorHash()` - function to calculate a color from a text string +- `utils.reduceToText` + - shrinking HTML content and React elements to plain text, used for `` - new icons - `artefact-task-sqlupdatequeryoperator` - `artefact-task-customsqlexecution` @@ -113,8 +115,6 @@ This is a major release, and it might be not compatible with your current usage - `intent` display was aligned with other components, `intent="primary"` is now `intent="accent"`, in most cases it may be better to use `affirmative={true}` or `elevated={true}` instead of primary/accent intent - `` - `intent` display was aligned with other components, `intent="primary"` is now `intent="accent"`, in most cases it may be better to use `elevated={true}` instead of using intent -- `` - - extracted utility for shrinking content to plain text. The utility, `reduceToText`, can be used independently from the component. ### Deprecated From 16de95be4605d43d2aae3e1bb2c5a6b9f89b858c Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 6 Nov 2025 16:08:24 +0100 Subject: [PATCH 43/70] define properties only 1 time and reuse them --- src/common/utils/reduceToText.tsx | 34 +++++++--------------- src/components/TextReducer/TextReducer.tsx | 8 +++-- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/common/utils/reduceToText.tsx b/src/common/utils/reduceToText.tsx index 46aa17d6..c8cf938b 100644 --- a/src/common/utils/reduceToText.tsx +++ b/src/common/utils/reduceToText.tsx @@ -2,34 +2,21 @@ import React from "react"; import { renderToString } from "react-dom/server"; import * as ReactIs from "react-is"; +import { TextReducerProps } from "./../../components/TextReducer/TextReducer"; -interface ReduceToTextFuncType { +export interface ReduceToTextFuncType { ( /** * Component or text to reduce HTML markup content to plain text. */ input: React.ReactNode | React.ReactNode[] | string, - options?: { - /** - * Maximum number of nodes that are used from the HTML content. - * An HTML element with multiple sub elements is count as only 1 node. - */ - maxNodes?: number; - /** - * Set maximum string length of returned content. - */ - maxLength?: number; - } - ): string -} - + options?: Pick + ): string; +} -export const reduceToText: ReduceToTextFuncType = ( - input, - options -) => { - const { maxNodes, maxLength} = options || {}; - let content: React.ReactNode | React.ReactNode[] = input; +export const reduceToText: ReduceToTextFuncType = (input, options) => { + const { maxNodes, maxLength } = options || {}; + const content: React.ReactNode | React.ReactNode[] = input; let nodeCount = 0; const onlyText = (nodes: React.ReactNode | React.ReactNode[]): string => { @@ -54,8 +41,7 @@ export const reduceToText: ReduceToTextFuncType = ( .join(" "); }; - let text = - typeof content === "string" ? content : onlyText(content); + let text = typeof content === "string" ? content : onlyText(content); // Basic HTML cleanup text = text.replace(/<[^\s][^>]*>/g, "").replace(/\n/g, " "); @@ -65,4 +51,4 @@ export const reduceToText: ReduceToTextFuncType = ( } return text.trim(); -} +}; diff --git a/src/components/TextReducer/TextReducer.tsx b/src/components/TextReducer/TextReducer.tsx index 1c2115b2..16a89ff2 100644 --- a/src/components/TextReducer/TextReducer.tsx +++ b/src/components/TextReducer/TextReducer.tsx @@ -1,9 +1,9 @@ import React from "react"; +import { reduceToText } from "../../common/utils/reduceToText"; import { CLASSPREFIX as eccgui } from "../../configuration/constants"; import { OverflowText, OverflowTextProps } from "./../Typography"; -import { reduceToText } from "../../common/utils/reduceToText"; export interface TextReducerProps extends Pick, "children"> { /** @@ -37,7 +37,11 @@ export const TextReducer = ({ useOverflowTextWrapper, overflowTextProps, }: TextReducerProps) => { - const shrinkedContent = reduceToText(children, {maxLength, maxNodes}) + if (typeof children === "undefined") { + return <>; + } + + const shrinkedContent = reduceToText(children, { maxLength, maxNodes }); return useOverflowTextWrapper ? ( Date: Fri, 7 Nov 2025 15:21:15 +0100 Subject: [PATCH 44/70] Fix simpleDialogProps parameter in StickyNoteModal --- src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx b/src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx index 1492c5cd..924cb7e5 100644 --- a/src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx +++ b/src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx @@ -40,7 +40,7 @@ export interface StickyNoteModalProps { /** * Forward other properties to the `SimpleModal` element that is used for this dialog. */ - simpleDialogProps?: Omit; + simpleDialogProps?: Omit; /** * Code editor props */ From 1a729b879d6a584303122fbe0a88036603c61ed7 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Mon, 10 Nov 2025 15:18:30 +0100 Subject: [PATCH 45/70] add short delay before the context overlay placeholder is replaced by the real element --- src/components/ContextOverlay/ContextOverlay.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/components/ContextOverlay/ContextOverlay.tsx b/src/components/ContextOverlay/ContextOverlay.tsx index 1d3991f9..a5d50b37 100644 --- a/src/components/ContextOverlay/ContextOverlay.tsx +++ b/src/components/ContextOverlay/ContextOverlay.tsx @@ -39,6 +39,8 @@ export const ContextOverlay = ({ }: ContextOverlayProps) => { const placeholderRef = React.useRef(null); const eventMemory = React.useRef(undefined); + const swapDelay = React.useRef(null); + const swapDelayTime = 15; const [placeholder, setPlaceholder] = React.useState( // use placeholder only for "simple" overlays without special states !otherPopoverProps.disabled && @@ -51,8 +53,15 @@ export const ContextOverlay = ({ React.useEffect(() => { if (placeholderRef.current) { const swap = (ev: MouseEvent | globalThis.FocusEvent) => { - eventMemory.current = ev.type === "focusin" ? "afterfocus" : "afterhover"; - setPlaceholder(false); + if (swapDelay.current) { + clearTimeout(swapDelay.current); + } + swapDelay.current = setTimeout(() => { + // we delay the swap to prevent unwanted effects + // (e.g. event hickup after replacing elements when it is not really necessary) + eventMemory.current = ev.type === "focusin" ? "afterfocus" : "afterhover"; + setPlaceholder(false); + }, swapDelayTime); }; (placeholderRef.current as HTMLElement).addEventListener("mouseenter", swap); (placeholderRef.current as HTMLElement).addEventListener("focusin", swap); @@ -87,7 +96,7 @@ export const ContextOverlay = ({ PlaceholderElement, { ...otherPopoverProps?.targetProps, - className: `${BlueprintClasses.POPOVER_TARGET} ${targetClassName}`, + className: `${BlueprintClasses.POPOVER_TARGET} ${targetClassName} ${eccgui}-contextoverlay__wrapper--placeholder`, ref: placeholderRef, }, React.cloneElement(childTarget, { From 2956b3849aecdfed605132d4d6208d6699cf3890 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Tue, 11 Nov 2025 14:57:42 +0100 Subject: [PATCH 46/70] lighten success color a bit to make it more distinct from text color on icons and lines --- src/configuration/_palettes.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configuration/_palettes.scss b/src/configuration/_palettes.scss index 6d56df90..6d2c1de5 100644 --- a/src/configuration/_palettes.scss +++ b/src/configuration/_palettes.scss @@ -13,7 +13,7 @@ $eccgui-color-palette-light: ( ), "semantic": ( "info": eccgui-create-color-tints(#e5f4fb #aecfe3 #77aaca #4086b2 #096199), - "success": eccgui-create-color-tints(#e8f5e9 #b2c6b4 #7c967e #466749 #2f5e3a), + "success": eccgui-create-color-tints(#e8f5e9 #b1d4b7 #7ab286 #429154 #0b6f22), "warning": eccgui-create-color-tints(#fff3e0 #fad2b3 #f5b287 #f0915a #eb702d), "danger": eccgui-create-color-tints(#fff5f6 #edbfc0 #db8989 #c95253 #b71c1c), ), From b16dd3b0fc07d2cd61c176fd5dc7173b710cb57c Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Tue, 11 Nov 2025 16:16:05 +0100 Subject: [PATCH 47/70] use variables for edge rules and allow anti-aliasing --- src/extensions/react-flow/_config.scss | 11 ++++++++--- src/extensions/react-flow/edges/_edges.scss | 18 +++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/extensions/react-flow/_config.scss b/src/extensions/react-flow/_config.scss index 15f3a9d9..296cfadc 100644 --- a/src/extensions/react-flow/_config.scss +++ b/src/extensions/react-flow/_config.scss @@ -7,9 +7,14 @@ $reactflow-node-color: $eccgui-color-workspace-text !default; $reactflow-node-font-size: $eccgui-size-typo-caption !default; $reactflow-node-border-width: 2 * $button-border-width !default; $reactflow-node-border-radius: $button-border-radius !default; -$reactflow-edge-stroke-opacity-default: 0.8 !default; -$reactflow-edge-stroke-opacity-hover: 0.9 !default; -$reactflow-edge-stroke-opacity-selected: 1 !default; +$reactflow-edge-rendering: geometricprecision !default; +$reactflow-edge-stroke-width-default: 2px !default; +$reactflow-edge-stroke-width-hover: 2px !default; +$reactflow-edge-stroke-width-selected: 2px !default; +$reactflow-edge-stroke-opacity-default: $eccgui-opacity-muted !default; +$reactflow-edge-stroke-opacity-hover: $eccgui-opacity-narrow !default; +$reactflow-edge-stroke-opacity-selected: $eccgui-opacity-regular !default; +$reactflow-edge-stroke-opacity-interaction-ratio: $eccgui-opacity-ghostly !default; $reactflow-edge-stroke-color-default: #{eccgui-color-var("identity", "text", "700")} !default; $reactflow-edge-stroke-color-hover: $reactflow-edge-stroke-color-default !default; $reactflow-edge-stroke-color-selected: $eccgui-color-accent !default; diff --git a/src/extensions/react-flow/edges/_edges.scss b/src/extensions/react-flow/edges/_edges.scss index f134be25..9583398b 100644 --- a/src/extensions/react-flow/edges/_edges.scss +++ b/src/extensions/react-flow/edges/_edges.scss @@ -10,7 +10,7 @@ path[class*="react-flow__edge"], rect[class*="react-flow__edge"] { stroke-opacity: $reactflow-edge-stroke-opacity-default; - shape-rendering: optimizespeed; + shape-rendering: $reactflow-edge-rendering; } text[class*="react-flow__edge"] { @@ -60,11 +60,15 @@ path.react-flow__edge-path { stroke: currentcolor; stroke-opacity: $reactflow-edge-stroke-opacity-default; - stroke-width: 2; + stroke-width: $reactflow-edge-stroke-width-default; + + .react-flow__edge:hover & { + stroke-width: $reactflow-edge-stroke-width-hover; + } .react-flow__edge.selected & { stroke: currentcolor; - stroke-width: 2; + stroke-width: $reactflow-edge-stroke-width-selected; } } @@ -78,11 +82,11 @@ path.react-flow__edge-interaction { } .react-flow__edge:hover & { - stroke-opacity: $reactflow-edge-stroke-opacity-hover * 0.2; + stroke-opacity: $reactflow-edge-stroke-opacity-hover * $reactflow-edge-stroke-opacity-interaction-ratio; } .react-flow__edge.selected & { - stroke-opacity: $reactflow-edge-stroke-opacity-selected * 0.2; + stroke-opacity: $reactflow-edge-stroke-opacity-selected * $reactflow-edge-stroke-opacity-interaction-ratio; } } @@ -96,11 +100,11 @@ path.react-flow__edge-path-glow { } .react-flow__edge:hover & { - stroke-opacity: $reactflow-edge-stroke-opacity-hover * 0.2; + stroke-opacity: $reactflow-edge-stroke-opacity-hover * $reactflow-edge-stroke-opacity-interaction-ratio; } .react-flow__edge.selected & { - stroke-opacity: $reactflow-edge-stroke-opacity-selected * 0.2; + stroke-opacity: $reactflow-edge-stroke-opacity-selected * $reactflow-edge-stroke-opacity-interaction-ratio; } .react-flow__edge.animated & { From f8169784fd1d42528e925330f545b336cbae19b6 Mon Sep 17 00:00:00 2001 From: arausly Date: Wed, 12 Nov 2025 10:51:49 +0100 Subject: [PATCH 48/70] added more icons --- src/components/Icon/canonicalIconNames.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/Icon/canonicalIconNames.tsx b/src/components/Icon/canonicalIconNames.tsx index 5c1897d5..0f9401cc 100644 --- a/src/components/Icon/canonicalIconNames.tsx +++ b/src/components/Icon/canonicalIconNames.tsx @@ -25,6 +25,17 @@ const canonicalIcons = { "artefact-task-concatenatetofile": icons.DocumentExport, "artefact-task-sqlupdatequeryoperator": icons.Sql, "artefact-task-customsqlexecution": icons.Sql, + "artefact-task-multitablemerge": icons.JoinFull, + "artefact-task-jsonparseroperator": icons.JsonReference, + "artefact-task-searchaddresses": icons.SearchLocate, + "artefact-task-sparkfunction": icons.Flash, + "artefact-task-eccencadataplatformgraphstorefileuploadoperator": icons.Upload, + "artefact-task-xsltoperator": icons.TransformCode, + "artefact-task-distinctby": icons.Filter, + "artefact-task-template": icons.Template, + "artefact-task-merge": icons.Connect, + "artefact-task-scheduler": icons.EventSchedule, + "artefact-task-sendemail": icons.Email, "artefact-dataset-csv": icons.Csv, "artefact-dataset-eccencadataplatform": icons.DataVis_1, "artefact-dataset-excel": icons.Xls, From 94944969a9a96dbe6f944655d7405156a082e77e Mon Sep 17 00:00:00 2001 From: arausly Date: Wed, 12 Nov 2025 10:57:56 +0100 Subject: [PATCH 49/70] added change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c2a0bfc..47b9424d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ This is a major release, and it might be not compatible with your current usage - Add `ModalContext` to track open/close state of all used application modals. - Add `modalId` property to give a modal a unique ID for tracking purposes. - `preventReactFlowEvents`: adds 'nopan', 'nowheel' and 'nodrag' classes to overlay classes in order to prevent react-flow to react to drag and pan actions in modals. +- Added more icons for build tasks (CMEM-6898) ### Removed From 22223c58dc48ebe20b781b45858effbf6524bce3 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 13 Nov 2025 16:27:08 +0100 Subject: [PATCH 50/70] fix auto-coloring of buttons in notifications --- src/components/Notification/notification.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Notification/notification.scss b/src/components/Notification/notification.scss index 04963e53..8df9a59b 100644 --- a/src/components/Notification/notification.scss +++ b/src/components/Notification/notification.scss @@ -87,7 +87,7 @@ flex-grow: 0; flex-shrink: 0; - .#{$ns}-button:not([class*="intent"]) { + .#{$ns}-button:not([class*="intent"]):not(:disabled) { color: inherit; } } From 011768595315f261f36c176240ad3ede443c9fe5 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Mon, 17 Nov 2025 14:20:07 +0100 Subject: [PATCH 51/70] add new icons for togglers and states --- CHANGELOG.md | 5 +++++ src/components/Icon/canonicalIconNames.tsx | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47b9424d..86eebc4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,11 @@ This is a major release, and it might be not compatible with your current usage - `toggler-caretleft` - `toggler-micon` - `toggler-micoff` + - `toggler-radio` + - `toggler-radio-checked` + - `state-progress` + - `state-progress-error` + - `state-progress-warning` - ``: - Add `ModalContext` to track open/close state of all used application modals. - Add `modalId` property to give a modal a unique ID for tracking purposes. diff --git a/src/components/Icon/canonicalIconNames.tsx b/src/components/Icon/canonicalIconNames.tsx index 0f9401cc..28cc41c7 100644 --- a/src/components/Icon/canonicalIconNames.tsx +++ b/src/components/Icon/canonicalIconNames.tsx @@ -175,6 +175,9 @@ const canonicalIcons = { "state-info": icons.InformationFilled, "state-locked": icons.Locked, "state-partlychecked": icons.CheckboxIndeterminate, + "state-progress": icons.InProgress, + "state-progress-error": icons.InProgressError, + "state-progress-warning": icons.InProgressWarning, "state-protected": icons.DocumentProtected, "state-success": icons.CheckmarkFilled, "state-unchecked": icons.Checkbox, @@ -193,6 +196,8 @@ const canonicalIcons = { "toggler-moveleft": icons.ChevronLeft, "toggler-moveright": icons.ChevronRight, "toggler-list": icons.List, + "toggler-radio": icons.RadioButton, + "toggler-radio-checked": icons.RadioButtonChecked, "toggler-rowexpand": icons.RowExpand, "toggler-rowcollapse": icons.RowCollapse, "toggler-showless": icons.ChevronUp, From 35a0eed18c020cf8f9204117b9db151993fe4c23 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Mon, 17 Nov 2025 15:36:33 +0100 Subject: [PATCH 52/70] add more icons --- CHANGELOG.md | 3 +++ src/components/Icon/canonicalIconNames.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86eebc4b..ba775df9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,12 +57,15 @@ This is a major release, and it might be not compatible with your current usage - new icons - `artefact-task-sqlupdatequeryoperator` - `artefact-task-customsqlexecution` + - `item-legend` + - `operation-tour` - `toggler-carettop` - `toggler-caretleft` - `toggler-micon` - `toggler-micoff` - `toggler-radio` - `toggler-radio-checked` + - `state-flagged` - `state-progress` - `state-progress-error` - `state-progress-warning` diff --git a/src/components/Icon/canonicalIconNames.tsx b/src/components/Icon/canonicalIconNames.tsx index 28cc41c7..54bbec7f 100644 --- a/src/components/Icon/canonicalIconNames.tsx +++ b/src/components/Icon/canonicalIconNames.tsx @@ -88,6 +88,7 @@ const canonicalIcons = { "item-execution": icons.Run, "item-info": icons.Information, "item-launch": icons.Launch, + "item-legend": icons.Legend, "item-moremenu": icons.OverflowMenuVertical, "item-question": icons.Help, "item-reload": icons.Renew, @@ -158,6 +159,7 @@ const canonicalIcons = { "operation-logout": icons.Logout, "operation-magic": icons.MagicWand, "operation-merge": icons.DirectionMerge, + "operation-tour": icons.Tour, "operation-redo": icons.Redo, "operation-search": icons.Search, "operation-send": icons.Send, @@ -172,6 +174,7 @@ const canonicalIcons = { "state-confirmed": icons.ThumbsUp, "state-danger": icons.ErrorFilled, "state-declined": icons.ThumbsDown, + "state-flagged": icons.Flag, "state-info": icons.InformationFilled, "state-locked": icons.Locked, "state-partlychecked": icons.CheckboxIndeterminate, From 35043523115097a04b5cdda6b33878818f24c8fa Mon Sep 17 00:00:00 2001 From: arausly Date: Tue, 18 Nov 2025 11:41:40 +0100 Subject: [PATCH 53/70] removed plugin to allow md expectations --- CHANGELOG.md | 1 + src/cmem/markdown/Markdown.tsx | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba775df9..368b6ccc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,7 @@ This is a major release, and it might be not compatible with your current usage - use `` with `configuration`, or define it yourself - SCSS variables `$eccgui-color-application-text` and `$eccgui-color-application-background` were removed - use `$eccgui-color-workspace-text` and `$eccgui-color-workspace-background` +- Removed `remarkTypograf` from markdown plugin tray to maintain display expectation ### Fixed diff --git a/src/cmem/markdown/Markdown.tsx b/src/cmem/markdown/Markdown.tsx index 2668dbcb..7544d8f9 100644 --- a/src/cmem/markdown/Markdown.tsx +++ b/src/cmem/markdown/Markdown.tsx @@ -2,7 +2,6 @@ import React from "react"; import ReactMarkdown from "react-markdown"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; // @ts-ignore: No declaration file for module (TODO: should be @ts-expect-error but GUI elements is used inside project with `noImplicitAny=false`) -import remarkTypograf from "@mavrin/remark-typograf"; import rehypeExternalLinks from "rehype-external-links"; import rehypeRaw from "rehype-raw"; import { remarkDefinitionList } from "remark-definition-list"; @@ -55,7 +54,7 @@ const configDefault = { @see https://github.com/remarkjs/react-markdown#api */ // @see https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins - remarkPlugins: [remarkGfm, remarkTypograf, remarkDefinitionList] as PluggableList, + remarkPlugins: [remarkGfm, remarkDefinitionList] as PluggableList, // @see https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins rehypePlugins: [] as PluggableList, allowedElements: [ From a76f62656c8b7049b19f9ebcbda49d34acde09ed Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Tue, 18 Nov 2025 13:27:26 +0100 Subject: [PATCH 54/70] switch arrow direction for asc/desc sort icon --- CHANGELOG.md | 1 + src/components/Icon/canonicalIconNames.tsx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 368b6ccc..07c41a5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,7 @@ This is a major release, and it might be not compatible with your current usage - `intent` display was aligned with other components, `intent="primary"` is now `intent="accent"`, in most cases it may be better to use `affirmative={true}` or `elevated={true}` instead of primary/accent intent - `` - `intent` display was aligned with other components, `intent="primary"` is now `intent="accent"`, in most cases it may be better to use `elevated={true}` instead of using intent +- icons: arrow directions for `list-sortasc` and `list-sortdesc` were switched, up arrow is now used for ascending sort ### Deprecated diff --git a/src/components/Icon/canonicalIconNames.tsx b/src/components/Icon/canonicalIconNames.tsx index 54bbec7f..93ed7f87 100644 --- a/src/components/Icon/canonicalIconNames.tsx +++ b/src/components/Icon/canonicalIconNames.tsx @@ -28,7 +28,7 @@ const canonicalIcons = { "artefact-task-multitablemerge": icons.JoinFull, "artefact-task-jsonparseroperator": icons.JsonReference, "artefact-task-searchaddresses": icons.SearchLocate, - "artefact-task-sparkfunction": icons.Flash, + "artefact-task-sparkfunction": icons.Flash, "artefact-task-eccencadataplatformgraphstorefileuploadoperator": icons.Upload, "artefact-task-xsltoperator": icons.TransformCode, "artefact-task-distinctby": icons.Filter, @@ -106,8 +106,8 @@ const canonicalIcons = { "item-hidedetails": icons.ViewOff, "item-image": icons.Image, - "list-sortasc": icons.ArrowDown, - "list-sortdesc": icons.ArrowUp, + "list-sortasc": icons.ArrowUp, + "list-sortdesc": icons.ArrowDown, "list-sort": icons.ArrowsVertical, "module-accesscontrol": icons.UserAdmin, From 1b14a616f23a767220c185d39d03f884fc462be2 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Tue, 18 Nov 2025 15:45:03 +0100 Subject: [PATCH 55/70] update jest package --- package.json | 4 +- yarn.lock | 720 +++++++++++++++++++++++++-------------------------- 2 files changed, 361 insertions(+), 363 deletions(-) diff --git a/package.json b/package.json index 389e4f49..ab732b94 100644 --- a/package.json +++ b/package.json @@ -150,8 +150,8 @@ "eslint-plugin-simple-import-sort": "^12.1.1", "husky": "4", "identity-obj-proxy": "^3.0.0", - "jest": "^30.0.5", - "jest-environment-jsdom": "^30.0.5", + "jest": "^30.2.0", + "jest-environment-jsdom": "^30.2.0", "jest-pnp-resolver": "^1.2.3", "lint-staged": "^15.5.1", "node-sass-package-importer": "^5.3.3", diff --git a/yarn.lock b/yarn.lock index 2d050d83..52af1ffc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1924,50 +1924,50 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.0.5.tgz#d7d027c2db5c64c20a973b7f3e57b49956d6c335" - integrity sha512-xY6b0XiL0Nav3ReresUarwl2oIz1gTnxGbGpho9/rbUWsLH0f1OD/VT84xs8c7VmH7MChnLb0pag6PhZhAdDiA== +"@jest/console@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.2.0.tgz#c52fcd5b58fdd2e8eb66b2fd8ae56f2f64d05b28" + integrity sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ== dependencies: - "@jest/types" "30.0.5" + "@jest/types" "30.2.0" "@types/node" "*" chalk "^4.1.2" - jest-message-util "30.0.5" - jest-util "30.0.5" + jest-message-util "30.2.0" + jest-util "30.2.0" slash "^3.0.0" -"@jest/core@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.0.5.tgz#b5778922d2928f676636e3ec199829554e61e452" - integrity sha512-fKD0OulvRsXF1hmaFgHhVJzczWzA1RXMMo9LTPuFXo9q/alDbME3JIyWYqovWsUBWSoBcsHaGPSLF9rz4l9Qeg== +"@jest/core@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.2.0.tgz#813d59faa5abd5510964a8b3a7b17cc77b775275" + integrity sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ== dependencies: - "@jest/console" "30.0.5" + "@jest/console" "30.2.0" "@jest/pattern" "30.0.1" - "@jest/reporters" "30.0.5" - "@jest/test-result" "30.0.5" - "@jest/transform" "30.0.5" - "@jest/types" "30.0.5" + "@jest/reporters" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" ansi-escapes "^4.3.2" chalk "^4.1.2" ci-info "^4.2.0" exit-x "^0.2.2" graceful-fs "^4.2.11" - jest-changed-files "30.0.5" - jest-config "30.0.5" - jest-haste-map "30.0.5" - jest-message-util "30.0.5" + jest-changed-files "30.2.0" + jest-config "30.2.0" + jest-haste-map "30.2.0" + jest-message-util "30.2.0" jest-regex-util "30.0.1" - jest-resolve "30.0.5" - jest-resolve-dependencies "30.0.5" - jest-runner "30.0.5" - jest-runtime "30.0.5" - jest-snapshot "30.0.5" - jest-util "30.0.5" - jest-validate "30.0.5" - jest-watcher "30.0.5" + jest-resolve "30.2.0" + jest-resolve-dependencies "30.2.0" + jest-runner "30.2.0" + jest-runtime "30.2.0" + jest-snapshot "30.2.0" + jest-util "30.2.0" + jest-validate "30.2.0" + jest-watcher "30.2.0" micromatch "^4.0.8" - pretty-format "30.0.5" + pretty-format "30.2.0" slash "^3.0.0" "@jest/diff-sequences@30.0.1": @@ -1975,35 +1975,35 @@ resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz#0ededeae4d071f5c8ffe3678d15f3a1be09156be" integrity sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw== -"@jest/environment-jsdom-abstract@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.0.5.tgz#7299cca59b3e84547ca3d1bbd4e7d36b4b44d426" - integrity sha512-gpWwiVxZunkoglP8DCnT3As9x5O8H6gveAOpvaJd2ATAoSh7ZSSCWbr9LQtUMvr8WD3VjG9YnDhsmkCK5WN1rQ== +"@jest/environment-jsdom-abstract@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.2.0.tgz#1313f9b3b509c31298c241203161b36622865181" + integrity sha512-kazxw2L9IPuZpQ0mEt9lu9Z98SqR74xcagANmMBU16X0lS23yPc0+S6hGLUz8kVRlomZEs/5S/Zlpqwf5yu6OQ== dependencies: - "@jest/environment" "30.0.5" - "@jest/fake-timers" "30.0.5" - "@jest/types" "30.0.5" + "@jest/environment" "30.2.0" + "@jest/fake-timers" "30.2.0" + "@jest/types" "30.2.0" "@types/jsdom" "^21.1.7" "@types/node" "*" - jest-mock "30.0.5" - jest-util "30.0.5" + jest-mock "30.2.0" + jest-util "30.2.0" -"@jest/environment@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.0.5.tgz#eaaae0403c7d3f8414053c2224acc3011e1c3a1b" - integrity sha512-aRX7WoaWx1oaOkDQvCWImVQ8XNtdv5sEWgk4gxR6NXb7WBUnL5sRak4WRzIQRZ1VTWPvV4VI4mgGjNL9TeKMYA== +"@jest/environment@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.2.0.tgz#1e673cdb8b93ded707cf6631b8353011460831fa" + integrity sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g== dependencies: - "@jest/fake-timers" "30.0.5" - "@jest/types" "30.0.5" + "@jest/fake-timers" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - jest-mock "30.0.5" + jest-mock "30.2.0" -"@jest/expect-utils@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.0.5.tgz#9d42e4b8bc80367db30abc6c42b2cb14073f66fc" - integrity sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew== +"@jest/expect-utils@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.2.0.tgz#4f95413d4748454fdb17404bf1141827d15e6011" + integrity sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA== dependencies: - "@jest/get-type" "30.0.1" + "@jest/get-type" "30.1.0" "@jest/expect-utils@^29.7.0": version "29.7.0" @@ -2012,40 +2012,40 @@ dependencies: jest-get-type "^29.6.3" -"@jest/expect@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.0.5.tgz#2bbd101df4869f5d171c3cfee881f810f1525005" - integrity sha512-6udac8KKrtTtC+AXZ2iUN/R7dp7Ydry+Fo6FPFnDG54wjVMnb6vW/XNlf7Xj8UDjAE3aAVAsR4KFyKk3TCXmTA== +"@jest/expect@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.2.0.tgz#9a5968499bb8add2bbb09136f69f7df5ddbf3185" + integrity sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA== dependencies: - expect "30.0.5" - jest-snapshot "30.0.5" + expect "30.2.0" + jest-snapshot "30.2.0" -"@jest/fake-timers@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.0.5.tgz#c028a9465a44b7744cb2368196bed89ce13c7054" - integrity sha512-ZO5DHfNV+kgEAeP3gK3XlpJLL4U3Sz6ebl/n68Uwt64qFFs5bv4bfEEjyRGK5uM0C90ewooNgFuKMdkbEoMEXw== +"@jest/fake-timers@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.2.0.tgz#0941ddc28a339b9819542495b5408622dc9e94ec" + integrity sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw== dependencies: - "@jest/types" "30.0.5" + "@jest/types" "30.2.0" "@sinonjs/fake-timers" "^13.0.0" "@types/node" "*" - jest-message-util "30.0.5" - jest-mock "30.0.5" - jest-util "30.0.5" + jest-message-util "30.2.0" + jest-mock "30.2.0" + jest-util "30.2.0" -"@jest/get-type@30.0.1": - version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.0.1.tgz#0d32f1bbfba511948ad247ab01b9007724fc9f52" - integrity sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw== +"@jest/get-type@30.1.0": + version "30.1.0" + resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.1.0.tgz#4fcb4dc2ebcf0811be1c04fd1cb79c2dba431cbc" + integrity sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA== -"@jest/globals@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.0.5.tgz#ca70e0ac08ab40417cf8cd92bcb76116c2ccca63" - integrity sha512-7oEJT19WW4oe6HR7oLRvHxwlJk2gev0U9px3ufs8sX9PoD1Eza68KF0/tlN7X0dq/WVsBScXQGgCldA1V9Y/jA== +"@jest/globals@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.2.0.tgz#2f4b696d5862664b89c4ee2e49ae24d2bb7e0988" + integrity sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw== dependencies: - "@jest/environment" "30.0.5" - "@jest/expect" "30.0.5" - "@jest/types" "30.0.5" - jest-mock "30.0.5" + "@jest/environment" "30.2.0" + "@jest/expect" "30.2.0" + "@jest/types" "30.2.0" + jest-mock "30.2.0" "@jest/pattern@30.0.1": version "30.0.1" @@ -2055,16 +2055,16 @@ "@types/node" "*" jest-regex-util "30.0.1" -"@jest/reporters@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.0.5.tgz#b83585e6448d390a8d92a641c567f1655976d5c6" - integrity sha512-mafft7VBX4jzED1FwGC1o/9QUM2xebzavImZMeqnsklgcyxBto8mV4HzNSzUrryJ+8R9MFOM3HgYuDradWR+4g== +"@jest/reporters@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.2.0.tgz#a36b28fcbaf0c4595250b108e6f20e363348fd91" + integrity sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "30.0.5" - "@jest/test-result" "30.0.5" - "@jest/transform" "30.0.5" - "@jest/types" "30.0.5" + "@jest/console" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" "@jridgewell/trace-mapping" "^0.3.25" "@types/node" "*" chalk "^4.1.2" @@ -2077,9 +2077,9 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^5.0.0" istanbul-reports "^3.1.3" - jest-message-util "30.0.5" - jest-util "30.0.5" - jest-worker "30.0.5" + jest-message-util "30.2.0" + jest-util "30.2.0" + jest-worker "30.2.0" slash "^3.0.0" string-length "^4.0.2" v8-to-istanbul "^9.0.1" @@ -2098,12 +2098,12 @@ dependencies: "@sinclair/typebox" "^0.27.8" -"@jest/snapshot-utils@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.0.5.tgz#e23a0e786f174e8cff7f150c1cfbdc9cb7cc81a4" - integrity sha512-XcCQ5qWHLvi29UUrowgDFvV4t7ETxX91CbDczMnoqXPOIcZOxyNdSjm6kV5XMc8+HkxfRegU/MUmnTbJRzGrUQ== +"@jest/snapshot-utils@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz#387858eb90c2f98f67bff327435a532ac5309fbe" + integrity sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug== dependencies: - "@jest/types" "30.0.5" + "@jest/types" "30.2.0" chalk "^4.1.2" graceful-fs "^4.2.11" natural-compare "^1.4.0" @@ -2117,42 +2117,42 @@ callsites "^3.1.0" graceful-fs "^4.2.11" -"@jest/test-result@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.0.5.tgz#064c5210c24d5ea192fb02ceddad3be1cfa557c8" - integrity sha512-wPyztnK0gbDMQAJZ43tdMro+qblDHH1Ru/ylzUo21TBKqt88ZqnKKK2m30LKmLLoKtR2lxdpCC/P3g1vfKcawQ== +"@jest/test-result@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.2.0.tgz#9c0124377fb7996cdffb86eda3dbc56eacab363d" + integrity sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg== dependencies: - "@jest/console" "30.0.5" - "@jest/types" "30.0.5" + "@jest/console" "30.2.0" + "@jest/types" "30.2.0" "@types/istanbul-lib-coverage" "^2.0.6" collect-v8-coverage "^1.0.2" -"@jest/test-sequencer@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.0.5.tgz#c6dba8fc3c386dd793c087626e8508ff1ead19f4" - integrity sha512-Aea/G1egWoIIozmDD7PBXUOxkekXl7ueGzrsGGi1SbeKgQqCYCIf+wfbflEbf2LiPxL8j2JZGLyrzZagjvW4YQ== +"@jest/test-sequencer@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz#bf0066bc72e176d58f5dfa7f212b6e7eee44f221" + integrity sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q== dependencies: - "@jest/test-result" "30.0.5" + "@jest/test-result" "30.2.0" graceful-fs "^4.2.11" - jest-haste-map "30.0.5" + jest-haste-map "30.2.0" slash "^3.0.0" -"@jest/transform@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.0.5.tgz#f8ca2e9f7466b77b406807d3bef1f6790dd384e4" - integrity sha512-Vk8amLQCmuZyy6GbBht1Jfo9RSdBtg7Lks+B0PecnjI8J+PCLQPGh7uI8Q/2wwpW2gLdiAfiHNsmekKlywULqg== +"@jest/transform@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.2.0.tgz#54bef1a4510dcbd58d5d4de4fe2980a63077ef2a" + integrity sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA== dependencies: "@babel/core" "^7.27.4" - "@jest/types" "30.0.5" + "@jest/types" "30.2.0" "@jridgewell/trace-mapping" "^0.3.25" - babel-plugin-istanbul "^7.0.0" + babel-plugin-istanbul "^7.0.1" chalk "^4.1.2" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.11" - jest-haste-map "30.0.5" + jest-haste-map "30.2.0" jest-regex-util "30.0.1" - jest-util "30.0.5" + jest-util "30.2.0" micromatch "^4.0.8" pirates "^4.0.7" slash "^3.0.0" @@ -2179,10 +2179,10 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.0.5.tgz#29a33a4c036e3904f1cfd94f6fe77f89d2e1cc05" - integrity sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ== +"@jest/types@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.2.0.tgz#1c678a7924b8f59eafd4c77d56b6d0ba976d62b8" + integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== dependencies: "@jest/pattern" "30.0.1" "@jest/schemas" "30.0.5" @@ -3946,15 +3946,15 @@ babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-jest@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.0.5.tgz#7cc7dd03d0d613125d458521f635b8c2361e89cc" - integrity sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg== +babel-jest@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.2.0.tgz#fd44a1ec9552be35ead881f7381faa7d8f3b95ac" + integrity sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw== dependencies: - "@jest/transform" "30.0.5" + "@jest/transform" "30.2.0" "@types/babel__core" "^7.20.5" - babel-plugin-istanbul "^7.0.0" - babel-preset-jest "30.0.1" + babel-plugin-istanbul "^7.0.1" + babel-preset-jest "30.2.0" chalk "^4.1.2" graceful-fs "^4.2.11" slash "^3.0.0" @@ -3991,10 +3991,10 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-istanbul@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz#629a178f63b83dc9ecee46fd20266283b1f11280" - integrity sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw== +babel-plugin-istanbul@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz#d8b518c8ea199364cf84ccc82de89740236daf92" + integrity sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@istanbuljs/load-nyc-config" "^1.0.0" @@ -4002,13 +4002,11 @@ babel-plugin-istanbul@^7.0.0: istanbul-lib-instrument "^6.0.2" test-exclude "^6.0.0" -babel-plugin-jest-hoist@30.0.1: - version "30.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz#f271b2066d2c1fb26a863adb8e13f85b06247125" - integrity sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ== +babel-plugin-jest-hoist@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz#94c250d36b43f95900f3a219241e0f4648191ce2" + integrity sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA== dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.27.3" "@types/babel__core" "^7.20.5" babel-plugin-jest-hoist@^29.6.3: @@ -4066,7 +4064,7 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-current-node-syntax@^1.1.0: +babel-preset-current-node-syntax@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== @@ -4087,13 +4085,13 @@ babel-preset-current-node-syntax@^1.1.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-jest@30.0.1: - version "30.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz#7d28db9531bce264e846c8483d54236244b8ae88" - integrity sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw== +babel-preset-jest@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz#04717843e561347781d6d7f69c81e6bcc3ed11ce" + integrity sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ== dependencies: - babel-plugin-jest-hoist "30.0.1" - babel-preset-current-node-syntax "^1.1.0" + babel-plugin-jest-hoist "30.2.0" + babel-preset-current-node-syntax "^1.2.0" babel-preset-jest@^29.6.3: version "29.6.3" @@ -5731,17 +5729,17 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/expect/-/expect-30.0.5.tgz#c23bf193c5e422a742bfd2990ad990811de41a5a" - integrity sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ== +expect@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-30.2.0.tgz#d4013bed267013c14bc1199cec8aa57cee9b5869" + integrity sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw== dependencies: - "@jest/expect-utils" "30.0.5" - "@jest/get-type" "30.0.1" - jest-matcher-utils "30.0.5" - jest-message-util "30.0.5" - jest-mock "30.0.5" - jest-util "30.0.5" + "@jest/expect-utils" "30.2.0" + "@jest/get-type" "30.1.0" + jest-matcher-utils "30.2.0" + jest-message-util "30.2.0" + jest-mock "30.2.0" + jest-util "30.2.0" expect@^29.0.0: version "29.7.0" @@ -7257,96 +7255,96 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jest-changed-files@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.0.5.tgz#ec448f83bd9caa894dd7da8707f207c356a19924" - integrity sha512-bGl2Ntdx0eAwXuGpdLdVYVr5YQHnSZlQ0y9HVDu565lCUAe9sj6JOtBbMmBBikGIegne9piDDIOeiLVoqTkz4A== +jest-changed-files@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.2.0.tgz#602266e478ed554e1e1469944faa7efd37cee61c" + integrity sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ== dependencies: execa "^5.1.1" - jest-util "30.0.5" + jest-util "30.2.0" p-limit "^3.1.0" -jest-circus@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.0.5.tgz#9b4d44feb56c7ffe14411ad7fc08af188c5d4da7" - integrity sha512-h/sjXEs4GS+NFFfqBDYT7y5Msfxh04EwWLhQi0F8kuWpe+J/7tICSlswU8qvBqumR3kFgHbfu7vU6qruWWBPug== +jest-circus@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.2.0.tgz#98b8198b958748a2f322354311023d1d02e7603f" + integrity sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg== dependencies: - "@jest/environment" "30.0.5" - "@jest/expect" "30.0.5" - "@jest/test-result" "30.0.5" - "@jest/types" "30.0.5" + "@jest/environment" "30.2.0" + "@jest/expect" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" chalk "^4.1.2" co "^4.6.0" dedent "^1.6.0" is-generator-fn "^2.1.0" - jest-each "30.0.5" - jest-matcher-utils "30.0.5" - jest-message-util "30.0.5" - jest-runtime "30.0.5" - jest-snapshot "30.0.5" - jest-util "30.0.5" + jest-each "30.2.0" + jest-matcher-utils "30.2.0" + jest-message-util "30.2.0" + jest-runtime "30.2.0" + jest-snapshot "30.2.0" + jest-util "30.2.0" p-limit "^3.1.0" - pretty-format "30.0.5" + pretty-format "30.2.0" pure-rand "^7.0.0" slash "^3.0.0" stack-utils "^2.0.6" -jest-cli@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.0.5.tgz#c3fbfdabd1a5c428429476f915a1ba6d0774cc50" - integrity sha512-Sa45PGMkBZzF94HMrlX4kUyPOwUpdZasaliKN3mifvDmkhLYqLLg8HQTzn6gq7vJGahFYMQjXgyJWfYImKZzOw== +jest-cli@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.2.0.tgz#1780f8e9d66bf84a10b369aea60aeda7697dcc67" + integrity sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA== dependencies: - "@jest/core" "30.0.5" - "@jest/test-result" "30.0.5" - "@jest/types" "30.0.5" + "@jest/core" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/types" "30.2.0" chalk "^4.1.2" exit-x "^0.2.2" import-local "^3.2.0" - jest-config "30.0.5" - jest-util "30.0.5" - jest-validate "30.0.5" + jest-config "30.2.0" + jest-util "30.2.0" + jest-validate "30.2.0" yargs "^17.7.2" -jest-config@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.0.5.tgz#567cf39b595229b786506a496c22e222d5e8d480" - integrity sha512-aIVh+JNOOpzUgzUnPn5FLtyVnqc3TQHVMupYtyeURSb//iLColiMIR8TxCIDKyx9ZgjKnXGucuW68hCxgbrwmA== +jest-config@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.2.0.tgz#29df8c50e2ad801cc59c406b50176c18c362a90b" + integrity sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA== dependencies: "@babel/core" "^7.27.4" - "@jest/get-type" "30.0.1" + "@jest/get-type" "30.1.0" "@jest/pattern" "30.0.1" - "@jest/test-sequencer" "30.0.5" - "@jest/types" "30.0.5" - babel-jest "30.0.5" + "@jest/test-sequencer" "30.2.0" + "@jest/types" "30.2.0" + babel-jest "30.2.0" chalk "^4.1.2" ci-info "^4.2.0" deepmerge "^4.3.1" glob "^10.3.10" graceful-fs "^4.2.11" - jest-circus "30.0.5" - jest-docblock "30.0.1" - jest-environment-node "30.0.5" + jest-circus "30.2.0" + jest-docblock "30.2.0" + jest-environment-node "30.2.0" jest-regex-util "30.0.1" - jest-resolve "30.0.5" - jest-runner "30.0.5" - jest-util "30.0.5" - jest-validate "30.0.5" + jest-resolve "30.2.0" + jest-runner "30.2.0" + jest-util "30.2.0" + jest-validate "30.2.0" micromatch "^4.0.8" parse-json "^5.2.0" - pretty-format "30.0.5" + pretty-format "30.2.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.0.5.tgz#b40f81e0c0d13e5b81c4d62b0d0dfa6a524ee0fd" - integrity sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A== +jest-diff@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.2.0.tgz#e3ec3a6ea5c5747f605c9e874f83d756cba36825" + integrity sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A== dependencies: "@jest/diff-sequences" "30.0.1" - "@jest/get-type" "30.0.1" + "@jest/get-type" "30.1.0" chalk "^4.1.2" - pretty-format "30.0.5" + pretty-format "30.2.0" jest-diff@^29.7.0: version "29.7.0" @@ -7358,66 +7356,66 @@ jest-diff@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-docblock@30.0.1: - version "30.0.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-30.0.1.tgz#545ff59f2fa88996bd470dba7d3798a8421180b1" - integrity sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA== +jest-docblock@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-30.2.0.tgz#42cd98d69f887e531c7352309542b1ce4ee10256" + integrity sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA== dependencies: detect-newline "^3.1.0" -jest-each@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.0.5.tgz#5962264ff246cd757ba44db096c1bc5b4835173e" - integrity sha512-dKjRsx1uZ96TVyejD3/aAWcNKy6ajMaN531CwWIsrazIqIoXI9TnnpPlkrEYku/8rkS3dh2rbH+kMOyiEIv0xQ== +jest-each@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.2.0.tgz#39e623ae71641c2ac3ee69b3ba3d258fce8e768d" + integrity sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ== dependencies: - "@jest/get-type" "30.0.1" - "@jest/types" "30.0.5" + "@jest/get-type" "30.1.0" + "@jest/types" "30.2.0" chalk "^4.1.2" - jest-util "30.0.5" - pretty-format "30.0.5" + jest-util "30.2.0" + pretty-format "30.2.0" -jest-environment-jsdom@^30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-30.0.5.tgz#36351cc8a14fcd54945da0beb029af493d7d5764" - integrity sha512-BmnDEoAH+jEjkPrvE9DTKS2r3jYSJWlN/r46h0/DBUxKrkgt2jAZ5Nj4wXLAcV1KWkRpcFqA5zri9SWzJZ1cCg== +jest-environment-jsdom@^30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-30.2.0.tgz#e95e0921ed22be974f1d8a324766d12b1844cb2c" + integrity sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ== dependencies: - "@jest/environment" "30.0.5" - "@jest/environment-jsdom-abstract" "30.0.5" + "@jest/environment" "30.2.0" + "@jest/environment-jsdom-abstract" "30.2.0" "@types/jsdom" "^21.1.7" "@types/node" "*" jsdom "^26.1.0" -jest-environment-node@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.0.5.tgz#6a98dd80e0384ead67ed05643381395f6cda93c9" - integrity sha512-ppYizXdLMSvciGsRsMEnv/5EFpvOdXBaXRBzFUDPWrsfmog4kYrOGWXarLllz6AXan6ZAA/kYokgDWuos1IKDA== +jest-environment-node@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.2.0.tgz#3def7980ebd2fd86e74efd4d2e681f55ab38da0f" + integrity sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA== dependencies: - "@jest/environment" "30.0.5" - "@jest/fake-timers" "30.0.5" - "@jest/types" "30.0.5" + "@jest/environment" "30.2.0" + "@jest/fake-timers" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - jest-mock "30.0.5" - jest-util "30.0.5" - jest-validate "30.0.5" + jest-mock "30.2.0" + jest-util "30.2.0" + jest-validate "30.2.0" jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.0.5.tgz#fdd0daa322b02eb34267854cff2859fae21e92a6" - integrity sha512-dkmlWNlsTSR0nH3nRfW5BKbqHefLZv0/6LCccG0xFCTWcJu8TuEwG+5Cm75iBfjVoockmO6J35o5gxtFSn5xeg== +jest-haste-map@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.2.0.tgz#808e3889f288603ac70ff0ac047598345a66022e" + integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== dependencies: - "@jest/types" "30.0.5" + "@jest/types" "30.2.0" "@types/node" "*" anymatch "^3.1.3" fb-watchman "^2.0.2" graceful-fs "^4.2.11" jest-regex-util "30.0.1" - jest-util "30.0.5" - jest-worker "30.0.5" + jest-util "30.2.0" + jest-worker "30.2.0" micromatch "^4.0.8" walker "^1.0.8" optionalDependencies: @@ -7442,23 +7440,23 @@ jest-haste-map@^29.7.0: optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.0.5.tgz#00cfd2b323f48d8f4416b0a3e05fcf4c51f18864" - integrity sha512-3Uxr5uP8jmHMcsOtYMRB/zf1gXN3yUIc+iPorhNETG54gErFIiUhLvyY/OggYpSMOEYqsmRxmuU4ZOoX5jpRFg== +jest-leak-detector@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz#292fdca7b7c9cf594e1e570ace140b01d8beb736" + integrity sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ== dependencies: - "@jest/get-type" "30.0.1" - pretty-format "30.0.5" + "@jest/get-type" "30.1.0" + pretty-format "30.2.0" -jest-matcher-utils@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz#dff3334be58faea4a5e1becc228656fbbfc2467d" - integrity sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ== +jest-matcher-utils@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz#69a0d4c271066559ec8b0d8174829adc3f23a783" + integrity sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg== dependencies: - "@jest/get-type" "30.0.1" + "@jest/get-type" "30.1.0" chalk "^4.1.2" - jest-diff "30.0.5" - pretty-format "30.0.5" + jest-diff "30.2.0" + pretty-format "30.2.0" jest-matcher-utils@^29.7.0: version "29.7.0" @@ -7470,18 +7468,18 @@ jest-matcher-utils@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-message-util@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.0.5.tgz#dd12ffec91dd3fa6a59cbd538a513d8e239e070c" - integrity sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA== +jest-message-util@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.2.0.tgz#fc97bf90d11f118b31e6131e2b67fc4f39f92152" + integrity sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw== dependencies: "@babel/code-frame" "^7.27.1" - "@jest/types" "30.0.5" + "@jest/types" "30.2.0" "@types/stack-utils" "^2.0.3" chalk "^4.1.2" graceful-fs "^4.2.11" micromatch "^4.0.8" - pretty-format "30.0.5" + pretty-format "30.2.0" slash "^3.0.0" stack-utils "^2.0.6" @@ -7500,14 +7498,14 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.0.5.tgz#ef437e89212560dd395198115550085038570bdd" - integrity sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ== +jest-mock@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.2.0.tgz#69f991614eeb4060189459d3584f710845bff45e" + integrity sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw== dependencies: - "@jest/types" "30.0.5" + "@jest/types" "30.2.0" "@types/node" "*" - jest-util "30.0.5" + jest-util "30.2.0" jest-pnp-resolver@^1.2.3: version "1.2.3" @@ -7524,117 +7522,117 @@ jest-regex-util@^29.6.3: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.5.tgz#53be4c51d296c84a0e75608e7b77b6fe92dbac29" - integrity sha512-/xMvBR4MpwkrHW4ikZIWRttBBRZgWK4d6xt3xW1iRDSKt4tXzYkMkyPfBnSCgv96cpkrctfXs6gexeqMYqdEpw== +jest-resolve-dependencies@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz#3370e2c0b49cc560f6a7e8ec3a59dd99525e1a55" + integrity sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w== dependencies: jest-regex-util "30.0.1" - jest-snapshot "30.0.5" + jest-snapshot "30.2.0" -jest-resolve@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.0.5.tgz#f52f91600070b7073db465dc553eee5471ea8e06" - integrity sha512-d+DjBQ1tIhdz91B79mywH5yYu76bZuE96sSbxj8MkjWVx5WNdt1deEFRONVL4UkKLSrAbMkdhb24XN691yDRHg== +jest-resolve@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.2.0.tgz#2e2009cbd61e8f1f003355d5ec87225412cebcd7" + integrity sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A== dependencies: chalk "^4.1.2" graceful-fs "^4.2.11" - jest-haste-map "30.0.5" + jest-haste-map "30.2.0" jest-pnp-resolver "^1.2.3" - jest-util "30.0.5" - jest-validate "30.0.5" + jest-util "30.2.0" + jest-validate "30.2.0" slash "^3.0.0" unrs-resolver "^1.7.11" -jest-runner@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.0.5.tgz#5cbaaf85964246da4f65d697f186846f23cd9b5a" - integrity sha512-JcCOucZmgp+YuGgLAXHNy7ualBx4wYSgJVWrYMRBnb79j9PD0Jxh0EHvR5Cx/r0Ce+ZBC4hCdz2AzFFLl9hCiw== - dependencies: - "@jest/console" "30.0.5" - "@jest/environment" "30.0.5" - "@jest/test-result" "30.0.5" - "@jest/transform" "30.0.5" - "@jest/types" "30.0.5" +jest-runner@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.2.0.tgz#c62b4c3130afa661789705e13a07bdbcec26a114" + integrity sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ== + dependencies: + "@jest/console" "30.2.0" + "@jest/environment" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" chalk "^4.1.2" emittery "^0.13.1" exit-x "^0.2.2" graceful-fs "^4.2.11" - jest-docblock "30.0.1" - jest-environment-node "30.0.5" - jest-haste-map "30.0.5" - jest-leak-detector "30.0.5" - jest-message-util "30.0.5" - jest-resolve "30.0.5" - jest-runtime "30.0.5" - jest-util "30.0.5" - jest-watcher "30.0.5" - jest-worker "30.0.5" + jest-docblock "30.2.0" + jest-environment-node "30.2.0" + jest-haste-map "30.2.0" + jest-leak-detector "30.2.0" + jest-message-util "30.2.0" + jest-resolve "30.2.0" + jest-runtime "30.2.0" + jest-util "30.2.0" + jest-watcher "30.2.0" + jest-worker "30.2.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.0.5.tgz#d6a7e22687264240d1786d6f7682ac6a2872e552" - integrity sha512-7oySNDkqpe4xpX5PPiJTe5vEa+Ak/NnNz2bGYZrA1ftG3RL3EFlHaUkA1Cjx+R8IhK0Vg43RML5mJedGTPNz3A== +jest-runtime@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.2.0.tgz#395ea792cde048db1b0cd1a92dc9cb9f1921bf8a" + integrity sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg== dependencies: - "@jest/environment" "30.0.5" - "@jest/fake-timers" "30.0.5" - "@jest/globals" "30.0.5" + "@jest/environment" "30.2.0" + "@jest/fake-timers" "30.2.0" + "@jest/globals" "30.2.0" "@jest/source-map" "30.0.1" - "@jest/test-result" "30.0.5" - "@jest/transform" "30.0.5" - "@jest/types" "30.0.5" + "@jest/test-result" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" chalk "^4.1.2" cjs-module-lexer "^2.1.0" collect-v8-coverage "^1.0.2" glob "^10.3.10" graceful-fs "^4.2.11" - jest-haste-map "30.0.5" - jest-message-util "30.0.5" - jest-mock "30.0.5" + jest-haste-map "30.2.0" + jest-message-util "30.2.0" + jest-mock "30.2.0" jest-regex-util "30.0.1" - jest-resolve "30.0.5" - jest-snapshot "30.0.5" - jest-util "30.0.5" + jest-resolve "30.2.0" + jest-snapshot "30.2.0" + jest-util "30.2.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-snapshot@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.0.5.tgz#6600716eef2e6d8ea1dd788ae4385f3a2791b11f" - integrity sha512-T00dWU/Ek3LqTp4+DcW6PraVxjk28WY5Ua/s+3zUKSERZSNyxTqhDXCWKG5p2HAJ+crVQ3WJ2P9YVHpj1tkW+g== +jest-snapshot@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.2.0.tgz#266fbbb4b95fc4665ce6f32f1f38eeb39f4e26d0" + integrity sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA== dependencies: "@babel/core" "^7.27.4" "@babel/generator" "^7.27.5" "@babel/plugin-syntax-jsx" "^7.27.1" "@babel/plugin-syntax-typescript" "^7.27.1" "@babel/types" "^7.27.3" - "@jest/expect-utils" "30.0.5" - "@jest/get-type" "30.0.1" - "@jest/snapshot-utils" "30.0.5" - "@jest/transform" "30.0.5" - "@jest/types" "30.0.5" - babel-preset-current-node-syntax "^1.1.0" + "@jest/expect-utils" "30.2.0" + "@jest/get-type" "30.1.0" + "@jest/snapshot-utils" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" + babel-preset-current-node-syntax "^1.2.0" chalk "^4.1.2" - expect "30.0.5" + expect "30.2.0" graceful-fs "^4.2.11" - jest-diff "30.0.5" - jest-matcher-utils "30.0.5" - jest-message-util "30.0.5" - jest-util "30.0.5" - pretty-format "30.0.5" + jest-diff "30.2.0" + jest-matcher-utils "30.2.0" + jest-message-util "30.2.0" + jest-util "30.2.0" + pretty-format "30.2.0" semver "^7.7.2" synckit "^0.11.8" -jest-util@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.0.5.tgz#035d380c660ad5f1748dff71c4105338e05f8669" - integrity sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g== +jest-util@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.2.0.tgz#5142adbcad6f4e53c2776c067a4db3c14f913705" + integrity sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA== dependencies: - "@jest/types" "30.0.5" + "@jest/types" "30.2.0" "@types/node" "*" chalk "^4.1.2" ci-info "^4.2.0" @@ -7653,40 +7651,40 @@ jest-util@^29.7.0: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.0.5.tgz#d26fd218b8d566bff48fd98880b8ea94fd0d8456" - integrity sha512-ouTm6VFHaS2boyl+k4u+Qip4TSH7Uld5tyD8psQ8abGgt2uYYB8VwVfAHWHjHc0NWmGGbwO5h0sCPOGHHevefw== +jest-validate@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.2.0.tgz#273eaaed4c0963b934b5b31e96289edda6e0a2ef" + integrity sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw== dependencies: - "@jest/get-type" "30.0.1" - "@jest/types" "30.0.5" + "@jest/get-type" "30.1.0" + "@jest/types" "30.2.0" camelcase "^6.3.0" chalk "^4.1.2" leven "^3.1.0" - pretty-format "30.0.5" + pretty-format "30.2.0" -jest-watcher@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.0.5.tgz#90db6e3f582b88085bde58f7555cbdd3a1beb10d" - integrity sha512-z9slj/0vOwBDBjN3L4z4ZYaA+pG56d6p3kTUhFRYGvXbXMWhXmb/FIxREZCD06DYUwDKKnj2T80+Pb71CQ0KEg== +jest-watcher@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.2.0.tgz#f9c055de48e18c979e7756a3917e596e2d69b07b" + integrity sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg== dependencies: - "@jest/test-result" "30.0.5" - "@jest/types" "30.0.5" + "@jest/test-result" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" ansi-escapes "^4.3.2" chalk "^4.1.2" emittery "^0.13.1" - jest-util "30.0.5" + jest-util "30.2.0" string-length "^4.0.2" -jest-worker@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.0.5.tgz#0b85cbab10610303e8d84e214f94d8f052c3cd04" - integrity sha512-ojRXsWzEP16NdUuBw/4H/zkZdHOa7MMYCk4E430l+8fELeLg/mqmMlRhjL7UNZvQrDmnovWZV4DxX03fZF48fQ== +jest-worker@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.2.0.tgz#fd5c2a36ff6058ec8f74366ec89538cc99539d26" + integrity sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g== dependencies: "@types/node" "*" "@ungap/structured-clone" "^1.3.0" - jest-util "30.0.5" + jest-util "30.2.0" merge-stream "^2.0.0" supports-color "^8.1.1" @@ -7709,15 +7707,15 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/jest/-/jest-30.0.5.tgz#ee62729fb77829790d67c660d852350fbde315ce" - integrity sha512-y2mfcJywuTUkvLm2Lp1/pFX8kTgMO5yyQGq/Sk/n2mN7XWYp4JsCZ/QXW34M8YScgk8bPZlREH04f6blPnoHnQ== +jest@^30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-30.2.0.tgz#9f0a71e734af968f26952b5ae4b724af82681630" + integrity sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A== dependencies: - "@jest/core" "30.0.5" - "@jest/types" "30.0.5" + "@jest/core" "30.2.0" + "@jest/types" "30.2.0" import-local "^3.2.0" - jest-cli "30.0.5" + jest-cli "30.2.0" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -9507,10 +9505,10 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -pretty-format@30.0.5: - version "30.0.5" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.0.5.tgz#e001649d472800396c1209684483e18a4d250360" - integrity sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw== +pretty-format@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.2.0.tgz#2d44fe6134529aed18506f6d11509d8a62775ebe" + integrity sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA== dependencies: "@jest/schemas" "30.0.5" ansi-styles "^5.2.0" From 2db3b63197358ec92e8f0fae0155aed676526f46 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Tue, 18 Nov 2025 16:05:19 +0100 Subject: [PATCH 56/70] improve changelog, add section about migration to new major version --- CHANGELOG.md | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c41a5f..a7e850f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p This is a major release, and it might be not compatible with your current usage of our library. Please read about the necessary changes in the section about how to migrate. +### Migration from v24 to v25 + +- remove deprecated components, properties and imports from your project, if the info cannot be found here then it was already mentioned in **Deprecated** sections of the past changelogs +- in case you set your own colors before importing GUI elements you need to update your configuration to the new color palette structure, see `README.md` +- change `intent="primary"` to `intent="accent"` for ` - - - ); -}; - -const InnerModal = () => { - return ; -}; - -const MiddleModal = () => { - return ( - - - - ); -}; - -/** Shows the current stack of open modals. */ -const TrackingContent = () => { - const modalContext = React.useContext(ModalContext); - - return ( -
      - {(modalContext.openModalStack() ?? []).map((modalId, idx) => ( -
    • - {idx + 1}. {modalId} -
    • - ))} -
    - ); -}; - -/** - * `ModalContext` can be used as provider to track a stack of modals. - * - * ```(Javascript) - * const ContextTemplate = () => { - * const { setModalOpen, openModalStack } = useModalContext(); - * return ( - * - * - * - * - * - * ); - * }; - * - * const OtherModal = () => { - * const modalContext = React.useContext(ModalContext); - * return ( - * - *
      - * {(modalContext.openModalStack ?? []).map((modalId, idx) => ( - *
    • - * {idx + 1}. {modalId} - *
    • - * ))} - *
    - *
    - * ); - * }; - * ``` - */ -export const NestedModalWithContext = ContextTemplate.bind({}); -NestedModalWithContext.args = { - children: [ - - - , - ], -}; diff --git a/src/components/Dialog/stories/ModalContext.stories.tsx b/src/components/Dialog/stories/ModalContext.stories.tsx new file mode 100644 index 00000000..5ef7a166 --- /dev/null +++ b/src/components/Dialog/stories/ModalContext.stories.tsx @@ -0,0 +1,154 @@ +import React from "react"; +import { Classes, OverlaysProvider } from "@blueprintjs/core"; +import { Meta } from "@storybook/react"; + +import { + Button, + Card, + CardContent, + Modal, + ModalContext, + ModalContextProps, + ModalSize, + Spacing, + useModalContext, +} from "./../../../../index"; + +/** + * `ModalContext` can be used as provider to track a stack of modals. + * + * ```(Javascript) + * import { ModalContext, SimpleDialog } from "@eccenca/gui-elements"; + * + * const ContextTemplate = () => { + * const { setModalOpen, openModalStack } = useModalContext(); + * return ( + * + * + * + * + * + * ); + * }; + * + * const OtherModal = () => { + * const modalContext = React.useContext(ModalContext); + * return ( + * + *
      + * {(modalContext.openModalStack ?? []).map((modalId, idx) => ( + *
    • + * {idx + 1}. {modalId} + *
    • + * ))} + *
    + *
    + * ); + * }; + * ``` + */ +export default { + title: "Components/Dialog/ModalContext", + decorators: [ + (Story) => ( + +
    + +
    +
    + ), + ], +} as Meta; + +export const Usage = () => { + const { setModalOpen, openModalStack } = useModalContext(); + + return ( + + + + + , + + ); +}; + +const ModalContent = ({ children }: React.HTMLAttributes) => { + return ( + + {children} + + ); +}; + +/** Component for nested modals. */ +const ExampleModal = ({ + id, + size, + children, +}: { + id?: string; + size: ModalSize; + children?: React.HTMLAttributes["children"]; +}) => { + const [isOpen, setIsOpen] = React.useState(true); + const [portalElement, setPortalElement] = React.useState(); + + React.useEffect(() => { + setPortalElement(document.getElementById("modalPortal")!); + }, []); + + return ( + { + // workaround, Blueprint attach a class to body tht prevents scrolling, probably it is attached to the wrong portal + document.body.classList.remove(Classes.OVERLAY_OPEN); + }} + > + + Modal with constant modal ID "{id}". + + + + {children} + + + + + ); +}; + +const InnerModal = () => { + return ; +}; + +const MiddleModal = () => { + return ( + + + + ); +}; + +/** Shows the current stack of open modals. */ +const TrackingContent = () => { + const modalContext = React.useContext(ModalContext); + + return ( +
      + {(modalContext.openModalStack() ?? []).map((modalId, idx) => ( +
    • + {idx + 1}. {modalId} +
    • + ))} +
    + ); +}; From 14eb3845eb6da37032d77006607b3fffc8469013 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Tue, 25 Nov 2025 15:23:42 +0100 Subject: [PATCH 65/70] remove wrong example string --- src/components/Dialog/stories/ModalContext.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Dialog/stories/ModalContext.stories.tsx b/src/components/Dialog/stories/ModalContext.stories.tsx index 5ef7a166..f5b90e7c 100644 --- a/src/components/Dialog/stories/ModalContext.stories.tsx +++ b/src/components/Dialog/stories/ModalContext.stories.tsx @@ -68,7 +68,6 @@ export const Usage = () => { - , ); }; From d6a63d3920567939778285675abb9ffe171a8593 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Wed, 26 Nov 2025 12:34:30 +0100 Subject: [PATCH 66/70] add library to en/decode HTML entities --- package.json | 2 ++ yarn.lock | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/package.json b/package.json index ab732b94..1a671be9 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "codemirror": "^6.0.1", "color": "^4.2.3", "compute-scroll-into-view": "^3.1.1", + "he": "^1.2.0", "jshint": "^2.13.6", "lodash": "^4.17.21", "n3": "^1.25.1", @@ -134,6 +135,7 @@ "@testing-library/react": "^12.1.5", "@types/codemirror": "^5.60.15", "@types/color": "^3.0.6", + "@types/he": "^1.2.3", "@types/jest": "^29.5.14", "@types/jshint": "^2.12.4", "@types/lodash": "^4.17.16", diff --git a/yarn.lock b/yarn.lock index 52af1ffc..022500e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3079,6 +3079,11 @@ dependencies: "@types/unist" "*" +"@types/he@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/he/-/he-1.2.3.tgz#c33ca3096f30cbd5d68d78211572de3f9adff75a" + integrity sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA== + "@types/hoist-non-react-statics@^3.3.0": version "3.3.6" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz#6bba74383cdab98e8db4e20ce5b4a6b98caed010" From 329461b63f9b1f9b3dc8cd3286ac8fb35a90cdc6 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Wed, 26 Nov 2025 12:44:40 +0100 Subject: [PATCH 67/70] add decode utility method and use it as an option for the reduceToText functionality --- CHANGELOG.md | 1 + src/common/index.ts | 8 +++-- src/common/utils/reduceToText.tsx | 29 +++++++++++++++++-- .../TextReducer/TextReducer.stories.tsx | 3 +- src/components/TextReducer/TextReducer.tsx | 18 +++++++++--- 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bccc72d..59ec733b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ This is a major release, and it might be not compatible with your current usage - `colorCalculateDistance()`: calculates the difference between 2 colors using the simple CIE76 formula - `textToColorHash()`: calculates a color from a text string - `reduceToText`: shrinks HTML content and React elements to plain text, used for `` + - `decodeHtmlEntities`: decode a string of HTML text, map HTML entities back to UTF-8 char - SCSS color functions - `eccgui-color-var`: returns a var of a custom property used for palette color - `eccgui-color-mix`: mix 2 colors in `srgb`, works with all types of color values and CSS custom properties diff --git a/src/common/index.ts b/src/common/index.ts index 161a07f9..ab989fa3 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,3 +1,5 @@ +import { decode } from "he"; + import { invisibleZeroWidthCharacters } from "./utils/characters"; import { colorCalculateDistance } from "./utils/colorCalculateDistance"; import decideContrastColorValue from "./utils/colorDecideContrastvalue"; @@ -6,7 +8,8 @@ import getColorConfiguration from "./utils/getColorConfiguration"; import { getScrollParent } from "./utils/getScrollParent"; import { getGlobalVar, setGlobalVar } from "./utils/globalVars"; import { openInNewTab } from "./utils/openInNewTab"; -import { reduceToText } from "./utils/reduceToText" +import { reduceToText } from "./utils/reduceToText"; +export type { DecodeOptions as DecodeHtmlEntitiesOptions } from "he"; export type { IntentTypes as IntentBaseTypes } from "./Intent"; export const utils = { @@ -20,5 +23,6 @@ export const utils = { getScrollParent, getEnabledColorsFromPalette, textToColorHash, - reduceToText + reduceToText, + decodeHtmlEntities: decode, }; diff --git a/src/common/utils/reduceToText.tsx b/src/common/utils/reduceToText.tsx index c8cf938b..740c2265 100644 --- a/src/common/utils/reduceToText.tsx +++ b/src/common/utils/reduceToText.tsx @@ -3,6 +3,7 @@ import { renderToString } from "react-dom/server"; import * as ReactIs from "react-is"; import { TextReducerProps } from "./../../components/TextReducer/TextReducer"; +import { DecodeHtmlEntitiesOptions, utils } from "./../"; export interface ReduceToTextFuncType { ( @@ -10,12 +11,12 @@ export interface ReduceToTextFuncType { * Component or text to reduce HTML markup content to plain text. */ input: React.ReactNode | React.ReactNode[] | string, - options?: Pick + options?: Pick ): string; } export const reduceToText: ReduceToTextFuncType = (input, options) => { - const { maxNodes, maxLength } = options || {}; + const { maxNodes, maxLength, decodeHtmlEntities } = options || {}; const content: React.ReactNode | React.ReactNode[] = input; let nodeCount = 0; @@ -46,6 +47,30 @@ export const reduceToText: ReduceToTextFuncType = (input, options) => { // Basic HTML cleanup text = text.replace(/<[^\s][^>]*>/g, "").replace(/\n/g, " "); + if (decodeHtmlEntities) { + const decodeDefaultOptions = { + isAttributeValue: true, + strict: true, + } as DecodeHtmlEntitiesOptions; + let decodeErrors = 0; + // we decode in pieces to some error tolerance even in strict mode + text = text + .split(" ") + .map((value) => { + try { + return utils.decodeHtmlEntities(value, { ...decodeDefaultOptions }); + } catch { + decodeErrors++; + return value; + } + }) + .join(" "); + if (decodeErrors > 0) { + // eslint-disable-next-line no-console + console.warn(`${decodeErrors} parse error(s) for decodeHtmlEntities, return un-decoded text`, text); + } + } + if (typeof maxLength === "number") { text = text.slice(0, maxLength); } diff --git a/src/components/TextReducer/TextReducer.stories.tsx b/src/components/TextReducer/TextReducer.stories.tsx index b8125046..48aa0468 100644 --- a/src/components/TextReducer/TextReducer.stories.tsx +++ b/src/components/TextReducer/TextReducer.stories.tsx @@ -18,8 +18,9 @@ Default.args = { , "Simple text with URL http://example.com/ that should not get parsed.", "a < b to test equations in text like b > a.", + `Something with a "quote" in it.`, <> - {`* This\n* is\n* a\n* list\n\nwritten in Markdown.`} + {`* This\n* is\n* a\n* list\n\nwritten in Markdown\n* containing a few HTML 'entities' & "quotes".`}

    Block with sub elements

    diff --git a/src/components/TextReducer/TextReducer.tsx b/src/components/TextReducer/TextReducer.tsx index 16a89ff2..544bdd4d 100644 --- a/src/components/TextReducer/TextReducer.tsx +++ b/src/components/TextReducer/TextReducer.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { reduceToText } from "../../common/utils/reduceToText"; +import { DecodeHtmlEntitiesOptions, utils } from "../../common"; import { CLASSPREFIX as eccgui } from "../../configuration/constants"; import { OverflowText, OverflowTextProps } from "./../Typography"; @@ -24,6 +24,17 @@ export interface TextReducerProps extends Pick * Specify more `OverflowText` properties used when `useOverflowTextWrapper` is set to `true`. */ overflowTextProps?: Omit; + /** + * If you transform HTML markup to text then the result could contain HTML entity encoded strings. + * By enabling this option they are decoded back to it's original char. + */ + decodeHtmlEntities?: boolean; + /** + * Set the options used to decode the html entities, if `decodeHtmlEntities` is enabled. + * Internally we use `he` library, see their [documentation on decode options](https://www.npmjs.com/package/he#hedecodehtml-options). + * If not used we use `{ isAttributeValue: true, strict: true }` as default value. + */ + decodeHtmlEntitiesOptions?: DecodeHtmlEntitiesOptions; } /** @@ -32,16 +43,15 @@ export interface TextReducerProps extends Pick */ export const TextReducer = ({ children, - maxNodes, - maxLength, useOverflowTextWrapper, overflowTextProps, + ...reduceToTextOptions }: TextReducerProps) => { if (typeof children === "undefined") { return <>; } - const shrinkedContent = reduceToText(children, { maxLength, maxNodes }); + const shrinkedContent = utils.reduceToText(children, reduceToTextOptions); return useOverflowTextWrapper ? ( Date: Wed, 26 Nov 2025 12:51:30 +0100 Subject: [PATCH 68/70] fix overwriting default options for decoding and add tests --- src/common/utils/reduceToText.tsx | 5 ++- .../TextReducer/TextReducer.test.tsx | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/components/TextReducer/TextReducer.test.tsx diff --git a/src/common/utils/reduceToText.tsx b/src/common/utils/reduceToText.tsx index 740c2265..92802f54 100644 --- a/src/common/utils/reduceToText.tsx +++ b/src/common/utils/reduceToText.tsx @@ -58,7 +58,10 @@ export const reduceToText: ReduceToTextFuncType = (input, options) => { .split(" ") .map((value) => { try { - return utils.decodeHtmlEntities(value, { ...decodeDefaultOptions }); + return utils.decodeHtmlEntities(value, { + ...decodeDefaultOptions, + ...options?.decodeHtmlEntitiesOptions, + }); } catch { decodeErrors++; return value; diff --git a/src/components/TextReducer/TextReducer.test.tsx b/src/components/TextReducer/TextReducer.test.tsx new file mode 100644 index 00000000..1aa4bdce --- /dev/null +++ b/src/components/TextReducer/TextReducer.test.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { render } from "@testing-library/react"; + +import "@testing-library/jest-dom"; + +import { Markdown, TextReducer } from "./../../"; +import { Default as TextReducerStory } from "./TextReducer.stories"; + +describe("TextReducer", () => { + it("should display encoded HTML entities by default if they are used in the transformed markup", () => { + const { queryByText } = render(); + expect(queryByText("'entities' & "quotes"", { exact: false })).not.toBeNull(); + expect(queryByText(`'entities' & "quotes"`, { exact: false })).toBeNull(); + }); + it("should not display encoded HTML entities if `decodeHtmlEntities` is enabled", () => { + const { queryByText } = render(); + expect(queryByText("'entities' & "quotes"", { exact: false })).toBeNull(); + expect(queryByText(`'entities' & "quotes"`, { exact: false })).not.toBeNull(); + }); + it("should only decode if correct encoded HTML entities are found (strict mode)", () => { + const { queryByText } = render( + + && foo&bar + + ); + expect(queryByText("& & foo&bar", { exact: false })).not.toBeNull(); + expect(queryByText("& & foo&bar", { exact: false })).toBeNull(); + }); + it("should allow decoding non-strict encoded HTML entities", () => { + const { queryByText } = render( + + && foo&bar + + ); + expect(queryByText("& & foo&bar", { exact: false })).toBeNull(); + expect(queryByText("& & foo&bar", { exact: false })).not.toBeNull(); + }); +}); From 4f9c67b77a1aa47402a38004d06a2b4894a47fb7 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Wed, 26 Nov 2025 13:01:23 +0100 Subject: [PATCH 69/70] fix some typos --- CHANGELOG.md | 2 +- src/common/utils/reduceToText.tsx | 2 +- src/components/TextReducer/TextReducer.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59ec733b..68b1dfa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,7 @@ This is a major release, and it might be not compatible with your current usage - `colorCalculateDistance()`: calculates the difference between 2 colors using the simple CIE76 formula - `textToColorHash()`: calculates a color from a text string - `reduceToText`: shrinks HTML content and React elements to plain text, used for `` - - `decodeHtmlEntities`: decode a string of HTML text, map HTML entities back to UTF-8 char + - `decodeHtmlEntities`: decode a string of HTML text, map HTML entities back to UTF-8 chars - SCSS color functions - `eccgui-color-var`: returns a var of a custom property used for palette color - `eccgui-color-mix`: mix 2 colors in `srgb`, works with all types of color values and CSS custom properties diff --git a/src/common/utils/reduceToText.tsx b/src/common/utils/reduceToText.tsx index 92802f54..d929ff41 100644 --- a/src/common/utils/reduceToText.tsx +++ b/src/common/utils/reduceToText.tsx @@ -53,7 +53,7 @@ export const reduceToText: ReduceToTextFuncType = (input, options) => { strict: true, } as DecodeHtmlEntitiesOptions; let decodeErrors = 0; - // we decode in pieces to some error tolerance even in strict mode + // we decode in pieces to apply some error tolerance even in strict mode text = text .split(" ") .map((value) => { diff --git a/src/components/TextReducer/TextReducer.tsx b/src/components/TextReducer/TextReducer.tsx index 544bdd4d..b73d2ee9 100644 --- a/src/components/TextReducer/TextReducer.tsx +++ b/src/components/TextReducer/TextReducer.tsx @@ -30,9 +30,9 @@ export interface TextReducerProps extends Pick */ decodeHtmlEntities?: boolean; /** - * Set the options used to decode the html entities, if `decodeHtmlEntities` is enabled. + * Set the options used to decode the HTML entities, if `decodeHtmlEntities` is enabled. * Internally we use `he` library, see their [documentation on decode options](https://www.npmjs.com/package/he#hedecodehtml-options). - * If not used we use `{ isAttributeValue: true, strict: true }` as default value. + * If not set we use `{ isAttributeValue: true, strict: true }` as default value. */ decodeHtmlEntitiesOptions?: DecodeHtmlEntitiesOptions; } From be337c359d66279483b5d9c97de26711acf181ff Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 27 Nov 2025 14:30:48 +0100 Subject: [PATCH 70/70] Clean up test --- .../TextReducer/TextReducer.test.tsx | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/TextReducer/TextReducer.test.tsx b/src/components/TextReducer/TextReducer.test.tsx index 1aa4bdce..691adf2d 100644 --- a/src/components/TextReducer/TextReducer.test.tsx +++ b/src/components/TextReducer/TextReducer.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render } from "@testing-library/react"; +import {render, RenderResult} from "@testing-library/react"; import "@testing-library/jest-dom"; @@ -7,15 +7,21 @@ import { Markdown, TextReducer } from "./../../"; import { Default as TextReducerStory } from "./TextReducer.stories"; describe("TextReducer", () => { + const textMustExist = (queryByText: RenderResult["queryByText"], text: string) => { + expect(queryByText(text, { exact: false })).not.toBeNull(); + } + const textMustNotExist = (queryByText: RenderResult["queryByText"], text: string) => { + expect(queryByText(text, { exact: false })).toBeNull(); + } it("should display encoded HTML entities by default if they are used in the transformed markup", () => { const { queryByText } = render(); - expect(queryByText("'entities' & "quotes"", { exact: false })).not.toBeNull(); - expect(queryByText(`'entities' & "quotes"`, { exact: false })).toBeNull(); + textMustExist(queryByText, "'entities' & "quotes""); + textMustNotExist(queryByText, `'entities' & "quotes"`); }); it("should not display encoded HTML entities if `decodeHtmlEntities` is enabled", () => { const { queryByText } = render(); - expect(queryByText("'entities' & "quotes"", { exact: false })).toBeNull(); - expect(queryByText(`'entities' & "quotes"`, { exact: false })).not.toBeNull(); + textMustNotExist(queryByText, "'entities' & "quotes""); + textMustExist(queryByText, `'entities' & "quotes"`); }); it("should only decode if correct encoded HTML entities are found (strict mode)", () => { const { queryByText } = render( @@ -23,8 +29,8 @@ describe("TextReducer", () => { && foo&bar ); - expect(queryByText("& & foo&bar", { exact: false })).not.toBeNull(); - expect(queryByText("& & foo&bar", { exact: false })).toBeNull(); + textMustExist(queryByText, "& & foo&bar"); + textMustNotExist(queryByText, "& & foo&bar"); }); it("should allow decoding non-strict encoded HTML entities", () => { const { queryByText } = render( @@ -32,7 +38,7 @@ describe("TextReducer", () => { && foo&bar ); - expect(queryByText("& & foo&bar", { exact: false })).toBeNull(); - expect(queryByText("& & foo&bar", { exact: false })).not.toBeNull(); + textMustNotExist(queryByText, "& & foo&bar"); + textMustExist(queryByText, "& & foo&bar"); }); });