Skip to content

Commit

Permalink
feat(textbox): surface maxWidth prop on textbox and related components
Browse files Browse the repository at this point in the history
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

fix #5494, fix #5234
  • Loading branch information
tomdavies73 committed Nov 18, 2022
1 parent 636b1f2 commit 5072a0b
Show file tree
Hide file tree
Showing 31 changed files with 617 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/components/date/date.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ const DateInput = ({

DateInput.propTypes = {
...Textbox.propTypes,
maxWidth: undefined,
...marginPropTypes,
/** Pass any props that match the [DayPickerProps](https://react-day-picker-v7.netlify.app/docs/getting-started/)
* interface to override default behaviors
Expand Down
1 change: 1 addition & 0 deletions src/components/date/date.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
29 changes: 28 additions & 1 deletion src/components/decimal/decimal.spec.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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 correctly as 100% as maxWidth has been left with no value", () => {
mount(<Decimal maxWidth="" />);
assertStyleMatch(
{
maxWidth: "100%",
},
wrapper.find(InputPresentation)
);
});
});
});
9 changes: 9 additions & 0 deletions src/components/decimal/decimal.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
5 changes: 5 additions & 0 deletions src/components/decimal/decimal.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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" };

Expand Down
27 changes: 27 additions & 0 deletions src/components/decimal/decimal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,33 @@ context("Tests for Decimal component", () => {
});
});

it.each([
["10%", "10%"],
["30%", "30%"],
["50%", "50%"],
["80%", "80%"],
["100%", "100%"],
])(
"should use %s as maxWidth and render it with correct corresponding max-width percentage",
(input, totalWidth) => {
CypressMountWithProviders(<Decimal maxWidth={input} />);

getDataElementByValue("input")
.parent()
.parent()
.should("have.css", "max-width", totalWidth);
}
);

it("as maxWidth has no value it should render as 100%", () => {
CypressMountWithProviders(<Decimal maxWidth="" />);

getDataElementByValue("input")
.parent()
.parent()
.should("have.css", "max-width", "100%");
});

it("can have a custom onChange handler", () => {
const CustomDecimalComponent = ({ onChange, value, ...props }) => {
const [state, setState] = React.useState("0.01");
Expand Down
39 changes: 38 additions & 1 deletion src/components/grouped-character/grouped-character.spec.tsx
Original file line number Diff line number Diff line change
@@ -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];
Expand Down Expand Up @@ -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 correctly as 100% as maxWidth has been left with no value", () => {
const wrapper = renderGroupedCharacter({ maxWidth: "" });

assertStyleMatch(
{
maxWidth: "100%",
},
wrapper.find(InputPresentation)
);
});
});
});
24 changes: 24 additions & 0 deletions src/components/grouped-character/grouped-character.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
27 changes: 27 additions & 0 deletions src/components/grouped-character/grouped-character.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,33 @@ context("Tests for GroupedCharacter component", () => {
);
});

it.each([
["10%", "10%"],
["30%", "30%"],
["50%", "50%"],
["80%", "80%"],
["100%", "100%"],
])(
"should use %s as maxWidth and render it with correct corresponding max-width percentage",
(input, totalWidth) => {
CypressMountWithProviders(<GroupedCharacterComponent maxWidth={input} />);

getDataElementByValue("input")
.parent()
.parent()
.should("have.css", "max-width", totalWidth);
}
);

it("as 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;

Expand Down
25 changes: 25 additions & 0 deletions src/components/number/number.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 correctly as 100% as maxWidth has been left with no value", () => {
wrapper = renderNumberInput({ maxWidth: "" });
assertStyleMatch(
{
maxWidth: "100%",
},
wrapper.find(InputPresentation)
);
});
});
});

function renderNumberInput(props, renderer = mount) {
Expand Down
12 changes: 12 additions & 0 deletions src/components/number/number.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
27 changes: 27 additions & 0 deletions src/components/number/number.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,33 @@ context("Tests for Number component", () => {
);
});

it.each([
["10%", "10%"],
["30%", "30%"],
["50%", "50%"],
["80%", "80%"],
["100%", "100%"],
])(
"should use %s as maxWidth and render it with correct corresponding max-width percentage",
(input, totalWidth) => {
CypressMountWithProviders(<NumberInputComponent maxWidth={input} />);

getDataElementByValue("input")
.parent()
.parent()
.should("have.css", "max-width", totalWidth);
}
);

it("as 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;
Expand Down
7 changes: 7 additions & 0 deletions src/components/search/search.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -74,6 +79,7 @@ export const Search = ({
name,
threshold = 3,
searchWidth,
maxWidth,
searchButton,
placeholder,
variant = "default",
Expand Down Expand Up @@ -229,6 +235,7 @@ export const Search = ({
<StyledSearch
isFocused={isFocused}
searchWidth={searchWidth}
maxWidth={maxWidth}
searchIsActive={searchIsActive}
searchHasValue={!isControlled ? !!searchValue?.length : !!value?.length}
showSearchButton={searchButton}
Expand Down
Loading

0 comments on commit 5072a0b

Please sign in to comment.