Skip to content

Commit 7e16630

Browse files
Merge pull request #466 from AgnosticUI/issue-465/adaptive-questionnaire-vue-lit
feat(sdui): port adaptive questionnaire to Vue and Lit demos (#465)
2 parents d37f5af + c500406 commit 7e16630

File tree

22 files changed

+765
-105
lines changed

22 files changed

+765
-105
lines changed

v2/lib/src/components/Input/core/_Input.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,11 @@ export class AgInput extends FaceMixin(LitElement) implements InputProps {
592592
if (this.onChange) {
593593
this.onChange(e);
594594
}
595+
596+
// Native `change` is composed:false so it stops at the shadow root.
597+
// Re-dispatch a composed change from the host so Vue @change listeners
598+
// and other external DOM listeners receive it.
599+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
595600
}
596601

597602
/**

v2/lib/src/components/Input/vue/VueInput.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
:type="type"
1717
:placeholder="placeholder"
1818
:label="label"
19+
:labelPosition="labelPosition"
1920
:ariaLabel="ariaLabel"
2021
:helpText="helpText"
2122
:errorMessage="errorMessage"
2223
:size="size"
2324
:rows="rows"
2425
:cols="cols"
26+
:min="min"
27+
:max="max"
2528
@click="handleClick"
2629
@input="handleInput"
2730
@change="handleChange"

v2/sdui/DEV-WORKFLOW.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# SDUI Demo Development Workflow
2+
3+
This guide explains the local dependency chain, what to rebuild after upstream changes, and
4+
how to avoid stale-cache pitfalls.
5+
6+
## Dependency chain
7+
8+
```
9+
v2/lib (agnosticui-core — web components, Vue/React wrappers)
10+
└── v2/sdui/schema (@agnosticui/schema — AgNode type definitions)
11+
└── v2/sdui/renderers/react (@agnosticui/render-react)
12+
└── v2/sdui/renderers/vue (@agnosticui/render-vue)
13+
└── v2/sdui/renderers/lit (@agnosticui/render-lit)
14+
└── v2/sdui/demo (React demo app)
15+
└── v2/sdui/demo-vue (Vue demo app)
16+
└── v2/sdui/demo-lit (Lit demo app)
17+
```
18+
19+
Each arrow is a `file:` dependency in `package.json`. npm installs these as **symlinks** in
20+
`node_modules`, so demos always resolve to the local source. However, all packages under
21+
`v2/lib` and `v2/sdui/renderers/*` are **TypeScript projects that must be compiled to `dist/`
22+
before their consumers can see changes.**
23+
24+
## When to rebuild what
25+
26+
| You changed... | Rebuild command | Then... |
27+
|---|---|---|
28+
| `v2/lib/src/**` (core components, Vue/React wrappers) | `cd v2/lib && npm run build` | Restart the affected demo dev server |
29+
| `v2/sdui/schema/src/**` | `cd v2/sdui/schema && npm run build` | Rebuild any affected renderer (see below) |
30+
| `v2/sdui/renderers/vue/src/**` | `cd v2/sdui/renderers/vue && npm run build` | Restart demo-vue dev server |
31+
| `v2/sdui/renderers/lit/src/**` | `cd v2/sdui/renderers/lit && npm run build` | Restart demo-lit dev server |
32+
| `v2/sdui/renderers/react/src/**` | `cd v2/sdui/renderers/react && npm run build` | Restart demo (React) dev server |
33+
| Fixture files in `v2/sdui/demo/src/fixtures/**` | None — imported as raw TypeScript by all three demos | Just save; Vite HMR picks it up |
34+
35+
### Rebuilding everything from scratch
36+
37+
```bash
38+
# From the repo root (v2/)
39+
cd lib && npm run build && cd ..
40+
cd sdui/schema && npm run build && cd ../..
41+
cd sdui/renderers/react && npm run build && cd ../../..
42+
cd sdui/renderers/vue && npm run build && cd ../../..
43+
cd sdui/renderers/lit && npm run build && cd ../../..
44+
```
45+
46+
## First-time setup for a demo app
47+
48+
If a demo's `node_modules` folder does not exist yet (e.g. after a fresh clone or after
49+
deleting it), run `npm install` inside the demo directory. npm resolves all `file:` deps as
50+
symlinks automatically.
51+
52+
```bash
53+
# Example for demo-lit (which ships without node_modules committed)
54+
cd v2/sdui/demo-lit && npm install && npm run dev
55+
```
56+
57+
## The Vite pre-bundle cache (`.vite/deps`)
58+
59+
Vite pre-bundles certain dependencies into `.vite/deps/` for faster startup. The demos use
60+
`optimizeDeps.noDiscovery: true` plus an explicit `include` list, so **`agnosticui-core` and
61+
the SDUI renderers are NOT pre-bundled** — they are served directly from the symlinked `dist/`
62+
folders on disk.
63+
64+
The only package currently pre-bundled in demo-vue is `vue` itself. You should not need to
65+
clear the Vite cache after a normal upstream rebuild.
66+
67+
**However**, if you ever see a demo serving unmistakably stale code after a rebuild and dev
68+
server restart, clear the cache:
69+
70+
```bash
71+
# Stop the dev server first, then:
72+
rm -rf v2/sdui/demo-vue/node_modules/.vite
73+
rm -rf v2/sdui/demo-lit/node_modules/.vite
74+
rm -rf v2/sdui/demo/node_modules/.vite
75+
# Then restart: npm run dev
76+
```
77+
78+
## Convenience scripts (add to each demo's `package.json`)
79+
80+
You can add the following scripts to each demo app to make the rebuild loop faster. These are
81+
intentionally not committed by default because they encode absolute paths; adjust for your
82+
machine.
83+
84+
```jsonc
85+
// v2/sdui/demo-vue/package.json (example)
86+
"scripts": {
87+
"dev": "vite",
88+
"build": "vue-tsc -b && vite build",
89+
"preview": "vite preview",
90+
"rebuild:lib": "npm run build --prefix ../../lib",
91+
"rebuild:renderer": "npm run build --prefix ../renderers/vue",
92+
"rebuild:all": "npm run rebuild:lib && npm run rebuild:renderer",
93+
"fresh": "npm run rebuild:all && rm -rf node_modules/.vite && npm run dev"
94+
}
95+
```
96+
97+
Run `npm run fresh` after any upstream change: it rebuilds both lib and the renderer, clears
98+
the Vite pre-bundle cache, then starts the dev server.
99+
100+
## Common pitfalls
101+
102+
### Symptom: a prop (e.g. `min`, `max`) is passed in the fixture but has no effect in the UI
103+
104+
**Cause:** The renderer dist is stale — the prop is in the renderer's TypeScript source but
105+
was added after the last `npm run build`.
106+
107+
**Fix:** Rebuild the affected renderer.
108+
109+
```bash
110+
cd v2/sdui/renderers/vue && npm run build
111+
# or
112+
cd v2/sdui/renderers/lit && npm run build
113+
```
114+
115+
### Symptom: an event (e.g. `@change`) fires in React but not in Vue or Lit
116+
117+
**Cause:** Native DOM events like `change` are `composed: false` by default, so they stop at
118+
the shadow root of a LitElement and never reach outside listeners. Only `input` and `click`
119+
are `composed: true` natively.
120+
121+
**Fix:** In the web component's event handler, re-dispatch a composed `CustomEvent` (or a
122+
plain `Event` with `composed: true`) from the host element so external listeners receive it.
123+
All AgnosticUI form components should do this; if one is missing it, add it to the
124+
component's `_handle*` method in `v2/lib/src/`.
125+
126+
### Symptom: Vue `@change` on `ag-input` never fires
127+
128+
Same root cause as above. The fix is in `v2/lib/src/components/Input/core/_Input.ts`:
129+
`_handleChange` must dispatch `new Event('change', { bubbles: true, composed: true })` from
130+
`this` (the host element) after handling the native event.
131+
132+
### Symptom: a Vue wrapper prop (e.g. `min`, `labelPosition`) is declared but silently ignored
133+
134+
**Cause:** Vue 3 `defineProps` removes declared props from `$attrs`. If a prop is declared in
135+
`withDefaults(defineProps<...>(), {...})` but is NOT explicitly bound in the template, Vue
136+
will not forward it to the underlying web component.
137+
138+
**Fix:** Add the binding to the wrapper template. Example in `VueInput.vue`:
139+
140+
```html
141+
:min="min"
142+
:max="max"
143+
:labelPosition="labelPosition"
144+
```
145+
146+
React is not affected (all props are forwarded automatically via `@lit/react` `createComponent`).
147+
Lit is not affected (direct property binding in the renderer template).
148+
149+
## Summary checklist after an upstream change
150+
151+
- [ ] `cd v2/lib && npm run build` if any core component or Vue/React wrapper changed
152+
- [ ] `cd v2/sdui/renderers/[vue|lit|react] && npm run build` if the corresponding renderer changed
153+
- [ ] `cd v2/sdui/schema && npm run build` if the schema types changed
154+
- [ ] Restart the demo dev server
155+
- [ ] If something still looks stale: `rm -rf node_modules/.vite` in the demo, then restart

v2/sdui/demo-lit/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

v2/sdui/demo-lit/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
"scripts": {
77
"dev": "vite",
88
"build": "tsc -b && vite build",
9-
"preview": "vite preview"
9+
"preview": "vite preview",
10+
"rebuild:lib": "npm run build --prefix ../../lib",
11+
"rebuild:renderer": "npm run build --prefix ../renderers/lit",
12+
"rebuild:all": "npm run rebuild:lib && npm run rebuild:renderer",
13+
"fresh": "npm run rebuild:all && rm -rf node_modules/.vite && npm run dev"
1014
},
1115
"dependencies": {
1216
"@agnosticui/render-lit": "file:../renderers/lit",

v2/sdui/demo-lit/src/AgSduiDemo.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ import { state } from 'lit/decorators.js';
33
import 'agnosticui-core/header';
44
import './components/WorkflowPicker';
55
import './components/StreamingOutput';
6+
import './components/AdaptiveOutput';
67
import './SkinSwitcher';
78

9+
const ADAPTIVE_WORKFLOW = 'adaptive-questionnaire';
10+
811
export class AgSduiDemo extends LitElement {
912
static styles = css`
1013
:host {
1114
display: block;
1215
}
1316
1417
.demo-layout {
15-
max-width: 800px;
18+
max-width: 900px;
1619
margin: 0 auto;
1720
margin-block-start: var(--ag-space-8, 2rem);
1821
padding: 0 1.5rem 2rem;
@@ -55,11 +58,32 @@ export class AgSduiDemo extends LitElement {
5558
color: var(--ag-text-muted, #666);
5659
}
5760
61+
.demo-picker-section label {
62+
display: block;
63+
font-size: 0.85rem;
64+
font-weight: 600;
65+
text-transform: uppercase;
66+
letter-spacing: 0.05em;
67+
color: var(--ag-text-muted, #666);
68+
margin-bottom: 0.75rem;
69+
}
70+
71+
.demo-output-body {
72+
display: flex;
73+
flex-direction: column;
74+
gap: var(--ag-space-4, 1rem);
75+
min-height: 120px;
76+
}
77+
5878
`;
5979

6080
@state() private workflow = 'contact-form';
6181
@state() private seed = 0;
6282

83+
private get isAdaptive() {
84+
return this.workflow === ADAPTIVE_WORKFLOW;
85+
}
86+
6387
private handleSelect(e: Event) {
6488
this.workflow = (e as CustomEvent<string>).detail;
6589
this.seed = 0;
@@ -90,14 +114,16 @@ export class AgSduiDemo extends LitElement {
90114
<div class="demo-output-header">
91115
<span class="demo-output-label">Generated output</span>
92116
<ag-button shape="rounded" @click=${this.handleRegenerate}>
93-
Regenerate
117+
${this.isAdaptive ? 'Restart' : 'Regenerate'}
94118
</ag-button>
95119
</div>
96120
<div class="demo-output-body">
97-
<ag-streaming-output
98-
.workflow=${this.workflow}
99-
.seed=${this.seed}
100-
></ag-streaming-output>
121+
${this.isAdaptive
122+
? html`<ag-adaptive-output></ag-adaptive-output>`
123+
: html`<ag-streaming-output
124+
.workflow=${this.workflow}
125+
.seed=${this.seed}
126+
></ag-streaming-output>`}
101127
</div>
102128
</section>
103129
</div>

0 commit comments

Comments
 (0)