From 52bd09a1e2e52396511692bc6fb8e0b972742980 Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 29 Jan 2026 17:31:46 +0100 Subject: [PATCH 1/5] add NotAvailable component --- src/components/NotAvailable/NotAvailable.tsx | 69 ++++++++++++++++++++ src/components/index.ts | 1 + 2 files changed, 70 insertions(+) create mode 100644 src/components/NotAvailable/NotAvailable.tsx diff --git a/src/components/NotAvailable/NotAvailable.tsx b/src/components/NotAvailable/NotAvailable.tsx new file mode 100644 index 00000000..e5e405e5 --- /dev/null +++ b/src/components/NotAvailable/NotAvailable.tsx @@ -0,0 +1,69 @@ +import React from "react"; + +import { + CLASSPREFIX as eccgui, + Tag, + TagProps, + Tooltip, + TooltipProps, +} from "../../../index"; +import { TestableComponent } from "../interfaces"; +export interface NotAvailableProps extends TestableComponent, Pick, Pick { + /** + * Text displayed by the element. + */ + label?: TagProps["children"]; + /** + * Add a tooltip to the element. + * You need to set an empty string `""` to remove it. + */ + tooltip?: TooltipProps["content"]; + /** + * Specify the display of the used `Tag` component. + */ + tagProps?: Omit; + /** + * Specify the display of the used `Tooltip` component. + */ + tooltipProps?: Omit; + /** + * Do not use the `Tag` component for the display. + * The `intent` state can be displayed only on the tooltip then. + */ + noTag?: boolean; +} + +/** + * Simple placeholder element that can be used to display info about missing data. + */ +export const NotAvailable = ({ + label = "n/a", + tooltip = "not available", + icon, + intent, + tagProps, + tooltipProps, + className, + noTag = false, + ...otherProps +}: NotAvailableProps) => { + const defaultTagProps : TagProps = { icon, intent, emphasis: "weaker", className: `${eccgui}-notavailable` + className ? ` ${className}` : "" }; + const naElement = noTag === false ? ( + + { label ?? "n/a"} + + ) : <>{ label ?? "n/a"}; + const defaultTooltipProps : TooltipProps = { + children: naElement, + content: tooltip, + intent, + }; + + return tooltip ? : naElement; +}; + +export default NotAvailable; diff --git a/src/components/index.ts b/src/components/index.ts index 6f7fdf95..5e1c496d 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -23,6 +23,7 @@ export * from "./Link/Link"; export * from "./List/List"; export * from "./Menu"; export * from "./MultiSuggestField"; +export * from "./NotAvailable/NotAvailable"; export * from "./Notification"; export * from "./OverviewItem"; export * from "./Pagination/Pagination"; From b921c68529c4dbd4242b1bc51b278eec7cf94ffa Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Mon, 2 Feb 2026 10:32:54 +0100 Subject: [PATCH 2/5] add tooltip indicator if no tag is used --- src/components/NotAvailable/NotAvailable.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/NotAvailable/NotAvailable.tsx b/src/components/NotAvailable/NotAvailable.tsx index e5e405e5..47b14adb 100644 --- a/src/components/NotAvailable/NotAvailable.tsx +++ b/src/components/NotAvailable/NotAvailable.tsx @@ -61,6 +61,7 @@ export const NotAvailable = ({ children: naElement, content: tooltip, intent, + addIndicator: noTag, }; return tooltip ? : naElement; From 8764a22c290a5a9711e4c20ca7946c03d92798da Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Wed, 4 Feb 2026 15:01:01 +0100 Subject: [PATCH 3/5] provide an inline option for content blob togglers --- .../ContentBlobToggler/ContentBlobToggler.tsx | 15 +++++++++++---- .../StringPreviewContentBlobToggler.tsx | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/cmem/ContentBlobToggler/ContentBlobToggler.tsx b/src/cmem/ContentBlobToggler/ContentBlobToggler.tsx index 26b214ab..91b9a06c 100644 --- a/src/cmem/ContentBlobToggler/ContentBlobToggler.tsx +++ b/src/cmem/ContentBlobToggler/ContentBlobToggler.tsx @@ -1,6 +1,6 @@ -import React, { useState } from "react"; +import React, { useState} from "react"; -import { Link, Spacing } from "../../index"; +import { Link, Spacing, InlineText } from "../../index"; export interface ContentBlobTogglerProps extends React.HTMLAttributes { /** @@ -31,6 +31,10 @@ export interface ContentBlobTogglerProps extends React.HTMLAttributes {!isExtended ? ( <> @@ -76,7 +81,7 @@ export function ContentBlobToggler({ {fullviewContent} {enableToggler && (
- + {forceInline ? <>{" "} : } ); + + return forceInline ? {tooglerDisplay} : tooglerDisplay; } diff --git a/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx b/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx index e5ec7a79..08c19a43 100644 --- a/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx +++ b/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx @@ -54,6 +54,7 @@ export function StringPreviewContentBlobToggler({ allowedHtmlElementsInPreview, noTogglerContentSuffix, firstNonEmptyLineOnly, + ...otherContentBlobTogglerProps }: StringPreviewContentBlobTogglerProps) { // need to test `firstNonEmptyLineOnly` until property is removed const useOnlyTest: StringPreviewContentBlobTogglerProps["useOnly"] = firstNonEmptyLineOnly @@ -105,6 +106,7 @@ export function StringPreviewContentBlobToggler({ fullviewContent={fullviewContent} startExtended={startExtended} enableToggler={enableToggler} + {...otherContentBlobTogglerProps} /> ); } From 432f99d9d33271c637eeac99a431325fa4cc308e Mon Sep 17 00:00:00 2001 From: Michael Haschke Date: Thu, 5 Feb 2026 12:26:17 +0100 Subject: [PATCH 4/5] add story for NotAvailable and update changelog --- CHANGELOG.md | 12 ++++++++---- .../NotAvailable/NotAvailable.stories.tsx | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 src/components/NotAvailable/NotAvailable.stories.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b550f94..0d666913 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - `` - component for hiding elements in specific media - `` - - force children to get displayed as inline content + - force children to get displayed as inline content +- `` + - simple placeholder element that can be used to display info about missing data - `` - - `useOnly` property: specify if only parts of the content should be used for the shortened preview, this property replaces `firstNonEmptyLineOnly` + - `useOnly` property: specify if only parts of the content should be used for the shortened preview, this property replaces `firstNonEmptyLineOnly` +- `` + - `forceInline` property: force inline rendering ### Fixed @@ -21,7 +25,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - create more whitespace inside `small` tag - reduce visual impact of border - `` - - take Markdown rendering into account before testing the maximum preview length + - take Markdown rendering into account before testing the maximum preview length - `` - header-menu items are vertically centered now @@ -41,7 +45,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Deprecated - `` - - `firstNonEmptyLineOnly` will be removed, is replaced by `useOnly="firstNonEmptyLine"` + - `firstNonEmptyLineOnly` will be removed, is replaced by `useOnly="firstNonEmptyLine"` ## [25.0.0] - 2025-12-01 diff --git a/src/components/NotAvailable/NotAvailable.stories.tsx b/src/components/NotAvailable/NotAvailable.stories.tsx new file mode 100644 index 00000000..29dde1e3 --- /dev/null +++ b/src/components/NotAvailable/NotAvailable.stories.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import { Meta, StoryFn } from "@storybook/react"; + +import { NotAvailable } from "../../../index"; + +export default { + title: "Components/NotAvailable", + component: NotAvailable, + argTypes: {}, +} as Meta; + +const TemplateFull: StoryFn = (args) => ; + +export const Default = TemplateFull.bind({}); +Default.args = {}; From 60624bdd7b00851e9946a2d13ea25cb9cf4ea462 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Fri, 6 Feb 2026 15:29:55 +0100 Subject: [PATCH 5/5] Add isValidNewOption to MultiSuggestField --- CHANGELOG.md | 2 ++ src/components/MultiSelect/MultiSelect.tsx | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d666913..d8b3b5e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - `useOnly` property: specify if only parts of the content should be used for the shortened preview, this property replaces `firstNonEmptyLineOnly` - `` - `forceInline` property: force inline rendering +- ``: + - `isValidNewOption` property: Checks if an input string is or can be turned into a valid new option. ### Fixed diff --git a/src/components/MultiSelect/MultiSelect.tsx b/src/components/MultiSelect/MultiSelect.tsx index 3685ed86..b0e0bd5f 100644 --- a/src/components/MultiSelect/MultiSelect.tsx +++ b/src/components/MultiSelect/MultiSelect.tsx @@ -70,9 +70,11 @@ interface MultiSuggestFieldCommonProps */ newItemCreationText?: string; /** - * Allows to creates new item from a given query. If this is not provided then no new items can be created. + * Allows to create new item from a given query. If this is not provided then no new items can be created. */ createNewItemFromQuery?: (query: string) => T; + /** Validates if a new item can be created from the current query string. */ + isValidNewOption?: (query: string) => boolean; /** * Items that were newly created and not taken from the list will be post-fixed with this string. */ @@ -159,6 +161,7 @@ export function MultiSuggestField({ newItemPostfix = " (new item)", disabled, createNewItemFromQuery, + isValidNewOption, requestDelay = 0, clearQueryOnSelection = false, className, @@ -387,7 +390,9 @@ export function MultiSuggestField({ */ const handleOnKeyUp = (event: React.KeyboardEvent) => { if (event.key === "Enter" && !filteredItems.length && !!requestState.current.query && createNewItemFromQuery) { - createNewItem(requestState.current.query); + if(!isValidNewOption || isValidNewOption(requestState.current.query)) { + createNewItem(requestState.current.query); + } } inputRef.current?.focus(); }; @@ -403,7 +408,11 @@ export function MultiSuggestField({ if (focusedItem) { onItemSelect(focusedItem); } else { - onItemSelect(createNewItem(requestState.current.query)); + if (!isValidNewOption || isValidNewOption(requestState.current.query)) { + onItemSelect(createNewItem(requestState.current.query)); + } else { + return + } } requestState.current.query = ""; setTimeout(() => inputRef.current?.focus()); @@ -418,7 +427,7 @@ export function MultiSuggestField({ * @returns */ const newItemRenderer = (label: string, active: boolean, handleClick: React.MouseEventHandler) => { - if (!createNewItemFromQuery) return undefined; + if (!createNewItemFromQuery || isValidNewOption && !isValidNewOption(label)) return undefined; const clickHandler = (e: React.MouseEvent) => { createNewItem(label); handleClick(e);