Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- `<ApplicationViewability />`
- component for hiding elements in specific media
- `<InlineText />`
- force children to get displayed as inline content
- force children to get displayed as inline content
- `<NotAvailable />`
- simple placeholder element that can be used to display info about missing data
- `<StringPreviewContentBlobToggler />`
- `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`
- `<ContentBlobToggler />`
- `forceInline` property: force inline rendering
- `<MultiSuggestField />`:
- `isValidNewOption` property: Checks if an input string is or can be turned into a valid new option.

### Fixed

- `<Tag />`
- create more whitespace inside `small` tag
- reduce visual impact of border
- `<StringPreviewContentBlobToggler />`
- take Markdown rendering into account before testing the maximum preview length
- take Markdown rendering into account before testing the maximum preview length
- `<NodeContent />`
- header-menu items are vertically centered now

Expand All @@ -41,7 +47,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Deprecated

- `<StringPreviewContentBlobToggler />`
- `firstNonEmptyLineOnly` will be removed, is replaced by `useOnly="firstNonEmptyLine"`
- `firstNonEmptyLineOnly` will be removed, is replaced by `useOnly="firstNonEmptyLine"`

## [25.0.0] - 2025-12-01

Expand Down
15 changes: 11 additions & 4 deletions src/cmem/ContentBlobToggler/ContentBlobToggler.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement> {
/**
Expand Down Expand Up @@ -31,6 +31,10 @@ export interface ContentBlobTogglerProps extends React.HTMLAttributes<HTMLDivEle
Callback if toggler is necessary. Default: true
*/
enableToggler?: boolean;
/**
* Force always inline rendering.
*/
forceInline?: boolean;
}

/** Shows a preview with the option to expand to a full view (and back). */
Expand All @@ -42,6 +46,7 @@ export function ContentBlobToggler({
fullviewContent,
startExtended = false,
enableToggler = true,
forceInline = false,
...otherProps
}: ContentBlobTogglerProps) {
const [isExtended, setViewState] = useState(startExtended);
Expand All @@ -51,7 +56,7 @@ export function ContentBlobToggler({
setViewState(!isExtended);
};

return (
const tooglerDisplay = (
<div className={className} {...otherProps}>
{!isExtended ? (
<>
Expand All @@ -76,7 +81,7 @@ export function ContentBlobToggler({
{fullviewContent}
{enableToggler && (
<div>
<Spacing size="small" />
{forceInline ? <>{" "}</> : <Spacing size="small" />}
<Link
data-test-id={"content-blob-toggler-less-link"}
href="#less"
Expand All @@ -92,4 +97,6 @@ export function ContentBlobToggler({
)}
</div>
);

return forceInline ? <InlineText>{tooglerDisplay}</InlineText> : tooglerDisplay;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -105,6 +106,7 @@ export function StringPreviewContentBlobToggler({
fullviewContent={fullviewContent}
startExtended={startExtended}
enableToggler={enableToggler}
{...otherContentBlobTogglerProps}
/>
);
}
Expand Down
17 changes: 13 additions & 4 deletions src/components/MultiSelect/MultiSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ interface MultiSuggestFieldCommonProps<T>
*/
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.
*/
Expand Down Expand Up @@ -159,6 +161,7 @@ export function MultiSuggestField<T>({
newItemPostfix = " (new item)",
disabled,
createNewItemFromQuery,
isValidNewOption,
requestDelay = 0,
clearQueryOnSelection = false,
className,
Expand Down Expand Up @@ -387,7 +390,9 @@ export function MultiSuggestField<T>({
*/
const handleOnKeyUp = (event: React.KeyboardEvent<HTMLElement>) => {
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();
};
Expand All @@ -403,7 +408,11 @@ export function MultiSuggestField<T>({
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());
Expand All @@ -418,7 +427,7 @@ export function MultiSuggestField<T>({
* @returns
*/
const newItemRenderer = (label: string, active: boolean, handleClick: React.MouseEventHandler<HTMLElement>) => {
if (!createNewItemFromQuery) return undefined;
if (!createNewItemFromQuery || isValidNewOption && !isValidNewOption(label)) return undefined;
const clickHandler = (e: React.MouseEvent<HTMLElement>) => {
createNewItem(label);
handleClick(e);
Expand Down
15 changes: 15 additions & 0 deletions src/components/NotAvailable/NotAvailable.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof NotAvailable>;

const TemplateFull: StoryFn<typeof NotAvailable> = (args) => <NotAvailable {...args} />;

export const Default = TemplateFull.bind({});
Default.args = {};
70 changes: 70 additions & 0 deletions src/components/NotAvailable/NotAvailable.tsx
Original file line number Diff line number Diff line change
@@ -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<TagProps, "icon" | "className">, Pick<TooltipProps, "intent"> {
/**
* 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<TagProps, "icon" | "intent" | "children">;
/**
* Specify the display of the used `Tooltip` component.
*/
tooltipProps?: Omit<TooltipProps, "content" | "intent" | "children">;
/**
* 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 ? (
<Tag
{...defaultTagProps}
{...tagProps}
{...otherProps}
>
{ label ?? "n/a"}
</Tag>
) : <>{ label ?? "n/a"}</>;
const defaultTooltipProps : TooltipProps = {
children: naElement,
content: tooltip,
intent,
addIndicator: noTag,
};

return tooltip ? <Tooltip {...defaultTooltipProps} {...tooltipProps} /> : naElement;
};

export default NotAvailable;
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading