Repository: Sage/carbon (ts)

Issue:
Allow input label to exceed length of input box on single line
### Desired behaviour

Allow a text label to be longer than the text box, without the label wrapping over multiple lines.

### Current behaviour

When attempting to make an input a certain size width, the input label is wrapping over multiple lines if it is longer than the box. While this is sometimes unavoidable in certain situations, I would not expect this to happen when the design is not in a stressed state.

It is forcing me to make some inputs exceedingly and unacceptably wide. This breaks good user experience practice of appropriately sized text boxes, to match the users expectation of data required.

I believe this is only an issue when using inputs with the label above. There seems a prop can be used to give the desired outcome when using in line labels.

### Suggested Solution

Allow the input width to be set without the label wrapping. As the design shrinks, if the overall label does fit the width of the container, then I would expect this to naturally wrap.



### 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(textarea, textbox): surface maxWidth prop on components FE-5506
fix #5494, fix #5234
### Proposed behaviour

<!--
A clear and concise description of what changes this PR makes.

If applicable, add screenshots of a codesandbox to help explain your request. You can paste these directly into GitHub.

Please DO NOT share screenshots or the source code of your project.

You can create a codesandbox to show the behaviour before/after this pull request by forking this template https://codesandbox.io/s/carbon-quickstart-j5pb2

If you include a CodeSandbox link, the bot will fork it with the new built version of carbon.
If you have a commit that includes fixes #XXXX and issue #XXXX has a CodeSandbox link in the body, the bot will fork
it with the new built version of carbon.
-->

adds `maxWidth` prop to internal `InputPresentation`

surfaces the `maxWidth` prop on `Textarea` component to allow consumers to set max-width on the
input and to allow label and validation strings to exceed its width

![image](https://user-images.githubusercontent.com/114918852/202751014-9f23b345-c590-4e6d-a1db-b22eed8799d0.png)

surfaces the `maxWidth` prop on `Textbox`, `Decimal`, `GroupedCharacter`, `Number`, `Search`,
`MultiSelect`, `FilterableSelect` and `SimpleSelect` components to allow consumers to set max-width
on the input and to allow label and validation strings to exceed its width

![image](https://user-images.githubusercontent.com/114918852/202751134-69805348-ef61-4e47-b591-6ab90dcec870.png)

![image](https://user-images.githubusercontent.com/114918852/202751256-9c5ad0c6-59ca-4fcc-bfe3-1dcd853c600c.png)

![image](https://user-images.githubusercontent.com/114918852/202751358-58531f67-a985-46d9-be65-cb2ac5238f4f.png)

### Current behaviour

<!--
A clear and concise description of the behaviour before this change.

If applicable, add screenshots. You can paste these directly into GitHub.
-->

No support for `maxWidth` on `Textarea` and all `Textbox` based components.

### 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] 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

### Additional context

<!-- Add any other context or links about the pull request here. -->

Has not been surfaced on `DateInput`, `DateRange` and `NumeralDate`, as they have static width.

### Testing instructions

<!-- How can a reviewer test this PR? -->

Test sandbox from linked issues and added stories for all described inputs.

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/__internal__/input/__snapshots__/input-presentation.spec.tsx.snap b/src/__internal__/input/__snapshots__/input-presentation.spec.tsx.snap
index 695674852..c9886f80c 100644
--- a/src/__internal__/input/__snapshots__/input-presentation.spec.tsx.snap
+++ b/src/__internal__/input/__snapshots__/input-presentation.spec.tsx.snap
@@ -10,6 +10,7 @@ exports[`InputPresentation renders presentational div and context provider for i
   display: -ms-flexbox;
   display: flex;
   position: relative;
+  max-width: 100%;
 }
 
 .c1 {
diff --git a/src/__internal__/input/input-presentation.component.tsx b/src/__internal__/input/input-presentation.component.tsx
index b76841179..b9af5abad 100644
--- a/src/__internal__/input/input-presentation.component.tsx
+++ b/src/__internal__/input/input-presentation.component.tsx
@@ -16,6 +16,11 @@ export interface CommonInputPresentationProps extends ValidationProps {
   align?: string;
   /** The width of the input as a percentage */
   inputWidth?: number;
+  /**
+   * Prop for specifying the max-width of the input.
+   * Leaving the `maxWidth` prop with no value will default the width to '100%'
+   */
+  maxWidth?: string;
   /** If true, the component will be read-only */
   readOnly?: boolean;
   /** Size of an input */
@@ -33,6 +38,7 @@ const InputPresentation = ({
   children,
   positionedChildren,
   inputWidth,
+  maxWidth,
   align,
   disabled,
   readOnly,
@@ -63,7 +69,10 @@ const InputPresentation = ({
   };
 
   return (
-    <StyledInputPresentationContainer inputWidth={inputWidth}>
+    <StyledInputPresentationContainer
+      inputWidth={inputWidth}
+      maxWidth={maxWidth}
+    >
       {positionedChildren}
       <InputPresentationStyle
         hasFocus={hasFocus}
diff --git a/src/__internal__/input/input-presentation.spec.tsx b/src/__internal__/input/input-presentation.spec.tsx
index 393510d0a..10db0c2d9 100644
--- a/src/__internal__/input/input-presentation.spec.tsx
+++ b/src/__internal__/input/input-presentation.spec.tsx
@@ -115,6 +115,26 @@ describe("InputPresentation", () => {
       });
     });
 
+    describe("maxWidth", () => {
+      it("renders correctly with a custom maxWidth", () => {
+        assertStyleMatch(
+          {
+            maxWidth: "54%",
+          },
+          render({ maxWidth: "54%", children: "Children" })
+        );
+      });
+
+      it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+        assertStyleMatch(
+          {
+            maxWidth: "100%",
+          },
+          render({ maxWidth: "", children: "Children" })
+        );
+      });
+    });
+
     describe.each([
       ["error", "var(--colorsSemanticNegative500)"],
       ["warning", "var(--colorsSemanticCaution500)"],
diff --git a/src/__internal__/input/input-presentation.style.ts b/src/__internal__/input/input-presentation.style.ts
index 377aecdae..7685dfe98 100644
--- a/src/__internal__/input/input-presentation.style.ts
+++ b/src/__internal__/input/input-presentation.style.ts
@@ -6,11 +6,12 @@ import { InputContextProps } from "../input-behaviour";
 import { CarbonProviderProps } from "../../components/carbon-provider";
 
 export const StyledInputPresentationContainer = styled.div<
-  Pick<CommonInputPresentationProps, "inputWidth">
+  Pick<CommonInputPresentationProps, "inputWidth" | "maxWidth">
 >`
   flex: 0 0 ${({ inputWidth }) => inputWidth}%;
   display: flex;
   position: relative;
+  max-width: ${({ maxWidth }) => (maxWidth ? `${maxWidth}` : "100%")};
 `;
 
 function stylingForValidations({
diff --git a/src/components/date/date.stories.mdx b/src/components/date/date.stories.mdx
index 39527f730..f7376b643 100644
--- a/src/components/date/date.stories.mdx
+++ b/src/components/date/date.stories.mdx
@@ -631,6 +631,7 @@ Due to the `Textbox` component being used internally by the `Date` component the
 <ArgsTable
   of={Textbox}
   exclude={[
+    "maxWidth",
     "value",
     "formattedValue",
     "rawValue",
diff --git a/src/components/decimal/decimal.spec.tsx b/src/components/decimal/decimal.spec.tsx
index 82e16af59..44c01dede 100644
--- a/src/components/decimal/decimal.spec.tsx
+++ b/src/components/decimal/decimal.spec.tsx
@@ -1,9 +1,13 @@
 import React from "react";
 import ReactDOM from "react-dom";
 import { mount as enzymeMount, ReactWrapper } from "enzyme";
+import { InputPresentation } from "../../__internal__/input";
 
 import Decimal, { DecimalProps, CustomEvent } from "./decimal.component";
-import { testStyledSystemMargin } from "../../__spec_helper__/test-utils";
+import {
+  assertStyleMatch,
+  testStyledSystemMargin,
+} from "../../__spec_helper__/test-utils";
 import Textbox from "../textbox/textbox.component";
 import Label from "../../__internal__/label";
 import FormFieldStyle from "../../__internal__/form-field/form-field.style";
@@ -1457,4 +1461,27 @@ describe("Decimal", () => {
       expect(label.prop("isRequired")).toBe(true);
     });
   });
+
+  describe("when maxWidth is passed", () => {
+    it("should be passed to InputPresentation", () => {
+      mount(<Decimal maxWidth="67%" />);
+
+      assertStyleMatch(
+        {
+          maxWidth: "67%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+
+    it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+      mount(<Decimal maxWidth="" />);
+      assertStyleMatch(
+        {
+          maxWidth: "100%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+  });
 });
diff --git a/src/components/decimal/decimal.stories.mdx b/src/components/decimal/decimal.stories.mdx
index 9a9839aae..a70012939 100644
--- a/src/components/decimal/decimal.stories.mdx
+++ b/src/components/decimal/decimal.stories.mdx
@@ -93,6 +93,15 @@ import Decimal from "carbon-react/lib/components/decimal";
   />
 </Canvas>
 
+### With custom maxWidth
+
+<Canvas>
+  <Story
+    name="with custom maxWidth"
+    story={stories.WithCustomMaxWidth}
+  />
+</Canvas>
+
 ### With fieldHelp
 
 <Canvas>
diff --git a/src/components/decimal/decimal.stories.tsx b/src/components/decimal/decimal.stories.tsx
index 612fa848c..9d0c8812b 100644
--- a/src/components/decimal/decimal.stories.tsx
+++ b/src/components/decimal/decimal.stories.tsx
@@ -91,6 +91,11 @@ WithCustomLabelWidthAndInputWidth.args = {
   labelInline: true,
 };
 
+export const WithCustomMaxWidth = DefaultStory.bind({});
+WithCustomMaxWidth.args = {
+  maxWidth: "50%",
+};
+
 export const WithFieldHelp = DefaultStory.bind({});
 WithFieldHelp.args = { fieldHelp: "Help" };
 
diff --git a/src/components/grouped-character/grouped-character.spec.tsx b/src/components/grouped-character/grouped-character.spec.tsx
index 15d5f6ee1..54b873dfb 100644
--- a/src/components/grouped-character/grouped-character.spec.tsx
+++ b/src/components/grouped-character/grouped-character.spec.tsx
@@ -1,16 +1,29 @@
 import React from "react";
 import { HTMLAttributes, mount, ReactWrapper } from "enzyme";
 
-import { testStyledSystemMargin } from "../../__spec_helper__/test-utils";
 import GroupedCharacter, {
   GroupedCharacterProps,
 } from "./grouped-character.component";
+import {
+  assertStyleMatch,
+  testStyledSystemMargin,
+} from "../../__spec_helper__/test-utils";
 import FormFieldStyle from "../../__internal__/form-field/form-field.style";
 import Label from "../../__internal__/label";
+import { InputPresentation } from "../../__internal__/input";
 
 const mountComponent = (props: GroupedCharacterProps) =>
   mount(<GroupedCharacter {...props} />);
 
+function renderGroupedCharacter(
+  props: Partial<GroupedCharacterProps>,
+  renderer = mount
+) {
+  return renderer(
+    <GroupedCharacter groups={[2, 2, 3]} separator="-" {...props} />
+  );
+}
+
 describe("GroupedCharacter", () => {
   jest.useFakeTimers();
   const basicGroupConfig = [2, 2, 4];
@@ -245,4 +258,28 @@ describe("GroupedCharacter", () => {
       expect(label.prop("isRequired")).toBe(true);
     });
   });
+
+  describe("when maxWidth is passed", () => {
+    it("should be passed to InputPresentation", () => {
+      const wrapper = renderGroupedCharacter({ maxWidth: "67%" });
+
+      assertStyleMatch(
+        {
+          maxWidth: "67%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+
+    it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+      const wrapper = renderGroupedCharacter({ maxWidth: "" });
+
+      assertStyleMatch(
+        {
+          maxWidth: "100%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+  });
 });
diff --git a/src/components/grouped-character/grouped-character.stories.mdx b/src/components/grouped-character/grouped-character.stories.mdx
index 0d1e8e62f..bc16847f9 100644
--- a/src/components/grouped-character/grouped-character.stories.mdx
+++ b/src/components/grouped-character/grouped-character.stories.mdx
@@ -84,6 +84,30 @@ import GroupedCharacter from "carbon-react/lib/components/grouped-character";
   />
 </Canvas>
 
+### With custom maxWidth
+
+<Canvas>
+  <Story name="with custom maxWidth">
+    {() => {
+      const [state, setState] = useState("1231231");
+      const setValue = ({ target }) => {
+        setState(target.value.rawValue);
+      };
+      return (
+        <GroupedCharacter
+          label="GroupedCharacter"
+          value={state}
+          onChange={setValue}
+          groups={[2, 2, 3]}
+          separator="-"
+          maxWidth="50%"
+        />
+      );
+    }}
+  </Story>
+</Canvas>
+
+
 ### With fieldHelp
 
 <Canvas>
diff --git a/src/components/number/number.spec.js b/src/components/number/number.spec.js
index 52c7c4123..938d90b3b 100644
--- a/src/components/number/number.spec.js
+++ b/src/components/number/number.spec.js
@@ -3,6 +3,8 @@ import { mount } from "enzyme";
 import Number from "./number.component";
 import Textbox from "../textbox";
 import Label from "../../__internal__/label";
+import { assertStyleMatch } from "../../__spec_helper__/test-utils";
+import InputPresentation from "../../__internal__/input/input-presentation.component";
 
 describe("Number Input", () => {
   let wrapper, input, onChangeFn, onKeyDownFn;
@@ -127,6 +129,29 @@ describe("Number Input", () => {
       expect(label.prop("isRequired")).toBe(true);
     });
   });
+
+  describe("when maxWidth is passed", () => {
+    it("should be passed to InputPresentation", () => {
+      wrapper = renderNumberInput({ maxWidth: "67%" });
+
+      assertStyleMatch(
+        {
+          maxWidth: "67%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+
+    it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+      wrapper = renderNumberInput({ maxWidth: "" });
+      assertStyleMatch(
+        {
+          maxWidth: "100%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+  });
 });
 
 function renderNumberInput(props, renderer = mount) {
diff --git a/src/components/number/number.stories.mdx b/src/components/number/number.stories.mdx
index d2659cdb5..89fb4b134 100644
--- a/src/components/number/number.stories.mdx
+++ b/src/components/number/number.stories.mdx
@@ -124,6 +124,18 @@ import Number from "carbon-react/lib/components/number";
   </Story>
 </Canvas>
 
+### With custom maxWidth
+
+<Canvas>
+  <Story name="with custom maxWidth">
+    <Number
+      label="Number"
+      value="123456"
+      maxWidth="50%"
+    />
+  </Story>
+</Canvas>
+
 ### With fieldHelp
 
 <Canvas>
diff --git a/src/components/search/search.component.tsx b/src/components/search/search.component.tsx
index 706fcc2cb..6c28b8de2 100644
--- a/src/components/search/search.component.tsx
+++ b/src/components/search/search.component.tsx
@@ -50,6 +50,11 @@ export interface SearchProps extends ValidationProps, MarginProps {
    * Leaving the `searchWidth` prop with no value will default the width to '100%'
    */
   searchWidth?: string;
+  /**
+   * Prop for specifying the max-width of the input.
+   * Leaving the `maxWidth` prop with no value will default the width to '100%'
+   */
+  maxWidth?: string;
   /** Prop for active search threshold. This must be a positive number */
   threshold?: number;
   /** Prop for `controlled` use */
@@ -74,6 +79,7 @@ export const Search = ({
   name,
   threshold = 3,
   searchWidth,
+  maxWidth,
   searchButton,
   placeholder,
   variant = "default",
@@ -229,6 +235,7 @@ export const Search = ({
     <StyledSearch
       isFocused={isFocused}
       searchWidth={searchWidth}
+      maxWidth={maxWidth}
       searchIsActive={searchIsActive}
       searchHasValue={!isControlled ? !!searchValue?.length : !!value?.length}
       showSearchButton={searchButton}
diff --git a/src/components/search/search.spec.tsx b/src/components/search/search.spec.tsx
index bc5fd282b..c745d48ef 100644
--- a/src/components/search/search.spec.tsx
+++ b/src/components/search/search.spec.tsx
@@ -16,10 +16,6 @@ import Icon from "../icon";
 import TextBox from "../textbox";
 import { rootTagTest } from "../../__internal__/utils/helpers/tags/tags-specs";
 
-function renderSearch(props: SearchProps) {
-  return mount(<Search {...props} />);
-}
-
 describe("Search", () => {
   let wrapper: ReactWrapper;
   let onBlur: jest.Mock;
@@ -30,6 +26,10 @@ describe("Search", () => {
 
   testStyledSystemMargin((props) => <Search value="" {...props} />);
 
+  function renderSearch(props: SearchProps) {
+    return mount(<Search {...props} />);
+  }
+
   describe("styles", () => {
     it("matches the expected styles", () => {
       assertStyleMatch(
@@ -583,4 +583,24 @@ describe("Search", () => {
       expect(stopPropagationFn).not.toHaveBeenCalled();
     });
   });
+
+  describe("when maxWidth is passed", () => {
+    it("should be passed to InputPresentation", () => {
+      assertStyleMatch(
+        {
+          maxWidth: "67%",
+        },
+        renderSearch({ value: "search", maxWidth: "67%" })
+      );
+    });
+
+    it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+      assertStyleMatch(
+        {
+          maxWidth: "100%",
+        },
+        renderSearch({ value: "search", maxWidth: "" })
+      );
+    });
+  });
 });
diff --git a/src/components/search/search.stories.mdx b/src/components/search/search.stories.mdx
index 67d5d2341..1011f3d3f 100644
--- a/src/components/search/search.stories.mdx
+++ b/src/components/search/search.stories.mdx
@@ -141,6 +141,19 @@ In this example the `searchWidth` prop is 70%.
   </Story>
 </Canvas>
 
+### With custom maxWidth
+
+In this example the `maxWidth` prop is 50%.
+
+<Canvas>
+  <Story 
+    name="with custom maxWidth"
+    parameters={{ info: { disable: true } }}
+  >
+    <Search defaultValue="Here is some text" searchButton maxWidth="50%" />
+  </Story>
+</Canvas>
+
 ### Search with Alternative Styling
 
 In this example the `variant` prop is "dark" and `searchButton` is true.
diff --git a/src/components/search/search.style.ts b/src/components/search/search.style.ts
index e0399c0c0..588180626 100644
--- a/src/components/search/search.style.ts
+++ b/src/components/search/search.style.ts
@@ -14,6 +14,7 @@ interface StyledSearchProps {
   searchHasValue?: boolean;
   searchIsActive?: boolean;
   searchWidth?: string;
+  maxWidth?: string;
   showSearchButton?: boolean;
   variant?: string;
 }
@@ -22,6 +23,7 @@ const StyledSearch = styled.div<StyledSearchProps>`
   ${({
     isFocused,
     searchWidth,
+    maxWidth,
     searchIsActive,
     searchHasValue,
     showSearchButton,
@@ -41,6 +43,7 @@ const StyledSearch = styled.div<StyledSearchProps>`
     return css`
       ${margin}
       width: ${searchWidth ? `${searchWidth}` : "100%"};
+      max-width: ${maxWidth ? `${maxWidth}` : "100%"};
       padding-bottom: 2px;
       background-color: transparent;
       border-bottom: 2px solid ${variantColor};
diff --git a/src/components/select/filterable-select/filterable-select.spec.js b/src/components/select/filterable-select/filterable-select.spec.js
index 35aa1abf3..1fa50d572 100644
--- a/src/components/select/filterable-select/filterable-select.spec.js
+++ b/src/components/select/filterable-select/filterable-select.spec.js
@@ -15,6 +15,7 @@ import Button from "../../button";
 import Label from "../../../__internal__/label";
 import InputIconToggle from "../../../__internal__/input-icon-toggle";
 import guid from "../../../__internal__/utils/helpers/guid";
+import { InputPresentation } from "../../../__internal__/input";
 
 jest.mock("../../../__internal__/utils/helpers/guid");
 guid.mockImplementation(() => "guid-12345");
@@ -1046,6 +1047,30 @@ describe("coverage filler for else path", () => {
   wrapper.find("input").simulate("blur");
 });
 
+describe("when maxWidth is passed", () => {
+  it("should be passed to InputPresentation", () => {
+    const wrapper = renderSelect({ maxWidth: "67%" });
+
+    assertStyleMatch(
+      {
+        maxWidth: "67%",
+      },
+      wrapper.find(InputPresentation)
+    );
+  });
+
+  it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+    const wrapper = renderSelect({ maxWidth: "" });
+
+    assertStyleMatch(
+      {
+        maxWidth: "100%",
+      },
+      wrapper.find(InputPresentation)
+    );
+  });
+});
+
 function renderSelect(props = {}, renderer = mount) {
   return renderer(getSelect(props));
 }
diff --git a/src/components/select/filterable-select/filterable-select.stories.mdx b/src/components/select/filterable-select/filterable-select.stories.mdx
index 79d31d606..b42b39fe5 100644
--- a/src/components/select/filterable-select/filterable-select.stories.mdx
+++ b/src/components/select/filterable-select/filterable-select.stories.mdx
@@ -614,6 +614,41 @@ This prop will be called every time a user scrolls to the bottom of the list.
   </Story>
 </Canvas>
 
+### With custom maxWidth
+
+In this example the `maxWidth` prop is 50%.
+
+<Canvas>
+  <Story name="with custom maxWidth">
+    <FilterableSelect
+      name="simple"
+      id="simple"
+      label="color"
+      maxWidth="50%"
+      onOpen={partialAction("onOpen")}
+      onChange={partialAction("onChange")}
+      onClick={partialAction("onClick")}
+      onFilterChange={partialAction("onFilterChange")}
+      onFocus={partialAction("onFocus")}
+      onBlur={partialAction("onBlur")}
+      onKeyDown={partialAction("onKeyDown")}
+    >
+      <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>
+
+
 ### Required
 
 You can use the `required` prop to indicate if the field is mandatory.
diff --git a/src/components/select/multi-select/multi-select.spec.js b/src/components/select/multi-select/multi-select.spec.js
index b0e042f97..8d1218a15 100644
--- a/src/components/select/multi-select/multi-select.spec.js
+++ b/src/components/select/multi-select/multi-select.spec.js
@@ -14,6 +14,7 @@ import { StyledSelectList } from "../select-list/select-list.style";
 import Pill from "../../pill";
 import Label from "../../../__internal__/label";
 import InputPresentationStyle from "../../../__internal__/input/input-presentation.style";
+import { InputPresentation } from "../../../__internal__/input";
 
 describe("MultiSelect", () => {
   testStyledSystemMargin((props) => getSelect(props));
@@ -1006,6 +1007,30 @@ describe("coverage filler for else path", () => {
   wrapper.find("input").simulate("blur");
 });
 
+describe("when maxWidth is passed", () => {
+  it("should be passed to InputPresentation", () => {
+    const wrapper = renderSelect({ maxWidth: "67%" });
+
+    assertStyleMatch(
+      {
+        maxWidth: "67%",
+      },
+      wrapper.find(InputPresentation)
+    );
+  });
+
+  it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+    const wrapper = renderSelect({ maxWidth: "" });
+
+    assertStyleMatch(
+      {
+        maxWidth: "100%",
+      },
+      wrapper.find(InputPresentation)
+    );
+  });
+});
+
 function renderSelect(props = {}, renderer = mount) {
   return renderer(getSelect(props));
 }
diff --git a/src/components/select/multi-select/multi-select.stories.mdx b/src/components/select/multi-select/multi-select.stories.mdx
index 7f21a117a..644e943e3 100644
--- a/src/components/select/multi-select/multi-select.stories.mdx
+++ b/src/components/select/multi-select/multi-select.stories.mdx
@@ -520,6 +520,33 @@ See <LinkTo kind="Documentation/Colors" name="page" story="page">Colors</LinkTo>
   </Story>
 </Canvas>
 
+### With custom maxWidth
+
+In this example the `maxWidth` prop is 50%.
+
+<Canvas>
+  <Story name="with custom maxWidth">
+    <MultiSelect
+      name="simple"
+      id="simple"
+      maxWidth="50%"
+      label="color"
+    >
+      <Option text="Amber" value="1" borderColor="#FFBF00" fill />
+      <Option text="Black" value="2" borderColor="blackOpacity65" fill />
+      <Option text="Blue" value="3" borderColor="productBlue" />
+      <Option text="Brown" value="4" borderColor="brown" fill />
+      <Option text="Green" value="5" borderColor="productGreen" />
+      <Option text="Orange" value="6" borderColor="orange" />
+      <Option text="Pink" value="7" borderColor="pink" />
+      <Option text="Purple" value="8" borderColor="purple" />
+      <Option text="Red" value="9" borderColor="red" fill />
+      <Option text="White" value="10" borderColor="white" />
+      <Option text="Yellow" value="11" borderColor="yellow" fill />
+    </MultiSelect>
+  </Story>
+</Canvas>
+
 #### New designs validation
 
 <Canvas>
diff --git a/src/components/select/select-textbox/select-textbox.component.js b/src/components/select/select-textbox/select-textbox.component.js
index 9aff41f1b..b08a00ee4 100644
--- a/src/components/select/select-textbox/select-textbox.component.js
+++ b/src/components/select/select-textbox/select-textbox.component.js
@@ -199,6 +199,11 @@ const formInputPropTypes = {
   labelWidth: PropTypes.number,
   /** Width of an input in percentage. Works only when labelInline is true */
   inputWidth: PropTypes.number,
+  /**
+   * Prop for specifying the max-width of the input.
+   * Leaving the `maxWidth` prop with no value will default the width to '100%'
+   */
+  maxWidth: PropTypes.string,
   /**
    * @ignore
    * @private
diff --git a/src/components/select/simple-select/simple-select.spec.js b/src/components/select/simple-select/simple-select.spec.js
index 736ca0446..774ff8964 100644
--- a/src/components/select/simple-select/simple-select.spec.js
+++ b/src/components/select/simple-select/simple-select.spec.js
@@ -14,6 +14,7 @@ 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";
+import { InputPresentation } from "../../../__internal__/input";
 
 describe("SimpleSelect", () => {
   describe("when an HTML element is clicked when the SelectList is open", () => {
@@ -846,6 +847,30 @@ describe("coverage filler for else path", () => {
   simulateKeyDown(wrapper, "F1");
 });
 
+describe("when maxWidth is passed", () => {
+  it("should be passed to InputPresentation", () => {
+    const wrapper = renderSelect({ maxWidth: "67%" });
+
+    assertStyleMatch(
+      {
+        maxWidth: "67%",
+      },
+      wrapper.find(InputPresentation)
+    );
+  });
+
+  it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+    const wrapper = renderSelect({ maxWidth: "" });
+
+    assertStyleMatch(
+      {
+        maxWidth: "100%",
+      },
+      wrapper.find(InputPresentation)
+    );
+  });
+});
+
 function simulateKeyDown(container, key, options = {}) {
   const selectText = container.find("[data-element='select-text']").first();
 
diff --git a/src/components/select/simple-select/simple-select.stories.mdx b/src/components/select/simple-select/simple-select.stories.mdx
index 6fa8ad244..4c2d5ff71 100644
--- a/src/components/select/simple-select/simple-select.stories.mdx
+++ b/src/components/select/simple-select/simple-select.stories.mdx
@@ -280,6 +280,28 @@ If there is no `id` prop specified on an object, then the exact objects will be
   </Story>
 </Canvas>
 
+### With custom maxWidth
+
+In this example the `maxWidth` prop is 100%.
+
+<Canvas>
+  <Story name="With custom maxWidth">
+    <Select name="simple" id="simple" label="color" maxWidth="100%">
+      <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>
+
 ### With isLoading prop
 
 When `isLoading` prop is passed, a loader will be appended at the end of the Select List. That functionality could be used to load the options asynchronously.
diff --git a/src/components/textarea/textarea.component.tsx b/src/components/textarea/textarea.component.tsx
index 5b4cdbf15..9eb3973d7 100644
--- a/src/components/textarea/textarea.component.tsx
+++ b/src/components/textarea/textarea.component.tsx
@@ -69,6 +69,11 @@ export interface TextareaProps
   inputIcon?: IconType;
   /** Width of an input in percentage. Works only when labelInline is true */
   inputWidth?: number;
+  /**
+   * Prop for specifying the max width of the input.
+   * Leaving the `maxWidth` prop with no value will default the width to '100%'
+   */
+  maxWidth?: string;
   /** The content of the label for the input */
   label?: string;
   /** Inline label alignment */
@@ -140,6 +145,7 @@ export const Textarea = ({
   validationOnLabel = false,
   adaptiveLabelBreakpoint,
   inputWidth,
+  maxWidth,
   labelWidth = 30,
   tooltipPosition,
   value,
@@ -235,6 +241,7 @@ export const Textarea = ({
       inputWidth={
         typeof inputWidth === "number" ? inputWidth : 100 - labelWidth
       }
+      maxWidth={maxWidth}
       error={error}
       warning={warning}
       info={info}
diff --git a/src/components/textarea/textarea.spec.tsx b/src/components/textarea/textarea.spec.tsx
index 5176ef452..07952bbf8 100644
--- a/src/components/textarea/textarea.spec.tsx
+++ b/src/components/textarea/textarea.spec.tsx
@@ -375,6 +375,29 @@ describe("Textarea", () => {
       expect(wrapper.find(InputPresentation).props().inputWidth).toBe(55);
     });
   });
+
+  describe("when maxWidth is passed", () => {
+    it("should be passed to InputPresentation", () => {
+      wrapper = renderTextarea({ maxWidth: "67%" });
+
+      assertStyleMatch(
+        {
+          maxWidth: "67%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+
+    it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+      wrapper = renderTextarea({ maxWidth: "" });
+      assertStyleMatch(
+        {
+          maxWidth: "100%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+  });
 });
 
 describe("componentWillUnmount", () => {
diff --git a/src/components/textarea/textarea.stories.mdx b/src/components/textarea/textarea.stories.mdx
index 969124f2f..0c95a68ba 100644
--- a/src/components/textarea/textarea.stories.mdx
+++ b/src/components/textarea/textarea.stories.mdx
@@ -102,6 +102,12 @@ import Textarea from "carbon-react/lib/components/textarea";
   />
 </Canvas>
 
+### With custom maxWidth
+
+<Canvas>
+  <Story name="with maxWidth" story={stories.MaxWidthStory} />
+</Canvas>
+
 ### With fieldHelp
 
 <Canvas>
diff --git a/src/components/textarea/textarea.stories.tsx b/src/components/textarea/textarea.stories.tsx
index fee83da25..eee67943f 100644
--- a/src/components/textarea/textarea.stories.tsx
+++ b/src/components/textarea/textarea.stories.tsx
@@ -100,6 +100,10 @@ export const FieldHelpStory: ComponentStory<typeof Textarea> = () => {
   return <Textarea label="Textarea" fieldHelp="Help" />;
 };
 
+export const MaxWidthStory: ComponentStory<typeof Textarea> = () => {
+  return <Textarea label="Textarea" maxWidth="70%" />;
+};
+
 export const LabelHelpStory: ComponentStory<typeof Textarea> = () => {
   return <Textarea label="Textarea" labelHelp="Help" helpAriaLabel="Help" />;
 };
diff --git a/src/components/textbox/textbox.component.tsx b/src/components/textbox/textbox.component.tsx
index bee2a8c85..a164a1318 100644
--- a/src/components/textbox/textbox.component.tsx
+++ b/src/components/textbox/textbox.component.tsx
@@ -58,6 +58,11 @@ export interface CommonTextboxProps
   iconTabIndex?: number;
   /** The width of the input as a percentage */
   inputWidth?: number;
+  /**
+   * Prop for specifying the max width of the input.
+   * Leaving the `maxWidth` prop with no value will default the width to '100%'
+   */
+  maxWidth?: string;
   /** Additional child elements to display before the input */
   leftChildren?: React.ReactNode;
   /** Label content */
@@ -158,6 +163,7 @@ export const Textbox = ({
   validationOnLabel = false,
   labelWidth = 30,
   inputWidth,
+  maxWidth,
   prefix,
   adaptiveLabelBreakpoint,
   required,
@@ -220,6 +226,7 @@ export const Textbox = ({
       warning={warning}
       info={info}
       inputWidth={inputWidth || 100 - labelWidth}
+      maxWidth={maxWidth}
       positionedChildren={positionedChildren}
       hasIcon={hasIconInside}
     >
diff --git a/src/components/textbox/textbox.spec.tsx b/src/components/textbox/textbox.spec.tsx
index 2bedd0ba7..e6cfa7eec 100644
--- a/src/components/textbox/textbox.spec.tsx
+++ b/src/components/textbox/textbox.spec.tsx
@@ -349,6 +349,30 @@ describe("Textbox", () => {
     });
   });
 
+  describe("when maxWidth is passed", () => {
+    it("should be passed to InputPresentation", () => {
+      const wrapper = mount(<Textbox maxWidth="67%" />);
+
+      assertStyleMatch(
+        {
+          maxWidth: "67%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+
+    it("renders with maxWidth as 100% when no maxWidth is specified", () => {
+      const wrapper = mount(<Textbox maxWidth="" />);
+
+      assertStyleMatch(
+        {
+          maxWidth: "100%",
+        },
+        wrapper.find(InputPresentation)
+      );
+    });
+  });
+
   describe("new validations", () => {
     const renderWithNewValidations = ({
       error,
diff --git a/src/components/textbox/textbox.stories.mdx b/src/components/textbox/textbox.stories.mdx
index 1c54d87f0..6d05519ff 100644
--- a/src/components/textbox/textbox.stories.mdx
+++ b/src/components/textbox/textbox.stories.mdx
@@ -155,6 +155,18 @@ import Textbox from "carbon-react/lib/components/textbox";
   </Story>
 </Canvas>
 
+### With custom maxWidth
+
+<Canvas>
+  <Story name="with custom maxWidth">
+    <Textbox
+      label="Textbox"
+      value="Textbox"
+      maxWidth="50%"
+    />
+  </Story>
+</Canvas>
+
 ### With fieldHelp
 
 <Canvas>


Test patch:
diff --git a/src/components/decimal/decimal.test.js b/src/components/decimal/decimal.test.js
index ae7116aae..bdfdd7fed 100644
--- a/src/components/decimal/decimal.test.js
+++ b/src/components/decimal/decimal.test.js
@@ -159,6 +159,27 @@ context("Tests for Decimal component", () => {
         .should("not.have.value", inputValue)
         .and("have.attr", "readOnly");
     });
+
+    it.each(["10%", "30%", "50%", "80%", "100%"])(
+      "should check maxWidth as %s for Decimal component",
+      (maxWidth) => {
+        CypressMountWithProviders(<Decimal maxWidth={maxWidth} />);
+
+        getDataElementByValue("input")
+          .parent()
+          .parent()
+          .should("have.css", "max-width", maxWidth);
+      }
+    );
+
+    it("when maxWidth has no value it should render as 100%", () => {
+      CypressMountWithProviders(<Decimal maxWidth="" />);
+
+      getDataElementByValue("input")
+        .parent()
+        .parent()
+        .should("have.css", "max-width", "100%");
+    });
   });
 
   describe("check Decimal input", () => {
diff --git a/src/components/grouped-character/grouped-character.test.js b/src/components/grouped-character/grouped-character.test.js
index 8b0e8a765..ce2cbc408 100644
--- a/src/components/grouped-character/grouped-character.test.js
+++ b/src/components/grouped-character/grouped-character.test.js
@@ -181,6 +181,29 @@ context("Tests for GroupedCharacter component", () => {
     );
   });
 
+  it.each(["10%", "30%", "50%", "80%", "100%"])(
+    "should check maxWidth as %s for GroupedCharacter component",
+    (maxWidth) => {
+      CypressMountWithProviders(
+        <GroupedCharacterComponent maxWidth={maxWidth} />
+      );
+
+      getDataElementByValue("input")
+        .parent()
+        .parent()
+        .should("have.css", "max-width", maxWidth);
+    }
+  );
+
+  it("when maxWidth has no value it should render as 100%", () => {
+    CypressMountWithProviders(<GroupedCharacterComponent maxWidth="" />);
+
+    getDataElementByValue("input")
+      .parent()
+      .parent()
+      .should("have.css", "max-width", "100%");
+  });
+
   describe("check events for GroupedCharacter component", () => {
     let callback;
 
diff --git a/src/components/number/number.test.js b/src/components/number/number.test.js
index c621f6f78..40de9cf7b 100644
--- a/src/components/number/number.test.js
+++ b/src/components/number/number.test.js
@@ -244,6 +244,27 @@ context("Tests for Number component", () => {
     );
   });
 
+  it.each(["10%", "30%", "50%", "80%", "100%"])(
+    "should check maxWidth as %s for Number component",
+    (maxWidth) => {
+      CypressMountWithProviders(<NumberInputComponent maxWidth={maxWidth} />);
+
+      getDataElementByValue("input")
+        .parent()
+        .parent()
+        .should("have.css", "max-width", maxWidth);
+    }
+  );
+
+  it("when maxWidth has no value it should render as 100%", () => {
+    CypressMountWithProviders(<NumberInputComponent maxWidth="" />);
+
+    getDataElementByValue("input")
+      .parent()
+      .parent()
+      .should("have.css", "max-width", "100%");
+  });
+
   describe("check events for Number component", () => {
     const inputValue = "1";
     let callback;
diff --git a/src/components/search/search.test.js b/src/components/search/search.test.js
index 1bc32ef76..bcfff3d31 100644
--- a/src/components/search/search.test.js
+++ b/src/components/search/search.test.js
@@ -137,6 +137,38 @@ context("Test for Search component", () => {
       }
     );
 
+    it.each([
+      ["10%", "134px"],
+      ["34%", "458px"],
+      ["70%", "944px"],
+      ["100%", "1348px"],
+    ])(
+      "should render Search with maxWidth prop set to %s",
+      (widthInPercentage, widthVal) => {
+        CypressMountWithProviders(
+          <SearchComponent maxWidth={widthInPercentage} />
+        );
+
+        searchDefault().then(($el) => {
+          expect($el[0].getBoundingClientRect().width).to.be.within(
+            parseToIntElement(widthVal),
+            parseToIntElement(widthVal) + 2
+          );
+        });
+      }
+    );
+
+    it("when maxWidth has no value it should render as 100%", () => {
+      CypressMountWithProviders(<SearchComponent maxWidth="" />);
+
+      searchDefault().then(($el) => {
+        expect($el[0].getBoundingClientRect().width).to.be.within(
+          parseToIntElement("1348"),
+          parseToIntElement("1348") + 2
+        );
+      });
+    });
+
     it.each([
       ["default", "rgb(102, 132, 148)"],
       ["dark", "rgb(153, 173, 183)"],
diff --git a/src/components/select/filterable-select/filterable-select.test.js b/src/components/select/filterable-select/filterable-select.test.js
index 2a02d7e58..a7880c042 100644
--- a/src/components/select/filterable-select/filterable-select.test.js
+++ b/src/components/select/filterable-select/filterable-select.test.js
@@ -808,6 +808,29 @@ context("Tests for Filterable Select component", () => {
       }
     );
 
+    it.each(["10%", "30%", "50%", "80%", "100%"])(
+      "should check maxWidth as %s for FilterableSelect component",
+      (maxWidth) => {
+        CypressMountWithProviders(
+          <FilterableSelectComponent maxWidth={maxWidth} />
+        );
+
+        getDataElementByValue("input")
+          .parent()
+          .parent()
+          .should("have.css", "max-width", maxWidth);
+      }
+    );
+
+    it("when maxWidth has no value it should render as 100%", () => {
+      CypressMountWithProviders(<FilterableSelectComponent maxWidth="" />);
+
+      getDataElementByValue("input")
+        .parent()
+        .parent()
+        .should("have.css", "max-width", "100%");
+    });
+
     it("should not open the list with focus on Filterable Select input", () => {
       CypressMountWithProviders(<FilterableSelectComponent />);
 
diff --git a/src/components/select/multi-select/multi-select.test.js b/src/components/select/multi-select/multi-select.test.js
index 34bd3220f..7535afa39 100644
--- a/src/components/select/multi-select/multi-select.test.js
+++ b/src/components/select/multi-select/multi-select.test.js
@@ -700,6 +700,27 @@ context("Tests for Multi Select component", () => {
       }
     );
 
+    it.each(["10%", "30%", "50%", "80%", "100%"])(
+      "should check maxWidth as %s for MultiSelect component",
+      (maxWidth) => {
+        CypressMountWithProviders(<MultiSelectComponent maxWidth={maxWidth} />);
+
+        getDataElementByValue("input")
+          .parent()
+          .parent()
+          .should("have.css", "max-width", maxWidth);
+      }
+    );
+
+    it("when maxWidth has no value it should render as 100%", () => {
+      CypressMountWithProviders(<MultiSelectComponent maxWidth="" />);
+
+      getDataElementByValue("input")
+        .parent()
+        .parent()
+        .should("have.css", "max-width", "100%");
+    });
+
     it("should not open the list with focus on Multi Select input", () => {
       CypressMountWithProviders(<MultiSelectComponent />);
 
diff --git a/src/components/select/simple-select/simple-select.test.js b/src/components/select/simple-select/simple-select.test.js
index 66cd02589..e6975ac3c 100644
--- a/src/components/select/simple-select/simple-select.test.js
+++ b/src/components/select/simple-select/simple-select.test.js
@@ -671,6 +671,29 @@ context("Tests for Simple Select component", () => {
       }
     );
 
+    it.each(["10%", "30%", "50%", "80%", "100%"])(
+      "should check maxWidth as %s for SimpleSelect component",
+      (maxWidth) => {
+        CypressMountWithProviders(
+          <SimpleSelectComponent maxWidth={maxWidth} />
+        );
+
+        getDataElementByValue("input")
+          .parent()
+          .parent()
+          .should("have.css", "max-width", maxWidth);
+      }
+    );
+
+    it("when maxWidth has no value it should render as 100%", () => {
+      CypressMountWithProviders(<SimpleSelectComponent maxWidth="" />);
+
+      getDataElementByValue("input")
+        .parent()
+        .parent()
+        .should("have.css", "max-width", "100%");
+    });
+
     it("should open the list with mouse click on Select input", () => {
       CypressMountWithProviders(<SimpleSelectComponent />);
 
diff --git a/src/components/textarea/textarea-test.stories.tsx b/src/components/textarea/textarea-test.stories.tsx
index 7695ce0c7..a6971d19b 100644
--- a/src/components/textarea/textarea-test.stories.tsx
+++ b/src/components/textarea/textarea-test.stories.tsx
@@ -103,6 +103,11 @@ export default {
         type: "number",
       },
     },
+    maxWidth: {
+      control: {
+        type: "text",
+      },
+    },
   },
   args: {
     expandable: false,
diff --git a/src/components/textarea/textarea.test.js b/src/components/textarea/textarea.test.js
index 5e6481f85..4d22fc74f 100644
--- a/src/components/textarea/textarea.test.js
+++ b/src/components/textarea/textarea.test.js
@@ -199,6 +199,27 @@ context("Tests for Textarea component", () => {
       }
     );
 
+    it.each(["10%", "30%", "50%", "80%", "100%"])(
+      "should check maxWidth as %s for TextArea component",
+      (maxWidth) => {
+        CypressMountWithProviders(<TextareaComponent maxWidth={maxWidth} />);
+
+        getDataElementByValue("input")
+          .parent()
+          .parent()
+          .should("have.css", "max-width", maxWidth);
+      }
+    );
+
+    it("when maxWidth has no value it should render as 100%", () => {
+      CypressMountWithProviders(<TextareaComponent maxWidth="" />);
+
+      getDataElementByValue("input")
+        .parent()
+        .parent()
+        .should("have.css", "max-width", "100%");
+    });
+
     it.each([
       ["11", "11"],
       ["10", "10"],
diff --git a/src/components/textbox/textbox-test.stories.tsx b/src/components/textbox/textbox-test.stories.tsx
index 151097b1d..bf42de571 100644
--- a/src/components/textbox/textbox-test.stories.tsx
+++ b/src/components/textbox/textbox-test.stories.tsx
@@ -150,14 +150,14 @@ export const Default = (args: CommonTextboxArgs) => {
     setState(value);
   };
   return (
-    <div style={{ width: "296px" }}>
+    <div>
       <Textbox
         m={2}
         onClick={action("onClick")}
+        maxWidth="70%"
         iconOnClick={action("iconOnClick")}
         value={state}
         onChange={setValue}
-        {...getCommonTextboxArgsWithSpecialCaracters(args)}
       />
     </div>
   );
diff --git a/src/components/textbox/textbox.test.js b/src/components/textbox/textbox.test.js
index 8cc4b80ac..e05875659 100644
--- a/src/components/textbox/textbox.test.js
+++ b/src/components/textbox/textbox.test.js
@@ -206,6 +206,27 @@ context("Tests for Textbox component", () => {
       }
     );
 
+    it.each(["10%", "30%", "50%", "80%", "100%"])(
+      "should check maxWidth as %s for TextBox component",
+      (maxWidth) => {
+        CypressMountWithProviders(<TextboxComponent maxWidth={maxWidth} />);
+
+        getDataElementByValue("input")
+          .parent()
+          .parent()
+          .should("have.css", "max-width", maxWidth);
+      }
+    );
+
+    it("when maxWidth has no value it should render as 100%", () => {
+      CypressMountWithProviders(<TextboxComponent maxWidth="" />);
+
+      getDataElementByValue("input")
+        .parent()
+        .parent()
+        .should("have.css", "max-width", "100%");
+    });
+
     it("should render Textbox with required prop", () => {
       CypressMountWithProviders(<TextboxComponent required />);