diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68702ec7..6a1b437e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- `noTogglerContentSuffix`: Allows to add non-string elements at the end of the content if the full description is shown, i.e. no toggler is necessary. This allows to add non-string elements to both the full-view content and the pure string content.
- ``
- An optional custom search function property has been added, it defines how to filter elements.
+ - Added a prop `limitHeightOpened` to limit the height of the dropdown by automatically calculating the available height in vh.
- `` and ``
- helper components to create flex layouts for positioning sub elements
- stop misusing `Toolbar*` components to do that (anti pattern)
diff --git a/src/components/MultiSelect/MultiSelect.tsx b/src/components/MultiSelect/MultiSelect.tsx
index 3adfb6ac..091d83c9 100644
--- a/src/components/MultiSelect/MultiSelect.tsx
+++ b/src/components/MultiSelect/MultiSelect.tsx
@@ -119,6 +119,12 @@ interface MultiSelectCommonProps
* If not provided, values are filtered by their labels
*/
searchPredicate?: (item: T, query: string) => boolean;
+ /**
+ * Limits the height of the input target plus its dropdown menu when it is opened.
+ * Need to be a `number not greater than 100` (as `vh`, a unit describing a length relative to the viewport height) or `true` (equals 100).
+ * If not set than the dropdown menu cannot be larger that appr. the half of the available viewport hight.
+ */
+ limitHeightOpened?: boolean | number;
}
/** @deprecated (v25) use MultiSuggestFieldProps */
@@ -172,6 +178,7 @@ function MultiSelect({
"data-testid": dataTestid,
wrapperProps,
searchPredicate,
+ limitHeightOpened,
...otherMultiSelectProps
}: MultiSelectProps) {
// Options created by a user
@@ -184,6 +191,8 @@ function MultiSelect({
const [selectedItems, setSelectedItems] = React.useState(() =>
prePopulateWithItems ? [...items] : externalSelectedItems ? [...externalSelectedItems] : []
);
+ // Max height of the menu
+ const [calculatedMaxHeight, setCalculatedMaxHeight] = React.useState(null);
//currently focused element in popover list
const [focusedItem, setFocusedItem] = React.useState(null);
@@ -244,6 +253,29 @@ function MultiSelect({
setSelectedItems(externalSelectedItems);
}, [externalSelectedItems?.map((item) => itemId(item)).join("|")]);
+ React.useEffect(() => {
+ const calculateMaxHeight = () => {
+ if (inputRef.current) {
+ // Get the height of the input target
+ const inputTargetHeight = inputRef.current.getBoundingClientRect().height;
+ // Calculate the menu dropdown by using the limited height reduced by the target height
+ setCalculatedMaxHeight(`calc(${maxHeightToProcess}vh - ${inputTargetHeight}px)`);
+ }
+ };
+
+ const removeListener = () => {
+ window.removeEventListener("resize", calculateMaxHeight);
+ };
+
+ if (!limitHeightOpened || (typeof limitHeightOpened === "number" && limitHeightOpened > 100))
+ return removeListener;
+ const maxHeightToProcess = typeof limitHeightOpened === "number" ? limitHeightOpened : 100;
+
+ calculateMaxHeight();
+ window.addEventListener("resize", calculateMaxHeight);
+ return removeListener;
+ }, [limitHeightOpened, selectedItems]);
+
/**
* using the equality prop specified checks if an item has already been selected
* @param matcher
@@ -514,6 +546,11 @@ function MultiSelect({
{
"data-test-id": dataTestId ? dataTestId + "_drowpdown" : undefined,
"data-testid": dataTestid ? dataTestid + "_dropdown" : undefined,
+ style: calculatedMaxHeight
+ ? ({
+ "--eccgui-multisuggestfield-max-height": `${calculatedMaxHeight}`,
+ } as React.CSSProperties)
+ : undefined,
} as BlueprintMultiSelectProps["popoverContentProps"]
}
/>
diff --git a/src/components/MultiSuggestField/MultiSuggestField.stories.tsx b/src/components/MultiSuggestField/MultiSuggestField.stories.tsx
index 23fb1cb0..370d87b7 100644
--- a/src/components/MultiSuggestField/MultiSuggestField.stories.tsx
+++ b/src/components/MultiSuggestField/MultiSuggestField.stories.tsx
@@ -1,5 +1,6 @@
import React, { useCallback, useMemo, useState } from "react";
import { loremIpsum } from "react-lorem-ipsum";
+import { OverlaysProvider } from "@blueprintjs/core";
import { Meta, StoryFn } from "@storybook/react";
import { fn } from "@storybook/test";
@@ -7,7 +8,7 @@ import { MultiSuggestField, MultiSuggestFieldSelectionProps, SimpleDialog } from
const testLabels = loremIpsum({
p: 1,
- avgSentencesPerParagraph: 5,
+ avgSentencesPerParagraph: 50,
avgWordsPerSentence: 1,
startWithLoremIpsum: false,
random: false,
@@ -16,8 +17,8 @@ const testLabels = loremIpsum({
.split(".")
.map((item) => item.trim());
-const items = new Array(5).fill(undefined).map((_, id) => {
- const testLabel = testLabels[id];
+const items = new Array(50).fill(undefined).map((_, id) => {
+ const testLabel = `${testLabels[id]}${id + 1}`;
return { testLabel, testId: `${testLabel}-id` };
});
@@ -36,9 +37,9 @@ export default {
const Template: StoryFn = (args) => {
return (
-