diff --git a/CHANGELOG.md b/CHANGELOG.md
index b11167ddb1..b99ad6e93c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,8 +15,10 @@ Currently, this repo is in Prerelease. When it is released, this project will ad
- Temporarily renaming `FilterBar`, `MultiSelect`, `MultiSelectGroup`, `useMultiSelect`, and `useFilterBar` Storybook page files so they don't show up in the Storybook sidebar.
- Updates the `Slider` component to use appropriate `aria-label` values for the slider thumbs and text input fields.
+- Updates `TextInput` so it no longer incorrectly overwrites the `aria-describedby` value to undefined when part of the `DatePicker` component.
- Updates `DatePicker` so that focus remains on input after value is changed.
- Updates the `FeedbackBox` component to remove the underline on the component's `Privacy Policy` link.
+- Updates `DatePicker` to pass a `additionalHelperTextIds` to its `TextInput` if needed so that the `aria-describedby` value can be associated with all relevant `helperText`s.
### Fixes
diff --git a/src/components/DatePicker/DatePicker.mdx b/src/components/DatePicker/DatePicker.mdx
index e94e483373..236ab94db0 100644
--- a/src/components/DatePicker/DatePicker.mdx
+++ b/src/components/DatePicker/DatePicker.mdx
@@ -58,6 +58,11 @@ the "date range" mode by wrapping the elements in a `
` element with
its own `` label for the group. Note that this is in addition to the two
labels that each ` ` element is associated with.
+`helperText` is also associated with the ` ` elements via the `aria-describedby`
+attribute. If a `helperTextFrom` or `helperTextTo` is passed in addition to a general
+`helperText` for the entire `DatePicker`, then the `aria-describedby` attribute of the
+` ` element will be "[general-helperText-id] [input-helperText-id]".
+
When `showLabel` is set to false, the single ` ` element's `aria-label`
attribute is set to the required `labelText` value. In the "date range" mode,
when `showLabel` is set to false, the ` `'s `` will have the
diff --git a/src/components/DatePicker/DatePicker.test.tsx b/src/components/DatePicker/DatePicker.test.tsx
index 726cf1f923..aeb91df863 100644
--- a/src/components/DatePicker/DatePicker.test.tsx
+++ b/src/components/DatePicker/DatePicker.test.tsx
@@ -216,13 +216,18 @@ describe("DatePicker", () => {
/>
);
- // When not errored, we expect only the helper text to appear.
- expect(
- screen.getByLabelText(/Select the date you want to visit NYPL/i)
- ).toBeInTheDocument();
expect(
screen.getByText("Note that the Library may be closed on Sundays.")
).toBeInTheDocument();
+
+ // The input should be associated with the helper text via aria-describedby.
+ const input = screen.getByRole("textbox");
+ expect(input).toHaveAttribute(
+ "aria-describedby",
+ "datePicker-start-helperText"
+ );
+
+ // When not errored, we expect only the helper text to appear.
expect(
screen.queryByText("Please select a valid date.")
).not.toBeInTheDocument();
@@ -243,6 +248,13 @@ describe("DatePicker", () => {
expect(
screen.getByText("Please select a valid date.")
).toBeInTheDocument();
+
+ // The input should be associated with the error text via aria-describedby.
+ // The error text replaces the original helper text.
+ expect(input).toHaveAttribute(
+ "aria-describedby",
+ "datePicker-start-helperText"
+ );
});
it("should not render the helper text or invalid text when 'showHelperInvalidText' is false", () => {
@@ -569,6 +581,7 @@ describe("DatePicker", () => {
isDateRange
/>
);
+
// There are two labels for each input.
expect(screen.getByText("From")).toBeInTheDocument();
expect(screen.getByText("To")).toBeInTheDocument();
@@ -582,6 +595,24 @@ describe("DatePicker", () => {
).toBeInTheDocument();
// Helper text for the "To" input
expect(screen.getByText(/Note for the 'to' field./i)).toBeInTheDocument();
+
+ const fromInput = screen.getByRole("textbox", { name: "From" });
+ const toInput = screen.getByRole("textbox", { name: "To" });
+
+ // The `fromInput` should have an `aria-describedby` value of both the id of
+ // the `helperText` and the id of the `helperTextFrom` in that order - from
+ // more general to more specific.
+ expect(fromInput).toHaveAttribute(
+ "aria-describedby",
+ "datePicker-helper-text datePicker-start-helperText"
+ );
+ // The `toInput` should have an `aria-describedby` value of both the id of
+ // the `helperText` and the id of the `helperTextTo` in that order - from
+ // more general to more specific.
+ expect(toInput).toHaveAttribute(
+ "aria-describedby",
+ "datePicker-helper-text datePicker-end-helperText"
+ );
});
it("should render different states based on respective props", () => {
diff --git a/src/components/DatePicker/DatePicker.tsx b/src/components/DatePicker/DatePicker.tsx
index 7cb85dfbee..20133618dd 100644
--- a/src/components/DatePicker/DatePicker.tsx
+++ b/src/components/DatePicker/DatePicker.tsx
@@ -385,6 +385,7 @@ export const DatePicker = chakra(
...baseCustomTextInputAttrs,
helperText: helperTextTo,
};
+
// These props are used to follow the pattern recommended by
// the react-datepicker plugin.
startDatePickerAttrs = {
@@ -407,6 +408,12 @@ export const DatePicker = chakra(
}
@@ -425,6 +432,12 @@ export const DatePicker = chakra(
}
diff --git a/src/components/DatePicker/__snapshots__/DatePicker.test.tsx.snap b/src/components/DatePicker/__snapshots__/DatePicker.test.tsx.snap
index 0543078755..f01a320444 100644
--- a/src/components/DatePicker/__snapshots__/DatePicker.test.tsx.snap
+++ b/src/components/DatePicker/__snapshots__/DatePicker.test.tsx.snap
@@ -445,6 +445,7 @@ exports[`DatePicker Date Range renders the UI snapshot correctly 4`] = `
className="css-79elbk"
>
;
*/
export const WithControls: Story = {
args: {
+ additionalHelperTextIds: undefined,
className: undefined,
defaultValue: undefined,
helperText: "Choose wisely.",
@@ -255,6 +256,7 @@ export const HTMLHelperText: Story = {
export const Textarea: Story = {
args: {
+ additionalHelperTextIds: undefined,
className: undefined,
defaultValue: undefined,
helperText: "Let it all out.",
diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx
index 7894597da3..aec2ea2b79 100644
--- a/src/components/TextInput/TextInput.tsx
+++ b/src/components/TextInput/TextInput.tsx
@@ -44,6 +44,9 @@ export const TextInputFormats = {
export type TextInputVariants = "default" | "searchBar" | "searchBarSelect";
export interface InputProps {
+ /** FOR INTERNAL DS USE ONLY: additional helper text id(s) to be used for the input's `aria-describedby` value.
+ * If more than one, separate each with a space */
+ additionalHelperTextIds?: string;
/** A class name for the TextInput parent div. */
className?: string;
/** The starting value of the input field. */
@@ -126,6 +129,7 @@ export const TextInput = chakra(
forwardRef(
(props, ref: React.Ref) => {
const {
+ additionalHelperTextIds,
className,
defaultValue,
helperText,
@@ -200,6 +204,7 @@ export const TextInput = chakra(
id,
labelText,
name: "TextInput",
+ additionalHelperTextIds,
showLabel,
});
@@ -256,8 +261,8 @@ export const TextInput = chakra(
ref: finalRef,
// The `step` attribute is useful for the number type.
step: type === "number" ? step : null,
- ...ariaAttributes,
...rest,
+ ...ariaAttributes,
};
// For `input` and `textarea`, all attributes are the same but `input`
// also needs `type` and `value` to render correctly.
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index f4872a8cd1..bc81046384 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -52,6 +52,7 @@ interface GetAriaAttrsProps {
id: string;
labelText: HelperErrorTextType;
name: string;
+ additionalHelperTextIds?: string;
showLabel: boolean;
}
/**
@@ -63,6 +64,7 @@ export const getAriaAttrs = ({
id,
labelText,
name,
+ additionalHelperTextIds,
showLabel,
}: GetAriaAttrsProps): AriaAttributes => {
let ariaAttributes: AriaAttributes = {};
@@ -78,9 +80,10 @@ export const getAriaAttrs = ({
? `${labelText} - ${footnote}`
: (labelText as string);
} else if (footnote) {
- ariaAttributes["aria-describedby"] = `${id}-helperText`;
+ ariaAttributes["aria-describedby"] = `${
+ additionalHelperTextIds ? additionalHelperTextIds + " " : ""
+ }${id}-helperText`;
}
-
return ariaAttributes;
};