Skip to content

Commit

Permalink
Merge pull request #6363 from Sage/FE-6241
Browse files Browse the repository at this point in the history
fix(textarea, textbox): ensure inputHint and labelHelp props do not render two seperate DOM elements
  • Loading branch information
tomdavies73 authored Oct 27, 2023
2 parents a54d91f + 0df5be9 commit 909d7d1
Show file tree
Hide file tree
Showing 17 changed files with 142 additions and 48 deletions.
6 changes: 6 additions & 0 deletions src/components/decimal/decimal.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ import Decimal from "carbon-react/lib/components/decimal";
<Story name="with fieldHelp" story={stories.WithFieldHelp} />
</Canvas>

### With inputHint

<Canvas>
<Story name="with inputHint" story={stories.WithInputHint} />
</Canvas>

### With labelHelp

<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 @@ -108,6 +108,11 @@ WithLabelHelp.args = {
helpAriaLabel: "Help",
};

export const WithInputHint = DefaultStory.bind({});
WithInputHint.args = {
inputHint: "Hint text (optional).",
};

export const Required = DefaultStory.bind({});
Required.args = { required: true };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ import GroupedCharacter from "carbon-react/lib/components/grouped-character";
<Story story={stories.FieldHelp} name="with fieldHelp" />
</Canvas>

### with inputHint

When the `inputHint` prop is passed, please use a full stop `.` at the end. This forces a pause
before any other announcements, this well help screen reader users understand the hint fully.

<Canvas>
<Story name="with inputHint" story={stories.InputHint} />
</Canvas>

### With labelHelp

<Canvas>
Expand Down
19 changes: 19 additions & 0 deletions src/components/grouped-character/grouped-character.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,25 @@ export const FieldHelp = () => {
);
};

export const InputHint = () => {
const [state, setState] = useState("1231231");

const setValue = ({ target }: CustomEvent) => {
setState(target.value.rawValue);
};

return (
<GroupedCharacter
label="GroupedCharacter"
value={state}
onChange={setValue}
groups={[2, 2, 3]}
separator="-"
inputHint="Hint text (optional)."
/>
);
};

export const LabelHelp = () => {
const [state, setState] = useState("1231231");

Expand Down
9 changes: 9 additions & 0 deletions src/components/number/number.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ import Number from "carbon-react/lib/components/number";
<Story name="with fieldHelp" story={stories.WithFieldHelp} />
</Canvas>

### with inputHint

When the `inputHint` prop is passed, please use a full stop `.` at the end. This forces a pause
before any other announcements, this well help screen reader users understand the hint fully.

<Canvas>
<Story name="with inputHint" story={stories.WithInputHint} />
</Canvas>

### With labelHelp

<Canvas>
Expand Down
4 changes: 4 additions & 0 deletions src/components/number/number.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ export const WithFieldHelp: ComponentStory<typeof Number> = () => (
<Number label="Number" value="123456" fieldHelp="Help" />
);

export const WithInputHint: ComponentStory<typeof Number> = () => (
<Number label="Number" value="123456" inputHint="Hint text (optional)." />
);

export const WithLabelHelp: ComponentStory<typeof Number> = () => (
<Number label="Number" value="123456" labelHelp="Help" helpAriaLabel="Help" />
);
3 changes: 3 additions & 0 deletions src/components/password/password.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ the input type will not be able to be changed to `text`. To be used primarily to

### with inputHint

When the `inputHint` prop is passed, please use a full stop `.` at the end. This forces a pause
before any other announcements, this well help screen reader users understand the hint fully.

<Canvas>
<Story name="with inputHint" story={stories.InputHint} />
</Canvas>
Expand Down
6 changes: 3 additions & 3 deletions src/components/password/password.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const InputHint = () => {

return (
<Password
inputHint="Please enter a password with at least 8 characters"
inputHint="Hint text (optional)."
label="Password"
value={state}
onChange={setValue}
Expand Down Expand Up @@ -402,7 +402,7 @@ export const NewDesignsValidation = () => {
label={`${size} - ${validationType}`}
value={state}
onChange={setValue}
labelHelp="Hint text (optional)"
inputHint="Hint text (optional)"
size={size}
{...{ [validationType]: "Message" }}
/>
Expand All @@ -411,7 +411,7 @@ export const NewDesignsValidation = () => {
label={`readOnly - ${size} - ${validationType}`}
value="Password"
size={size}
labelHelp="Hint text (optional)"
inputHint="Hint text (optional)."
readOnly
{...{ [validationType]: "Message" }}
/>
Expand Down
22 changes: 9 additions & 13 deletions src/components/textarea/textarea.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ import StyledTextarea, { MIN_HEIGHT } from "./textarea.style";
import { TooltipProvider } from "../../__internal__/tooltip-provider";
import useInputAccessibility from "../../hooks/__internal__/useInputAccessibility";
import { NewValidationContext } from "../carbon-provider/carbon-provider.component";
import {
ErrorBorder,
StyledHintText,
StyledInputHint,
} from "../textbox/textbox.style";
import { ErrorBorder, StyledHintText } from "../textbox/textbox.style";
import ValidationMessage from "../../__internal__/validation-message";
import Box from "../box";
import Logger from "../../__internal__/utils/logger";
Expand Down Expand Up @@ -84,7 +80,10 @@ export interface TextareaProps
label?: string;
/** Inline label alignment */
labelAlign?: "left" | "right";
/** Text applied to label help tooltip */
/** [Legacy] Text applied to label help tooltip. When opted into new design validations
* it will render as a hint above the input, unless an `inputHint`
* prop is also passed
*/
labelHelp?: React.ReactNode;
/** When true, label is placed in line an input */
labelInline?: boolean;
Expand Down Expand Up @@ -382,13 +381,10 @@ export const Textarea = React.forwardRef(
adaptiveLabelBreakpoint={adaptiveLabelBreakpoint}
validationRedesignOptIn={validationRedesignOptIn}
>
{inputHint ? (
<StyledInputHint id={inputHintId} data-element="input-hint">
{inputHint}
</StyledInputHint>
) : null}
{validationRedesignOptIn && labelHelp && (
<StyledHintText>{labelHelp}</StyledHintText>
{(inputHint || (labelHelp && validationRedesignOptIn)) && (
<StyledHintText id={inputHintId} data-element="input-hint">
{inputHint || labelHelp}
</StyledHintText>
)}
{validationRedesignOptIn ? (
<Box position="relative">
Expand Down
23 changes: 16 additions & 7 deletions src/components/textarea/textarea.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ import { StyledLabelContainer } from "../../__internal__/label/label.style";
import Tooltip from "../tooltip";
import StyledHelp from "../help/help.style";
import CarbonProvider from "../carbon-provider/carbon-provider.component";
import {
ErrorBorder,
StyledHintText,
StyledInputHint,
} from "../textbox/textbox.style";
import { ErrorBorder, StyledHintText } from "../textbox/textbox.style";
import StyledValidationMessage from "../../__internal__/validation-message/validation-message.style";
import StyledTextarea from "./textarea.style";
import Logger from "../../__internal__/utils/logger";
Expand Down Expand Up @@ -257,12 +253,12 @@ describe("Textarea", () => {
describe("and inputHint props are present", () => {
it("renders a character counter hint", () => {
wrapper = mount(<Textarea value="test string" inputHint="foo" />);
expect(wrapper.find(StyledInputHint).text()).toBe("foo");
expect(wrapper.find(StyledHintText).text()).toBe("foo");
});

it("assigns a character counter hint via guid", () => {
wrapper = mount(<Textarea value="test string" inputHint="bar" />);
expect(wrapper.find(StyledInputHint).prop("id")).toBe(mockedGuid);
expect(wrapper.find(StyledHintText).prop("id")).toBe(mockedGuid);
});

it("should render a valid 'aria-describedby' on input", () => {
Expand All @@ -271,6 +267,19 @@ describe("Textarea", () => {
mockedGuid
);
});

it("rendered text should be provided via inputHint, when labelHelp is also passed", () => {
wrapper = mount(
<CarbonProvider validationRedesignOptIn>
<Textarea
labelHelp="labelHelp"
inputHint="inputHint"
error="foo"
/>
</CarbonProvider>
);
expect(wrapper.find(StyledHintText).text()).toBe("inputHint");
});
});

describe("and fieldHelp props are present", () => {
Expand Down
9 changes: 9 additions & 0 deletions src/components/textarea/textarea.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ Include the formatted number count wherever makes sense for the language you're
<Story name="with fieldHelp" story={stories.FieldHelpStory} />
</Canvas>

### With inputHint

When the `inputHint` prop is passed, please use a full stop `.` at the end. This forces a pause
before any other announcements, this well help screen reader users understand the hint fully.

<Canvas>
<Story name="with inputHint" story={stories.InputHintStory} />
</Canvas>

### With labelHelp

<Canvas>
Expand Down
6 changes: 6 additions & 0 deletions src/components/textarea/textarea.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ export const MaxWidthStory: ComponentStory<typeof Textarea> = () => {
return <Textarea label="Textarea" maxWidth="70%" />;
};

export const InputHintStory: ComponentStory<typeof Textarea> = () => {
return <Textarea label="Textarea" inputHint="Hint text (optional)." />;
};

export const LabelHelpStory: ComponentStory<typeof Textarea> = () => {
return <Textarea label="Textarea" labelHelp="Help" helpAriaLabel="Help" />;
};
Expand Down Expand Up @@ -231,11 +235,13 @@ export const NewDesignValidationStory: ComponentStory<typeof Textarea> = () => {
>
<Textarea
label={`${validationType}`}
inputHint="Hint text (optional)."
{...{ [validationType]: "Message" }}
m={4}
/>
<Textarea
label={`readOnly - ${validationType}`}
inputHint="Hint text (optional)."
readOnly
{...{ [validationType]: "Message" }}
m={4}
Expand Down
18 changes: 9 additions & 9 deletions src/components/textbox/textbox.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import StyledPrefix from "./__internal__/prefix.style";
import { TooltipProvider } from "../../__internal__/tooltip-provider";
import useCharacterCount from "../../hooks/__internal__/useCharacterCount";
import useInputAccessibility from "../../hooks/__internal__/useInputAccessibility/useInputAccessibility";
import { ErrorBorder, StyledInputHint, StyledHintText } from "./textbox.style";
import { ErrorBorder, StyledHintText } from "./textbox.style";
import ValidationMessage from "../../__internal__/validation-message";
import { NewValidationContext } from "../carbon-provider/carbon-provider.component";
import NumeralDateContext from "../numeral-date/numeral-date-context";
Expand Down Expand Up @@ -79,7 +79,10 @@ export interface CommonTextboxProps
label?: string;
/** Inline label alignment */
labelAlign?: "left" | "right";
/** A message that the Help component will display */
/** [Legacy] Text applied to label help tooltip. When opted into new design validations
* it will render as a hint above the input, unless an `inputHint`
* prop is also passed
*/
labelHelp?: React.ReactNode;
/** When true label is inline */
labelInline?: boolean;
Expand Down Expand Up @@ -342,13 +345,10 @@ export const Textbox = React.forwardRef(
validationRedesignOptIn={validationRedesignOptIn}
{...filterStyledSystemMarginProps(props)}
>
{inputHint ? (
<StyledInputHint id={inputHintId} data-element="input-hint">
{inputHint}
</StyledInputHint>
) : null}
{validationRedesignOptIn && labelHelp && (
<StyledHintText>{labelHelp}</StyledHintText>
{(inputHint || (labelHelp && validationRedesignOptIn)) && (
<StyledHintText id={inputHintId} data-element="input-hint">
{inputHint || labelHelp}
</StyledHintText>
)}
{validationRedesignOptIn ? (
<Box position="relative">
Expand Down
15 changes: 12 additions & 3 deletions src/components/textbox/textbox.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
import Tooltip from "../tooltip";
import StyledHelp from "../help/help.style";
import createGuid from "../../__internal__/utils/helpers/guid";
import { ErrorBorder, StyledHintText, StyledInputHint } from "./textbox.style";
import { ErrorBorder, StyledHintText } from "./textbox.style";
import StyledValidationMessage from "../../__internal__/validation-message/validation-message.style";
import CarbonProvider from "../carbon-provider/carbon-provider.component";
import Logger from "../../__internal__/utils/logger";
Expand Down Expand Up @@ -403,12 +403,12 @@ describe("Textbox", () => {
describe("and inputHint props are present", () => {
it("renders a character counter hint", () => {
const wrapper = mount(<Textbox value="test string" inputHint="foo" />);
expect(wrapper.find(StyledInputHint).text()).toBe("foo");
expect(wrapper.find(StyledHintText).text()).toBe("foo");
});

it("assigns a character counter hint via guid", () => {
const wrapper = mount(<Textbox value="test string" inputHint="bar" />);
expect(wrapper.find(StyledInputHint).prop("id")).toBe(mockedGuid);
expect(wrapper.find(StyledHintText).prop("id")).toBe(mockedGuid);
});

it("should render a valid 'aria-describedby' on input", () => {
Expand All @@ -417,6 +417,15 @@ describe("Textbox", () => {
mockedGuid
);
});

it("rendered text should be provided via inputHint, when labelHelp is also passed", () => {
const wrapper = mount(
<CarbonProvider validationRedesignOptIn>
<Textbox labelHelp="labelHelp" inputHint="inputHint" error="foo" />
</CarbonProvider>
);
expect(wrapper.find(StyledHintText).text()).toBe("inputHint");
});
});

describe("when maxWidth is passed", () => {
Expand Down
9 changes: 9 additions & 0 deletions src/components/textbox/textbox.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ Include the formatted number count wherever makes sense for the language you're
<Story name="with fieldHelp" story={stories.WithFieldHelp} />
</Canvas>

### With inputHint

When the `inputHint` prop is passed, please use a full stop `.` at the end. This forces a pause
before any other announcements, this well help screen reader users understand the hint fully.

<Canvas>
<Story name="with inputHint" story={stories.WithInputHint} />
</Canvas>

### With labelHelp

<Canvas>
Expand Down
14 changes: 12 additions & 2 deletions src/components/textbox/textbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ export const WithFieldHelp: ComponentStory<typeof Textbox> = () => {
return <Textbox label="Textbox" value="Textbox" fieldHelp="Help" />;
};

export const WithInputHint: ComponentStory<typeof Textbox> = () => {
return (
<Textbox
label="Textbox"
value="Textbox"
inputHint="Hint text (optional)."
/>
);
};

export const WithLabelHelp: ComponentStory<typeof Textbox> = () => {
return (
<Textbox
Expand Down Expand Up @@ -271,7 +281,7 @@ export const NewDesignsValidation: ComponentStory<typeof Textbox> = () => {
m={4}
label={`${size} - ${validationType}`}
defaultValue="Textbox"
labelHelp="Hint text (optional)"
inputHint="Hint text (optional)."
size={size}
{...{ [validationType]: "Message" }}
/>
Expand All @@ -280,7 +290,7 @@ export const NewDesignsValidation: ComponentStory<typeof Textbox> = () => {
label={`readOnly - ${size} - ${validationType}`}
defaultValue="Textbox"
size={size}
labelHelp="Hint text (optional)"
inputHint="Hint text (optional)."
readOnly
{...{ [validationType]: "Message" }}
/>
Expand Down
Loading

0 comments on commit 909d7d1

Please sign in to comment.