Repository: Sage/carbon (ts)

Issue:
Impossible to change height of Filterable Select
### Desired behaviour

https://carbon.sage.com/?path=/docs/select-filterable--default-story#new-designs-validation
<img width="1165" alt="Zrzut ekranu 2022-06-23 o 13 52 55" src="https://user-images.githubusercontent.com/102224763/175292646-764ca426-4025-4c98-ab0b-fcd21b3c0374.png">




### Current behaviour

height is hardcoded (dropdown)

### Suggested Solution

add prop to change height

### CodeSandbox or Storybook URL

_No response_

### Anything else we should know?

_No response_

### Confidentiality

- [X] I confirm there is no confidential or commercially sensitive information included.

Pull request:
feat: add optional list height prop - FE-5313
### Proposed behaviour

Closes #5257
This PR adds support for `listMaxHeight` prop which allows to override the `max-height` css rule on the SelectList

### Checklist

<!-- Each PR should include the following -->

- [x] Commits follow our style guide
- [x] Related issues linked in commit messages if required
- [x] Screenshots are included in the PR if useful
- [x] All themes are supported if required
- [x] Unit tests added or updated if required
- [x] Cypress automation tests added or updated if required
- [x] Storybook added or updated if required
- [x] Translations added or updated (including creating or amending translation keys table in storybook) if required
- [x] Typescript `d.ts` file added or updated if required

#### QA

- [ ] Tested in CodeSandbox/storybook
- [ ] Add new Cypress test coverage if required
- [ ] Carbon implementation matches Design System/designs
- [ ] UI Tests GitHub check reviewed if required

### Testing instructions

<!-- How can a reviewer test this PR? -->

The following CodeSandbox is an example of the broken behaviour.
You can see the new behaviour by looking at the version in the comment by `codesandbox[bot]`.

<!-- Add CodeSandbox here -->


Patch:
diff --git a/src/components/select/filterable-select/filterable-select.component.js b/src/components/select/filterable-select/filterable-select.component.js
index 8fa467527..588ed4250 100644
--- a/src/components/select/filterable-select/filterable-select.component.js
+++ b/src/components/select/filterable-select/filterable-select.component.js
@@ -38,6 +38,7 @@ const FilterableSelect = React.forwardRef(
       noResultsMessage,
       disablePortal,
       listActionButton,
+      listMaxHeight,
       onListAction,
       isLoading,
       disabled,
@@ -506,6 +507,7 @@ const FilterableSelect = React.forwardRef(
         noResultsMessage={noResultsMessage}
         disablePortal={disablePortal}
         listActionButton={listActionButton}
+        listMaxHeight={listMaxHeight}
         onListAction={handleOnListAction}
         isLoading={isLoading}
         readOnly={readOnly}
@@ -590,6 +592,8 @@ FilterableSelect.propTypes = {
   onListScrollBottom: PropTypes.func,
   /** Overrides the default tooltip position */
   tooltipPosition: PropTypes.oneOf(["top", "bottom", "left", "right"]),
+  /** Maximum list height - defaults to 180 */
+  listMaxHeight: PropTypes.number,
   /** Placement of the select list in relation to the input element */
   listPlacement: PropTypes.oneOf([
     "auto",
diff --git a/src/components/select/filterable-select/filterable-select.d.ts b/src/components/select/filterable-select/filterable-select.d.ts
index 718c76d03..470180130 100644
--- a/src/components/select/filterable-select/filterable-select.d.ts
+++ b/src/components/select/filterable-select/filterable-select.d.ts
@@ -44,6 +44,8 @@ export interface FilterableSelectProps
   value?: string | Record<string, unknown>;
   /** Overrides the default tooltip position */
   tooltipPosition?: "top" | "bottom" | "left" | "right";
+  /** Maximum list height - defaults to 180 */
+  listMaxHeight?: number;
   /** Placement of the select list in relation to the input element */
   listPlacement?:
     | "auto"
diff --git a/src/components/select/filterable-select/filterable-select.spec.js b/src/components/select/filterable-select/filterable-select.spec.js
index 881d6dcd2..a2846dd3e 100644
--- a/src/components/select/filterable-select/filterable-select.spec.js
+++ b/src/components/select/filterable-select/filterable-select.spec.js
@@ -2,11 +2,15 @@ import React, { useRef } from "react";
 import { act } from "react-dom/test-utils";
 import { mount } from "enzyme";
 
-import { testStyledSystemMargin } from "../../../__spec_helper__/test-utils";
+import {
+  assertStyleMatch,
+  testStyledSystemMargin,
+} from "../../../__spec_helper__/test-utils";
 import FilterableSelect from "./filterable-select.component";
 import Textbox from "../../textbox";
 import Option from "../option/option.component";
 import SelectList from "../select-list/select-list.component";
+import { StyledSelectList } from "../select-list/select-list.style";
 import Button from "../../button";
 import Label from "../../../__internal__/label";
 import InputIconToggle from "../../../__internal__/input-icon-toggle";
@@ -45,6 +49,16 @@ describe("FilterableSelect", () => {
     expect(mockRef.current).toBe(wrapper.find("input").getDOMNode());
   });
 
+  describe("when listMaxHeight prop is provided", () => {
+    it("overrides default list max-height", () => {
+      mount(getSelect());
+      const wrapper = renderSelect({ listMaxHeight: 120, openOnFocus: true });
+
+      wrapper.find(Textbox).find('[type="dropdown"]').first().simulate("click");
+      assertStyleMatch({ maxHeight: "120px" }, wrapper.find(StyledSelectList));
+    });
+  });
+
   it("when text is passed in placeholder prop, input element in textbox uses it as placeholder text", () => {
     const placeholder = "foobaz";
     const wrapper = renderSelect({ placeholder });
diff --git a/src/components/select/filterable-select/filterable-select.stories.mdx b/src/components/select/filterable-select/filterable-select.stories.mdx
index e8308032e..749d003e8 100644
--- a/src/components/select/filterable-select/filterable-select.stories.mdx
+++ b/src/components/select/filterable-select/filterable-select.stories.mdx
@@ -100,6 +100,33 @@ You can use `listPlacement` prop to set the position of the select list relative
   </Story>
 </Canvas>
 
+### List height
+
+You can use `listMaxHeight` prop to override default max height value of select list.
+
+<Canvas>
+  <Story name="list height">
+    <FilterableSelect
+      listMaxHeight={2000}
+      name="list height"
+      id="list-height"
+      label="List height"
+    >
+      <Option text="Amber" value="1" />
+      <Option text="Black" value="2" />
+      <Option text="Blue" value="3" />
+      <Option text="Brown" value="4" />
+      <Option text="Green" value="5" />
+      <Option text="Orange" value="6" />
+      <Option text="Pink" value="7" />
+      <Option text="Purple" value="8" />
+      <Option text="Red" value="9" />
+      <Option text="White" value="10" />
+      <Option text="Yellow" value="11" />
+    </FilterableSelect>
+  </Story>
+</Canvas>
+
 ### Controlled Usage
 
 <Canvas>
diff --git a/src/components/select/multi-select/multi-select.component.js b/src/components/select/multi-select/multi-select.component.js
index 75cc8542b..6c4b6a8f1 100644
--- a/src/components/select/multi-select/multi-select.component.js
+++ b/src/components/select/multi-select/multi-select.component.js
@@ -60,6 +60,7 @@ const MultiSelect = React.forwardRef(
       "data-element": dataElement,
       "data-role": dataRole,
       listPlacement = "bottom-start",
+      listMaxHeight,
       flipEnabled = true,
       wrapPillText = true,
       ...textboxProps
@@ -514,6 +515,7 @@ const MultiSelect = React.forwardRef(
         tableHeader={tableHeader}
         multiColumn={multiColumn}
         listPlacement={listPlacement}
+        listMaxHeight={listMaxHeight}
         flipEnabled={flipEnabled}
         loaderDataRole="multi-select-list-loader"
         multiselectValues={actualValue}
@@ -599,6 +601,8 @@ MultiSelect.propTypes = {
   isLoading: PropTypes.bool,
   /** Overrides the default tooltip position */
   tooltipPosition: PropTypes.oneOf(["top", "bottom", "left", "right"]),
+  /** Maximum list height - defaults to 180 */
+  listMaxHeight: PropTypes.number,
   /** Placement of the select list in relation to the input element */
   listPlacement: PropTypes.oneOf([
     "auto",
diff --git a/src/components/select/multi-select/multi-select.d.ts b/src/components/select/multi-select/multi-select.d.ts
index d5a8f98a5..78cafc877 100644
--- a/src/components/select/multi-select/multi-select.d.ts
+++ b/src/components/select/multi-select/multi-select.d.ts
@@ -39,6 +39,8 @@ export interface MultiSelectProps
   value?: string[] | Record<string, unknown>[];
   /** Overrides the default tooltip position */
   tooltipPosition?: "top" | "bottom" | "left" | "right";
+  /** Maximum list height - defaults to 180 */
+  listMaxHeight?: number;
   /** Placement of the select list in relation to the input element */
   listPlacement?:
     | "auto"
diff --git a/src/components/select/multi-select/multi-select.spec.js b/src/components/select/multi-select/multi-select.spec.js
index 9eaea193d..d5e105eee 100644
--- a/src/components/select/multi-select/multi-select.spec.js
+++ b/src/components/select/multi-select/multi-select.spec.js
@@ -95,6 +95,16 @@ describe("MultiSelect", () => {
     expect(mockRef.current).toBe(wrapper.find("input").getDOMNode());
   });
 
+  describe("when listMaxHeight prop is provided", () => {
+    it("overrides default list max-height", () => {
+      mount(getSelect());
+      const wrapper = renderSelect({ listMaxHeight: 120, openOnFocus: true });
+
+      wrapper.find(Textbox).find('[type="dropdown"]').first().simulate("click");
+      assertStyleMatch({ maxHeight: "120px" }, wrapper.find(StyledSelectList));
+    });
+  });
+
   describe("disablePortal", () => {
     it("renders SelectList with a disablePortal prop assigned", () => {
       const wrapper = renderSelect({ disablePortal: true });
diff --git a/src/components/select/multi-select/multi-select.stories.mdx b/src/components/select/multi-select/multi-select.stories.mdx
index 633c4cd39..cc65fd4ec 100644
--- a/src/components/select/multi-select/multi-select.stories.mdx
+++ b/src/components/select/multi-select/multi-select.stories.mdx
@@ -98,6 +98,33 @@ You can use `listPlacement` prop to set the position of the select list relative
   </Story>
 </Canvas>
 
+### List height
+
+You can use `listMaxHeight` prop to override default max height value of select list.
+
+<Canvas>
+  <Story name="list height">
+    <MultiSelect
+      listMaxHeight={2000}
+      name="list height"
+      id="list-height"
+      label="List height"
+    >
+      <Option text="Amber" value="1" />
+      <Option text="Black" value="2" />
+      <Option text="Blue" value="3" />
+      <Option text="Brown" value="4" />
+      <Option text="Green" value="5" />
+      <Option text="Orange" value="6" />
+      <Option text="Pink" value="7" />
+      <Option text="Purple" value="8" />
+      <Option text="Red" value="9" />
+      <Option text="White" value="10" />
+      <Option text="Yellow" value="11" />
+    </MultiSelect>
+  </Story>
+</Canvas>
+
 ### Controlled Usage
 
 <Canvas>
diff --git a/src/components/select/select-list/select-list-container.style.js b/src/components/select/select-list/select-list-container.style.js
index e100dcbfb..1c889ae6f 100644
--- a/src/components/select/select-list/select-list-container.style.js
+++ b/src/components/select/select-list/select-list-container.style.js
@@ -7,7 +7,7 @@ const StyledSelectListContainer = styled.div`
   ${({ placement }) => placement === "top-start" && "bottom: 0"};
   min-width: 100%;
   max-width: 870px;
-  height: ${({ height }) => height};
+  height: 100%;
   overflow: hidden;
   animation: fadeIn 250ms ease-out;
 
diff --git a/src/components/select/select-list/select-list.component.js b/src/components/select/select-list/select-list.component.js
index 6c7ae4e78..a9d4176d6 100644
--- a/src/components/select/select-list/select-list.component.js
+++ b/src/components/select/select-list/select-list.component.js
@@ -48,6 +48,7 @@ const fixedPopoverModifiers = [
 const SelectList = React.forwardRef(
   (
     {
+      listMaxHeight = 180,
       listActionButton,
       id,
       labelId,
@@ -486,7 +487,6 @@ const SelectList = React.forwardRef(
           >
             <StyledSelectListContainer
               data-element="select-list-wrapper"
-              height={listHeight}
               placement={placement.current}
               {...listProps}
             >
@@ -500,6 +500,7 @@ const SelectList = React.forwardRef(
                 tabIndex="-1"
                 isLoading={isLoading}
                 multiColumn={multiColumn}
+                maxHeight={listMaxHeight}
               >
                 {selectListContent}
                 {isLoading && loader()}
@@ -540,6 +541,8 @@ SelectList.propTypes = {
   highlightedValue: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
   /** True for default text button or a Button Component to be rendered */
   listActionButton: PropTypes.oneOfType([PropTypes.bool, PropTypes.element]),
+  /** Maximum list height - defaults to 180 */
+  listMaxHeight: PropTypes.number,
   /** A callback for when the Action Button is triggered */
   onListAction: PropTypes.func,
   /** If true the loader animation is displayed below the last option */
diff --git a/src/components/select/select-list/select-list.spec.js b/src/components/select/select-list/select-list.spec.js
index ac5af8113..4b8f9d6c3 100644
--- a/src/components/select/select-list/select-list.spec.js
+++ b/src/components/select/select-list/select-list.spec.js
@@ -551,11 +551,6 @@ describe("SelectList", () => {
         })
         .update();
 
-      assertStyleMatch(
-        { height: "100px" },
-        wrapper.find(StyledSelectListContainer)
-      );
-
       assertStyleMatch(
         { height: "100px" },
         wrapper.find(StyledPopoverContainer)
@@ -663,7 +658,7 @@ describe("SelectList", () => {
           .update();
         assertStyleMatch(
           { height: "150px" },
-          wrapper.find(StyledSelectListContainer)
+          wrapper.find(StyledPopoverContainer)
         );
       });
     });
@@ -692,7 +687,7 @@ describe("SelectList", () => {
 
       assertStyleMatch(
         { height: "60px" },
-        wrapper.find(StyledSelectListContainer)
+        wrapper.find(StyledPopoverContainer)
       );
     });
   });
diff --git a/src/components/select/select-list/select-list.style.js b/src/components/select/select-list/select-list.style.js
index 15e2397e9..5b1069cd2 100644
--- a/src/components/select/select-list/select-list.style.js
+++ b/src/components/select/select-list/select-list.style.js
@@ -26,7 +26,7 @@ const StyledSelectList = styled.ul`
     align-items: flex-start;
     flex-direction: column;
     list-style-type: none;
-    max-height: ${({ maxHeight }) => `${maxHeight}`};
+    max-height: ${({ maxHeight }) => `${maxHeight}`}px;
     margin: 0;
     outline: none;
     overflow-x: hidden;
diff --git a/src/components/select/simple-select/simple-select.component.js b/src/components/select/simple-select/simple-select.component.js
index 5e31d3c8d..f5c8e31a6 100644
--- a/src/components/select/simple-select/simple-select.component.js
+++ b/src/components/select/simple-select/simple-select.component.js
@@ -43,6 +43,7 @@ const SimpleSelect = React.forwardRef(
       onBlur,
       disablePortal,
       isLoading,
+      listMaxHeight,
       onListScrollBottom,
       tableHeader,
       multiColumn,
@@ -391,6 +392,7 @@ const SimpleSelect = React.forwardRef(
         onSelectListClose={onSelectListClose}
         highlightedValue={selectedValue}
         disablePortal={disablePortal}
+        listMaxHeight={listMaxHeight}
         isLoading={isLoading}
         onListScrollBottom={onListScrollBottom}
         tableHeader={tableHeader}
@@ -467,6 +469,8 @@ SimpleSelect.propTypes = {
   onListScrollBottom: PropTypes.func,
   /** Overrides the default tooltip position */
   tooltipPosition: PropTypes.oneOf(["top", "bottom", "left", "right"]),
+  /** Maximum list height - defaults to 180 */
+  listMaxHeight: PropTypes.number,
   /** Placement of the select list in relation to the input element */
   listPlacement: PropTypes.oneOf([
     "auto",
diff --git a/src/components/select/simple-select/simple-select.d.ts b/src/components/select/simple-select/simple-select.d.ts
index f689fe073..085c6eda9 100644
--- a/src/components/select/simple-select/simple-select.d.ts
+++ b/src/components/select/simple-select/simple-select.d.ts
@@ -37,6 +37,8 @@ export interface SimpleSelectProps
   value?: string | Record<string, unknown>;
   /** Overrides the default tooltip position */
   tooltipPosition?: "top" | "bottom" | "left" | "right";
+  /** Maximum list height - defaults to 180 */
+  listMaxHeight?: number;
   /** Placement of the select list in relation to the input element */
   listPlacement?:
     | "auto"
diff --git a/src/components/select/simple-select/simple-select.spec.js b/src/components/select/simple-select/simple-select.spec.js
index 69046a0ee..2a327224e 100644
--- a/src/components/select/simple-select/simple-select.spec.js
+++ b/src/components/select/simple-select/simple-select.spec.js
@@ -10,6 +10,7 @@ import SimpleSelect from "./simple-select.component";
 import Textbox from "../../textbox";
 import Option from "../option/option.component";
 import SelectList from "../select-list/select-list.component";
+import { StyledSelectList } from "../select-list/select-list.style";
 import InputIconToggleStyle from "../../../__internal__/input-icon-toggle/input-icon-toggle.style";
 import InputPresentationStyle from "../../../__internal__/input/input-presentation.style";
 import Label from "../../../__internal__/label";
@@ -119,6 +120,16 @@ describe("SimpleSelect", () => {
     );
   });
 
+  describe("when listMaxHeight prop is provided", () => {
+    it("overrides default list max-height", () => {
+      mount(getSelect());
+      const wrapper = renderSelect({ listMaxHeight: 120, openOnFocus: true });
+
+      simulateSelectTextboxEvent(wrapper, "focus");
+      assertStyleMatch({ maxHeight: "120px" }, wrapper.find(StyledSelectList));
+    });
+  });
+
   describe("when the transparent prop is set to true", () => {
     let wrapper;
 
diff --git a/src/components/select/simple-select/simple-select.stories.mdx b/src/components/select/simple-select/simple-select.stories.mdx
index a80333b48..c132ab5aa 100644
--- a/src/components/select/simple-select/simple-select.stories.mdx
+++ b/src/components/select/simple-select/simple-select.stories.mdx
@@ -113,6 +113,33 @@ You can use `listPlacement` prop to set the position of the select list relative
   </Story>
 </Canvas>
 
+### List height
+
+You can use `listMaxHeight` prop to override default max height value of select list.
+
+<Canvas>
+  <Story name="list height">
+    <Select
+      listMaxHeight={2000}
+      name="list height"
+      id="list-height"
+      label="List height"
+    >
+      <Option text="Amber" value="1" />
+      <Option text="Black" value="2" />
+      <Option text="Blue" value="3" />
+      <Option text="Brown" value="4" />
+      <Option text="Green" value="5" />
+      <Option text="Orange" value="6" />
+      <Option text="Pink" value="7" />
+      <Option text="Purple" value="8" />
+      <Option text="Red" value="9" />
+      <Option text="White" value="10" />
+      <Option text="Yellow" value="11" />
+    </Select>
+  </Story>
+</Canvas>
+
 ### Controlled usage
 
 <Canvas>


Test patch:
diff --git a/src/components/select/filterable-select/filterable-select.test.js b/src/components/select/filterable-select/filterable-select.test.js
index c531557d2..3522062a2 100644
--- a/src/components/select/filterable-select/filterable-select.test.js
+++ b/src/components/select/filterable-select/filterable-select.test.js
@@ -1032,6 +1032,17 @@ context("Tests for Filterable Select component", () => {
       );
     });
 
+    it("should render option list with proper maxHeight value", () => {
+      const maxHeight = 200;
+      CypressMountWithProviders(
+        <FilterableSelectComponent listMaxHeight={maxHeight} />
+      );
+      dropdownButton().click();
+      selectList()
+        .should("have.css", "max-height", `${maxHeight}px`)
+        .and("be.visible");
+    });
+
     it.each([
       ["top", "0px", "0px", "0px", "0px"],
       ["bottom", "600px", "0px", "0px", "0px"],
diff --git a/src/components/select/simple-select/simple-select.test.js b/src/components/select/simple-select/simple-select.test.js
index b5a67ab6b..3d986e251 100644
--- a/src/components/select/simple-select/simple-select.test.js
+++ b/src/components/select/simple-select/simple-select.test.js
@@ -871,6 +871,17 @@ context("Tests for Simple Select component", () => {
       selectListOptionGroup("1").should("have.text", "Group one");
     });
 
+    it("should render option list with proper maxHeight value", () => {
+      const maxHeight = 200;
+      CypressMountWithProviders(
+        <SimpleSelectComponent listMaxHeight={maxHeight} />
+      );
+      selectText().click();
+      selectList()
+        .should("have.css", "max-height", `${maxHeight}px`)
+        .and("be.visible");
+    });
+
     it.each([
       ["top", "300px", "0px", "200px", "0px"],
       ["bottom", "0px", "0px", "0px", "0px"],