|
1 | 1 | --- |
2 | 2 | name: writing-atlas3-plugins |
3 | | -description: Use when building, scaffolding or changing an Atlas3 / OHDSI ATLAS v3 plugin — "build an atlas plugin that…", "add a plugin to atlas", a new Atlas panel/tab/FAB/menu entry, or an Atlas plugin that has to match a Figma design. Not for Data2Evidence portal apps. |
| 3 | +description: Use when building, scaffolding or changing an Atlas3 / OHDSI ATLAS v3 plugin — "build an atlas plugin that…", "add a plugin to atlas", a new Atlas panel/tab/FAB/menu entry, or an Atlas plugin that has to match a Figma design. Also use when you need an Atlas3 component's real props/events or the host plugin contract: the published package ships no source or types, so this skill covers checking out OHDSI/Atlas3, reading @ohdsi/atlas-ui's actual signatures, and building the UI from the Atlas* component library rather than hand-rolled markup. Not for Data2Evidence portal apps. |
4 | 4 | --- |
5 | 5 |
|
6 | 6 | # Writing Atlas3 plugins |
7 | 7 |
|
8 | 8 | An Atlas3 plugin is a **single-spa parcel** built in **`system` module format**, |
9 | 9 | dropped into the host's `public/plugins/<id>/` and declared in a JSON manifest. |
10 | | -`/home/ph/code/Atlas3/docs/plugin-development-with-atlas-ui.md` is the reference |
11 | | -for the contract, `PluginProps`, the `Atlas*` component library and theming — |
12 | | -**read it before implementing**. This skill is the operational wrapper: how to |
13 | | -scaffold, name, build, register and verify. Run the app with |
14 | | -**`testing-atlas3-locally`**. |
| 10 | +`docs/plugin-development-with-atlas-ui.md` **inside the Atlas3 checkout** is the |
| 11 | +reference for the contract, `PluginProps`, the `Atlas*` component library and |
| 12 | +theming — **read it before implementing**. This skill is the operational |
| 13 | +wrapper: how to check out the source, find the real contracts, scaffold, name, |
| 14 | +build, register and verify. Run the app with **`testing-atlas3-locally`**. |
| 15 | + |
| 16 | +**You cannot do this job from `node_modules` alone.** The published |
| 17 | +`@ohdsi/atlas3` package sets `"files": ["dist"]` and ships **no `.d.ts` and no |
| 18 | +source** — just a built bundle. Every component signature, prop, event and type |
| 19 | +below exists *only* in the Atlas3 git repository. Guessing a prop name because |
| 20 | +it sounds right is the single most common way these plugins fail. Check the |
| 21 | +source out (step 1) and read it. |
15 | 22 |
|
16 | 23 | ## Workflow |
17 | 24 |
|
18 | | -1. **Design first, if there is one.** A Figma link (or "match this design") → |
| 25 | +1. **Check out Atlas3 — always, before anything else.** Clone it if it isn't |
| 26 | + already present, then work *inside* it under `plugins-dev/`: the reference |
| 27 | + plugin's `@ohdsi/atlas-ui` and `outDir` paths are relative to that location. |
| 28 | + |
| 29 | + ```bash |
| 30 | + ATLAS3_DIR="${ATLAS3_DIR:-$HOME/code/Atlas3}" |
| 31 | + [ -d "$ATLAS3_DIR/.git" ] || git clone git@github.com:OHDSI/Atlas3.git "$ATLAS3_DIR" |
| 32 | + git -C "$ATLAS3_DIR" fetch origin --quiet |
| 33 | + ``` |
| 34 | + |
| 35 | + A pre-existing checkout may be stale or on someone's feature branch — check |
| 36 | + `git -C "$ATLAS3_DIR" log --oneline -1` and `git status` before trusting it; |
| 37 | + `git pull` on `develop` if it is behind. If the SSH remote is unavailable, |
| 38 | + fall back to `https://github.com/OHDSI/Atlas3.git`. |
| 39 | +2. **Read the real contracts out of that checkout** before writing a component — |
| 40 | + see *Finding the exact contract in source* below. For worked examples beyond |
| 41 | + the hello-world starter, read the shipped plugins in `OHDSI/trex-notebook` |
| 42 | + (see *Reference implementations*). |
| 43 | +3. **Design first, if there is one.** A Figma link (or "match this design") → |
19 | 44 | use **`pulling-figma-mockups`** BEFORE writing any component, then implement |
20 | 45 | against the `.spec.json` (see *Figma → Atlas tokens* below). |
21 | | -2. **Locate Atlas3.** `/home/ph/code/Atlas3` locally, otherwise clone |
22 | | - `OHDSI/Atlas3`. Work *inside* it, under `plugins-dev/` — the reference |
23 | | - plugin's `@ohdsi/atlas-ui` and `outDir` paths are relative to that location. |
24 | | -3. **Scaffold by copying**, never from scratch: |
| 46 | +4. **Scaffold by copying**, never from scratch: |
25 | 47 | `cp -r plugins-dev/hello-world-plugin plugins-dev/<plugin-id>`, then delete |
26 | 48 | its `package-lock.json`. You get a complete working parcel: `package.json`, |
27 | 49 | `vite.config.mjs`, `tsconfig.json`, `tsconfig.node.json`, `src/main.ts`, |
28 | 50 | `src/App.vue`. |
29 | | -4. **Rename the id consistently** (table below). A mismatch fails silently — |
| 51 | +5. **Rename the id consistently** (table below). A mismatch fails silently — |
30 | 52 | the app boots, the plugin just never appears. |
31 | | -5. **Build the component library once**, from the Atlas3 root: |
| 53 | +6. **Build the component library once**, from the Atlas3 root: |
32 | 54 | `npm run lib:build`. `@ohdsi/atlas-ui`'s `exports` point at the gitignored |
33 | 55 | `packages/atlas-ui/dist/`, absent in a fresh checkout; without it the plugin |
34 | 56 | build dies on *Failed to resolve entry for package @ohdsi/atlas-ui*. |
35 | | -6. **Implement.** Leave `main.ts` alone (lifecycles + `buildVuetifyOptions()`); |
36 | | - put your UI in `App.vue` and components beside it. |
37 | | -7. **Build:** `cd plugins-dev/<plugin-id> && npm install && npm run build` |
| 57 | +7. **Implement out of `Atlas*` components** (see *Build the UI from the Atlas |
| 58 | + component library* — this is the rule, not a preference). Leave `main.ts` |
| 59 | + alone (lifecycles + `buildVuetifyOptions()`); put your UI in `App.vue` and |
| 60 | + components beside it. |
| 61 | +8. **Build:** `cd plugins-dev/<plugin-id> && npm install && npm run build` |
38 | 62 | → `public/plugins/<plugin-id>/index.system.js`. |
39 | | -8. **Register** (below), then verify with **`testing-atlas3-locally`**. |
| 63 | +9. **Register** (below), then verify with **`testing-atlas3-locally`**. |
| 64 | + |
| 65 | +## Build the UI from the Atlas component library |
| 66 | + |
| 67 | +**Every piece of UI you render must come from an `Atlas*` component when one |
| 68 | +exists for the job.** The point of a plugin is that it is indistinguishable |
| 69 | +from native Atlas — an `Atlas*` component already carries the theme, the |
| 70 | +spacing scale, the dark-mode palette, the focus/ARIA behaviour and the |
| 71 | +Atlas-specific defaults. Hand-rolled markup gets none of that and drifts the |
| 72 | +moment the design system moves. |
| 73 | + |
| 74 | +The precedence, highest first: |
| 75 | + |
| 76 | +1. **`Atlas*` component** from `@ohdsi/atlas-ui` — always, if one fits. |
| 77 | + `packages/atlas-ui/index.ts` is the list of what exists; check it before |
| 78 | + concluding there is nothing. |
| 79 | +2. **Raw Vuetify (`v-*`)** — only for a primitive with no `Atlas*` wrapper. |
| 80 | + Style it with theme tokens (`--v-theme-*`, `--atlas-*`), never literal |
| 81 | + colors or pixel values. |
| 82 | +3. **Your own markup** — last resort, for genuinely bespoke layout. Same token |
| 83 | + rule applies, and it still sits inside `AtlasPageShell`/`AtlasContainer`. |
| 84 | + |
| 85 | +Never introduce a *third-party* UI kit or icon set into a plugin — no Element |
| 86 | +Plus, no Bootstrap, no Tailwind. Vuetify + `@ohdsi/atlas-ui` + `mdi` icons is |
| 87 | +the whole toolkit; a second one ships a duplicate CSS reset and visibly breaks |
| 88 | +the shell. |
| 89 | + |
| 90 | +If you conclude no `Atlas*` component fits, say which one you looked at and why |
| 91 | +it did not, in your reply. That is a design-system gap worth reporting, and it |
| 92 | +is the difference between a considered fallback and a silent one. |
| 93 | + |
| 94 | +## Finding the exact contract in source |
| 95 | + |
| 96 | +Read these files in the Atlas3 checkout — do not infer any of it, and do not |
| 97 | +reach for a raw Vuetify component when an `Atlas*` wrapper exists. |
| 98 | + |
| 99 | +| What you need | Read (paths relative to the Atlas3 checkout) | |
| 100 | +|---|---| |
| 101 | +| **Which components exist** — the authoritative inventory | `packages/atlas-ui/index.ts` (every export in one file) | |
| 102 | +| **A component's exact props, events, slots, defaults** | `src/components/ui/<Name>.vue` — read its `defineProps`/`defineEmits`; charts live under `src/components/ui/charts/` | |
| 103 | +| **Host → plugin contract** (`PluginProps`, `AuthContext`, `PluginMessageBus`) | `src/models/PluginModels.ts` — also the zod schema the manifest is validated against | |
| 104 | +| **Theme tokens and the Vuetify options builder** | `src/ui/tokens.ts`, `src/ui/theme.ts` (`buildVuetifyOptions`), generated `src/ui/tokens.css` | |
| 105 | +| **Chart data types and option builders** | `src/ui/chart-types.ts`, `src/ui/chart-config.ts` (`CHART_COLORS`, `multiLineChartOptions`, …) | |
| 106 | +| **A complete working parcel** | `plugins-dev/hello-world-plugin/` | |
| 107 | +| **The narrative guide** | `docs/plugin-development-with-atlas-ui.md` | |
| 108 | + |
| 109 | +Fast ways to answer a concrete question, run from the checkout root: |
| 110 | + |
| 111 | +```bash |
| 112 | +# Is there an Atlas component for this, and what is it called? |
| 113 | +grep -n 'export { default as Atlas' packages/atlas-ui/index.ts |
| 114 | + |
| 115 | +# What props/events does one actually take? |
| 116 | +sed -n '1,80p' src/components/ui/AtlasDataTable.vue |
| 117 | + |
| 118 | +# What non-component values are exported (tokens, builders, types)? |
| 119 | +grep -nE '^export (\{|type|const)' packages/atlas-ui/index.ts | grep -v 'default as' |
| 120 | +``` |
| 121 | + |
| 122 | +The inventory currently spans form controls (`AtlasTextField`, `AtlasSelect`, |
| 123 | +`AtlasAutocomplete`, `AtlasCheckbox`, `AtlasRadioGroup`, `AtlasSwitch`), layout |
| 124 | +(`AtlasPageShell`, `AtlasContainer`, `AtlasRow`, `AtlasCol`, `AtlasCard`, |
| 125 | +`AtlasDivider`, `AtlasSpacer`), feedback (`AtlasAlert`, `AtlasBanner`, |
| 126 | +`AtlasSnackbar`, `AtlasDialog`, `AtlasProgressLinear`, `AtlasProgressCircular`, |
| 127 | +`AtlasSkeleton`), navigation (`AtlasTabs`/`AtlasTab`, `AtlasMenu`, `AtlasList`/ |
| 128 | +`AtlasListItem`, `AtlasPagination`), data display (`AtlasDataTable`, |
| 129 | +`AtlasChip`, `AtlasBadge`, `AtlasAvatar`, `AtlasTooltip`, `AtlasIcon`) and |
| 130 | +charts (`AtlasBarChart`, `AtlasLineChart`, `AtlasPieChart`, `AtlasBoxPlotChart`, |
| 131 | +`AtlasTreemapChart`, `AtlasSunburstChart`, `AtlasTrellisChart`, |
| 132 | +`AtlasChartExport`). Treat that list as a hint about *where to look*, not as |
| 133 | +truth — `packages/atlas-ui/index.ts` in the checkout you just pulled is truth. |
| 134 | + |
| 135 | +## Reference implementations |
| 136 | + |
| 137 | +`OHDSI/trex-notebook` ships the production Atlas3 plugins and is the best |
| 138 | +source of worked patterns beyond the hello-world starter. Check it out the same |
| 139 | +way (`git clone git@github.com:OHDSI/trex-notebook.git`); in a Data2Evidence |
| 140 | +checkout it is already vendored at `plugins/atlas/trex-notebook/`. |
| 141 | + |
| 142 | +Its **UI** plugins — `network`, `notebook-plugin`, `results-viewer`, `sibyl`, |
| 143 | +`strategus`, `studies` (each under `plugins/`) — all depend on |
| 144 | +`@ohdsi/atlas-ui`, build `formats: ['system']`, and are worth reading for how a |
| 145 | +real plugin structures views, state (Pinia) and API calls. The `*-api` siblings |
| 146 | +(`hades-api`, `metadata-api`, `network-api`) are backend plugins with no UI — |
| 147 | +not templates for this task. |
| 148 | + |
| 149 | +Note they depend on a **published** `"@ohdsi/atlas-ui": "^0.1.0-…"` version, |
| 150 | +whereas a plugin developed inside the Atlas3 monorepo uses |
| 151 | +`file:../../packages/atlas-ui`. Match whichever tree you are working in. |
| 152 | + |
| 153 | +**Do not use `plugins/ui/apps/vue-mri-ui-lib` (the D2E patient-analytics app) |
| 154 | +as a template.** It is a large standalone SAPUI5-era Vue app that predates this |
| 155 | +contract: it is not a single-spa parcel, does not use `@ohdsi/atlas-ui`, and |
| 156 | +copying it produces a plugin that neither themes nor mounts correctly. |
40 | 157 |
|
41 | 158 | ## The id appears in five places — all must agree |
42 | 159 |
|
|
0 commit comments