doc: Tutorials for new users

This commit is contained in:
2025-11-03 21:36:45 +01:00
parent 9fbd10ffbc
commit 03656a1f19
9 changed files with 2080 additions and 0 deletions

View File

@@ -0,0 +1,221 @@
# Tutorial 3: Bi-Directional Data Binding
This **third tutorial** builds directly on your `hello-world` component and introduces **two-way interaction** (bi-directional data binding).
Youll learn how to let the user type into an input field and see the greeting update live — while continuing to follow a **TDD-first** approach.
---
## 🧭 1. Goal
> The `<hello-world>` component should display a greeting and an input box.
> Typing into the input should update the greeting **in real time**.
> The `name` property should still be readable/writable programmatically.
---
## 🧪 2. Step 1 — Write the failing test first
Create a new test file:
`src/components/hello-world/hello-world.input.test.js`
```javascript
import "./hello-world.js";
describe("<hello-world> (interactive input)", () => {
it("renders an input element and shows default greeting", () => {
const el = document.createElement("hello-world");
document.body.appendChild(el);
const input = el.shadowRoot.querySelector("input");
const greeting = el.shadowRoot.querySelector(".greeting");
expect(input).to.exist;
expect(greeting.textContent.trim()).to.equal("Hello, World!");
});
it("updates greeting when user types into input", async () => {
const el = document.createElement("hello-world");
document.body.appendChild(el);
const input = el.shadowRoot.querySelector("input");
input.value = "Agent";
input.dispatchEvent(new Event("input"));
await el.updateComplete;
const greeting = el.shadowRoot.querySelector(".greeting");
expect(greeting.textContent.trim()).to.equal("Hello, Agent!");
});
it("reflects property changes in input value", async () => {
const el = document.createElement("hello-world");
document.body.appendChild(el);
el.name = "Nova";
await el.updateComplete;
const input = el.shadowRoot.querySelector("input");
expect(input.value).to.equal("Nova");
});
});
```
Run tests:
```bash
npm test
```
They will all **fail** — as expected.
---
## 🧩 3. Step 2 — Implement the feature
Edit `src/components/hello-world/hello-world.js` and replace the render logic:
```javascript
import { LitElement, html, css } from "lit";
export class HelloWorld extends LitElement {
static properties = {
name: { type: String }
};
constructor() {
super();
this.name = "World";
}
static styles = css`
.container {
font-family: system-ui, sans-serif;
font-size: 1.5rem;
color: #007acc;
padding: 1rem;
text-align: center;
}
input {
font-size: 1rem;
margin-top: 1rem;
padding: 0.3rem 0.6rem;
border: 1px solid #ccc;
border-radius: 6px;
width: 60%;
text-align: center;
}
`;
render() {
return html`
<div class="container">
<div class="greeting">Hello, ${this.name}!</div>
<input
type="text"
.value=${this.name}
@input=${this._onInput}
aria-label="Name input"
/>
</div>
`;
}
_onInput(event) {
this.name = event.target.value;
}
}
customElements.define("hello-world", HelloWorld);
```
---
## 🧪 4. Step 3 — Run the tests again
```bash
npm test
```
✅ All three tests should now pass.
---
## ⚡ 5. Step 4 — Try it live
Update `src/index.html` to:
```html
<hello-world></hello-world>
```
Run:
```bash
npm run dev
```
In your browser:
* Youll see “Hello, World!”
* Type “Coulomb” in the input box.
* The greeting updates instantly: **Hello, Coulomb!**
---
## 🧠 6. Step 5 — Explore two-way binding manually
Open DevTools Console and run:
```javascript
document.querySelector("hello-world").name = "Bernd";
```
The input value updates automatically, and the greeting reflects the change too.
Thats the power of Lits **reactive updates** combined with the native DOM event loop.
---
## 📚 7. Step 6 — Optional Storybook story
`src/components/hello-world/hello-world.interactive.stories.js`
```javascript
import "./hello-world.js";
export default {
title: "UI/Hello World (Interactive)"
};
export const Interactive = () => `<hello-world></hello-world>`;
```
When you add Storybook later, this story will provide a live, interactive playground.
---
## 🔍 8. Key TDD lessons learned
| Concept | Explanation |
| -------------------- | ------------------------------------------------------------ |
| **Event testing** | Simulate user input with `dispatchEvent(new Event('input'))` |
| **Reactive updates** | Use `await el.updateComplete` to wait for re-render |
| **Two-way binding** | Property changes update DOM; DOM events update property |
| **Isolation** | jsdom tests confirm behavior without running a browser |
---
## ✅ 9. Summary
Youve now completed:
1. Static rendering (`Hello World!`)
2. Reactive property rendering (`Hello, ${name}!`)
3. Bi-directional interaction (live input → UI → property → UI)
You can now **test-drive** any UI component using the same methodology.
---
Continue to the **fourth tutorial** on **component composition** — i.e., building a small dashboard that uses multiple custom components together, still under test Control.
xxx