From e350e288d4a8aba7eb7a55652f3b17d055b21159 Mon Sep 17 00:00:00 2001
From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com>
Date: Tue, 22 Jul 2025 00:13:16 -0400
Subject: [PATCH 1/3] Add more testing and usage information
---
README.md | 67 +++++++++++++++++++++++++++++++++++----------
src/render-react.ts | 34 +++++++++++++++++++++--
2 files changed, 84 insertions(+), 17 deletions(-)
diff --git a/README.md b/README.md
index 83d066a..5c951b3 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,59 @@ pnpm add @universal-ember/react
## Usage
-Because Ember's tests are the same syntax and style as app code, usage examples in real app code match the tests. Some samples:
+Import the react component into an ember component and render it.
+
+```gjs
+import { makeRenderable } from '@universal-ember/react';
+import { HelloWorld } from './hello-world.tsx';
+
+const Hello = makeRenderable(HelloWorld);
+
+
+
+
+ and with props!
+
+
+ or with a props-bag
+
+
+```
+
+Note that react components should be defined using jsx or tsx. Using jsx/tsx syntax in js/ts is confusing[^and-incorrect] and should be avoided.
+
+[^and-incorrect]: and incorrect -- the impact of JSX and TSX being supported in JS and TS files without the `x` extension has wreaked tons of havoc on the broader JavaScript ecosystem.
+
+### Accessing the owner
+
+```jsx
+import { getOwner } from '@ember/owner';
+
+function MyReactComponent(props) {
+ let owner = getOwner(props);
+ let store = owner.lookup('service:store');
+
+ return <>
+ ... do something with the store ...
+ >;
+}
+
+```
+
+## Testing
+
+testing with React components is a bit harder than with native ember components, because react testing doesn't have any sort of test-waiter system. The test-waiter system is something library-devs use to make testing easier for app developers, so that app develpers never need to worry about `waitUntil`-style timing.
+
+That said, all `@ember/test-helpers` should still work with React subtrees.
+Just the same, `testing-library` works well across both frameworks.
+
+But, in React, it's very important to minimize the number of effects use, preferring data derivation, and only using effects as a last resort.
+
+
+### Examples
+
+
+These examples come from this library's own test suite.
```gjs
import { Greet, HelloWorld } from './hello-world.tsx';
@@ -40,19 +92,6 @@ module('makeRenderable', function (hooks) {
});
```
-Note that react components should be defined using jsx or tsx. Using jsx/tsx syntax in js/ts is confusing[^and-incorrect] and should be avoided.
-
-[^and-incorrect]: and incorrect -- the impact of JSX and TSX being supported in JS and TS files without the `x` extension has wreaked tons of havoc on the broader JavaScript ecosystem.
-
-## Testing
-
-testing with React components is a bit harder than with native ember components, because react testing doesn't have any sort of test-waiter system. The test-waiter system is something library-devs use to make testing easier for app developers, so that app develpers never need to worry about `waitUntil`-style timing.
-
-That said, all `@ember/test-helpers` should still work with React subtrees.
-Just the same, `testing-library` works well across both frameworks.
-
-But, in React, it's very important to minimize the number of effects use, preferring data derivation, and only using effects as a last resort.
-
## Contributing
diff --git a/src/render-react.ts b/src/render-react.ts
index 2dd85ce..378a769 100644
--- a/src/render-react.ts
+++ b/src/render-react.ts
@@ -3,9 +3,11 @@ import { waitForPromise } from '@ember/test-waiters';
import { setComponentTemplate } from '@ember/component';
import Modifier from 'ember-modifier';
import type { ComponentLike } from '@glint/template';
-import React from 'react';
+import React, { act } from 'react';
import { createRoot } from 'react-dom/client';
import Component from '@glimmer/component';
+import { macroCondition, isTesting } from '@embroider/macros';
+import { setOwner, getOwner } from '@ember/owner';
interface Signature {
Args: {
@@ -22,7 +24,14 @@ export function makeRenderable<
>(ReactComponent: ReactComponentType): ComponentLike> {
class ReactRoot extends Component> {
get props() {
- return this.args.props ? { ...this.args.props } : { ...this.args };
+ const owner = getOwner(this);
+ const props = this.args.props ? { ...this.args.props } : { ...this.args };
+
+ if (owner) {
+ setOwner(props, owner);
+ }
+
+ return props;
}
}
return setComponentTemplate(
@@ -53,8 +62,27 @@ class mount extends Modifier<{
) {
this.#root ||= createRoot(element);
- this.#root.render(React.createElement(component as any, props));
+ const toRender = React.createElement(component as any, props);
+ /**
+ * Subsequent re-renders will diff and replace contents as needed.
+ */
+ if (macroCondition(isTesting())) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ (window as any).IS_REACT_ACT_ENVIRONMENT = true;
+ act(() => {
+ this.#root?.render(toRender);
+ });
+ } else {
+ this.#root.render(toRender);
+ }
+ /**
+ * For ember's test waiter system.
+ * We don't know how long a react component will take to render,
+ * but it often doesn't finish synchronously.
+ *
+ * Waiting until the next animation frame before test executions continues.
+ */
waitForPromise(
(async () => {
await new Promise((resolve) => {
From ee54105e591698c2455d4f84fb93838610393df5 Mon Sep 17 00:00:00 2001
From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com>
Date: Tue, 22 Jul 2025 00:15:51 -0400
Subject: [PATCH 2/3] Updates to testing
---
.try.mjs | 51 -------------------------------------------------
README.md | 2 +-
vite.config.mjs | 7 ++-----
3 files changed, 3 insertions(+), 57 deletions(-)
diff --git a/.try.mjs b/.try.mjs
index 40bf32c..800056c 100644
--- a/.try.mjs
+++ b/.try.mjs
@@ -1,56 +1,5 @@
-// When building your addon for older Ember versions you need to have the required files
-const compatFiles = {
- 'ember-cli-build.js': `const EmberApp = require('ember-cli/lib/broccoli/ember-app');
-const { compatBuild } = require('@embroider/compat');
-module.exports = async function (defaults) {
- const { buildOnce } = await import('@embroider/vite');
- let app = new EmberApp(defaults);
- return compatBuild(app, buildOnce);
-};`,
- 'config/optional-features.json': JSON.stringify({
- 'application-template-wrapper': false,
- 'default-async-observers': true,
- 'jquery-integration': false,
- 'template-only-glimmer-components': true,
- 'no-implicit-route-model': true,
- }),
-};
-
-const compatDeps = {
- '@embroider/compat': '^4.0.3',
- 'ember-cli': '^5.12.0',
- 'ember-auto-import': '^2.10.0',
- '@ember/optional-features': '^2.2.0',
-};
-
export default {
scenarios: [
- {
- name: 'ember-lts-5.8',
- npm: {
- devDependencies: {
- 'ember-source': '~5.8.0',
- ...compatDeps,
- },
- },
- env: {
- ENABLE_COMPAT_BUILD: true,
- },
- files: compatFiles,
- },
- {
- name: 'ember-lts-5.12',
- npm: {
- devDependencies: {
- 'ember-source': '~5.12.0',
- ...compatDeps,
- },
- },
- env: {
- ENABLE_COMPAT_BUILD: true,
- },
- files: compatFiles,
- },
{
name: `ember-lts-6.4`,
npm: {
diff --git a/README.md b/README.md
index 5c951b3..f0fa52a 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ React integration for Ember with reactive updating.
## Compatibility
-- Ember.js v5.8 or above
+- Ember.js v6.3 or above, not that earlier won't work. But this repo isn't testing prior to 6.3
## Installation
diff --git a/vite.config.mjs b/vite.config.mjs
index 4d4e957..8800d93 100644
--- a/vite.config.mjs
+++ b/vite.config.mjs
@@ -1,15 +1,11 @@
import { defineConfig } from 'vite';
-import { extensions, ember, classicEmberSupport } from '@embroider/vite';
+import { extensions, ember } from '@embroider/vite';
import { babel } from '@rollup/plugin-babel';
import react from '@vitejs/plugin-react';
-// For scenario testing
-const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD);
-
export default defineConfig({
plugins: [
- ...(isCompat ? [classicEmberSupport()] : []),
ember(),
react(),
babel({
@@ -18,6 +14,7 @@ export default defineConfig({
}),
],
build: {
+ minify: false,
rollupOptions: {
input: {
tests: 'tests/index.html',
From d8325968aec57e181028c19b16d531b5f0af79f8 Mon Sep 17 00:00:00 2001
From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com>
Date: Tue, 22 Jul 2025 00:17:34 -0400
Subject: [PATCH 3/3] Fix tests
---
vite.config.mjs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/vite.config.mjs b/vite.config.mjs
index 8800d93..2cf607a 100644
--- a/vite.config.mjs
+++ b/vite.config.mjs
@@ -1,4 +1,4 @@
-import { defineConfig } from 'vite';
+import { defineConfig, splitVendorChunkPlugin } from 'vite';
import { extensions, ember } from '@embroider/vite';
import { babel } from '@rollup/plugin-babel';
@@ -6,6 +6,7 @@ import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
+ splitVendorChunkPlugin(),
ember(),
react(),
babel({