From dc8a164e1adf84977165d0d3dd881f08a80e159e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Piaggio?= Date: Mon, 29 Jan 2024 11:33:20 -0300 Subject: [PATCH] bump react to 18.2.0 --- doc/TESTING.md | 171 +++++++++++++-------------- doc/USAGE.md | 184 ++++++++++++++--------------- library/ghpages/html/dev2.html | 4 +- library/ghpages/html/dev3.html | 4 +- library/ghpages/html/prod.html | 4 +- library/project/Dependencies.scala | 4 +- 6 files changed, 182 insertions(+), 189 deletions(-) diff --git a/doc/TESTING.md b/doc/TESTING.md index 89498e9a0..e65b8df94 100644 --- a/doc/TESTING.md +++ b/doc/TESTING.md @@ -1,5 +1,4 @@ -Testing -======= +# Testing This file describes testing functionality provided by React.JS and scalajs-react.
It is plenty for simple and small unit tests. @@ -10,6 +9,7 @@ For larger and/or complicated tests, **it is highly recommended to use for how to write tests for real-world scalajs-react applications. #### Contents + - [Setup](#setup) - [`ReactTestUtils`](#reacttestutils) - [`Simulate` and `Simulation`](#simulate-and-simulation) @@ -18,31 +18,28 @@ for how to write tests for real-world scalajs-react applications. - [`Test Scripts`](#test-scripts) - [Fatal React warnings](#fatal-react-warnings) -Setup -===== +# Setup 1. Install PhantomJS. 2. Add the following to SBT: - ```scala - // scalajs-react test module - libraryDependencies += "com.github.japgolly.scalajs-react" %%% "test" % "2.1.1" % Test - - // React JS itself. - // NOTE: Requires react-with-addons.js instead of just react.js - jsDependencies += + ```scala + // scalajs-react test module + libraryDependencies += "com.github.japgolly.scalajs-react" %%% "test" % "2.1.1" % Test - "org.webjars.npm" % "react-dom" % "18.1.0" % Test - / "umd/react-dom-test-utils.development.js" - minified "umd/react-dom-test-utils.production.min.js" - dependsOn "umd/react-dom.development.js" - commonJSName "ReactTestUtils" - ``` + // React JS itself. + // NOTE: Requires react-with-addons.js instead of just react.js + jsDependencies += + "org.webjars.npm" % "react-dom" % "18.2.0" % Test + / "umd/react-dom-test-utils.development.js" + minified "umd/react-dom-test-utils.production.min.js" + dependsOn "umd/react-dom.development.js" + commonJSName "ReactTestUtils" + ``` -`ReactTestUtils` -================ +# `ReactTestUtils` The main bucket of testing utilities lies in `japgolly.scalajs.react.test.ReactTestUtils`. @@ -50,37 +47,41 @@ Half of the methods delegate to React.JS's [React.addons.TestUtils](https://face (for which there is a raw facade in `japgolly.scalajs.react.test.raw.ReactAddonsTestUtils` if you're interested). The other half are new functions added specifically in scalajs-react. -* Rendering into DOM with auto-removal - * `withRendered[M, A](u: Unmounted[M], intoBody: Boolean)(f: M => A): A` - * `withRenderedIntoDocument[M, A](u: Unmounted[M])(f: M => A): A` - * `withRenderedIntoBody[M, A](u: Unmounted[M])(f: M => A): A` - * `withNewBodyElement[A](use: Element => A): A` - * `newBodyElement(): Element` - * `removeNewBodyElement(e: Element): Unit` - * `renderIntoBody[M, A](u: Unmounted[M]): M` -* Asynchronously rendering into DOM with auto-removal - * `withRenderedAsync[M, A](u: Unmounted[M], intoBody: Boolean)(f: M => Future[A]): Future[A]` - * `withRenderedIntoDocumentAsync[M, A](u: Unmounted[M])(f: M => Future[A]): Future[A]` - * `withRenderedIntoBodyAsync[M, A](u: Unmounted[M])(f: M => Future[A]): Future[A]` - * `withNewBodyElementAsync[A](use: Element => Future[A]): Future[A]` -* Mounted props modification - * `replaceProps(component, mounted)(newProps: P): mounted'` - * `modifyProps(component, mounted)(f: P => P): mounted'` -* Other - * `removeReactInternals(html: String): String` - Removes internal annotations from HTML that React inserts. + +- Rendering into DOM with auto-removal + - `withRendered[M, A](u: Unmounted[M], intoBody: Boolean)(f: M => A): A` + - `withRenderedIntoDocument[M, A](u: Unmounted[M])(f: M => A): A` + - `withRenderedIntoBody[M, A](u: Unmounted[M])(f: M => A): A` + - `withNewBodyElement[A](use: Element => A): A` + - `newBodyElement(): Element` + - `removeNewBodyElement(e: Element): Unit` + - `renderIntoBody[M, A](u: Unmounted[M]): M` +- Asynchronously rendering into DOM with auto-removal + - `withRenderedAsync[M, A](u: Unmounted[M], intoBody: Boolean)(f: M => Future[A]): Future[A]` + - `withRenderedIntoDocumentAsync[M, A](u: Unmounted[M])(f: M => Future[A]): Future[A]` + - `withRenderedIntoBodyAsync[M, A](u: Unmounted[M])(f: M => Future[A]): Future[A]` + - `withNewBodyElementAsync[A](use: Element => Future[A]): Future[A]` +- Mounted props modification + - `replaceProps(component, mounted)(newProps: P): mounted'` + - `modifyProps(component, mounted)(f: P => P): mounted'` +- Other + - `removeReactInternals(html: String): String` - Removes internal annotations from HTML that React inserts. There's only one magic implicit method this time around: Mounted components get `.outerHtmlScrubbed()` which is shorthand for `ReactTestUtils.removeReactInternals(m.getDOMNode.outerHTML)`. -`Simulate` and `Simulation` -=========================== +# `Simulate` and `Simulation` + To make event simulation easier, certain event types have dedicated, strongly-typed case classes to wrap event data. For example, JS like + ```js // JavaScript -ReactAddons.TestUtils.Simulate.change(t, {target: {value: "Hi"}}) +ReactAddons.TestUtils.Simulate.change(t, { target: { value: "Hi" } }); ``` + becomes + ```scala // Scala Simulate.change(t, SimEvent.Change(value = "Hi")) @@ -94,6 +95,7 @@ If you'd like more composability and/or purity there's also `Simulation` which represents action (without a target). It does nothing until `.run` is called and a target is provided. Example: + ```scala val a = Simulation.focus val b = Simulation.change(SimEvent.Change(value = "hi")) @@ -110,14 +112,13 @@ val s = Simulation.focusChangeBlur("hi") s run component ``` - -Testing props changes -===================== +# Testing props changes When you want to simulate a parent component re-rendering a child component with different props, you can test the child directly using `ReactTestUtils.{modify,replace}Props`. Example of code to test: + ```scala class CP { var prev = "none" @@ -131,6 +132,7 @@ val CP = ScalaComponent.builder[String]("asd") ``` Example test case: + ```scala ReactTestUtils.withRenderedIntoDocument(CP("start")) { m => assert(m.outerHtmlScrubbed(), "
none → start
") @@ -143,16 +145,15 @@ ReactTestUtils.withRenderedIntoDocument(CP("start")) { m => } ``` - -`ReactTestVar` -============== +# `ReactTestVar` A `ReactTestVar[A]` is a wrapper around a `var a: A` that: -* can produce a `StateSnapshot[A]` with or without `Reusability` -* can produce a `StateAccess[A]` -* retains history when modified -* can perform arbitrary actions when modified -* can be reset + +- can produce a `StateSnapshot[A]` with or without `Reusability` +- can produce a `StateAccess[A]` +- retains history when modified +- can perform arbitrary actions when modified +- can be reset It's useful for testing components that accept `StateSnapshot[A]`/`StateAccess[A]` instances in their props. @@ -207,17 +208,15 @@ ReactTestUtils.withRenderedIntoDocument(component(testVar.stateAccess)) { m => } ``` - -Test Scripts -============ +# Test Scripts It's possible to write test scripts like -1. *click this* -2. *verify that* -3. *press the Back button* -4. *type name* -5. *press Enter* +1. _click this_ +2. _verify that_ +3. _press the Back button_ +4. _type name_ +5. _press Enter_ In case you missed the notice at the top of the file, that functionality is provided in a sister library called [Scala Test-State](https://github.com/japgolly/test-state). @@ -225,44 +224,42 @@ In case you missed the notice at the top of the file, that functionality is prov See [this example](https://github.com/japgolly/test-state/tree/master/example-react) for how to write tests for real-world scalajs-react applications. - -Fatal React warnings -==================== +# Fatal React warnings The easiest way to make `ReactTestUtils` to turn React warnings into runtime exceptions, is via a [config option](./CONFIG.md#testwarningsreact). Alternatively, you can do any of the following... -* Wrapping a test +- Wrapping a test - ```scala - import japgolly.scalajs.react.test.ReactTestUtilsConfig - ReactTestUtilsConfig.AroundReact.fatalReactWarnings { - // test code here - } - ``` + ```scala + import japgolly.scalajs.react.test.ReactTestUtilsConfig + ReactTestUtilsConfig.AroundReact.fatalReactWarnings { + // test code here + } + ``` -* Installing for all `ReactTestUtils` usage +- Installing for all `ReactTestUtils` usage - ```scala - import japgolly.scalajs.react.test.ReactTestUtilsConfig - ReactTestUtilsConfig.aroundReact.set( - ReactTestUtilsConfig.AroundReact.fatalReactWarnings) - ``` + ```scala + import japgolly.scalajs.react.test.ReactTestUtilsConfig + ReactTestUtilsConfig.aroundReact.set( + ReactTestUtilsConfig.AroundReact.fatalReactWarnings) + ``` -* Installing outside of test code +- Installing outside of test code - ```scala - import japgolly.scalajs.react.util.ConsoleHijack - ConsoleHijack.fatalReactWarnings.install() - ``` + ```scala + import japgolly.scalajs.react.util.ConsoleHijack + ConsoleHijack.fatalReactWarnings.install() + ``` -* Wrapping non-test code +- Wrapping non-test code - ```scala - import japgolly.scalajs.react.util.ConsoleHijack - ConsoleHijack.fatalReactWarnings { - // code here - } - ``` + ```scala + import japgolly.scalajs.react.util.ConsoleHijack + ConsoleHijack.fatalReactWarnings { + // code here + } + ``` diff --git a/doc/USAGE.md b/doc/USAGE.md index 287a2e58f..3fbadbaf7 100644 --- a/doc/USAGE.md +++ b/doc/USAGE.md @@ -1,11 +1,11 @@ -Usage -===== +# Usage This will attempt to show you how to use React in Scala. It is expected that you know how React itself works. #### Contents + - [Setup](#setup) - [Creating Virtual-DOM](#creating-virtual-dom) - [Callbacks](#callbacks) @@ -14,89 +14,82 @@ It is expected that you know how React itself works. - [React Extensions](#react-extensions) - [Gotchas](#gotchas) -Setup -===== +# Setup 1. Add [Scala.js](http://www.scala-js.org) to your project. -2. Add *scalajs-react* to SBT: +2. Add _scalajs-react_ to SBT: There are a number of different modules available. On this page we'll just use the `core` module but refer to the [Modules doc](./MODULES.md) to see other module options. - ```scala - // "core" = essentials only. No bells or whistles. - libraryDependencies += "com.github.japgolly.scalajs-react" %%% "core" % "2.1.1" - ``` +```scala +// "core" = essentials only. No bells or whistles. +libraryDependencies += "com.github.japgolly.scalajs-react" %%% "core" % "2.1.1" +``` 3. Add React to your build. - How to do this depends on your Scala.JS config and build setup. + How to do this depends on your Scala.JS config and build setup. - If you're using [scalajs-bundler](https://scalacenter.github.io/scalajs-bundler/), - add the following SBT settings to get started: + If you're using [scalajs-bundler](https://scalacenter.github.io/scalajs-bundler/), + add the following SBT settings to get started: - ```scala - enablePlugins(ScalaJSPlugin) + ```scala + enablePlugins(ScalaJSPlugin) - enablePlugins(ScalaJSBundlerPlugin) + enablePlugins(ScalaJSBundlerPlugin) - libraryDependencies += "com.github.japgolly.scalajs-react" %%% "core" % "2.1.1" + libraryDependencies += "com.github.japgolly.scalajs-react" %%% "core" % "2.1.1" - Compile / npmDependencies ++= Seq( - "react" -> "18.1.0", - "react-dom" -> "18.1.0") - ``` + Compile / npmDependencies ++= Seq( + "react" -> "18.2.0", + "react-dom" -> "18.2.0") + ``` - If you're using `jsDependencies`, add the following: + If you're using `jsDependencies`, add the following: - ```scala - // Required for React 18.1.0 - dependencyOverrides += "org.webjars.npm" % "scheduler" % "0.22.0", + ```scala + // Required for React 18.2.0 + dependencyOverrides += "org.webjars.npm" % "scheduler" % "0.22.0", - jsDependencies ++= Seq( + jsDependencies ++= Seq( - // Polyfill required for React 18.1.0 - "org.webjars.npm" % "fast-text-encoding" % "1.0.3" / "text.js" minified "text.min.js" + // Polyfill required for React 18.2.0 + "org.webjars.npm" % "fast-text-encoding" % "1.0.3" / "text.js" minified "text.min.js" - "org.webjars.npm" % "react" % "18.1.0" - / "umd/react.development.js" - minified "umd/react.production.min.js" - dependsOn "text.js" // <-- Load the fast-text-encoding polyfill before loading React itself - commonJSName "React", + "org.webjars.npm" % "react" % "18.2.0" + / "umd/react.development.js" + minified "umd/react.production.min.js" + dependsOn "text.js" // <-- Load the fast-text-encoding polyfill before loading React itself + commonJSName "React", - "org.webjars.npm" % "react-dom" % "18.1.0" - / "umd/react-dom.development.js" - minified "umd/react-dom.production.min.js" - dependsOn "umd/react.development.js" - commonJSName "ReactDOM", + "org.webjars.npm" % "react-dom" % "18.2.0" + / "umd/react-dom.development.js" + minified "umd/react-dom.production.min.js" + dependsOn "umd/react.development.js" + commonJSName "ReactDOM", - "org.webjars.npm" % "react-dom" % "18.1.0" - / "umd/react-dom-server.browser.development.js" - minified "umd/react-dom-server.browser.production.min.js" - dependsOn "umd/react-dom.development.js" - commonJSName "ReactDOMServer", - ), - ``` + "org.webjars.npm" % "react-dom" % "18.2.0" + / "umd/react-dom-server.browser.development.js" + minified "umd/react-dom-server.browser.production.min.js" + dependsOn "umd/react-dom.development.js" + commonJSName "ReactDOMServer", + ), + ``` [See here](IDE.md) for tips on configuring your IDE. - -Creating Virtual-DOM -==================== +# Creating Virtual-DOM See [VDOM.md](VDOM.md). - -Callbacks -========= +# Callbacks See [CALLBACK.md](CALLBACK.md). - -Creating Components -=================== +# Creating Components This is how to create components from Scala. (For JS components, see [INTEROP.md](INTEROP.md).) @@ -105,29 +98,30 @@ There is a component builder DSL beginning at `ScalaComponent.build`. You throw types and functions at it, call `build` and when it compiles you will have a React component. 1. The first step is to specify your component's properties type, and a component name. - ```scala - import japgolly.scalajs.react._ - import japgolly.scalajs.react.vdom.html_<^._ - object MyComponent { +```scala +import japgolly.scalajs.react._ +import japgolly.scalajs.react.vdom.html_<^._ - case class Props(/* TODO */) +object MyComponent { - val Component = - ScalaComponent.builder[Props] - | - } - ``` + case class Props(/* TODO */) -2. *(Optional)* If you want a stateful component, - call one of the methods beginning with `.initialState`. - Use your IDE to see the methods and the differences in their type signatures. + val Component = + ScalaComponent.builder[Props] + | +} +``` + +2. _(Optional)_ If you want a stateful component, + call one of the methods beginning with `.initialState`. + Use your IDE to see the methods and the differences in their type signatures. -3. *(Optional)* If you want a backend (explained below) for your component - (and you do for non-trivial components), call `.backend`. - If your backend has a `.render` function, instead of `.backend` here you can call `.renderBackend` - which will use a macro to instantiate your backend, and automatically choose the - appropriate `.render` function in the next step, bypassing it for you. +3. _(Optional)_ If you want a backend (explained below) for your component + (and you do for non-trivial components), call `.backend`. + If your backend has a `.render` function, instead of `.backend` here you can call `.renderBackend` + which will use a macro to instantiate your backend, and automatically choose the + appropriate `.render` function in the next step, bypassing it for you. 4. Choose from one of the many available `render` functions. Use your IDE to see the methods and the differences in their type signatures. @@ -135,8 +129,8 @@ You throw types and functions at it, call `build` and when it compiles you will and your backend has a `render` function, you can call `.renderBackend` here to have the builder automatically select the appropriate `render` function. -5. *(Optional)* Type in the name of one of the React lifecycle hooks (eg. `componentDidMount`) - to add that hook to your component. +5. _(Optional)_ Type in the name of one of the React lifecycle hooks (eg. `componentDidMount`) + to add that hook to your component. 6. Call `.build` and you're done.
If your props is a singleton type (eg. `Unit`) then the buider automatically @@ -144,6 +138,7 @@ You throw types and functions at it, call `build` and when it compiles you will props be specified. (See [TYPES.md](TYPES.md) for more info.) Example with props: + ```scala val Hello = ScalaComponent.builder[String] @@ -155,6 +150,7 @@ Hello("Draconus") ``` Example without props: + ```scala val NoArgs = ScalaComponent.builder[Unit] @@ -174,7 +170,7 @@ In plain React with JS, functions which can have access to the component's props are placed within the body of the component class. In scalajs-react you need another place for such functions as scalajs-react emphasises type-safety and provides different types for the component's scope at different points in the lifecycle. -Instead they should be placed in some arbitrary class you may provide, called a *backend*. +Instead they should be placed in some arbitrary class you may provide, called a _backend_. See the [online timer demo](http://japgolly.github.io/scalajs-react/#examples/timer) for an example. @@ -184,7 +180,8 @@ It will locate the `render` method, determine what the arguments need (props/sta types or the arg names when the types are ambiguous, and create the appropriate function at compile-time. If can also automate the creation of the backend, see below. -Example before: *(yuk!)* +Example before: _(yuk!)_ + ```scala type State = Vector[String] @@ -205,6 +202,7 @@ val Example = ScalaComponent.builder[Unit] ``` After: + ```scala class Backend(bs: BackendScope[Unit, State]) { def render(s: State): VdomElement = // ← Accept props, state and/or propsChildren as argument @@ -220,6 +218,7 @@ val Example = ScalaComponent.builder[Unit] ``` You can also create a backend yourself and still use `.renderBackend`: + ```scala val Example = ScalaComponent.builder[Unit] .initialState(Vector("hello", "world")) @@ -228,8 +227,7 @@ val Example = ScalaComponent.builder[Unit] .build ``` -Using Components -================ +# Using Components Once you've created a Scala React component, it mostly acts like a typical Scala case class. To use it, you create an instance. @@ -281,17 +279,17 @@ import org.scalajs.dom.document NoArgs().renderIntoDOM(document.body) ``` -React Extensions -================ +# React Extensions -* Where `setState(State)` is applicable, you can also run: - * `modState(State => State)` - * `modState((State, Props) => State)` - * `setStateOption(Option[State])` - * `modStateOption(State => Option[State])` - * `modStateOption((State, Props) => Option[State])` +- Where `setState(State)` is applicable, you can also run: -* React has a [classSet addon](https://facebook.github.io/react/docs/class-name-manipulation.html) + - `modState(State => State)` + - `modState((State, Props) => State)` + - `setStateOption(Option[State])` + - `modStateOption(State => Option[State])` + - `modStateOption((State, Props) => Option[State])` + +- React has a [classSet addon](https://facebook.github.io/react/docs/class-name-manipulation.html) for specifying multiple optional class attributes. The same mechanism is applicable with this library is as follows: ```scala @@ -312,7 +310,7 @@ React Extensions props.message) ``` -* Sometimes you want to allow a function to both get and affect a portion of a component's state. Anywhere that you can call `.setState()` you can also call `.zoomState()` to return an object that has the same `.setState()`, `.modState()` methods but only operates on a subset of the total state. +- Sometimes you want to allow a function to both get and affect a portion of a component's state. Anywhere that you can call `.setState()` you can also call `.zoomState()` to return an object that has the same `.setState()`, `.modState()` methods but only operates on a subset of the total state. ```scala def incrementCounter(s: StateAccessPure[Int]): Callback = @@ -342,7 +340,7 @@ React Extensions } ``` -* The `.getDOMNode` callback can sometimes execute when unmounted which is an increasingly annoying bug to track down. +- The `.getDOMNode` callback can sometimes execute when unmounted which is an increasingly annoying bug to track down. Since React 16 with its new burn-it-all-down error handling approach, an occurance of this can be fatal. In order to properly model the reality of the callback and ensure compile-time safety, rather than just getting back a VDOM reference, the return type is an ADT like this: @@ -369,13 +367,11 @@ React Extensions In unit tests you'll typically use `asMounted().asElement()` or `asMounted().asText()` for inspection. +# Gotchas -Gotchas -======= - -* `table(tr(...))` will appear to work fine at first then crash later. React needs `table(tbody(tr(...)))`. +- `table(tr(...))` will appear to work fine at first then crash later. React needs `table(tbody(tr(...)))`. -* React's `setState` functions are asynchronous; they don't apply invocations of `this.setState` until the end of `render` or the current callback. Calling `.state` after `.setState` will return the initial, original value, i.e. +- React's `setState` functions are asynchronous; they don't apply invocations of `this.setState` until the end of `render` or the current callback. Calling `.state` after `.setState` will return the initial, original value, i.e. ```scala val s1 = $.state @@ -390,5 +386,5 @@ Gotchas 1. Use `modState`. 2. Refactor your logic so that you only call `setState` once. -* Since `setState` and `modState` return callbacks, if you need to call them from outside of a component (e.g. by accessing the backend of a mounted component), call `.runNow()` to trigger the change; else the callback will never run. +- Since `setState` and `modState` return callbacks, if you need to call them from outside of a component (e.g. by accessing the backend of a mounted component), call `.runNow()` to trigger the change; else the callback will never run. See the [Callbacks](#callbacks) section for more detail. diff --git a/library/ghpages/html/dev2.html b/library/ghpages/html/dev2.html index 32da2b399..100801c39 100644 --- a/library/ghpages/html/dev2.html +++ b/library/ghpages/html/dev2.html @@ -3,8 +3,8 @@ - - + + diff --git a/library/ghpages/html/dev3.html b/library/ghpages/html/dev3.html index 969bdc09e..3a5a65d81 100644 --- a/library/ghpages/html/dev3.html +++ b/library/ghpages/html/dev3.html @@ -3,8 +3,8 @@ - - + + diff --git a/library/ghpages/html/prod.html b/library/ghpages/html/prod.html index dfbff9527..47314020f 100644 --- a/library/ghpages/html/prod.html +++ b/library/ghpages/html/prod.html @@ -3,8 +3,8 @@ - - + + diff --git a/library/project/Dependencies.scala b/library/project/Dependencies.scala index c5a824b13..5bc9cd69a 100644 --- a/library/project/Dependencies.scala +++ b/library/project/Dependencies.scala @@ -27,7 +27,7 @@ object Dependencies { val kindProjector = "0.13.2" val macrotaskExecutor = "1.0.0" val nyaya = "1.0.0" - val reactJs = "18.1.0" + val reactJs = "18.2.0" val scalaJsJavaTime = "1.0.0" val scalaJsSecureRandom = "1.0.0" val scalaTest = "3.2.11" @@ -83,7 +83,7 @@ object Dependencies { Dep.scalaJsDom.value, Dep.univEq.value, Dep.univEqCats.value, - "org.webjars.npm" % "scheduler" % "0.22.0", // Required for React 18.1.0 + "org.webjars.npm" % "scheduler" % "0.22.0", // Required for React 18.2.0 )) final case class ReactArtifact(filename: String) {