Skip to content

Commit

Permalink
chore: add aria-describedby
Browse files Browse the repository at this point in the history
  • Loading branch information
nuria1110 committed Nov 22, 2024
1 parent 7d5888b commit 3668281
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 28 deletions.
4 changes: 4 additions & 0 deletions src/__internal__/fieldset/fieldset.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export interface FieldsetProps extends MarginProps {
id?: string;
/** Content for the Help tooltip */
labelHelp?: React.ReactNode;
/** Id for the validation icon tooltip */
validationId?: string;
}

const Fieldset = ({
Expand All @@ -76,6 +78,7 @@ const Fieldset = ({
isDisabled,
isOptional,
labelHelp,
validationId,
...rest
}: FieldsetProps) => {
const { validationRedesignOptIn } = useContext(NewValidationContext);
Expand All @@ -102,6 +105,7 @@ const Fieldset = ({
warning={warning}
info={info}
tooltipFlipOverrides={["top", "bottom"]}
tooltipId={validationId}
/>
</StyledIconWrapper>
);
Expand Down
8 changes: 4 additions & 4 deletions src/components/date/date-test.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
CommonTextboxArgs,
commonTextboxArgTypes,
getCommonTextboxArgs,
getCommonTextboxArgsWithSpecialCaracters,
getCommonTextboxArgsWithSpecialCharacters,
} from "../textbox/textbox-test.stories";
import CarbonProvider from "../carbon-provider/carbon-provider.component";
import Box from "../box";
Expand Down Expand Up @@ -44,7 +44,7 @@ export const DateStory = (args: CommonTextboxArgs) => {
action("onKeyDown")((ev.target as HTMLInputElement).value)
}
onClick={(ev) => action("onClick")((ev.target as HTMLInputElement).value)}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
);
};
Expand Down Expand Up @@ -79,7 +79,7 @@ export const NewValidationStory = (args: CommonTextboxArgs) => {
onClick={(ev) =>
action("onClick")((ev.target as HTMLInputElement).value)
}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
</CarbonProvider>
);
Expand Down Expand Up @@ -191,7 +191,7 @@ export const I18NStory = (args: I18nArgs) => {
onClick={(ev) =>
action("onClick")((ev.target as HTMLInputElement).value)
}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
</I18nProvider>
</CarbonProvider>
Expand Down
12 changes: 6 additions & 6 deletions src/components/decimal/decimal-test.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
CommonTextboxArgs,
commonTextboxArgTypes,
getCommonTextboxArgs,
getCommonTextboxArgsWithSpecialCaracters,
getCommonTextboxArgsWithSpecialCharacters,
} from "../textbox/textbox-test.stories";
import CarbonProvider from "../carbon-provider/carbon-provider.component";

Expand Down Expand Up @@ -59,7 +59,7 @@ export const DecimalStory = (args: CommonTextboxArgs) => {
value={state}
onChange={handleChange}
onBlur={handleBlur}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
);
};
Expand All @@ -79,7 +79,7 @@ export const UncontrolledDecimalStory = (args: CommonTextboxArgs) => {
defaultValue="0.03"
onChange={handleChange}
onBlur={handleBlur}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
);
};
Expand Down Expand Up @@ -125,7 +125,7 @@ export const PostStory = ({
value={state}
onChange={handleChange}
onBlur={handleBlur}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
<button type="submit">Submit</button>
</form>
Expand All @@ -150,7 +150,7 @@ export const NewValidationStory = (args: CommonTextboxArgs) => {
m={2}
onChange={handleChange}
onBlur={handleBlur}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
</CarbonProvider>
);
Expand Down Expand Up @@ -180,7 +180,7 @@ export const DecimalCustomOnChangeStory = (args: CommonTextboxArgs) => {
value={state}
onChange={handleChange}
onBlur={handleBlur}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { action } from "@storybook/addon-actions";
import {
commonTextboxArgTypes,
getCommonTextboxArgs,
getCommonTextboxArgsWithSpecialCaracters,
getCommonTextboxArgsWithSpecialCharacters,
CommonTextboxArgs,
} from "../textbox/textbox-test.stories";
import GroupedCharacter, { CustomEvent } from "./grouped-character.component";
Expand Down Expand Up @@ -45,7 +45,7 @@ export const Default = ({ separator, groups, ...args }: StoryArgs) => {
onChange={onChange}
groups={groups}
separator={separator || " "}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
);
};
Expand All @@ -71,7 +71,7 @@ export const NewValidation = ({ separator, groups, ...args }: StoryArgs) => {
onChange={onChange}
groups={groups}
separator={separator || " "}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
</CarbonProvider>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/number/number-test.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
CommonTextboxArgs,
commonTextboxArgTypes,
getCommonTextboxArgs,
getCommonTextboxArgsWithSpecialCaracters,
getCommonTextboxArgsWithSpecialCharacters,
} from "../textbox/textbox-test.stories";

export default {
Expand Down Expand Up @@ -49,7 +49,7 @@ export const Default = ({
onChangeDeferred={
onChangeDeferredEnabled ? action("onChangeDeferred") : undefined
}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
);
};
Expand Down
29 changes: 23 additions & 6 deletions src/components/numeral-date/numeral-date.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import Logger from "../../__internal__/utils/logger";
import Locale from "../../locales/locale";
import FieldHelp from "../../__internal__/field-help";
import useIsAboveBreakpoint from "../../hooks/__internal__/useIsAboveBreakpoint";
import useInputAccessibility from "../../hooks/__internal__/useInputAccessibility";

let deprecateUncontrolledWarnTriggered = false;

Expand Down Expand Up @@ -263,6 +264,7 @@ export const NumeralDate = <DateType extends NumeralDateObject = FullDate>({

const { current: uniqueId } = useRef(id || guid());
const inputIds = useRef({ dd: guid(), mm: guid(), yyyy: guid() });
const inputHintId = useRef(guid());
const isControlled = useRef(value !== undefined);
const initialValue = isControlled.current ? value : defaultValue;

Expand Down Expand Up @@ -412,6 +414,20 @@ export const NumeralDate = <DateType extends NumeralDateObject = FullDate>({
}
};

const { validationId, fieldHelpId, ariaDescribedBy } = useInputAccessibility({
id: uniqueId,
// we still want to add the validationId to aria-describedby with the legacy validation
validationRedesignOptIn: true,
error: internalError,
warning: internalWarning,
info,
fieldHelp,
});

const combinedAriaDescribedBy = [inputHintId.current, ariaDescribedBy]
.filter(Boolean)
.join(" ");

const renderInputs = () => {
const hasInfo = validationRedesignOptIn ? info : undefined;

Expand Down Expand Up @@ -477,6 +493,7 @@ export const NumeralDate = <DateType extends NumeralDateObject = FullDate>({
info,
})}
tooltipPosition={tooltipPosition}
tooltipId={validationId}
/>
</StyledDateField>
</NumeralDateContext.Provider>
Expand Down Expand Up @@ -507,11 +524,13 @@ export const NumeralDate = <DateType extends NumeralDateObject = FullDate>({
legendAlign={labelAlign}
legendWidth={labelWidth}
legendSpacing={labelSpacing}
validationId={validationId}
aria-describedby={ariaDescribedBy}
{...filterStyledSystemMarginProps(rest)}
>
<Box display="block" mt={1}>
{renderInputs()}
{fieldHelp && <FieldHelp>{fieldHelp}</FieldHelp>}
{fieldHelp && <FieldHelp id={fieldHelpId}>{fieldHelp}</FieldHelp>}
</Box>
</StyledFieldset>
</TooltipProvider>
Expand All @@ -529,13 +548,11 @@ export const NumeralDate = <DateType extends NumeralDateObject = FullDate>({
isOptional={isOptional}
isDisabled={disabled}
name={name}
aria-describedby={labelHelp && `${uniqueId}-label-help`}
aria-describedby={combinedAriaDescribedBy}
{...filterStyledSystemMarginProps(rest)}
>
{labelHelp && (
<StyledHintText id={`${uniqueId}-label-help`}>
{labelHelp}
</StyledHintText>
<StyledHintText id={inputHintId.current}>{labelHelp}</StyledHintText>
)}

<Box position="relative" mt={1}>
Expand All @@ -544,7 +561,7 @@ export const NumeralDate = <DateType extends NumeralDateObject = FullDate>({
<ValidationMessage
error={internalError}
warning={internalWarning}
validationId={`${uniqueId}-validation-message`}
validationId={validationId}
/>
<ErrorBorder warning={!!(!internalError && internalWarning)} />
</>
Expand Down
40 changes: 40 additions & 0 deletions src/components/numeral-date/numeral-date.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2109,3 +2109,43 @@ test("should set the inputs to `readOnly` when the prop is passed", () => {
expect(monthInput).toHaveAttribute("readonly");
expect(yearInput).toHaveAttribute("readonly");
});

test.each(["error", "warning", "info"])(
"should render with expected accessible description when '%s' and 'fieldHelp' props are passed and 'validationRedesignOptIn' is false",
async (validation) => {
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
render(
<NumeralDate
value={{ dd: "", mm: "", yyyy: "" }}
onChange={() => {}}
fieldHelp="fieldHelp"
{...{ [validation]: "Message" }}
/>,
);

const dayInput = screen.getByRole("textbox", { name: "Day" });
await user.click(dayInput);

const fieldset = screen.getByRole("group");
expect(fieldset).toHaveAccessibleDescription("fieldHelp Message");
},
);

test.each(["error", "warning"])(
"should render with expected accessible description when '%s' and 'labelHelp' props are passed and 'validationRedesignOptIn' is true",
(validation) => {
render(
<CarbonProvider validationRedesignOptIn>
<NumeralDate
value={{ dd: "", mm: "", yyyy: "" }}
onChange={() => {}}
labelHelp="labelHelp"
{...{ [validation]: "Message" }}
/>
</CarbonProvider>,
);

const fieldset = screen.getByRole("group");
expect(fieldset).toHaveAccessibleDescription("labelHelp Message");
},
);
16 changes: 11 additions & 5 deletions src/components/textbox/textbox-test.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const getCommonTextboxArgs = (
labelWidth: 30,
inputWidth: 70,
labelAlign: undefined,
tooltipId: "",
}),
size: "medium",
inputIcon: undefined,
Expand All @@ -42,7 +43,7 @@ export interface CommonTextboxArgs {
placeholder: string;
}

export const getCommonTextboxArgsWithSpecialCaracters = (
export const getCommonTextboxArgsWithSpecialCharacters = (
args: CommonTextboxArgs,
) => {
const { prefix, fieldHelp, label, labelHelp, placeholder } = args;
Expand Down Expand Up @@ -93,6 +94,11 @@ export const commonTextboxArgTypes = (isNewValidation?: boolean) => ({
step: 1,
},
},
tooltipId: {
control: {
type: "text",
},
},
}),
adaptiveLabelBreakpoint: {
control: {
Expand Down Expand Up @@ -128,7 +134,7 @@ export const Default = (args: CommonTextboxArgs) => {
iconOnClick={action("iconOnClick")}
value={state}
onChange={setValue}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
/>
</div>
);
Expand All @@ -139,8 +145,8 @@ Default.args = getCommonTextboxArgs();

export const Multiple = (args: CommonTextboxArgs) => (
<div style={{ width: "296px" }}>
<Textbox m={2} {...getCommonTextboxArgsWithSpecialCaracters(args)} />
<Textbox m={2} {...getCommonTextboxArgsWithSpecialCaracters(args)} />
<Textbox m={2} {...getCommonTextboxArgsWithSpecialCharacters(args)} />
<Textbox m={2} {...getCommonTextboxArgsWithSpecialCharacters(args)} />
</div>
);

Expand All @@ -160,7 +166,7 @@ export const NewValidation = (args: CommonTextboxArgs) => {
<CarbonProvider validationRedesignOptIn>
<Textbox
m={2}
{...getCommonTextboxArgsWithSpecialCaracters(args)}
{...getCommonTextboxArgsWithSpecialCharacters(args)}
value={state}
onChange={setValue}
/>
Expand Down
11 changes: 9 additions & 2 deletions src/components/textbox/textbox.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ export interface CommonTextboxProps
helpAriaLabel?: string;
/** Flag to configure component as optional. */
isOptional?: boolean;
/** The id attribute for the validation tooltip */
tooltipId?: string;
}

export interface TextboxProps extends CommonTextboxProps {
Expand Down Expand Up @@ -190,6 +192,7 @@ export const Textbox = React.forwardRef(
"data-role": dataRole,
characterLimit,
helpAriaLabel,
tooltipId,
...props
}: TextboxProps,
ref: React.ForwardedRef<HTMLInputElement>,
Expand Down Expand Up @@ -299,7 +302,9 @@ export const Textbox = React.forwardRef(
placeholder={disabled || readOnly ? "" : placeholder}
readOnly={readOnly}
value={typeof formattedValue === "string" ? formattedValue : value}
validationIconId={validationRedesignOptIn ? undefined : validationId}
validationIconId={
validationRedesignOptIn ? undefined : tooltipId || validationId
}
{...props}
/>
{children}
Expand All @@ -318,7 +323,9 @@ export const Textbox = React.forwardRef(
size={size}
useValidationIcon={!(validationRedesignOptIn || validationOnLabel)}
warning={warning}
validationIconId={validationRedesignOptIn ? undefined : validationId}
validationIconId={
validationRedesignOptIn ? undefined : tooltipId || validationId
}
/>
</InputPresentation>
);
Expand Down
Loading

0 comments on commit 3668281

Please sign in to comment.