blob: e3acdb68b2f6d4a40e310ed68f80c3f5f183d864 [file] [log] [blame] [view]
<!--
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
# Zeppelin React
React micro-frontend that runs alongside the Angular host via [Webpack Module Federation](https://webpack.js.org/concepts/module-federation/).
- Design Document: [Micro Frontend Migration (Angular to React) Proposal](https://cwiki.apache.org/confluence/display/ZEPPELIN/Micro+Frontend+Migration%28Angular+to+React%29+Proposal)
## Migration roadmap
| Phase | Scope | Status |
|-------|-------|--------|
| 1 | Webpack 5 + Module Federation setup | Done |
| 1.5 | Published paragraph (pilot) | Done |
| 2 | Notebook and interpreter modules | Planned |
The published paragraph was picked as pilot because it's read-only and has almost no coupling to other modules.
## Architecture
```
Angular host (port 4200) React remote (port 3001)
┌─────────────────────────┐ ┌─────────────────────────┐
│ paragraph.component.ts │ │ webpack.config.js │
│ loads remoteEntry.js ──┼──────>│ ModuleFederationPlugin │
│ calls mount(el, props) │ │ name: 'reactApp' │
└─────────────────────────┘ │ exposes: │
│ ./PublishedParagraph │
└─────────────────────────┘
```
1. Angular loads `remoteEntry.js` from the React dev server or production assets.
2. The script registers `window.reactApp` as a Module Federation container.
3. Angular calls `container.get('./PublishedParagraph')` to get the module.
4. The module exports `mount(element, props)`, which calls `createRoot()` and renders into the DOM element.
Append `?react=true` to any published paragraph URL to activate React mode.
## Setup
Run `npm install` then `npm run dev` to start the dev server on `http://localhost:3001`.
The Angular host must be running on port 4200. From `zeppelin-web-angular/`, `npm start` runs both servers together.
## Build
From `projects/zeppelin-react/`, run `npm run build`. Output goes to `dist/`. In production, Angular loads `remoteEntry.js` from `/assets/react/` (see `environment.prod.ts`).
## Linting
From `projects/zeppelin-react/`, run `npm run lint` to check, `npm run lint:fix` to auto-fix. See `.eslintrc.json` for rules.
## Project structure
```
src/
├── components/
│ ├── common/ # Empty, Loading
│ ├── renderers/ # HTMLRenderer, ImageRenderer, TextRenderer
│ └── visualizations/ # TableVisualization, VisualizationControls
├── pages/
│ └── PublishedParagraph.tsx # entry component + mount()
├── templates/
│ └── SingleResultRenderer.tsx # routes result types to renderers
├── utils/ # tableUtils, textUtils, exportFile
└── main.ts # re-exports for Module Federation
```
## Adding a new React module
1. Create a component (e.g. `src/pages/ExampleFeature.tsx`).
2. Export a `mount(element, props)` function that creates a React root and renders the component.
3. Register in `webpack.config.js` under `exposes`:
```js
exposes: {
'./PublishedParagraph': './src/pages/PublishedParagraph',
'./ExampleFeature': './src/pages/ExampleFeature'
}
```
4. Re-export from `main.ts`:
```ts
export { ExampleFeature, mount as mountExampleFeature } from './pages/ExampleFeature';
```
5. Load from Angular (same pattern as `paragraph.component.ts`):
```ts
const factory = await container.get('./ExampleFeature');
const { mount } = factory();
mount(hostElement, props);
```