Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/__internal__/checkable-input/checkable-input.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ test("renders input with 'aria-describedby' as the id of the validation tooltip
input.focus();
});

expect(input).toHaveAttribute("aria-describedby", "foo-validation");
expect(input).toHaveAttribute("aria-describedby", "foo-validation-0");
});

test("appends the id of the validation tooltip to the input's 'aria-describedby' when fieldHelp is set and the input is focused", () => {
Expand All @@ -67,7 +67,7 @@ test("appends the id of the validation tooltip to the input's 'aria-describedby'

expect(input).toHaveAttribute(
"aria-describedby",
"foo-field-help foo-validation",
"foo-field-help foo-validation-0",
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ const ValidationMessage = ({
) : null;
};

export default ValidationMessage;
export default ValidationMessage;
16 changes: 8 additions & 8 deletions src/components/textbox/textbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,9 @@ test.each(validationTypes)(

expect(await screen.findByRole("tooltip")).toHaveAttribute(
"id",
"foo-validation",
"foo-validation-0",
);
expect(input).toHaveAttribute("aria-describedby", "foo-validation");
expect(input).toHaveAttribute("aria-describedby", "foo-validation-0");
},
);

Expand All @@ -507,11 +507,11 @@ test.each(validationTypes)(

expect(await screen.findByRole("tooltip")).toHaveAttribute(
"id",
`${mockedGuid}-validation`,
`${mockedGuid}-validation-0`,
);
expect(input).toHaveAttribute(
"aria-describedby",
`${mockedGuid}-validation`,
`${mockedGuid}-validation-0`,
);
},
);
Expand Down Expand Up @@ -569,12 +569,12 @@ test.each(validationTypes)(

expect(await screen.findByRole("tooltip")).toHaveAttribute(
"id",
"foo-validation",
"foo-validation-0",
);
expect(screen.getByText("baz")).toHaveAttribute("id", "foo-field-help");
expect(input).toHaveAttribute(
"aria-describedby",
"foo-field-help foo-validation",
"foo-field-help foo-validation-0",
);
},
);
Expand Down Expand Up @@ -752,11 +752,11 @@ describe("when rendered with new validations", () => {

expect(screen.getByText("bar")).toHaveAttribute(
"id",
`${mockId}-validation`,
`${mockId}-validation-0`,
);
expect(screen.getByRole("textbox")).toHaveAttribute(
"aria-describedby",
`${mockId}-validation`,
`${mockId}-validation-0`,
);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { renderHook } from "@testing-library/react";
import useInputAccessibility from ".";

const id = "test-id";
Expand All @@ -8,7 +9,11 @@ const warning = "warning";
const info = "info";

test("returns all aria related props when all arguments are passed", () => {
expect(useInputAccessibility({ id, label, fieldHelp })).toMatchObject({
const { result } = renderHook(() =>
useInputAccessibility({ id, label, fieldHelp }),
);

expect(result.current).toMatchObject({
labelId: `${id}-label`,
validationId: undefined,
fieldHelpId: `${id}-field-help`,
Expand All @@ -17,7 +22,9 @@ test("returns all aria related props when all arguments are passed", () => {
});

test("returns aria props without labelId when label is not provided", () => {
expect(useInputAccessibility({ id, fieldHelp })).toMatchObject({
const { result } = renderHook(() => useInputAccessibility({ id, fieldHelp }));

expect(result.current).toMatchObject({
labelId: undefined,
validationId: undefined,
fieldHelpId: `${id}-field-help`,
Expand All @@ -26,51 +33,103 @@ test("returns aria props without labelId when label is not provided", () => {
});

test("returns ariaDescribedBy with fieldHelp and validationId combined when the fieldHelp is provided and string validation is set, with validationRedesignOptIn set to true", () => {
expect(
const { result } = renderHook(() =>
useInputAccessibility({
id,
fieldHelp,
error,
validationRedesignOptIn: true,
}),
).toEqual(
expect.objectContaining({
ariaDescribedBy: `${id}-field-help ${id}-validation`,
}),
);

expect(result.current.ariaDescribedBy).toBe(
`${id}-field-help ${id}-validation-0`,
);
});

describe.each([error, info, warning])(
"when the id and %s prop is provided",
(key) => {
it(`returns validationIconId based on that id for ${key}`, () => {
expect(useInputAccessibility({ id, [key]: key })).toEqual(
expect.objectContaining({
validationId: `${id}-validation`,
}),
);
});

it(`returns undefined ariaDescribedby prop for ${key}`, () => {
expect(useInputAccessibility({ id, [key]: key })).toEqual(
expect.objectContaining({
ariaDescribedBy: undefined,
}),
);
});

it(`returns ariaDescribedBy containing validationId based on that id for ${key} with validationRedesignOptIn set to true`, () => {
expect(
useInputAccessibility({
id,
[key]: key,
validationRedesignOptIn: true,
}),
).toEqual(
expect.objectContaining({
ariaDescribedBy: `${id}-validation`,
}),
);
});
},
);
describe("when validation props are provided", () => {
it("returns validationId with counter for error", () => {
const { result } = renderHook(() => useInputAccessibility({ id, error }));

expect(result.current.validationId).toBe(`${id}-validation-0`);
});

it("returns validationId with counter for warning", () => {
const { result } = renderHook(() => useInputAccessibility({ id, warning }));

expect(result.current.validationId).toBe(`${id}-validation-0`);
});

it("returns validationId with counter for info", () => {
const { result } = renderHook(() => useInputAccessibility({ id, info }));

expect(result.current.validationId).toBe(`${id}-validation-0`);
});

it("returns undefined ariaDescribedby prop for error", () => {
const { result } = renderHook(() => useInputAccessibility({ id, error }));

expect(result.current.ariaDescribedBy).toBeUndefined();
});

it("returns undefined ariaDescribedby prop for warning", () => {
const { result } = renderHook(() => useInputAccessibility({ id, warning }));

expect(result.current.ariaDescribedBy).toBeUndefined();
});

it("returns undefined ariaDescribedby prop for info", () => {
const { result } = renderHook(() => useInputAccessibility({ id, info }));

expect(result.current.ariaDescribedBy).toBeUndefined();
});

it("returns ariaDescribedBy for error with validationRedesignOptIn", () => {
const { result } = renderHook(() =>
useInputAccessibility({
id,
error,
validationRedesignOptIn: true,
}),
);

expect(result.current.ariaDescribedBy).toBe(`${id}-validation-0`);
});

it("returns ariaDescribedBy for warning with validationRedesignOptIn", () => {
const { result } = renderHook(() =>
useInputAccessibility({
id,
warning,
validationRedesignOptIn: true,
}),
);

expect(result.current.ariaDescribedBy).toBe(`${id}-validation-0`);
});

it("returns ariaDescribedBy for info with validationRedesignOptIn", () => {
const { result } = renderHook(() =>
useInputAccessibility({
id,
info,
validationRedesignOptIn: true,
}),
);

expect(result.current.ariaDescribedBy).toBe(`${id}-validation-0`);
});
});

test("validationId changes when error message changes", () => {
const { result, rerender } = renderHook(
({ error }) => useInputAccessibility({ id, error }),
{ initialProps: { error: "First error" } },
);

expect(result.current.validationId).toBe(`${id}-validation-0`);

rerender({ error: "Second error" });

expect(result.current.validationId).toBe(`${id}-validation-1`);
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useRef, useEffect } from "react";

export default function useInputAccessibility({
id,
validationRedesignOptIn,
Expand All @@ -22,10 +24,30 @@ export default function useInputAccessibility({
} {
const labelId = label ? `${id}-label` : undefined;

const validationId = [error, warning, info].filter(
const currentValidationMessage =
(typeof error === "string" && error) ||
(typeof warning === "string" && warning) ||
(typeof info === "string" && info) ||
"";

const validationCounterRef = useRef(0);
const previousMessageRef = useRef<string>("");

useEffect(() => {
if (currentValidationMessage !== previousMessageRef.current) {
if (currentValidationMessage) {
validationCounterRef.current += 1;
}
previousMessageRef.current = currentValidationMessage;
}
}, [currentValidationMessage]);

const hasValidation = [error, warning, info].some(
(validation) => validation && typeof validation === "string",
).length
? `${id}-validation`
);

const validationId = hasValidation
? `${id}-validation-${validationCounterRef.current}`
: undefined;

const fieldHelpId = fieldHelp ? `${id}-field-help` : undefined;
Expand Down
Loading