diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b550f94..95e8968f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,9 +11,17 @@ 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
+- ``:
+ - `isValidNewOption` property: Checks if an input string is or can be turned into a valid new option.
+- ``
+ - `togglerSize`: replaces the deprecated `togglerLarge` property
### Fixed
@@ -21,9 +29,11 @@ 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
+- ``:
+ - border of the BlueprintJS `Tag` elements were fixed
### Changed
@@ -41,7 +51,9 @@ 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"`
+- ``
+ - `togglerLarge`: replaced by the more versatile `togglerSize` property
## [25.0.0] - 2025-12-01
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}
/>
);
}
diff --git a/src/components/Application/_header.scss b/src/components/Application/_header.scss
index 27d7fd49..0a13abb0 100644
--- a/src/components/Application/_header.scss
+++ b/src/components/Application/_header.scss
@@ -52,19 +52,22 @@ $shell-header-icon-03: $eccgui-color-applicationheader-text !default;
/// Item link
$shell-header-link: $blue-60 !default;
+/// Height
+$shell-header-height: mini-units(8) !default;
+
// load library sub component
@import "~@carbon/react/scss/components/ui-shell/header/index";
// tweak original layout
.#{$prefix}--header {
- height: mini-units(8);
+ height: $shell-header-height;
}
.#{$prefix}--header__action,
.#{$prefix}--header__action.#{$prefix}--btn--icon-only {
- width: mini-units(8);
- height: mini-units(8);
+ width: $shell-header-height;
+ height: $shell-header-height;
padding-block-start: 0;
background-color: transparent;
@@ -128,7 +131,7 @@ span.#{$prefix}--header__name {
}
.#{$prefix}--header__menu .#{$prefix}--header__menu-item {
- height: mini-units(8);
+ height: $shell-header-height;
}
// tweak original colors (as long as config does not work properly)
@@ -255,15 +258,15 @@ a.#{$prefix}--header__menu-item:focus > svg {
// adjust position of all other modal dialogs
.#{$ns}-dialog-container {
- top: mini-units(8);
- left: mini-units(8);
- width: calc(100% - #{mini-units(8)});
- min-height: calc(100% - #{mini-units(8)});
+ top: $shell-header-height;
+ left: $shell-header-height;
+ width: calc(100% - #{$shell-header-height});
+ min-height: calc(100% - #{$shell-header-height});
}
.#{$eccgui}-dialog__wrapper {
- max-width: calc(100vw - #{mini-units(8)} - #{2 * $eccgui-size-block-whitespace});
- max-height: calc(100vh - #{mini-units(8)} - #{2 * $eccgui-size-block-whitespace});
+ max-width: calc(100vw - #{$shell-header-height} - #{2 * $eccgui-size-block-whitespace});
+ max-height: calc(100vh - #{$shell-header-height} - #{2 * $eccgui-size-block-whitespace});
margin: 0;
}
}
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
index fcf25392..7efb0d02 100644
--- a/src/components/Button/Button.tsx
+++ b/src/components/Button/Button.tsx
@@ -104,13 +104,18 @@ export const Button = ({
const ButtonType = restProps.href ? BlueprintAnchorButton : BlueprintButton;
+ const iconSize = {
+ small: restProps["size"] === "small",
+ large: restProps["size"] === "large",
+ };
+
const button = (
: icon}
- rightIcon={typeof rightIcon === "string" ? : rightIcon}
+ icon={typeof icon === "string" ? : icon}
+ rightIcon={typeof rightIcon === "string" ? : rightIcon}
>
{children}
{badge && (
diff --git a/src/components/ContextOverlay/ContextMenu.tsx b/src/components/ContextOverlay/ContextMenu.tsx
index c51ca0b3..d0ec4bf3 100644
--- a/src/components/ContextOverlay/ContextMenu.tsx
+++ b/src/components/ContextOverlay/ContextMenu.tsx
@@ -2,7 +2,7 @@ import React, { ReactElement } from "react";
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
import { ValidIconName } from "../Icon/canonicalIconNames";
-import IconButton from "../Icon/IconButton";
+import { IconButton, IconButtonProps } from "../Icon/IconButton";
import { TestableComponent } from "../interfaces";
import Menu from "../Menu/Menu";
@@ -28,8 +28,13 @@ export interface ContextMenuProps extends TestableComponent {
* Text displayed as title or tooltip on toggler element.
*/
togglerText?: string;
+ /**
+ * Allow to de- and increase the size of the default toggler button.
+ */
+ togglerSize?: IconButtonProps["size"];
/**
* Toggler element is displayed larger than normal.
+ * @deprecated (v26) use `togglerSize="large" instead
*/
togglerLarge?: boolean;
/**
@@ -62,6 +67,7 @@ export const ContextMenu = ({
contextOverlayProps,
disabled,
togglerLarge = false,
+ togglerSize,
/* FIXME: The Tooltip component can interfere with the opened menu, since it is implemented via portal and may cover the menu,
so by default we use the title attribute instead of Tooltip. */
tooltipAsTitle = true,
@@ -76,7 +82,7 @@ export const ContextMenu = ({
tooltipAsTitle={tooltipAsTitle}
name={[togglerElement]}
text={togglerText}
- large={togglerLarge}
+ size={togglerLarge ? "large" : togglerSize}
disabled={!!disabled}
data-test-id={dataTestId ?? undefined}
data-testid={dataTestid ?? undefined}
diff --git a/src/components/Icon/IconButton.tsx b/src/components/Icon/IconButton.tsx
index 969ffde2..1be9f885 100644
--- a/src/components/Icon/IconButton.tsx
+++ b/src/components/Icon/IconButton.tsx
@@ -54,8 +54,8 @@ export const IconButton = ({
swapPlaceholderDelay: 10,
};
const iconProps = {
- small: restProps.small,
- large: restProps.large,
+ small: restProps.small || restProps["size"] === "small",
+ large: restProps.large || restProps["size"] === "large",
tooltipText: tooltipAsTitle ? undefined : text,
tooltipProps: tooltipProps
? {
diff --git a/src/components/MultiSelect/MultiSelect.tsx b/src/components/MultiSelect/MultiSelect.tsx
index 3766baf6..09dac3f8 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,
@@ -386,7 +389,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();
};
@@ -402,7 +407,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());
@@ -417,7 +426,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);
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 = {};
diff --git a/src/components/NotAvailable/NotAvailable.tsx b/src/components/NotAvailable/NotAvailable.tsx
new file mode 100644
index 00000000..47b14adb
--- /dev/null
+++ b/src/components/NotAvailable/NotAvailable.tsx
@@ -0,0 +1,70 @@
+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,
+ addIndicator: noTag,
+ };
+
+ return tooltip ? : naElement;
+};
+
+export default NotAvailable;
diff --git a/src/components/Tag/tag.scss b/src/components/Tag/tag.scss
index 8abfb2e1..43f05b95 100644
--- a/src/components/Tag/tag.scss
+++ b/src/components/Tag/tag.scss
@@ -30,8 +30,6 @@ $tag-round-adjustment: 0 !default;
@import "~@blueprintjs/core/src/components/tag/tag";
.#{$eccgui}-tag__item {
- --eccgui-tag-border-width: 1px;
-
flex-grow: 0;
flex-shrink: 0;
min-width: calc(#{$tag-height} - 2px);
@@ -141,6 +139,8 @@ $tag-round-adjustment: 0 !default;
}
.#{$ns}-tag {
+ --eccgui-tag-border-width: 1px;
+
border-style: solid;
border-width: var(--eccgui-tag-border-width);
diff --git a/src/components/Typography/typography.scss b/src/components/Typography/typography.scss
index 87d20c59..623e4797 100644
--- a/src/components/Typography/typography.scss
+++ b/src/components/Typography/typography.scss
@@ -63,12 +63,14 @@ mark {
line-height: $eccgui-size-typo-text-lineheight;
}
-.#{$eccgui}-typography__contentblock.#{$eccgui}-typography--small {
+.#{$eccgui}-typography__contentblock.#{$eccgui}-typography--small,
+.#{$eccgui}-typography--small {
font-size: $eccgui-size-typo-caption;
line-height: $eccgui-size-typo-caption-lineheight;
}
-.#{$eccgui}-typography__contentblock.#{$eccgui}-typography--large {
+.#{$eccgui}-typography__contentblock.#{$eccgui}-typography--large,
+.#{$eccgui}-typography--large {
font-size: $eccgui-size-typo-subtitle;
line-height: $eccgui-size-typo-subtitle-lineheight;
}
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";