Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8066745
Format files with OCamlformat 0.27.0
johnhaley81 Aug 22, 2025
b3f1f52
Add dataAttrs support for React elements
johnhaley81 Aug 21, 2025
740158f
Apply OCamlformat 0.27.0 formatting to feature branch
johnhaley81 Aug 22, 2025
916cd1f
Add project documentation for Claude Code
johnhaley81 Aug 23, 2025
bf982de
Implement zero-runtime data attributes support in PPX
johnhaley81 Aug 26, 2025
db9ead6
Update data attributes documentation for new JSX support
johnhaley81 Aug 26, 2025
a2a9f48
Remove redundant comments that describe what code does
johnhaley81 Aug 26, 2025
a467adf
Fix data attributes build and update test snapshots
johnhaley81 Aug 26, 2025
d42a9a5
Fix data attributes PPX bugs and restore working tests
johnhaley81 Aug 26, 2025
0470056
Add PPX deduplication fix
johnhaley81 Aug 27, 2025
3bbc1a6
Fix PPX external declaration deduplication for JSX data attributes
johnhaley81 Aug 27, 2025
b89c750
Remove temporary data attributes test file
johnhaley81 Aug 28, 2025
f8e2288
Add .serena/ to .gitignore
johnhaley81 Aug 28, 2025
4ca0aa3
Remove project-specific Claude instructions file
johnhaley81 Aug 29, 2025
2a41068
Fix critical PPX bug: DOM element externals not injected at structure…
johnhaley81 Sep 1, 2025
b37645d
Add comprehensive PPX fix analysis documentation
johnhaley81 Sep 1, 2025
cfd5e5f
Complete PPX data attributes fix with comprehensive test validation
johnhaley81 Sep 1, 2025
1cf9af5
Clean up codebase: remove analysis docs and comments
johnhaley81 Sep 2, 2025
4edc014
Fix PPX data attributes bug and update test patterns
johnhaley81 Sep 2, 2025
5fd0014
Streamline data attributes demo to showcase practical use cases
johnhaley81 Sep 2, 2025
ede06ea
Add comprehensive tests for data attributes with dynamic values
johnhaley81 Sep 3, 2025
7f7ab19
Fix getAttribute external binding for test runtime compatibility
johnhaley81 Sep 3, 2025
6a6f72e
Refactor test suite: consolidate data attributes tests and add warnin…
johnhaley81 Sep 3, 2025
7942e3e
Remove redundant React integration test file
johnhaley81 Sep 3, 2025
de819ed
Fix PPX test configuration: revert hardcoded paths to use opam binaries
johnhaley81 Sep 3, 2025
9d8d6d8
Format codebase with ocamlformat
johnhaley81 Sep 3, 2025
e61aa5a
Improve PPX comment clarity for domProps fallback logic
johnhaley81 Sep 3, 2025
3c7d5dc
Add Merlin hiding to generated data attribute externals and calls
johnhaley81 Sep 3, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ _build
# Editor
/.idea/
_opam
.serena/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's serena?

2 changes: 1 addition & 1 deletion .ocamlformat
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = 0.26.0
version = 0.27.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't change the ocamlformat version in a PR that adds a feature

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had that split out to #900, if that PR is good then this change will be removed from this PR when that other is added. I don't know how to do a merge chain from multiple PRs from a fork :(

78 changes: 78 additions & 0 deletions demo/main.re
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,83 @@ module WithoutForward = {
};
};

module DataAttrsDemo = {
[@react.component]
let make = () => {
<section>
<h3> {React.string("Data Attributes Demo")} </h3>
<p>
{React.string(
"Demonstrating common use cases for HTML data attributes with ReasonReact",
)}
</p>
<div className="demo-testing" data_testid="test-automation">
<h4> {React.string("Testing & QA")} </h4>
<button
data_testid="submit-btn"
data_cy="submit"
onClick={_ => Js.log("Form submitted")}>
{React.string("Submit Form")}
</button>
<input
data_testid="email-input"
data_cy="email"
placeholder="Enter email"
/>
</div>
<div className="demo-analytics" data_analytics="user-engagement">
<h4> {React.string("Analytics & Tracking")} </h4>
<button
data_action="click"
data_category="engagement"
data_label="cta-button"
onClick={_ => Js.log("Tracked click")}>
{React.string("Call to Action")}
</button>
<a href="#" data_track="external-link" data_destination="docs">
{React.string("External Documentation")}
</a>
</div>
<div className="demo-state" data_theme="dark" data_variant="compact">
<h4> {React.string("Component State")} </h4>
<div data_status="active" data_priority="high">
{React.string("High Priority Task")}
</div>
<span data_badge="new" data_count="5">
{React.string("Notifications")}
</span>
</div>
<div className="demo-a11y">
<h4> {React.string("Accessibility Support")} </h4>
<button data_tooltip="Save your current progress" data_placement="top">
{React.string("Save")}
</button>
<div data_role="alert" data_level="error">
{React.string("Please fix validation errors")}
</div>
</div>
<div
className="demo-complex"
data_testid="user-card"
data_analytics="profile-view"
data_theme="light"
data_status="premium"
style={ReactDOM.Style.make(
~padding="16px",
~border="2px solid #0066cc",
(),
)}>
<h4> {React.string("Complex Example")} </h4>
<p>
{React.string(
"Multiple data attributes working together for testing, analytics, theming, and state management.",
)}
</p>
</div>
</section>;
};
};

module App = {
[@react.component]
let make = (~initialValue) => {
Expand All @@ -237,6 +314,7 @@ module App = {
<UseReducerNoProblemo key="use-reducer-no-problemo" />
<FragmentsEverywhere key="fragments-everywhere" />
<WithoutForward key="without-forward" />
<DataAttrsDemo key="data-attrs-demo" />
</main>;
};
};
Expand Down
87 changes: 77 additions & 10 deletions docs/adding-data-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,88 @@
title: Adding data-* attributes
---

Reason doesn't support using props with dashes right now, ie: `data-id` or `data-whatever`. You can overcome this by creating a `Spread` component:
ReasonReact now supports data attributes directly in JSX with zero runtime overhead. Data attributes are transformed at compile-time for optimal performance.

## Direct JSX Support

You can now use `data_*` attributes directly in JSX:

```reason
/* Spread.re */
[@react.component]
let make = (~props, ~children) => React.cloneElement(children, props);
let make = () => {
<div data_testid="my-component" data_cy="cypress-test" className="container">
{React.string("Hello World")}
</div>
};
```

This compiles to efficient code with proper `data-*` attribute names in the DOM:
```html
<div data-testid="my-component" data-cy="cypress-test" class="container">
Hello World
</div>
```

Using Spread:
## How It Works

The PPX automatically detects `data_*` attributes and:
1. **Transforms names**: `data_testid` becomes `data-testid` in the DOM
2. **Zero runtime overhead**: All transformation happens at compile-time
3. **Backwards compatible**: Elements without data attributes use the standard path

## Examples

### Basic Usage
```reason
[@react.component]
let make = () =>
<Spread props={"data-cy": name}>
/* This div will now have the `data-cy` attribute in the DOM! */
<div />
</Spread>;
// Single data attribute
<button data_testid="submit-btn" onClick>
{React.string("Submit")}
</button>

// Multiple data attributes
<div data_testid="user-card" data_role="card" data_index="0">
{React.string("User content")}
</div>
```

### Combined with Other Props
```reason
<input
data_testid="email-input"
data_automation="login-form"
type_="email"
className="form-input"
placeholder="Enter email"
/>
```

### Backwards Compatibility
```reason
// Elements without data attributes work exactly as before
<div className="regular" id="normal">
{React.string("No data attributes")}
</div>
```

## Migration from Old Workarounds

If you were using the `Spread` component workaround:

### Before (Old Workaround)
```reason
<Spread props={"data-cy": "test"}>
<div />
</Spread>
```

### After (Direct JSX)
```reason
<div data_cy="test" />
```

## Technical Details

- **Compile-time transformation**: No runtime performance impact
- **DOM elements only**: Works with `div`, `span`, `button`, etc.
- **Automatic naming**: `data_testid` → `data-testid`, `data_user_id` → `data-user-id`
- **Type safe**: Compile-time validation of attribute syntax
Loading
Loading