@powerhousedao/academy 5.1.0-dev.7 → 5.1.0-dev.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +10 -5
- package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +116 -213
- package/docs/academy/01-GetStarted/04-WriteDocumentModelTests.md +378 -0
- package/docs/academy/01-GetStarted/{04-BuildToDoListEditor.md → 05-BuildToDoListEditor.md} +42 -7
- package/docs/academy/{01-GetStarted → 02-MasteryTrack/01-BuilderEnvironment}/05-VetraStudio.md +47 -6
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +5 -2
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/00-DocumentToolbar.mdx +4 -0
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/01-OperationHistory.md +4 -0
- package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +111 -35
- package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +255 -76
- package/docs/academy/03-ExampleUsecases/Chatroom/04-ImplementOperationReducers.md +281 -160
- package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +188 -35
- package/docs/academy/03-ExampleUsecases/Chatroom/06-LaunchALocalReactor.md +95 -7
- package/docs/academy/03-ExampleUsecases/Chatroom/_category_.json +1 -1
- package/docs/academy/03-ExampleUsecases/TodoList/02-ImplementTodoListDocumentModelReducerOperationHandlers.md +171 -0
- package/docs/academy/03-ExampleUsecases/TodoList/03-AddTestsForTodoListActions.md +462 -0
- package/docs/academy/03-ExampleUsecases/TodoList/05-ImplementTodoListDocumentEditorUIComponents.md +422 -0
- package/docs/academy/03-ExampleUsecases/TodoList/07-AddSharedComponentForShowingTodoListStats.md +384 -0
- package/docs/academy/03-ExampleUsecases/TodoList/_category_.json +8 -0
- package/docs/academy/03-ExampleUsecases/VetraPackageLibrary/VetraPackageLibrary.md +2 -2
- package/docs/academy/03-ExampleUsecases/VetraPackageLibrary/_category_.json +1 -1
- package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +6 -2
- package/docs/academy/04-APIReferences/01-ReactHooks.md +2 -2
- package/docs/academy/{01-GetStarted/07-VetraRemoteDrive.md → 04-APIReferences/06-VetraRemoteDrive.md} +1 -1
- package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +7 -39
- package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +72 -24
- package/docs/academy/08-Glossary.md +7 -0
- package/docs/academy/10-TodoListTutorial/02-ImplementTodoListDocumentModelReducerOperationHandlers.md +37 -41
- package/docs/academy/10-TodoListTutorial/03-AddTestsForTodoListActions.md +38 -30
- package/docs/academy/10-TodoListTutorial/05-ImplementTodoListDocumentEditorUIComponents.md +24 -16
- package/docusaurus.config.ts +20 -0
- package/package.json +1 -1
- package/sidebars.ts +22 -3
- package/src/css/custom.css +27 -0
- package/docs/academy/01-GetStarted/06-ReactorMCP.md +0 -58
- package/docs/academy/05-Architecture/02-ReferencingMonorepoPackages +0 -65
- package/docs/academy/05-Architecture/04-MovingBeyondCRUD +0 -61
- /package/docs/academy/{01-GetStarted → 02-MasteryTrack/01-BuilderEnvironment}/images/Modules.png +0 -0
- /package/docs/academy/{01-GetStarted → 02-MasteryTrack/01-BuilderEnvironment}/images/VetraStudioDrive.png +0 -0
- /package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/{02-RevisionHistoryTimeline → 02-RevisionHistoryTimeline/360/237/232/247"} +0 -0
- /package/docs/academy/{10-TodoListTutorial → 03-ExampleUsecases/TodoList}/00-GetTheStarterCode.md +0 -0
- /package/docs/academy/{10-TodoListTutorial → 03-ExampleUsecases/TodoList}/01-GenerateTodoListDocumentModel.md +0 -0
- /package/docs/academy/{10-TodoListTutorial → 03-ExampleUsecases/TodoList}/04-GenerateTodoListDocumentEditor.md +0 -0
- /package/docs/academy/{10-TodoListTutorial → 03-ExampleUsecases/TodoList}/06-GenerateTodoDriveExplorer.md +0 -0
- /package/docs/academy/05-Architecture/05-DocumentModelTheory/{01-WhatIsADocumentModel → 360/237/232/247 /01-WhatIsADocumentModel"} +0 -0
- /package/docs/academy/05-Architecture/05-DocumentModelTheory/{02-DAOandDocumentsModelsQ+A → 360/237/232/247 /02-DAOandDocumentsModelsQ+A"} +0 -0
- /package/docs/academy/05-Architecture/05-DocumentModelTheory/{02-domain-modeling → 360/237/232/247 /02-domain-modeling"} +0 -0
- /package/docs/academy/05-Architecture/05-DocumentModelTheory/{03-BenefitsOfDocumentModels → 360/237/232/247 /03-BenefitsOfDocumentModels"} +0 -0
- /package/docs/academy/05-Architecture/05-DocumentModelTheory/{04-UtilitiesAndTips → 360/237/232/247 /04-UtilitiesAndTips"} +0 -0
- /package/docs/academy/05-Architecture/05-DocumentModelTheory/{05-best-practices → 360/237/232/247 /05-best-practices"} +0 -0
- /package/docs/academy/05-Architecture/05-DocumentModelTheory/{_category_.json → 360/237/232/247 /_category_.json"} +0 -0
- /package/docs/academy/05-Architecture/05-DocumentModelTheory/{three-data-layers.png → 360/237/232/247 /three-data-layers.png"} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 5.1.0-dev.8 (2025-12-05)
|
|
2
|
+
|
|
3
|
+
This was a version bump only for @powerhousedao/academy to align it with other projects, there were no code changes.
|
|
4
|
+
|
|
1
5
|
## 5.1.0-dev.7 (2025-12-04)
|
|
2
6
|
|
|
3
7
|
This was a version bump only for @powerhousedao/academy to align it with other projects, there were no code changes.
|
|
@@ -175,18 +175,23 @@ document-models/todo-list/
|
|
|
175
175
|
└── schema.graphql # GraphQL schema
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
:::tip Check your work
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
To make sure everything works as expected:
|
|
181
181
|
|
|
182
182
|
```bash
|
|
183
|
+
# Check types compile correctly
|
|
184
|
+
pnpm tsc
|
|
185
|
+
|
|
186
|
+
# Check linting passes
|
|
187
|
+
pnpm lint
|
|
188
|
+
|
|
183
189
|
# Compare your generated files with step-2
|
|
184
190
|
git diff tutorial/step-2-generate-todo-list-document-model -- document-models/todo-list/
|
|
185
|
-
|
|
186
|
-
# List what was generated in the reference
|
|
187
|
-
git ls-tree -r --name-only tutorial/step-2-generate-todo-list-document-model document-models/todo-list/
|
|
188
191
|
```
|
|
189
192
|
|
|
193
|
+
:::
|
|
194
|
+
|
|
190
195
|
### Up next: reducers
|
|
191
196
|
|
|
192
197
|
Up next, you'll learn how to implement the runtime logic and components that will use the `TodoList` document model specification you've just created and exported.
|
|
@@ -1,70 +1,48 @@
|
|
|
1
|
-
# Implement the document model
|
|
1
|
+
# Implement the document model reducers
|
|
2
2
|
|
|
3
3
|
:::tip Tutorial Repository
|
|
4
|
-
📦 **Reference Code**:
|
|
5
|
-
- **Reducer Implementation**: [step-3-implement-reducer-operation-handlers](https://github.com/powerhouse-inc/todo-tutorial/tree/step-3-implement-reducer-operation-handlers)
|
|
6
|
-
- **Tests Implementation**: [step-4-implement-tests-for-todos-operations](https://github.com/powerhouse-inc/todo-tutorial/tree/step-4-implement-tests-for-todos-operations)
|
|
4
|
+
📦 **Reference Code**: [step-3-implement-reducer-operation-handlers](https://github.com/powerhouse-inc/todo-tutorial/tree/step-3-implement-reducer-operation-handlers)
|
|
7
5
|
|
|
8
|
-
This
|
|
9
|
-
1. **Step 3**: Implementing the reducer logic for add, update, and delete operations
|
|
10
|
-
2. **Step 4**: Writing comprehensive tests for the reducers
|
|
11
|
-
|
|
12
|
-
You can view the exact code changes between steps using `git diff step-3-implement-reducer-operation-handlers step-4-implement-tests-for-todos-operations`
|
|
6
|
+
This step focuses on implementing the reducer logic for add, update, and delete operations.
|
|
13
7
|
:::
|
|
14
8
|
|
|
15
9
|
<details>
|
|
16
10
|
<summary>📖 How to use this tutorial</summary>
|
|
17
11
|
|
|
18
|
-
This tutorial covers **two steps**: implementing reducers (step-3) and tests (step-4).
|
|
19
|
-
|
|
20
12
|
### Compare your reducer implementation
|
|
21
13
|
|
|
22
14
|
After implementing your reducers:
|
|
23
15
|
|
|
24
16
|
```bash
|
|
25
|
-
# Compare your reducers with
|
|
17
|
+
# Compare your reducers with the reference
|
|
26
18
|
git diff tutorial/step-3-implement-reducer-operation-handlers -- document-models/todo-list/src/reducers/
|
|
27
19
|
|
|
28
20
|
# View the reference reducer implementation
|
|
29
21
|
git show tutorial/step-3-implement-reducer-operation-handlers:document-models/todo-list/src/reducers/todos.ts
|
|
30
22
|
```
|
|
31
23
|
|
|
32
|
-
### Compare your tests
|
|
33
|
-
|
|
34
|
-
After writing tests:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
# Compare your tests with step-4
|
|
38
|
-
git diff tutorial/step-4-implement-tests-for-todos-operations -- document-models/todo-list/src/tests/
|
|
39
|
-
|
|
40
|
-
# See what changed from step-3 to step-4
|
|
41
|
-
git diff tutorial/step-3-implement-reducer-operation-handlers..tutorial/step-4-implement-tests-for-todos-operations
|
|
42
|
-
```
|
|
43
|
-
|
|
44
24
|
### Visual comparison with GitHub Desktop
|
|
45
25
|
|
|
46
26
|
After committing your work, compare visually:
|
|
47
27
|
1. **Branch** menu → **"Compare to Branch..."**
|
|
48
|
-
2. Select `tutorial/step-3-implement-reducer-operation-handlers`
|
|
28
|
+
2. Select `tutorial/step-3-implement-reducer-operation-handlers`
|
|
49
29
|
3. Review differences in the visual interface
|
|
50
30
|
|
|
51
|
-
See step 1 for detailed GitHub Desktop instructions.
|
|
52
|
-
|
|
53
31
|
### If you get stuck
|
|
54
32
|
|
|
55
33
|
View or reset to a specific step:
|
|
56
34
|
|
|
57
35
|
```bash
|
|
58
|
-
# View the reducer code
|
|
36
|
+
# View the reducer code
|
|
59
37
|
git show tutorial/step-3-implement-reducer-operation-handlers:document-models/todo-list/src/reducers/todos.ts
|
|
60
38
|
|
|
61
|
-
# Reset to step
|
|
39
|
+
# Reset to this step (WARNING: loses your changes)
|
|
62
40
|
git reset --hard tutorial/step-3-implement-reducer-operation-handlers
|
|
63
41
|
```
|
|
64
42
|
|
|
65
43
|
</details>
|
|
66
44
|
|
|
67
|
-
In this section, we will implement
|
|
45
|
+
In this section, we will implement the operation reducers for the **To-do List** document model. For this, you need to export the document model specification from Connect and import it into your Powerhouse project directory.
|
|
68
46
|
|
|
69
47
|
To export the document model specification, follow the steps in the [Define ToDoList Document Model](/academy/GetStarted/DefineToDoListDocumentModel) section.
|
|
70
48
|
|
|
@@ -86,21 +64,21 @@ Either step will import the document model specification into your Powerhouse pr
|
|
|
86
64
|
|
|
87
65
|

|
|
88
66
|
|
|
89
|
-
##
|
|
67
|
+
## Generate the document model code
|
|
90
68
|
|
|
91
69
|
The next steps will take place in the VSCode editor. Make sure to have it open and the terminal window inside VSCode open as well.
|
|
92
70
|
|
|
93
|
-
To write the operation reducers of the **To-do List** document model, you need to generate the document model code from the document model specification file you have exported
|
|
71
|
+
To write the operation reducers of the **To-do List** document model, you need to generate the document model code from the document model specification file you have exported.
|
|
94
72
|
|
|
95
|
-
|
|
73
|
+
Run the following command in the terminal:
|
|
96
74
|
|
|
97
75
|
```bash
|
|
98
76
|
ph generate TodoList.phd
|
|
99
77
|
```
|
|
100
78
|
|
|
101
|
-
|
|
79
|
+
## Explore the generated reducer file
|
|
102
80
|
|
|
103
|
-
|
|
81
|
+
Navigate to `/document-models/todo-list/src/reducers/todos.ts` and open it. You should see scaffolding code that needs to be filled for the three operations you specified:
|
|
104
82
|
|
|
105
83
|
```typescript
|
|
106
84
|
import type { TodoListTodosOperations } from "todo-tutorial/document-models/todo-list";
|
|
@@ -121,224 +99,149 @@ export const todoListTodosOperations: TodoListTodosOperations = {
|
|
|
121
99
|
};
|
|
122
100
|
```
|
|
123
101
|
|
|
124
|
-
##
|
|
102
|
+
## Implement the operation reducers
|
|
125
103
|
|
|
126
|
-
|
|
127
|
-
2. Save the file.
|
|
104
|
+
Let's implement each reducer one by one.
|
|
128
105
|
|
|
129
|
-
|
|
130
|
-
|
|
106
|
+
### Step 1: Add the import
|
|
107
|
+
|
|
108
|
+
First, add the `generateId` import at the top of the file:
|
|
131
109
|
|
|
132
110
|
```typescript
|
|
111
|
+
// added-line
|
|
133
112
|
import { generateId } from "document-model/core";
|
|
134
113
|
import type { TodoListTodosOperations } from "todo-tutorial/document-models/todo-list";
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Step 2: Implement addTodoItemOperation
|
|
135
117
|
|
|
136
|
-
|
|
118
|
+
Replace the boilerplate `addTodoItemOperation` with the actual implementation:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
137
121
|
export const todoListTodosOperations: TodoListTodosOperations = {
|
|
122
|
+
// removed-start
|
|
123
|
+
addTodoItemOperation(state, action) {
|
|
124
|
+
// TODO: Implement "addTodoItemOperation" reducer
|
|
125
|
+
throw new Error('Reducer "addTodoItemOperation" not yet implemented');
|
|
126
|
+
},
|
|
127
|
+
// removed-end
|
|
128
|
+
// added-start
|
|
138
129
|
addTodoItemOperation(state, action) {
|
|
139
|
-
// Generate a unique ID for the new todo item
|
|
140
|
-
// Using generateId() from document-model/core ensures uniqueness
|
|
141
130
|
const id = generateId();
|
|
142
|
-
|
|
143
|
-
// Add the new item to the state
|
|
144
|
-
// Powerhouse uses Immer.js, so this "mutation" is actually immutable
|
|
145
131
|
state.items.push({ ...action.input, id, checked: false });
|
|
146
132
|
},
|
|
147
|
-
|
|
133
|
+
// added-end
|
|
148
134
|
updateTodoItemOperation(state, action) {
|
|
149
|
-
//
|
|
150
|
-
const item = state.items.find((item) => item.id === action.input.id);
|
|
151
|
-
|
|
152
|
-
// Return early if item not found (you could also throw an error)
|
|
153
|
-
if (!item) return;
|
|
154
|
-
|
|
155
|
-
// Update only the fields that are provided (partial update)
|
|
156
|
-
// Using nullish coalescing (??) to keep existing values if undefined
|
|
157
|
-
item.text = action.input.text ?? item.text;
|
|
158
|
-
item.checked = action.input.checked ?? item.checked;
|
|
135
|
+
// ...
|
|
159
136
|
},
|
|
160
|
-
|
|
161
137
|
deleteTodoItemOperation(state, action) {
|
|
162
|
-
//
|
|
163
|
-
// This creates a new array without the deleted item
|
|
164
|
-
state.items = state.items.filter((item) => item.id !== action.input.id);
|
|
138
|
+
// ...
|
|
165
139
|
},
|
|
166
140
|
};
|
|
141
|
+
```
|
|
167
142
|
|
|
168
|
-
|
|
169
|
-
|
|
143
|
+
**What's happening here:**
|
|
144
|
+
- We generate a unique ID using `generateId()` from `document-model/core`
|
|
145
|
+
- We push a new item to the `items` array with the input text, new ID, and `checked: false`
|
|
146
|
+
- Under the hood, Powerhouse uses Immer.js, so this "mutation" is actually immutable
|
|
170
147
|
|
|
171
|
-
|
|
148
|
+
### Step 3: Implement updateTodoItemOperation
|
|
172
149
|
|
|
173
|
-
|
|
150
|
+
Replace the boilerplate `updateTodoItemOperation`:
|
|
174
151
|
|
|
175
|
-
|
|
152
|
+
```typescript
|
|
153
|
+
// removed-start
|
|
154
|
+
updateTodoItemOperation(state, action) {
|
|
155
|
+
// TODO: Implement "updateTodoItemOperation" reducer
|
|
156
|
+
throw new Error('Reducer "updateTodoItemOperation" not yet implemented');
|
|
157
|
+
},
|
|
158
|
+
// removed-end
|
|
159
|
+
// added-start
|
|
160
|
+
updateTodoItemOperation(state, action) {
|
|
161
|
+
const item = state.items.find((item) => item.id === action.input.id);
|
|
162
|
+
if (!item) return;
|
|
163
|
+
item.text = action.input.text ?? item.text;
|
|
164
|
+
item.checked = action.input.checked ?? item.checked;
|
|
165
|
+
},
|
|
166
|
+
// added-end
|
|
167
|
+
```
|
|
176
168
|
|
|
177
|
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
- Tests add, update, and delete operations
|
|
182
|
-
- Verifies both the operation history and the resulting state
|
|
169
|
+
**What's happening here:**
|
|
170
|
+
- We find the item by its ID
|
|
171
|
+
- We return early if the item is not found
|
|
172
|
+
- We use nullish coalescing (`??`) to only update fields that are provided
|
|
183
173
|
|
|
184
|
-
|
|
185
|
-
|
|
174
|
+
### Step 4: Implement deleteTodoItemOperation
|
|
175
|
+
|
|
176
|
+
Replace the boilerplate `deleteTodoItemOperation`:
|
|
186
177
|
|
|
187
178
|
```typescript
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
addTodoItem,
|
|
200
|
-
AddTodoItemInputSchema,
|
|
201
|
-
updateTodoItem,
|
|
202
|
-
UpdateTodoItemInputSchema,
|
|
203
|
-
deleteTodoItem,
|
|
204
|
-
DeleteTodoItemInputSchema,
|
|
205
|
-
TodoItemSchema,
|
|
206
|
-
} from "todo-tutorial/document-models/todo-list";
|
|
207
|
-
|
|
208
|
-
describe("Todos Operations", () => {
|
|
209
|
-
// Test adding a new todo item
|
|
210
|
-
it("should handle addTodoItem operation", () => {
|
|
211
|
-
const document = utils.createDocument();
|
|
212
|
-
const input: AddTodoItemInput = generateMock(AddTodoItemInputSchema());
|
|
213
|
-
|
|
214
|
-
const updatedDocument = reducer(document, addTodoItem(input));
|
|
215
|
-
expect(isTodoListDocument(updatedDocument)).toBe(true);
|
|
216
|
-
|
|
217
|
-
// Verify the operation was recorded
|
|
218
|
-
expect(updatedDocument.operations.global).toHaveLength(1);
|
|
219
|
-
expect(updatedDocument.operations.global[0].action.type).toBe("ADD_TODO_ITEM");
|
|
220
|
-
expect(updatedDocument.operations.global[0].action.input).toStrictEqual(input);
|
|
221
|
-
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// Test updating a todo item's text
|
|
225
|
-
it("should handle updateTodoItem operation to update text", () => {
|
|
226
|
-
const mockItem = generateMock(TodoItemSchema());
|
|
227
|
-
const input: UpdateTodoItemInput = generateMock(UpdateTodoItemInputSchema());
|
|
228
|
-
input.id = mockItem.id;
|
|
229
|
-
const newText = "new text";
|
|
230
|
-
input.text = newText;
|
|
231
|
-
input.checked = undefined;
|
|
232
|
-
|
|
233
|
-
const document = utils.createDocument({
|
|
234
|
-
global: {
|
|
235
|
-
items: [mockItem],
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
const updatedDocument = reducer(document, updateTodoItem(input));
|
|
240
|
-
expect(isTodoListDocument(updatedDocument)).toBe(true);
|
|
241
|
-
|
|
242
|
-
// Verify the operation was recorded
|
|
243
|
-
expect(updatedDocument.operations.global).toHaveLength(1);
|
|
244
|
-
expect(updatedDocument.operations.global[0].action.type).toBe("UPDATE_TODO_ITEM");
|
|
245
|
-
|
|
246
|
-
// Verify the state was updated correctly
|
|
247
|
-
const updatedItem = updatedDocument.state.global.items.find(
|
|
248
|
-
(item) => item.id === input.id,
|
|
249
|
-
);
|
|
250
|
-
expect(updatedItem?.text).toBe(newText);
|
|
251
|
-
expect(updatedItem?.checked).toBe(mockItem.checked);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// Test updating a todo item's checked status
|
|
255
|
-
it("should handle updateTodoItem operation to update checked", () => {
|
|
256
|
-
const mockItem = generateMock(TodoItemSchema());
|
|
257
|
-
const input: UpdateTodoItemInput = generateMock(UpdateTodoItemInputSchema());
|
|
258
|
-
input.id = mockItem.id;
|
|
259
|
-
const newChecked = !mockItem.checked;
|
|
260
|
-
input.checked = newChecked;
|
|
261
|
-
input.text = undefined;
|
|
262
|
-
|
|
263
|
-
const document = utils.createDocument({
|
|
264
|
-
global: {
|
|
265
|
-
items: [mockItem],
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
const updatedDocument = reducer(document, updateTodoItem(input));
|
|
270
|
-
expect(isTodoListDocument(updatedDocument)).toBe(true);
|
|
271
|
-
|
|
272
|
-
const updatedItem = updatedDocument.state.global.items.find(
|
|
273
|
-
(item) => item.id === input.id,
|
|
274
|
-
);
|
|
275
|
-
expect(updatedItem?.text).toBe(mockItem.text);
|
|
276
|
-
expect(updatedItem?.checked).toBe(newChecked);
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
// Test deleting a todo item
|
|
280
|
-
it("should handle deleteTodoItem operation", () => {
|
|
281
|
-
const mockItem = generateMock(TodoItemSchema());
|
|
282
|
-
const document = utils.createDocument({
|
|
283
|
-
global: {
|
|
284
|
-
items: [mockItem],
|
|
285
|
-
},
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
const input: DeleteTodoItemInput = generateMock(DeleteTodoItemInputSchema());
|
|
289
|
-
input.id = mockItem.id;
|
|
290
|
-
|
|
291
|
-
const updatedDocument = reducer(document, deleteTodoItem(input));
|
|
292
|
-
expect(isTodoListDocument(updatedDocument)).toBe(true);
|
|
293
|
-
|
|
294
|
-
// Verify the operation was recorded
|
|
295
|
-
expect(updatedDocument.operations.global).toHaveLength(1);
|
|
296
|
-
expect(updatedDocument.operations.global[0].action.type).toBe("DELETE_TODO_ITEM");
|
|
297
|
-
|
|
298
|
-
// Verify the item was removed from state
|
|
299
|
-
const updatedItems = updatedDocument.state.global.items;
|
|
300
|
-
expect(updatedItems).toHaveLength(0);
|
|
301
|
-
});
|
|
302
|
-
});
|
|
179
|
+
// removed-start
|
|
180
|
+
deleteTodoItemOperation(state, action) {
|
|
181
|
+
// TODO: Implement "deleteTodoItemOperation" reducer
|
|
182
|
+
throw new Error('Reducer "deleteTodoItemOperation" not yet implemented');
|
|
183
|
+
},
|
|
184
|
+
// removed-end
|
|
185
|
+
// added-start
|
|
186
|
+
deleteTodoItemOperation(state, action) {
|
|
187
|
+
state.items = state.items.filter((item) => item.id !== action.input.id);
|
|
188
|
+
},
|
|
189
|
+
// added-end
|
|
303
190
|
```
|
|
304
191
|
|
|
305
|
-
|
|
192
|
+
**What's happening here:**
|
|
193
|
+
- We filter out the item with the matching ID
|
|
194
|
+
- This creates a new array without the deleted item
|
|
306
195
|
|
|
307
|
-
|
|
196
|
+
## Complete reducer file
|
|
308
197
|
|
|
309
|
-
|
|
310
|
-
pnpm run test
|
|
311
|
-
```
|
|
198
|
+
Here's the complete implementation:
|
|
312
199
|
|
|
313
|
-
|
|
200
|
+
<details>
|
|
201
|
+
<summary>Complete todos.ts</summary>
|
|
314
202
|
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
|
|
203
|
+
```typescript
|
|
204
|
+
import { generateId } from "document-model/core";
|
|
205
|
+
import type { TodoListTodosOperations } from "todo-tutorial/document-models/todo-list";
|
|
318
206
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
207
|
+
export const todoListTodosOperations: TodoListTodosOperations = {
|
|
208
|
+
addTodoItemOperation(state, action) {
|
|
209
|
+
const id = generateId();
|
|
210
|
+
state.items.push({ ...action.input, id, checked: false });
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
updateTodoItemOperation(state, action) {
|
|
214
|
+
const item = state.items.find((item) => item.id === action.input.id);
|
|
215
|
+
if (!item) return;
|
|
216
|
+
item.text = action.input.text ?? item.text;
|
|
217
|
+
item.checked = action.input.checked ?? item.checked;
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
deleteTodoItemOperation(state, action) {
|
|
221
|
+
state.items = state.items.filter((item) => item.id !== action.input.id);
|
|
222
|
+
},
|
|
223
|
+
};
|
|
323
224
|
```
|
|
324
225
|
|
|
325
|
-
|
|
226
|
+
</details>
|
|
326
227
|
|
|
327
|
-
|
|
228
|
+
:::tip Check your work
|
|
328
229
|
|
|
329
|
-
|
|
230
|
+
To make sure everything works as expected:
|
|
330
231
|
|
|
331
232
|
```bash
|
|
332
|
-
#
|
|
333
|
-
|
|
233
|
+
# Check types compile correctly
|
|
234
|
+
pnpm tsc
|
|
334
235
|
|
|
335
|
-
#
|
|
336
|
-
|
|
236
|
+
# Check linting passes
|
|
237
|
+
pnpm lint
|
|
337
238
|
|
|
338
|
-
#
|
|
339
|
-
git diff tutorial/step-3-implement-reducer-operation-handlers
|
|
239
|
+
# Compare with reference implementation
|
|
240
|
+
git diff tutorial/step-3-implement-reducer-operation-handlers -- document-models/todo-list/src/reducers/
|
|
340
241
|
```
|
|
341
242
|
|
|
342
|
-
|
|
243
|
+
:::
|
|
244
|
+
|
|
245
|
+
## Up next: Writing tests
|
|
343
246
|
|
|
344
|
-
In the next chapter
|
|
247
|
+
In the next chapter, you'll write comprehensive tests to verify your reducer implementations work correctly.
|