From 7a5215a0fbf1e4a7097eea73d9a5f2aedfca5fd5 Mon Sep 17 00:00:00 2001
From: Zhihao Cui <5257855+origami-z@users.noreply.github.com>
Date: Fri, 13 Sep 2024 08:55:26 +0100
Subject: [PATCH] Fix data attributes error on pass through props (#4039)
---
.changeset/good-fishes-shout.md | 12 ++++++++++++
.../src/__tests__/__e2e__/checkbox/Checkbox.cy.tsx | 7 +++++++
.../core/src/__tests__/__e2e__/input/Input.cy.tsx | 7 +++++++
.../__e2e__/multiline-input/MultilineInput.cy.tsx | 10 ++++++++++
.../__e2e__/radio-button/RadioButton.cy.tsx | 10 ++++++++++
.../core/src/__tests__/__e2e__/switch/Switch.cy.tsx | 5 +++++
packages/core/src/checkbox/Checkbox.tsx | 3 ++-
packages/core/src/index.ts | 1 +
packages/core/src/input/Input.tsx | 3 ++-
.../core/src/multiline-input/MultilineInput.tsx | 4 +++-
packages/core/src/pill-input/PillInput.tsx | 3 ++-
packages/core/src/radio-button/RadioButton.tsx | 11 ++++++-----
packages/core/src/switch/Switch.tsx | 13 +++++++------
packages/core/src/types.ts | 8 ++++++++
14 files changed, 82 insertions(+), 15 deletions(-)
create mode 100644 .changeset/good-fishes-shout.md
create mode 100644 packages/core/src/types.ts
diff --git a/.changeset/good-fishes-shout.md b/.changeset/good-fishes-shout.md
new file mode 100644
index 00000000000..ab7e4c694c5
--- /dev/null
+++ b/.changeset/good-fishes-shout.md
@@ -0,0 +1,12 @@
+---
+"@salt-ds/core": patch
+---
+
+Fixed data-\* attributes not allowed on pass through props. Closes #3797.
+
+- `Checkbox.inputProps`
+- `Input.inputProps`
+- `MultilineInput.textAreaProps`
+- `PillInput.inputProps`
+- `RadioButton.inputProps`
+- `Switch.inputProps`
diff --git a/packages/core/src/__tests__/__e2e__/checkbox/Checkbox.cy.tsx b/packages/core/src/__tests__/__e2e__/checkbox/Checkbox.cy.tsx
index 969608bd3e9..51770ed72a5 100644
--- a/packages/core/src/__tests__/__e2e__/checkbox/Checkbox.cy.tsx
+++ b/packages/core/src/__tests__/__e2e__/checkbox/Checkbox.cy.tsx
@@ -1,6 +1,13 @@
import { Checkbox } from "@salt-ds/core";
describe("GIVEN a Checkbox", () => {
+ it("SHOULD support data attribute on inputProps", () => {
+ cy.mount(
+ ,
+ );
+ cy.findByTestId("customInput").should("be.checked");
+ });
+
describe("WHEN in an indeterminate state", () => {
it("THEN should have indeterminate set to true", () => {
cy.mount();
diff --git a/packages/core/src/__tests__/__e2e__/input/Input.cy.tsx b/packages/core/src/__tests__/__e2e__/input/Input.cy.tsx
index 770cc5dbed1..41c08b90ce9 100644
--- a/packages/core/src/__tests__/__e2e__/input/Input.cy.tsx
+++ b/packages/core/src/__tests__/__e2e__/input/Input.cy.tsx
@@ -7,6 +7,13 @@ describe("GIVEN an Input", () => {
cy.checkAxeComponent();
});
+ it("SHOULD support data attribute on inputProps", () => {
+ cy.mount(
+ ,
+ );
+ cy.findByTestId("customInput").should("have.value", "value");
+ });
+
describe("WHEN cy.mounted as an uncontrolled component", () => {
it("THEN it should cy.mount with the specified defaultValue", () => {
cy.mount();
diff --git a/packages/core/src/__tests__/__e2e__/multiline-input/MultilineInput.cy.tsx b/packages/core/src/__tests__/__e2e__/multiline-input/MultilineInput.cy.tsx
index 8e8b832d74d..9231f943ef7 100644
--- a/packages/core/src/__tests__/__e2e__/multiline-input/MultilineInput.cy.tsx
+++ b/packages/core/src/__tests__/__e2e__/multiline-input/MultilineInput.cy.tsx
@@ -11,6 +11,16 @@ const {
} = composeStories(multilineInputStories);
describe("GIVEN an MultilineInput", () => {
+ it("SHOULD support data attribute on textAreaProps", () => {
+ cy.mount(
+ ,
+ );
+ cy.findByTestId("customInput").should("have.value", "value");
+ });
+
it("should allow a default value to be set", () => {
const changeSpy = cy.stub().as("changeSpy");
const handleChange = (event: ChangeEvent) => {
diff --git a/packages/core/src/__tests__/__e2e__/radio-button/RadioButton.cy.tsx b/packages/core/src/__tests__/__e2e__/radio-button/RadioButton.cy.tsx
index 8b51b952360..041cd9d85f9 100644
--- a/packages/core/src/__tests__/__e2e__/radio-button/RadioButton.cy.tsx
+++ b/packages/core/src/__tests__/__e2e__/radio-button/RadioButton.cy.tsx
@@ -1,6 +1,16 @@
import { RadioButton } from "@salt-ds/core";
describe("GIVEN a RadioButton component", () => {
+ it("SHOULD support data attribute on inputProps", () => {
+ cy.mount(
+ ,
+ );
+ cy.findByTestId("customInput").should("have.value", "value");
+ });
+
describe("WHEN RadioButton is given a value", () => {
it("SHOULD render with the specified value", () => {
cy.mount();
diff --git a/packages/core/src/__tests__/__e2e__/switch/Switch.cy.tsx b/packages/core/src/__tests__/__e2e__/switch/Switch.cy.tsx
index cbd8aabd2f1..daf240ae30e 100644
--- a/packages/core/src/__tests__/__e2e__/switch/Switch.cy.tsx
+++ b/packages/core/src/__tests__/__e2e__/switch/Switch.cy.tsx
@@ -14,6 +14,11 @@ function ControlledSwitch({ onChange, disabled }: SwitchProps) {
}
describe("GIVEN a Switch", () => {
+ it("SHOULD support data attribute on inputProps", () => {
+ cy.mount();
+ cy.findByTestId("customInput").should("be.checked");
+ });
+
describe("WHEN using Tab to navigate", () => {
it("THEN should be focusable", () => {
cy.mount();
diff --git a/packages/core/src/checkbox/Checkbox.tsx b/packages/core/src/checkbox/Checkbox.tsx
index 9b5fc89e8da..426810ff2ee 100644
--- a/packages/core/src/checkbox/Checkbox.tsx
+++ b/packages/core/src/checkbox/Checkbox.tsx
@@ -11,6 +11,7 @@ import {
} from "react";
import { useFormFieldProps } from "../form-field-context";
import type { AdornmentValidationStatus } from "../status-adornment";
+import type { DataAttributes } from "../types";
import {
makePrefixer,
useControlled,
@@ -52,7 +53,7 @@ export interface CheckboxProps
/**
* Properties applied to the input element.
*/
- inputProps?: Partial>;
+ inputProps?: Partial> & DataAttributes;
/**
* The label to be shown next to the checkbox.
*/
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 43ce1577a44..21518f52394 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -61,3 +61,4 @@ export * from "./toggle-button-group";
export * from "./tooltip";
export * from "./utils";
export * from "./viewport";
+export * from "./types";
diff --git a/packages/core/src/input/Input.tsx b/packages/core/src/input/Input.tsx
index fec8d327788..df4bf5ad83d 100644
--- a/packages/core/src/input/Input.tsx
+++ b/packages/core/src/input/Input.tsx
@@ -13,6 +13,7 @@ import {
} from "react";
import { useFormFieldProps } from "../form-field-context";
import { StatusAdornment } from "../status-adornment";
+import type { DataAttributes } from "../types";
import { makePrefixer, useControlled } from "../utils";
import inputCss from "./Input.css";
@@ -37,7 +38,7 @@ export interface InputProps
/**
* [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element.
*/
- inputProps?: InputHTMLAttributes;
+ inputProps?: Partial> & DataAttributes;
/**
* Optional ref for the input component
*/
diff --git a/packages/core/src/multiline-input/MultilineInput.tsx b/packages/core/src/multiline-input/MultilineInput.tsx
index ad114b19e49..d013e7ee45c 100644
--- a/packages/core/src/multiline-input/MultilineInput.tsx
+++ b/packages/core/src/multiline-input/MultilineInput.tsx
@@ -16,6 +16,7 @@ import {
} from "react";
import { useFormFieldProps } from "../form-field-context";
import { StatusAdornment } from "../status-adornment";
+import type { DataAttributes } from "../types";
import { makePrefixer, useControlled, useForkRef } from "../utils";
import multilineInputCss from "./MultilineInput.css";
@@ -51,7 +52,8 @@ export interface MultilineInputProps
/**
* [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#Attributes) applied to the `textarea` element.
*/
- textAreaProps?: TextareaHTMLAttributes;
+ textAreaProps?: Partial> &
+ DataAttributes;
/**
* Optional ref for the textarea component
*/
diff --git a/packages/core/src/pill-input/PillInput.tsx b/packages/core/src/pill-input/PillInput.tsx
index 42e89c4a0d1..d2a9213935a 100644
--- a/packages/core/src/pill-input/PillInput.tsx
+++ b/packages/core/src/pill-input/PillInput.tsx
@@ -20,6 +20,7 @@ import {
import { useFormFieldProps } from "../form-field-context";
import { Pill } from "../pill";
import { StatusAdornment } from "../status-adornment";
+import type { DataAttributes } from "../types";
import { makePrefixer, useControlled, useForkRef, useId } from "../utils";
import { useTruncatePills } from "./useTruncatePills";
@@ -45,7 +46,7 @@ export interface PillInputProps
/**
* [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element.
*/
- inputProps?: InputHTMLAttributes;
+ inputProps?: Partial> & DataAttributes;
/**
* Optional ref for the input component
*/
diff --git a/packages/core/src/radio-button/RadioButton.tsx b/packages/core/src/radio-button/RadioButton.tsx
index 133e121301a..7103a92e55b 100644
--- a/packages/core/src/radio-button/RadioButton.tsx
+++ b/packages/core/src/radio-button/RadioButton.tsx
@@ -1,3 +1,5 @@
+import { useComponentCssInjection } from "@salt-ds/styles";
+import { useWindow } from "@salt-ds/window";
import { clsx } from "clsx";
import {
type ChangeEventHandler,
@@ -7,14 +9,13 @@ import {
type ReactNode,
forwardRef,
} from "react";
+import { useFormFieldProps } from "../form-field-context";
+import type { AdornmentValidationStatus } from "../status-adornment";
+import type { DataAttributes } from "../types";
import { makePrefixer, useControlled } from "../utils";
import { RadioButtonIcon } from "./RadioButtonIcon";
import { useRadioGroup } from "./internal/useRadioGroup";
-import { useComponentCssInjection } from "@salt-ds/styles";
-import { useWindow } from "@salt-ds/window";
-import { useFormFieldProps } from "../form-field-context";
-import type { AdornmentValidationStatus } from "../status-adornment";
import radioButtonCss from "./RadioButton.css";
const withBaseName = makePrefixer("saltRadioButton");
@@ -40,7 +41,7 @@ export interface RadioButtonProps
/**
* Props to be passed to the radio input
*/
- inputProps?: Partial>;
+ inputProps?: Partial> & DataAttributes;
/**
* The label to be shown next to the radio icon
*/
diff --git a/packages/core/src/switch/Switch.tsx b/packages/core/src/switch/Switch.tsx
index 9b55e3a24f4..901deb6d68b 100644
--- a/packages/core/src/switch/Switch.tsx
+++ b/packages/core/src/switch/Switch.tsx
@@ -1,3 +1,8 @@
+import {
+ type IconProps,
+ SuccessSmallSolidIcon,
+ SuccessSolidIcon,
+} from "@salt-ds/icons";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
import { clsx } from "clsx";
@@ -10,13 +15,9 @@ import {
} from "react";
import { useFormFieldProps } from "../form-field-context";
import { useDensity } from "../salt-provider";
+import type { DataAttributes } from "../types";
import { makePrefixer, useControlled } from "../utils";
-import {
- type IconProps,
- SuccessSmallSolidIcon,
- SuccessSolidIcon,
-} from "@salt-ds/icons";
import switchCss from "./Switch.css";
export interface SwitchProps
@@ -40,7 +41,7 @@ export interface SwitchProps
/**
* Properties applied to the input element.
*/
- inputProps?: Partial>;
+ inputProps?: Partial> & DataAttributes;
/**
* The label to be shown next to the checkbox.
*/
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
new file mode 100644
index 00000000000..bd9b3852e46
--- /dev/null
+++ b/packages/core/src/types.ts
@@ -0,0 +1,8 @@
+/**
+ * Support data-* attributes for pass through props, e.g. `Input.inputProps`.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-%2A
+ */
+export type DataAttributes = {
+ [dataAttribute: `data-${string}`]: string;
+};