Files
testdrive-jsui/tutorials/Tutorial 2 Reactive Properties.md

217 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tutorial 2 Reactive Properties
This second **TestDrive-UI** tutorial extends your previous `hello-world` component by introducing **reactive properties** (i.e., component inputs) and **dynamic rendering**, all under **TDD** control.
Well end up with a `<hello-world name="Kale"></hello-world>` component that greets a given name — and can change dynamically when the property updates.
---
## 🧭 1. Goal
> The component should display “Hello, [name]!”
> and automatically update when the `name` property changes.
If no `name` is given, it should default to “World”.
---
## 🧪 2. Step 1 — Write the failing test first
Create `src/components/hello-world/hello-world.props.test.js`:
```javascript
import "./hello-world.js";
describe("<hello-world> (with name property)", () => {
it("renders default greeting when no name is set", () => {
const el = document.createElement("hello-world");
document.body.appendChild(el);
const text = el.shadowRoot.textContent.trim();
expect(text).to.equal("Hello, World!");
});
it("renders custom greeting when name is set", async () => {
const el = document.createElement("hello-world");
document.body.appendChild(el);
el.name = "Kale";
// Wait for Lits update cycle
await el.updateComplete;
const text = el.shadowRoot.textContent.trim();
expect(text).to.equal("Hello, Kale!");
});
it("reacts to property change after initial render", async () => {
const el = document.createElement("hello-world");
document.body.appendChild(el);
el.name = "Aria";
await el.updateComplete;
let text = el.shadowRoot.textContent.trim();
expect(text).to.equal("Hello, Aria!");
el.name = "Nova";
await el.updateComplete;
text = el.shadowRoot.textContent.trim();
expect(text).to.equal("Hello, Nova!");
});
});
```
Run:
```bash
npm test
```
All tests should fail — we havent implemented anything yet.
---
## 🧩 3. Step 2 — Implement the feature
Open your existing `src/components/hello-world/hello-world.js`
and replace the class with this improved version:
```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`
div {
font-family: system-ui, sans-serif;
font-size: 1.5rem;
color: #007acc;
padding: 1rem;
text-align: center;
cursor: pointer;
user-select: none;
}
div:hover {
color: #005fa3;
}
`;
render() {
return html`<div @click=${this._onClick}>
Hello, ${this.name}!
</div>`;
}
_onClick() {
alert(`Hello, ${this.name}!`);
}
}
customElements.define("hello-world", HelloWorld);
```
Run the tests again:
```bash
npm test
```
✅ All should now pass.
---
## ⚡ 4. Step 3 — Try it live
Edit `src/index.html` to demonstrate both variants:
```html
<hello-world></hello-world>
<hello-world name="Coulomb"></hello-world>
```
Then:
```bash
npm run dev
```
In the browser youll see:
```
Hello, World!
Hello, Coulomb!
```
and both are clickable.
---
## 🔄 5. Step 4 — Live updates (optional exploration)
Open the browser console and type:
```javascript
document.querySelector("hello-world").name = "Agent";
```
The first greeting should **update instantly** to:
```
Hello, Agent!
```
Thats Lits reactive update mechanism at work.
---
## 🧭 6. Step 5 — Visual story (optional)
`src/components/hello-world/hello-world.stories.js`
```javascript
import "./hello-world.js";
export default {
title: "UI/Hello World (Reactive)"
};
export const Default = () => `<hello-world></hello-world>`;
export const CustomName = () => `<hello-world name="Bernd"></hello-world>`;
```
If Storybook is later installed, these stories will become live demos.
---
## 🧩 7. Key Takeaways
| Concept | Explanation |
| --------------------- | ------------------------------------------------- |
| **Reactive property** | Declared via `static properties = { ... }` in Lit |
| **Default values** | Set in the constructor |
| **Automatic updates** | Changing the property triggers re-render |
| **Testing updates** | Use `await el.updateComplete` before asserting |
---
## 🧪 8. What you learned
* How to **declare reactive component properties**
* How to **test reactivity** with Mocha + jsdom
* How to **update and verify UI behavior** in a TDD loop
---
Next, we can take it one level further:
> Add a **text input** inside `<hello-world>` that updates the `name` property live when the user types.
xxx