diff --git a/Makefile b/Makefile
index 7a88146d6..d5a930595 100644
--- a/Makefile
+++ b/Makefile
@@ -59,6 +59,9 @@ format-check: ## Checks if format is correct
.PHONY: install
install: ## Update the package dependencies when new deps are added to dune-project
@opam install . --deps-only --with-test
+# --force is needed because we are installing react@19 while other dependencies
+# require react@18. It's a good workaround to bypass the npm validation error
+# and test the rc versions of React
@npm install --force
.PHONY: init
diff --git a/demo/dune b/demo/dune
index 3f1c1ac86..851bbbc10 100644
--- a/demo/dune
+++ b/demo/dune
@@ -3,7 +3,7 @@
(alias melange-app)
(module_systems
(es6 mjs))
- (libraries reason-react jest melange.belt melange.dom)
+ (libraries reason-react melange.belt melange.dom)
(runtime_deps index.html)
(preprocess
(pps melange.ppx reason-react-ppx)))
diff --git a/ppx/reason_react_ppx.ml b/ppx/reason_react_ppx.ml
index afa7dc365..ca7c99e83 100644
--- a/ppx/reason_react_ppx.ml
+++ b/ppx/reason_react_ppx.ml
@@ -637,8 +637,10 @@ let jsxMapper =
let expr = mapper#expression ctxt expr in
match expr.pexp_desc with
| Pexp_fun (Labelled "key", _, _, _) | Pexp_fun (Optional "key", _, _, _) ->
- Location.raise_errorf ~loc:expr.pexp_loc
- ("~key cannot be accessed from the component props. Please set the key where the component is being used.")
+ raise
+ (Invalid_argument
+ "Key cannot be accessed inside of a component. Don't worry - you \
+ can always key a component from its parent!")
| Pexp_fun
( ((Optional label | Labelled label) as arg),
default,
diff --git a/src/React.re b/src/React.re
index ba02b4222..ae8cd326e 100644
--- a/src/React.re
+++ b/src/React.re
@@ -885,16 +885,24 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit =
external useDebugValue: ('value, ~format: 'value => string=?, unit) => unit =
"useDebugValue";
+[@mel.module "react"]
+external act: (unit => unit) => Js.Promise.t(unit) = "act";
+[@mel.module "react"]
+external actAsync: (unit => Js.Promise.t(unit)) => Js.Promise.t(unit) =
+ "act";
+
module Experimental = {
/* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */
+ /* https://react.dev/reference/react/use */
[@mel.module "react"] external usePromise: Js.Promise.t('a) => 'a = "use";
[@mel.module "react"] external useContext: Context.t('a) => 'a = "use";
+ /* https://react.dev/reference/react/useTransition */
[@mel.module "react"]
external useTransitionAsync:
unit => (bool, callbackAsync(callbackAsync(unit, unit), unit)) =
"useTransition";
- /* https://es.react.dev/reference/react/useOptimistic */
+ /* https://react.dev/reference/react/useOptimistic */
[@mel.module "react"]
external useOptimistic:
('state, ('state, 'optimisticValue) => 'state) =>
diff --git a/src/React.rei b/src/React.rei
index 372337618..66b4de0ee 100644
--- a/src/React.rei
+++ b/src/React.rei
@@ -573,6 +573,12 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit =
external useTransition: unit => (bool, callback(callback(unit, unit), unit)) =
"useTransition";
+[@mel.module "react"]
+external act: (unit => unit) => Js.Promise.t(unit) = "act";
+[@mel.module "react"]
+external actAsync: (unit => Js.Promise.t(unit)) => Js.Promise.t(unit) =
+ "act";
+
module Experimental: {
/* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */
[@mel.module "react"] external usePromise: Js.Promise.t('a) => 'a = "use";
diff --git a/src/ReactDOM.re b/src/ReactDOM.re
index 92b7c4d6c..bedb622f2 100644
--- a/src/ReactDOM.re
+++ b/src/ReactDOM.re
@@ -652,7 +652,7 @@ type domProps = {
[@mel.optional]
acceptCharset: option(string),
[@mel.optional]
- action: option(string),
+ action: option(string), /* uri */
[@mel.optional]
allowFullScreen: option(bool),
[@mel.optional]
diff --git a/src/ReactDOMTestUtils.re b/src/ReactDOMTestUtils.re
index 80615b4ec..24dafbff3 100644
--- a/src/ReactDOMTestUtils.re
+++ b/src/ReactDOMTestUtils.re
@@ -5,6 +5,7 @@ let undefined: undefined = Js.Undefined.empty;
[@mel.module "react"]
external reactAct: ((. unit) => undefined) => unit = "act";
+[@deprecated "use React.act instead"]
let act: (unit => unit) => unit =
func => {
let reactFunc =
@@ -19,6 +20,7 @@ let act: (unit => unit) => unit =
external reactActAsync: ((. unit) => Js.Promise.t('a)) => Js.Promise.t(unit) =
"act";
+[@deprecated "use React.actAsync instead"]
let actAsync = func => {
let reactFunc =
(.) => {
@@ -27,35 +29,73 @@ let actAsync = func => {
reactActAsync(reactFunc);
};
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
[@mel.module "react-dom/test-utils"]
external isElement: 'element => bool = "isElement";
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
[@mel.module "react-dom/test-utils"]
external isElementOfType: ('element, React.component('props)) => bool =
"isElementOfType";
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
[@mel.module "react-dom/test-utils"]
external isDOMComponent: 'element => bool = "isDOMComponent";
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
[@mel.module "react-dom/test-utils"]
external isCompositeComponent: 'element => bool = "isCompositeComponent";
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
[@mel.module "react-dom/test-utils"]
external isCompositeComponentWithType:
('element, React.component('props)) => bool =
"isCompositeComponentWithType";
module Simulate = {
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external click: Dom.element => unit = "click";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external clickWithEvent: (Dom.element, 'event) => unit = "click";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external change: Dom.element => unit = "change";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external blur: Dom.element => unit = "blur";
+
[@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
external changeWithEvent: (Dom.element, 'event) => unit = "change";
+
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
let changeWithValue = (element, value) => {
let event = {
"target": {
@@ -64,6 +104,10 @@ module Simulate = {
};
changeWithEvent(element, event);
};
+
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
let changeWithChecked = (element, value) => {
let event = {
"target": {
@@ -72,11 +116,23 @@ module Simulate = {
};
changeWithEvent(element, event);
};
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external canPlay: Dom.element => unit = "canPlay";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external timeUpdate: Dom.element => unit = "timeUpdate";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external ended: Dom.element => unit = "ended";
[@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
external focus: Dom.element => unit = "focus";
@@ -101,7 +157,9 @@ external body: Dom.document => option(Dom.element) = "body";
[@mel.send]
external createElement: (Dom.document, string) => Dom.element =
"createElement";
+
[@mel.send] external remove: Dom.element => unit = "remove";
+
[@mel.send]
external appendChild: (Dom.element, Dom.element) => Dom.element =
"appendChild";
@@ -111,19 +169,35 @@ let querySelectorAll = (element, string) => {
};
module DOM = {
- [@mel.return nullable] [@mel.get]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ]
+ [@mel.return nullable]
+ [@mel.get]
external value: Dom.element => option(string) = "value";
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ]
let findBySelector = (element, selector) =>
querySelector(element, selector);
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ]
let findByAllSelector = (element, selector) =>
querySelectorAll(element, selector);
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ]
let findBySelectorAndTextContent = (element, selector, content) =>
querySelectorAll(element, selector)
|> Array.find_opt(node => node->textContent === content);
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ]
let findBySelectorAndPartialTextContent = (element, selector, content) =>
querySelectorAll(element, selector)
|> Array.find_opt(node =>
@@ -131,6 +205,9 @@ module DOM = {
);
};
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
let prepareContainer = (container: ref(option(Dom.element)), ()) => {
let containerElement = document->createElement("div");
let _: option(_) =
@@ -138,11 +215,17 @@ let prepareContainer = (container: ref(option(Dom.element)), ()) => {
container := Some(containerElement);
};
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
let cleanupContainer = (container: ref(option(Dom.element)), ()) => {
let _: option(_) = Option.map(remove, container^);
container := None;
};
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
let getContainer = container => {
container.contents->Option.get;
};
diff --git a/src/ReactDOMTestUtils.rei b/src/ReactDOMTestUtils.rei
index 5f67f3345..bfab45e81 100644
--- a/src/ReactDOMTestUtils.rei
+++ b/src/ReactDOMTestUtils.rei
@@ -1,60 +1,145 @@
-let act: (unit => unit) => unit;
+let act: [@deprecated "use React.act instead"] ((unit => unit) => unit);
-let actAsync: (unit => Js.Promise.t('a)) => Js.Promise.t(unit);
+let actAsync:
+ [@deprecated "use React.actAsync instead"] (
+ (unit => Js.Promise.t('a)) => Js.Promise.t(unit)
+ );
[@mel.module "react-dom/test-utils"]
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
external isElement: 'element => bool = "isElement";
[@mel.module "react-dom/test-utils"]
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
external isElementOfType: ('element, React.component('props)) => bool =
"isElementOfType";
[@mel.module "react-dom/test-utils"]
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
external isDOMComponent: 'element => bool = "isDOMComponent";
[@mel.module "react-dom/test-utils"]
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
external isCompositeComponent: 'element => bool = "isCompositeComponent";
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
[@mel.module "react-dom/test-utils"]
external isCompositeComponentWithType:
('element, React.component('props)) => bool =
"isCompositeComponentWithType";
module Simulate: {
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external click: Dom.element => unit = "click";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external clickWithEvent: (Dom.element, 'event) => unit = "click";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external change: Dom.element => unit = "change";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external blur: Dom.element => unit = "blur";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external changeWithEvent: (Dom.element, 'event) => unit = "change";
let changeWithValue: (Dom.element, string) => unit;
let changeWithChecked: (Dom.element, bool) => unit;
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external canPlay: Dom.element => unit = "canPlay";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external timeUpdate: Dom.element => unit = "timeUpdate";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external ended: Dom.element => unit = "ended";
- [@mel.module "react-dom/test-utils"] [@mel.scope "Simulate"]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+ ]
+ [@mel.module "react-dom/test-utils"]
+ [@mel.scope "Simulate"]
external focus: Dom.element => unit = "focus";
};
module DOM: {
- [@mel.return nullable] [@mel.get]
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ]
+ [@mel.return nullable]
+ [@mel.get]
external value: Dom.element => option(string) = "value";
- let findBySelector: (Dom.element, string) => option(Dom.element);
- let findByAllSelector: (Dom.element, string) => array(Dom.element);
+ let findBySelector:
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ] (
+ (Dom.element, string) => option(Dom.element)
+ );
+ let findByAllSelector:
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ] (
+ (Dom.element, string) => array(Dom.element)
+ );
let findBySelectorAndTextContent:
- (Dom.element, string, string) => option(Dom.element);
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ] (
+ (Dom.element, string, string) => option(Dom.element)
+ );
let findBySelectorAndPartialTextContent:
- (Dom.element, string, string) => option(Dom.element);
+ [@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-webapi instead."
+ ] (
+ (Dom.element, string, string) => option(Dom.element)
+ );
};
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
let prepareContainer: (Stdlib.ref(option(Dom.element)), unit) => unit;
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
let cleanupContainer: (Stdlib.ref(option(Dom.element)), unit) => unit;
+[@deprecated
+ "ReactDOMTestUtils is deprecated, and will be removed in next version. Please use melange-testing-library instead."
+]
let getContainer: Stdlib.ref(option(Dom.element)) => Dom.element;
diff --git a/test/Form__test.re b/test/Form__test.re
index 256081650..dbbe7eee2 100644
--- a/test/Form__test.re
+++ b/test/Form__test.re
@@ -87,7 +87,7 @@ module App = {
React.useState(() =>
[
{
- text: "Hola!",
+ text: {j|¡Hola!|j},
sending: false,
key: 1,
},
@@ -136,7 +136,7 @@ describe("Form with useOptimistic", () => {
let container = ReactTestingLibrary.render(