@powerhousedao/academy 2.5.0-dev.25 β 2.5.0-dev.26
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/00-ExploreDemoPackage.md +4 -8
- package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +3 -1
- package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +1 -1
- package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +4 -4
- package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +5 -9
- package/docs/academy/01-GetStarted/home.mdx +1 -9
- package/docs/academy/01-GetStarted/styles.module.css +31 -0
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +7 -3
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +1 -1
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +14 -14
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +70 -2
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +58 -3
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +32 -12
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +103 -38
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +85 -228
- package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +5 -2
- package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +27 -16
- package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +8 -7
- package/package.json +1 -1
- package/sidebars.ts +0 -2
- package/docs/academy/06-ComponentLibrary/04-ScalarVsUIComponents.md +0 -28
- /package/docs/academy/{09-AIResources.md β 09-AIResources} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -18,7 +18,8 @@ Install the Powerhouse command-line tool `ph-cmd` and then use it to install a p
|
|
|
18
18
|
|
|
19
19
|
## Step 1: Install the Powerhouse CLI
|
|
20
20
|
|
|
21
|
-
You will use the Powerhouse CLI to launch a local environment with a "**
|
|
21
|
+
You will use the Powerhouse CLI to launch a local environment with a "**ToDoList Demo Package**".
|
|
22
|
+
This is also the package that you'll recreate during the tutorials and get's you familiar with Powerhouse.
|
|
22
23
|
|
|
23
24
|
```bash
|
|
24
25
|
pnpm install -g ph-cmd
|
|
@@ -36,18 +37,13 @@ Now, use the `ph` command to install the demo package into a global project.
|
|
|
36
37
|
|
|
37
38
|
```bash
|
|
38
39
|
# Install the package
|
|
39
|
-
ph install @powerhousedao/
|
|
40
|
+
ph install @powerhousedao/todo-demo-package.
|
|
40
41
|
```
|
|
41
42
|
|
|
42
|
-
This command downloads and sets up the
|
|
43
|
+
This command downloads and sets up the todo-demo-package, making its features available in your Powerhouse environment.
|
|
43
44
|
|
|
44
45
|
You have now successfully installed `ph-cmd` and added your first package!
|
|
45
46
|
|
|
46
|
-
:::info
|
|
47
|
-
Let's explore what exactly you can do with this package.
|
|
48
|
-
At the bottom of the Connect Application you'll find a button with 'invoice'. This allows you to open an invoice document. The invoice document helps contributors to get paid for their work through crypto, stablecoins or a number of other currencies. Contributors can change the state of their invoice from draft to issued, while processors or clients can follow up on the payment of these invoices. In the next steps you can explore this for yourself.
|
|
49
|
-
:::
|
|
50
|
-
|
|
51
47
|
## Step 3: Run the Connect App in Studio mode
|
|
52
48
|
To run the package locally in Connect Studio (our collaboration and contributor app), run the `ph connect` command.
|
|
53
49
|
|
|
@@ -82,7 +82,9 @@ In the terminal, you will be asked to enter the project name. Fill in the projec
|
|
|
82
82
|
|
|
83
83
|
A new browser window will open and you will see the Connect application. If it doesn't open automatically, you can open it manually by navigating to `http://localhost:3000/` in your browser.
|
|
84
84
|
|
|
85
|
-
You will see your local drive and a button to create a new drive.
|
|
85
|
+
You will see your local drive and a button to create a new drive.
|
|
86
|
+
If you local drive is not present navigate into Settings in the bottom left corner. Settings > Danger Zone > Clear Storage.
|
|
87
|
+
Clear the storage of your localhost application as it might has an old session cached.
|
|
86
88
|
|
|
87
89
|
:::tip
|
|
88
90
|
A **drive** is a folder to store and organize your documents in. Powerhouse offers the ability to build customized 'Drive Apps' for your documents. Think of a Drive App as a specialized lensβit offers **different ways to visualize, organize, and interact with** the data stored within a drive, making it more intuitive and efficient for specific use cases. To learn more, visit [Building A Drive App](/academy/MasteryTrack/BuildingUserExperiences/BuildingADriveExplorer)
|
|
@@ -79,7 +79,7 @@ To be able to define the document model, you need to open the document model edi
|
|
|
79
79
|
### The steps below show you how to do this:
|
|
80
80
|
|
|
81
81
|
1. In the Connect application, click on **'document model'** to open the document model specification editor.
|
|
82
|
-
2. Name your document model 'ToDoList' in the Connect application, paying close attention to capitalization.
|
|
82
|
+
2. Name your document model '**ToDoList**' in the Connect application, paying close attention to capitalization.
|
|
83
83
|
3. You'll be presented with a form to fill in metadata about the document model. Fill in the details in the respective fields.
|
|
84
84
|
|
|
85
85
|
In the **Document Type** field, type `powerhouse/todolist`. This defines the new type of document that will be created with this document model specification.
|
|
@@ -111,10 +111,10 @@ Here are the tests for the three operations implemented in the reducers file. Th
|
|
|
111
111
|
<details>
|
|
112
112
|
<summary>Operation Reducers Tests</summary>
|
|
113
113
|
```typescript
|
|
114
|
-
import utils from '../../gen/utils';
|
|
115
|
-
import { reducer } from '../../gen/reducer';
|
|
116
|
-
import * as creators from '../../gen/creators';
|
|
117
|
-
import { ToDoListDocument } from '../../gen/types';
|
|
114
|
+
import utils from '../../gen/utils.js';
|
|
115
|
+
import { reducer } from '../../gen/reducer.js';
|
|
116
|
+
import * as creators from '../../gen/creators.js';
|
|
117
|
+
import { ToDoListDocument } from '../../gen/types.js';
|
|
118
118
|
|
|
119
119
|
// REMARKS:
|
|
120
120
|
// These tests demonstrate the event sourcing principles of our document model.
|
|
@@ -29,7 +29,7 @@ Manual build steps are typically only needed when publishing packages.
|
|
|
29
29
|
|
|
30
30
|
## ToDoList Editor
|
|
31
31
|
|
|
32
|
-
Below is the complete code for the To-Do List editor.
|
|
32
|
+
Below is the complete code for the To-Do List editor. Paste this code in `editors/to-do-list/editor.tsx`.
|
|
33
33
|
|
|
34
34
|
<details>
|
|
35
35
|
<summary>Complete ToDoList Editor Example (using Tailwind CSS)</summary>
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
ToDoItem,
|
|
44
44
|
actions,
|
|
45
45
|
ToDoListDocument,
|
|
46
|
-
} from '../../document-models/to-do-list';
|
|
46
|
+
} from '../../document-models/to-do-list/index.js';
|
|
47
47
|
import { useState } from 'react';
|
|
48
48
|
|
|
49
49
|
// EditorProps is a generic type that provides the document and a dispatch function.
|
|
@@ -207,16 +207,12 @@ The editor will update dynamically, so you can play around with your editor styl
|
|
|
207
207
|
Congratulations!
|
|
208
208
|
If you managed to follow this tutorial until this point, you have successfully implemented the **ToDoList** document model with its reducer operations and editor.
|
|
209
209
|
|
|
210
|
-
### Up Next: Drive Explorers & Drive Apps
|
|
211
|
-
|
|
212
|
-
Now you can move on to creating a [custom drive explorer](/academy/MasteryTrack/BuildingUserExperiences/BuildingADriveExplorer) for your ToDoList document.
|
|
213
|
-
Imagine you have many ToDoLists sitting in a drive. A custom drive explorer will allow you to organize and track them at a glance, opening up a new world of possibilities to increase the functionality of your documents!
|
|
214
|
-
|
|
215
|
-
|
|
216
210
|
### Up Next: Mastery Track
|
|
217
211
|
|
|
218
|
-
In the [Mastery Track chapther: Document Model Creation](/academy/MasteryTrack/DocumentModelCreation/WhatIsADocumentModel) we guide you through the theoretics of the previous steps while created a more advanced ToDoList.
|
|
212
|
+
In the [Mastery Track chapther: Document Model Creation](/academy/MasteryTrack/DocumentModelCreation/WhatIsADocumentModel) we guide you through the theoretics of the previous steps while created a more advanced version of the ToDoList.
|
|
213
|
+
|
|
219
214
|
You will learn:
|
|
220
215
|
- The in's & out's of a document model.
|
|
221
216
|
- How to use UI & Scalar components from the Document Engineering system.
|
|
217
|
+
- How to build Custom Drive Apps or Drive Explorers.
|
|
222
218
|
|
|
@@ -85,15 +85,7 @@ import BrowserOnly from '@docusaurus/BrowserOnly';
|
|
|
85
85
|
position: 'relative'
|
|
86
86
|
}}>
|
|
87
87
|
<img src="/img/Union.svg" alt="Powerhouse Union" width="250" />
|
|
88
|
-
<div
|
|
89
|
-
position: 'absolute',
|
|
90
|
-
top: '18px',
|
|
91
|
-
backgroundColor: 'white',
|
|
92
|
-
padding: '8px 16px',
|
|
93
|
-
borderRadius: '8px',
|
|
94
|
-
textAlign: 'center',
|
|
95
|
-
zIndex: 2,
|
|
96
|
-
}}>
|
|
88
|
+
<div className={styles.masteryTrackTitle}>
|
|
97
89
|
<h3 className="card-title" style={{ margin: 0 }}>Mastery Track</h3>
|
|
98
90
|
</div>
|
|
99
91
|
</div>
|
|
@@ -96,4 +96,35 @@
|
|
|
96
96
|
:global(.path-button:hover) {
|
|
97
97
|
background: #ffffff;
|
|
98
98
|
border-color: #d1d5db;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.masteryTrackTitle {
|
|
102
|
+
background-color: white;
|
|
103
|
+
padding: 8px 16px;
|
|
104
|
+
border-radius: 8px;
|
|
105
|
+
text-align: center;
|
|
106
|
+
z-index: 2;
|
|
107
|
+
position: absolute;
|
|
108
|
+
top: 18px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:global([data-theme='dark']) .masteryTrackTitle {
|
|
112
|
+
background-color: var(--ifm-card-background-color);
|
|
113
|
+
border: 1px solid var(--ifm-color-emphasis-200);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
:global([data-theme='dark']) .pathCard {
|
|
117
|
+
background: var(--ifm-card-background-color);
|
|
118
|
+
border: 1px solid var(--ifm-color-emphasis-200);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
:global([data-theme='dark']) :global(.path-button) {
|
|
122
|
+
background: var(--ifm-color-emphasis-100);
|
|
123
|
+
border-color: var(--ifm-color-emphasis-200);
|
|
124
|
+
color: var(--ifm-font-color-primary);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:global([data-theme='dark']) :global(.path-button:hover) {
|
|
128
|
+
background: var(--ifm-color-emphasis-200);
|
|
129
|
+
border-color: var(--ifm-color-emphasis-300);
|
|
99
130
|
}
|
package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Create Powerhouse packages
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
:::warning
|
|
4
|
+
**This tutorial is a summary for builders that are familiar with building document models**.
|
|
5
|
+
It guides you through creating a Powerhouse Document Model, from initial setup to publishing a distributable package.
|
|
6
|
+
Please start with the '**Get Started**' Chapter or '**Document Model Creation**' section if you are unfamiliar with building a document model.
|
|
7
|
+
:::
|
|
4
8
|
|
|
5
9
|
<details>
|
|
6
|
-
<summary>Key Commands</summary>
|
|
10
|
+
<summary>Key Commands that you'll use in this flow</summary>
|
|
7
11
|
|
|
8
12
|
- `pnpm install -g ph-cmd`: Installs the Powerhouse CLI globally.
|
|
9
13
|
- `ph init`: Initializes a new Powerhouse project or sets up the local environment.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Powerhouse Builder Tooling
|
|
2
2
|
|
|
3
|
-
This page provides an overview of all the builder tooling offered by the Powerhouse ecosystem.
|
|
3
|
+
This page provides an overview of all the builder tooling offered by the Powerhouse ecosystem.
|
|
4
4
|
This list will be maintained and updated as our toolkit grows.
|
|
5
5
|
|
|
6
6
|
## Powerhouse Command Line Interface
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
:::tip
|
|
4
4
|
This chapter on **Document Model Creation** will help you with an in depth practicial understanding while building an **advanced ToDoList** document model.
|
|
5
|
-
|
|
5
|
+
Although not required, if you have completed the 'Get Started' tutorial you will revisit familiar topics and can update your existing document model.
|
|
6
6
|
:::
|
|
7
7
|
|
|
8
8
|
:::info **Definition: What is a Document Model?**
|
|
9
9
|
A Document Model is a programmable document structure that defines how data is stored, changed, and interpreted in a decentralized system. It acts like a living blueprintβcapturing state, tracking changes, and enabling interaction through defined operations.
|
|
10
|
-
:::
|
|
11
10
|
|
|
12
|
-
For instance, an invoice document model might define fields like issuer
|
|
11
|
+
For instance, an invoice document model might define fields like *issuer*, *lineItems*, and *status*, with operations such as *AddLineItem* and *MarkAsPaid*.
|
|
12
|
+
:::
|
|
13
13
|
|
|
14
14
|
A Document Model can be understood as:
|
|
15
15
|
- A structured software framework that represents and **manages business logic** within a digital environment.
|
|
@@ -43,19 +43,19 @@ Example of a **GraphQL-like state schema** for an invoice document:
|
|
|
43
43
|
|
|
44
44
|
```graphql
|
|
45
45
|
type InvoiceState {
|
|
46
|
-
id: OID!
|
|
47
|
-
issuer: OID!
|
|
48
|
-
recipient: OID!
|
|
49
|
-
status: String
|
|
50
|
-
dueDate: DateTime
|
|
46
|
+
id: OID! # Unique identifier for the invoice
|
|
47
|
+
issuer: OID! # Reference to the issuing entity
|
|
48
|
+
recipient: OID! # Reference to the recipient entity
|
|
49
|
+
status: String # (value: "DRAFT") # Invoice status
|
|
50
|
+
dueDate: DateTime # Payment due date
|
|
51
51
|
lineItems: [LineItem!]! # List of line items
|
|
52
|
-
totalAmount: Currency
|
|
52
|
+
totalAmount: Currency # Computed field for total invoice value
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
type LineItem {
|
|
56
56
|
id: OID!
|
|
57
57
|
description: String
|
|
58
|
-
quantity: Int
|
|
58
|
+
quantity: Int
|
|
59
59
|
unitPrice: Currency
|
|
60
60
|
}
|
|
61
61
|
```
|
|
@@ -64,7 +64,6 @@ type LineItem {
|
|
|
64
64
|
|
|
65
65
|
- Uses **GraphQL-like definitions** for a **clear, structured schema**.
|
|
66
66
|
- Supports **custom scalar types** like `OID`, `Currency`, and `DateTime`. Or other Web3 specific scalars
|
|
67
|
-
- Allows **default values** using `@default(value: "DRAFT")`.
|
|
68
67
|
- Defines **relationships** using object references (`OID!`).
|
|
69
68
|
|
|
70
69
|
The state schema acts as a **template** for document instances. Every new invoice created will follow this structure.
|
|
@@ -81,7 +80,7 @@ Example operations for modifying an invoice:
|
|
|
81
80
|
input AddLineItemInput {
|
|
82
81
|
invoiceId: OID!
|
|
83
82
|
description: String
|
|
84
|
-
quantity: Int
|
|
83
|
+
quantity: Int
|
|
85
84
|
unitPrice: Currency
|
|
86
85
|
}
|
|
87
86
|
|
|
@@ -95,7 +94,8 @@ input MarkAsPaidInput {
|
|
|
95
94
|
}
|
|
96
95
|
```
|
|
97
96
|
|
|
98
|
-
Each operation **modifies the document state** without altering past data.
|
|
97
|
+
Each operation **modifies the document state** without altering past data.
|
|
98
|
+
Instead, a new event is appended to the document history.
|
|
99
99
|
|
|
100
100
|
---
|
|
101
101
|
|
|
@@ -202,4 +202,4 @@ Document Models are a powerful primitive within the Powerhouse vision, offering
|
|
|
202
202
|
|
|
203
203
|
### Up Next: How to build a Document Model
|
|
204
204
|
|
|
205
|
-
In the next chapters, we'll teach you how to build a ToDoList document model while explaining all of the theory that is involved.
|
|
205
|
+
In the next chapters, we'll teach you how to build a ToDoList document model while explaining all of the theory that is involved.
|
|
@@ -31,6 +31,7 @@ You can modify types using lists and non-null indicators:
|
|
|
31
31
|
## Example: ToDoList State Schema
|
|
32
32
|
|
|
33
33
|
Let's revisit the `ToDoList` example from the "Define the ToDoList document specification" tutorial.
|
|
34
|
+
Only this time, we'll also add a 'Stats' type. Since we want to keep track of the number of completed To-Do's.
|
|
34
35
|
|
|
35
36
|
```graphql
|
|
36
37
|
# The state of our ToDoList
|
|
@@ -44,6 +45,12 @@ type ToDoItem {
|
|
|
44
45
|
text: String!
|
|
45
46
|
checked: Boolean!
|
|
46
47
|
}
|
|
48
|
+
# The statistics on our to-do's
|
|
49
|
+
type ToDoListStats {
|
|
50
|
+
total: Int!
|
|
51
|
+
checked: Int!
|
|
52
|
+
unchecked: Int!
|
|
53
|
+
}
|
|
47
54
|
```
|
|
48
55
|
|
|
49
56
|
### Breakdown:
|
|
@@ -59,6 +66,11 @@ type ToDoItem {
|
|
|
59
66
|
* `text: String!`: The textual description of the to-do item. It cannot be null, ensuring every to-do has a description.
|
|
60
67
|
* `checked: Boolean!`: Indicates whether the to-do item is completed. It defaults to a boolean value (true or false) and cannot be null.
|
|
61
68
|
|
|
69
|
+
* **`ToDoListStats` type**: This type holds the summary statistics for the to-do list.
|
|
70
|
+
* `total: Int!`: The total count of all to-do items. This field must be an integer and cannot be null.
|
|
71
|
+
* `checked: Int!`: The number of to-do items that are marked as completed. This must be an integer and cannot be null.
|
|
72
|
+
* `unchecked: Int!`: The number of to-do items that are still pending. This also must be an integer and cannot be null.
|
|
73
|
+
|
|
62
74
|
## Best Practices for Designing Your State Schema
|
|
63
75
|
|
|
64
76
|
1. **Start Simple, Iterate**: Begin with the core entities and properties. You can always expand and refine your schema as your understanding of the document's requirements grows.
|
|
@@ -73,5 +85,61 @@ type ToDoItem {
|
|
|
73
85
|
|
|
74
86
|
By carefully defining your state schema, you lay a solid foundation for your Powerhouse document model, making it robust, maintainable, and easy to work with. The schema dictates not only how data is stored but also how it can be queried and mutated through operations, which will be covered in the next section.
|
|
75
87
|
|
|
76
|
-
|
|
77
|
-
|
|
88
|
+
## Practical Implementation: Defining the State Schema in Connect
|
|
89
|
+
|
|
90
|
+
Now that you understand the concepts behind the state schema, let's put it into practice. This section will guide you through creating a document model specification for the advanced ToDoList example discussed above.
|
|
91
|
+
|
|
92
|
+
<details>
|
|
93
|
+
<summary>Tutorial: The State Schema Specification</summary>
|
|
94
|
+
|
|
95
|
+
### Prerequisites
|
|
96
|
+
|
|
97
|
+
- You have a Powerhouse project set up. If not, please follow the [Create a new Powerhouse Project](../../GetStarted/CreateNewPowerhouseProject) tutorial.
|
|
98
|
+
- Connect Studio is running. If not, navigate to your project directory in the terminal and run `ph connect`.
|
|
99
|
+
|
|
100
|
+
### Steps
|
|
101
|
+
|
|
102
|
+
1. **Create a New Document Model**:
|
|
103
|
+
- With Connect Studio open in your browser, navigate into your local drive.
|
|
104
|
+
- At the bottom of the page in the 'New Document' section, click the `DocumentModel` button to create a new document model specification.
|
|
105
|
+
|
|
106
|
+
2. **Define Document Metadata**:
|
|
107
|
+
- **Name**: Give your document model a descriptive name, for example, `ToDoList`. **Pay close attention to capitalization, as it influences our code.**
|
|
108
|
+
- **Document Type**: In the 'Document Type' field, enter a unique identifier for this document type, for instance, `powerhouse/todolist`.
|
|
109
|
+
|
|
110
|
+
3. **Specify the State Schema**:
|
|
111
|
+
- In the code editor provided, you'll see a template for a GraphQL schema.
|
|
112
|
+
- Replace the entire content of the editor with the advanced `ToDoList` schema we've designed in this chapter:
|
|
113
|
+
|
|
114
|
+
```graphql
|
|
115
|
+
# The state of our ToDoList
|
|
116
|
+
type ToDoListState {
|
|
117
|
+
items: [ToDoItem!]!
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# A single to-do item
|
|
121
|
+
type ToDoItem {
|
|
122
|
+
id: ID!
|
|
123
|
+
text: String!
|
|
124
|
+
checked: Boolean!
|
|
125
|
+
}
|
|
126
|
+
# The statistics on our to-do's
|
|
127
|
+
type ToDoListStats {
|
|
128
|
+
total: Int!
|
|
129
|
+
checked: Int!
|
|
130
|
+
unchecked: Int!
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
4. **Sync Schema and View Initial State**:
|
|
135
|
+
- After pasting the schema, click the **'Sync with schema'** button.
|
|
136
|
+
- This action processes your schema and generates an initial JSON state for your document model based on the `ToDoListState` type. You can view this initial state, which helps you verify that your schema is structured correctly.
|
|
137
|
+
|
|
138
|
+
For now, you can ignore the "Modules & Operations" section. We will define and implement the operations that modify this state in the upcoming sections of this Mastery Track.
|
|
139
|
+
|
|
140
|
+
By completing these steps, you have successfully specified the data structure for the advanced ToDoList document model. The next step is to define the operations that will allow users to interact with and change this state.
|
|
141
|
+
|
|
142
|
+
</details>
|
|
143
|
+
|
|
144
|
+
For a complete, working example, you can always refer to the [Example ToDoList Repository](./07-ExampleToDoListRepository.md) which contains the full implementation of the concepts discussed in this Mastery Track.
|
|
145
|
+
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md
CHANGED
|
@@ -16,10 +16,9 @@ Each operation acts as a command that, when applied, transitions the document fr
|
|
|
16
16
|
|
|
17
17
|
## Connecting Operations to the Schema
|
|
18
18
|
|
|
19
|
-
In the "Define ToDoList Document Model" guide, we used GraphQL `input` types to define the structure of the data required for each operation. Let's revisit that:
|
|
19
|
+
In the "Define ToDoList Document Model" chapter in the "Get Started" guide, we used GraphQL `input` types to define the structure of the data required for each operation. Let's revisit that:
|
|
20
20
|
|
|
21
21
|
```graphql
|
|
22
|
-
# From 02-DefineToDoListDocumentModel.md
|
|
23
22
|
# Defines a GraphQL input type for adding a new to-do item
|
|
24
23
|
input AddTodoItemInput {
|
|
25
24
|
id: ID!
|
|
@@ -96,7 +95,6 @@ The generated code from `ph generate` (as seen in `03-ImplementOperationReducers
|
|
|
96
95
|
For example, the `ToDoListToDoListOperations` type generated by Powerhouse will expect methods corresponding to `addTodoItemOperation`, `updateTodoItemOperation`, and `deleteTodoItemOperation`.
|
|
97
96
|
|
|
98
97
|
```typescript
|
|
99
|
-
// From 03-ImplementOperationReducers.md
|
|
100
98
|
import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js';
|
|
101
99
|
|
|
102
100
|
export const reducer: ToDoListToDoListOperations = {
|
|
@@ -112,6 +110,63 @@ export const reducer: ToDoListToDoListOperations = {
|
|
|
112
110
|
};
|
|
113
111
|
```
|
|
114
112
|
|
|
113
|
+
## Practical Implementation: Defining Operations in Connect
|
|
114
|
+
|
|
115
|
+
Now that you understand the theory, let's walk through the practical steps of defining these operations for our `ToDoList` document model within the Powerhouse Connect application.
|
|
116
|
+
|
|
117
|
+
<details>
|
|
118
|
+
<summary>Tutorial: Specifying `ToDoList` Operations</summary>
|
|
119
|
+
|
|
120
|
+
Assuming you have already defined the state schema for the `ToDoList` as covered in the previous section, follow these steps to add the operations:
|
|
121
|
+
|
|
122
|
+
1. **Create a Module for Operations:**
|
|
123
|
+
Below the schema editor in Connect, find the input field labeled `Add module`. Modules help organize your operations.
|
|
124
|
+
* Type `to_do_list` into the field and press Enter.
|
|
125
|
+
|
|
126
|
+
2. **Add the `ADD_TODO_ITEM` Operation:**
|
|
127
|
+
A new field, `Add operation`, will appear under your new module.
|
|
128
|
+
* Type `ADD_TODO_ITEM` into this field and press Enter.
|
|
129
|
+
* An editor will appear for the operation's input type. You need to define the data required for this operation. Paste the following GraphQL `input` definition into the editor:
|
|
130
|
+
|
|
131
|
+
```graphql
|
|
132
|
+
# Defines a GraphQL input type for adding a new to-do item
|
|
133
|
+
input AddTodoItemInput {
|
|
134
|
+
id: ID!
|
|
135
|
+
text: String!
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
3. **Add the `UPDATE_TODO_ITEM` Operation:**
|
|
140
|
+
* In the `Add operation` field again, type `UPDATE_TODO_ITEM` and press Enter.
|
|
141
|
+
* Paste the corresponding `input` definition into its editor:
|
|
142
|
+
|
|
143
|
+
```graphql
|
|
144
|
+
# Defines a GraphQL input type for updating a to-do item
|
|
145
|
+
input UpdateTodoItemInput {
|
|
146
|
+
id: ID!
|
|
147
|
+
text: String
|
|
148
|
+
checked: Boolean
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
4. **Add the `DELETE_TODO_ITEM` Operation:**
|
|
153
|
+
* Finally, type `DELETE_TODO_ITEM` in the `Add operation` field and press Enter.
|
|
154
|
+
* Paste its `input` definition:
|
|
155
|
+
|
|
156
|
+
```graphql
|
|
157
|
+
# Defines a GraphQL input type for deleting a to-do item
|
|
158
|
+
input DeleteTodoItemInput {
|
|
159
|
+
id: ID!
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
5. **Review and Export:**
|
|
164
|
+
After adding all three operations, your document model specification in Connect is complete for now. You can see how each operation (`ADD_TODO_ITEM`, etc.) is now explicitly linked to an input type that defines its payload.
|
|
165
|
+
|
|
166
|
+
The next step in a real project would be to click the `Export` button to save this specification file. In the next chapter, we will see how this exported file is used to generate code for our reducers.
|
|
167
|
+
|
|
168
|
+
</details>
|
|
169
|
+
|
|
115
170
|
## Conclusion
|
|
116
171
|
|
|
117
172
|
Specifying document operations is a foundational step in building robust and predictable document models in Powerhouse. By clearly defining the **"what" (the operation and its input)** before implementing the **"how" (the reducer logic)**, you create a clear contract for state transitions. This approach enhances type safety, testability, and the overall maintainability of your document model.
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md
CHANGED
|
@@ -90,22 +90,42 @@ Leveraging the `ph generate` command offers numerous advantages:
|
|
|
90
90
|
5. **Alignment with Powerhouse Ecosystem:** The generated code is designed to integrate seamlessly with other parts of the Powerhouse ecosystem, such as the reducer execution engine and UI components.
|
|
91
91
|
6. **Single Source of Truth:** Ensures that your codebase (especially types and action creators) stays synchronized with the document model specification defined in Connect. If the specification changes, regenerating the model will update these components accordingly.
|
|
92
92
|
|
|
93
|
-
##
|
|
93
|
+
## Practical Implementation: Generating the `ToDoList` Model
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
Now that you understand what the Document Model Generator does, let's walk through the practical steps of using it with our `ToDoList` example.
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
<details>
|
|
98
|
+
<summary>Tutorial: Generating the `ToDoList` Document Model</summary>
|
|
99
|
+
|
|
100
|
+
This tutorial assumes you have completed the previous steps in this Mastery Track, where you defined the state schema and operations for the `ToDoList` model in Connect and exported it.
|
|
101
|
+
|
|
102
|
+
### Prerequisites
|
|
103
|
+
|
|
104
|
+
* **`ToDoList.phdm.zip` file**: You must have the document model specification file exported from Connect. If you do not have this file, please revisit the previous sections on specifying the state schema and operations.
|
|
105
|
+
|
|
106
|
+
### Steps
|
|
107
|
+
|
|
108
|
+
1. **Place the Specification File in Your Project**:
|
|
109
|
+
* Navigate to the root directory of your Powerhouse project.
|
|
110
|
+
* Move or copy your `ToDoList.phdm.zip` file into this directory.
|
|
111
|
+
|
|
112
|
+
2. **Run the Generator Command**:
|
|
113
|
+
* Open your terminal in the root directory of your Powerhouse project.
|
|
114
|
+
* Execute the `ph generate` command, pointing to your specification file:
|
|
99
115
|
```bash
|
|
100
|
-
ph generate
|
|
116
|
+
ph generate ToDoList.phdm.zip
|
|
101
117
|
```
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
* `
|
|
105
|
-
*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
|
|
119
|
+
3. **Explore the Generated Files**:
|
|
120
|
+
* After the command completes successfully, you will find a new directory: `document-models/to-do-list/`.
|
|
121
|
+
* Take a moment to explore its contents, which will match the structure described earlier in this document:
|
|
122
|
+
* `spec.json` and `schema.graphql`: The definition of your model.
|
|
123
|
+
* `gen/`: Type-safe, generated code including `types.ts`, `operations.ts`, etc.
|
|
124
|
+
* `src/`: The skeleton for your implementation, most importantly `src/reducers/to-do-list.ts`, which will contain empty functions for `addTodoItemOperation`, `updateTodoItemOperation`, and `deleteTodoItemOperation`, ready for you to implement.
|
|
125
|
+
|
|
126
|
+
With these files generated, you have successfully scaffolded your document model. The project is now set up for you to implement the core business logic.
|
|
127
|
+
|
|
128
|
+
</details>
|
|
109
129
|
|
|
110
130
|
## Next Steps
|
|
111
131
|
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md
CHANGED
|
@@ -47,7 +47,6 @@ Let's break down its components and principles:
|
|
|
47
47
|
While you can write one large reducer that uses a `switch` statement or `if/else if` blocks based on `action.type`, Powerhouse's generated code typically encourages a more modular approach. You'll often implement a separate function for each operation, which are then combined into a main reducer object or map. The `ph generate` command usually sets up this structure for you. For example, in your `document-models/to-do-list/src/reducers/to-do-list.ts`, you'll find an object structure like this:
|
|
48
48
|
|
|
49
49
|
```typescript
|
|
50
|
-
// Example structure from 01-GetStarted/03-ImplementOperationReducers.md
|
|
51
50
|
import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js'; // Generated type for operations
|
|
52
51
|
import { ToDoListState } from '../../gen/types.js'; // Generated type for state
|
|
53
52
|
|
|
@@ -76,9 +75,9 @@ Let's break down its components and principles:
|
|
|
76
75
|
|
|
77
76
|
## Implementing Reducer Logic: A Practical Guide
|
|
78
77
|
|
|
79
|
-
Let's use our familiar `ToDoList` example
|
|
78
|
+
Let's use our familiar `ToDoList` example to illustrate common patterns. For this example, we'll assume our state schema has been updated to include a `stats` object to track the number of total, checked, and unchecked items.
|
|
80
79
|
|
|
81
|
-
|
|
80
|
+
Our `ToDoListState` now looks like this:
|
|
82
81
|
```typescript
|
|
83
82
|
interface ToDoItem {
|
|
84
83
|
id: string;
|
|
@@ -86,8 +85,15 @@ interface ToDoItem {
|
|
|
86
85
|
checked: boolean;
|
|
87
86
|
}
|
|
88
87
|
|
|
88
|
+
interface ToDoListStats {
|
|
89
|
+
total: number;
|
|
90
|
+
checked: number;
|
|
91
|
+
unchecked: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
89
94
|
interface ToDoListState {
|
|
90
95
|
items: ToDoItem[];
|
|
96
|
+
stats: ToDoListStats;
|
|
91
97
|
}
|
|
92
98
|
```
|
|
93
99
|
|
|
@@ -226,46 +232,105 @@ Using these types provides:
|
|
|
226
232
|
* **Autocompletion and IntelliSense**: Improved developer experience in your IDE.
|
|
227
233
|
* **Clearer code**: Types serve as documentation for the expected data structures.
|
|
228
234
|
|
|
229
|
-
##
|
|
235
|
+
## Practical Implementation: Writing the `ToDoList` Reducers
|
|
236
|
+
|
|
237
|
+
Now that you understand the principles, let's put them into practice by implementing the reducers for our `ToDoList` document model.
|
|
238
|
+
|
|
239
|
+
<details>
|
|
240
|
+
<summary>Tutorial: Implementing the `ToDoList` Reducers</summary>
|
|
230
241
|
|
|
231
|
-
|
|
232
|
-
1. Set up an initial state.
|
|
233
|
-
2. Create an action object (ideally using the generated action creators from `../../gen/creators.js` or `../../gen/operations.js`).
|
|
234
|
-
3. Call the reducer function with the initial state and the action.
|
|
235
|
-
4. Assert that the returned new state is what you expect it to be.
|
|
236
|
-
5. Crucially, also assert that the *original* state object was not modified.
|
|
242
|
+
This tutorial assumes you have followed the steps in the previous chapters, especially using `ph generate ToDoList.phdm.zip` to scaffold your document model's code.
|
|
237
243
|
|
|
238
|
-
|
|
244
|
+
### Implement the Operation Reducers
|
|
245
|
+
|
|
246
|
+
Navigate to `document-models/to-do-list/src/reducers/to-do-list.ts`. The generator will have created a skeleton file. Replace its contents with the following logic.
|
|
239
247
|
|
|
240
248
|
```typescript
|
|
241
|
-
|
|
242
|
-
import
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
249
|
+
import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js';
|
|
250
|
+
import { ToDoListState } from '../../gen/types.js'; // Assuming this now includes the 'stats' object
|
|
251
|
+
|
|
252
|
+
// REMARKS: This is our main reducer object. It implements all operations defined in the schema.
|
|
253
|
+
// The ToDoListToDoListOperations type is auto-generated from our GraphQL specification and ensures type safety.
|
|
254
|
+
export const reducer: ToDoListToDoListOperations = {
|
|
255
|
+
// REMARKS: The addTodoItemOperation adds a new item and updates our tracking statistics.
|
|
256
|
+
// - state: The current document state. Powerhouse uses a library like Immer.js,
|
|
257
|
+
// so you can write code that looks like it's mutating the state directly.
|
|
258
|
+
// Behind the scenes, Powerhouse ensures this results in an immutable update.
|
|
259
|
+
// - action: Contains the operation's 'type' and 'input' data from the client.
|
|
260
|
+
// - dispatch: A function to trigger subsequent operations (advanced, not used here).
|
|
261
|
+
addTodoItemOperation(state, action, dispatch) {
|
|
262
|
+
// REMARKS: We update our statistics for total and unchecked items.
|
|
263
|
+
state.stats.total += 1;
|
|
264
|
+
state.stats.unchecked += 1;
|
|
265
|
+
|
|
266
|
+
// REMARKS: We push the new to-do item into the items array.
|
|
267
|
+
// The data for the new item comes from the operation's input.
|
|
268
|
+
state.items.push({
|
|
269
|
+
id: action.input.id,
|
|
270
|
+
text: action.input.text,
|
|
271
|
+
checked: false, // New items always start as unchecked.
|
|
272
|
+
});
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
// REMARKS: The updateTodoItemOperation modifies an existing to-do item.
|
|
276
|
+
// It handles partial updates for text and checked status.
|
|
277
|
+
updateTodoItemOperation(state, action, dispatch) {
|
|
278
|
+
// REMARKS: First, we find the specific item we want to update using its ID.
|
|
279
|
+
const item = state.items.find(item => item.id === action.input.id);
|
|
280
|
+
|
|
281
|
+
// REMARKS: It's good practice to handle cases where the item might not be found.
|
|
282
|
+
if (!item) {
|
|
283
|
+
throw new Error(`Item with id ${action.input.id} not found`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// REMARKS: We only update the text if it was provided in the input.
|
|
287
|
+
// This allows for partial updates (e.g., just checking an item without changing its text).
|
|
288
|
+
if (action.input.text) {
|
|
289
|
+
item.text = action.input.text;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// REMARKS: When the checked status changes, we also update our statistics.
|
|
293
|
+
// We check for `true` and `false` explicitly.
|
|
294
|
+
if (action.input.checked) { // This is true only if action.input.checked is true
|
|
295
|
+
// Note: This assumes the item was previously unchecked. For a more robust implementation,
|
|
296
|
+
// you could check `if (item.checked === false)` before updating stats to prevent inconsistencies.
|
|
297
|
+
state.stats.unchecked -= 1;
|
|
298
|
+
state.stats.checked += 1;
|
|
299
|
+
item.checked = action.input.checked;
|
|
300
|
+
}
|
|
301
|
+
if (action.input.checked === false) {
|
|
302
|
+
// Note: This assumes the item was previously checked.
|
|
303
|
+
state.stats.unchecked += 1;
|
|
304
|
+
state.stats.checked -= 1;
|
|
305
|
+
item.checked = action.input.checked;
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
// REMARKS: The deleteTodoItemOperation removes an item from the list.
|
|
310
|
+
deleteTodoItemOperation(state, action, dispatch) {
|
|
311
|
+
// REMARKS: Before removing the item, we find it to determine its checked status.
|
|
312
|
+
// This is necessary to correctly decrement our statistics.
|
|
313
|
+
const item = state.items.find(item => item.id === action.input.id);
|
|
314
|
+
|
|
315
|
+
// REMARKS: We always decrement the total count.
|
|
316
|
+
state.stats.total -= 1;
|
|
317
|
+
|
|
318
|
+
// REMARKS: We then decrement the 'checked' or 'unchecked' count based on the item's status.
|
|
319
|
+
if (item?.checked) { // This is shorthand for item?.checked === true
|
|
320
|
+
state.stats.checked -= 1;
|
|
321
|
+
}
|
|
322
|
+
if (item?.checked === false) {
|
|
323
|
+
state.stats.unchecked -= 1;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// REMARKS: Finally, we create a new 'items' array that excludes the deleted item.
|
|
327
|
+
// Assigning to 'state.items' is handled by Powerhouse to produce a new immutable state.
|
|
328
|
+
state.items = state.items.filter(item => item.id !== action.input.id);
|
|
329
|
+
},
|
|
330
|
+
};
|
|
266
331
|
```
|
|
267
332
|
|
|
268
|
-
|
|
333
|
+
</details>
|
|
269
334
|
|
|
270
335
|
## Reducers and the Event Sourcing Model
|
|
271
336
|
|
|
@@ -279,4 +344,4 @@ This is why purity and immutability are so critical:
|
|
|
279
344
|
|
|
280
345
|
Implementing document reducers is where you breathe life into your document model's specification. By adhering to the principles of purity and immutability, and by leveraging the type safety provided by Powerhouse's code generation, you can build predictable, testable, and maintainable business logic. These reducers form the immutable backbone of your document's state management, perfectly aligning with the event sourcing architecture that underpins Powerhouse.
|
|
281
346
|
|
|
282
|
-
With your reducers implemented
|
|
347
|
+
With your reducers implemented, your document model is now functionally complete from a data manipulation perspective. The next chapter covers how to write tests for this logic to ensure its correctness and reliability.
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md
CHANGED
|
@@ -2,267 +2,124 @@
|
|
|
2
2
|
|
|
3
3
|
## Ensuring Robustness and Reliability
|
|
4
4
|
|
|
5
|
-
In the previous
|
|
5
|
+
In the previous chapter, we implemented the core reducer logic for our document model. Now, we reach a critical stage that underpins the reliability and correctness of our entire model: **Implementing Document Model Tests**.
|
|
6
6
|
|
|
7
|
-
Testing is not
|
|
7
|
+
Testing is not an afterthought; it's an integral part of the development lifecycle, especially in systems like Powerhouse where data integrity and predictable state transitions are paramount. Well-crafted tests serve as a safety net, allowing you to refactor and extend your document model with confidence.
|
|
8
8
|
|
|
9
|
-
This document
|
|
9
|
+
This document provides a practical, hands-on tutorial for testing the `ToDoList` document model reducers you have just built.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Practical Implementation: Writing and Running the ToDoList Tests
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
This tutorial assumes you have implemented the `ToDoList` reducers as described in the previous chapter and that the code generator has created a test file skeleton at `document-models/to-do-list/src/reducers/tests/to-do-list.test.ts`.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
3. **Integrity of Operation History**: While reducers focus on state, tests can also implicitly verify that operations are being correctly structured for the event log (as seen in example tests in `01-GetStarted/03-ImplementOperationReducers.md`).
|
|
18
|
-
4. **Regression Prevention**: Protects against accidental breakage of existing functionality when new features are added or existing code is refactored.
|
|
19
|
-
5. **Living Documentation**: Well-written tests can serve as examples of how operations are intended to be used and how they affect the document state.
|
|
15
|
+
<details>
|
|
16
|
+
<summary>Tutorial: Implementing and Running the `ToDoList` Reducer Tests</summary>
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
### 1. Implement the Reducer Tests
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
With the reducer logic in place, it's critical to test it. Navigate to the generated test file at `document-models/to-do-list/src/reducers/tests/to-do-list.test.ts` and replace its contents with the following test suite.
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
This suite tests each operation, verifying not only that the `items` array is correct, but also that our `stats` object is updated as expected and that the operation itself is recorded properly in the document's history.
|
|
26
23
|
|
|
27
|
-
Key tools and concepts you'll encounter in these test files include:
|
|
28
|
-
|
|
29
|
-
* **`describe(name, fn)`**: Creates a block that groups together several related tests.
|
|
30
|
-
* **`it(name, fn)`** or **`test(name, fn)`**: This is your actual test case.
|
|
31
|
-
* **`beforeEach(fn)`**: A function that runs before each test case in a `describe` block. Useful for setting up a common initial state for each test.
|
|
32
|
-
* **`expect(value)`**: Used with "matcher" functions to assert that a certain value meets expectations. Common matchers include `toBe()`, `toEqual()`, `toHaveLength()`, `toThrow()`, etc.
|
|
33
|
-
|
|
34
|
-
## Core Components for Testing Document Model Reducers
|
|
35
|
-
|
|
36
|
-
When testing your document model reducers, you'll primarily interact with artifacts generated by the Powerhouse Document Model Generator and the reducer logic you've written:
|
|
37
|
-
|
|
38
|
-
1. **Initial Document State (`utils.createDocument()`):**
|
|
39
|
-
* The `gen/utils.ts` file (or similar) often exports a `createDocument()` function. This utility, derived from your state schema (including default values), is crucial for creating a fresh, predictable starting state for your document model instance before each test.
|
|
40
|
-
* Example: `document = utils.createDocument();`
|
|
41
|
-
|
|
42
|
-
2. **Action Creators (`creators` or `operations`):**
|
|
43
|
-
* The `gen/operations.ts` or `gen/creators.ts` file exports action creator functions for each operation defined in your schema. Using these functions (e.g., `creators.addTodoItem(input)`) instead of manually constructing action objects is highly recommended because they ensure your actions are correctly typed and structured, reducing the chance of errors in your tests.
|
|
44
|
-
* Example: `const action = creators.addTodoItem({ id: '1', text: 'Test item' });`
|
|
45
|
-
|
|
46
|
-
3. **The Main Reducer (`reducer`):**
|
|
47
|
-
* The primary export from your reducer implementation file (e.g., `document-models/YourModelName/src/reducers/your-model-name.ts`) is the main reducer object or function that combines all your individual operation handlers. This is what you'll call with the current state and an action to get the new state.
|
|
48
|
-
* In some generated setups (like the `ToDoList` example from `01-GetStarted`), there might be a top-level `reducer` function exported from `gen/reducer.ts` that wraps your custom reducer logic and handles the overall document structure (state, operations history).
|
|
49
|
-
* Example: `const updatedDocument = reducer(document, action);`
|
|
50
|
-
|
|
51
|
-
4. **Generated Types (`types.ts`):**
|
|
52
|
-
* Using the TypeScript types from `gen/types.ts` (e.g., `ToDoListState`, `ToDoItem`, input types) in your test setup and assertions helps maintain type safety and clarity in your tests.
|
|
53
|
-
|
|
54
|
-
## Writing Effective Test Cases for Reducers
|
|
55
|
-
|
|
56
|
-
Let's draw inspiration from the `ToDoList` example tests found in `01-GetStarted/03-ImplementOperationReducers.md` and expand on the principles.
|
|
57
|
-
|
|
58
|
-
A typical test file structure:
|
|
59
24
|
```typescript
|
|
60
|
-
import utils from '../../gen/utils';
|
|
61
|
-
import { reducer } from '../../gen/reducer';
|
|
62
|
-
import * as creators from '../../gen/creators';
|
|
63
|
-
import { ToDoListDocument
|
|
64
|
-
|
|
65
|
-
describe('ToDoList Document Model Operations', () => {
|
|
66
|
-
let initialDocument: ToDoListDocument; // Or your specific document type
|
|
67
|
-
|
|
68
|
-
beforeEach(() => {
|
|
69
|
-
// Start with a fresh, empty document for each test
|
|
70
|
-
initialDocument = utils.createDocument();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Test suite for ADD_TODO_ITEM operation
|
|
74
|
-
describe('addTodoItemOperation', () => {
|
|
75
|
-
it('should add a new item to an empty list', () => {
|
|
76
|
-
const input = { id: 'task1', text: 'Buy groceries' };
|
|
77
|
-
const action = creators.addTodoItem(input);
|
|
78
|
-
|
|
79
|
-
const updatedDocument = reducer(initialDocument, action);
|
|
80
|
-
const state = updatedDocument.state.global; // Accessing the specific state slice
|
|
81
|
-
|
|
82
|
-
expect(state.items).toHaveLength(1);
|
|
83
|
-
expect(state.items[0]).toEqual({
|
|
84
|
-
id: 'task1',
|
|
85
|
-
text: 'Buy groceries',
|
|
86
|
-
checked: false, // Assuming default
|
|
87
|
-
});
|
|
88
|
-
// Verify immutability: the new items array should be a different instance
|
|
89
|
-
expect(state.items).not.toBe(initialDocument.state.global.items);
|
|
90
|
-
});
|
|
25
|
+
import utils from '../../gen/utils.js';
|
|
26
|
+
import { reducer } from '../../gen/reducer.js';
|
|
27
|
+
import * as creators from '../../gen/creators.js';
|
|
28
|
+
import { ToDoListDocument } from '../../gen/types.js';
|
|
91
29
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const firstItemInput = { id: 'task1', text: 'First task' };
|
|
95
|
-
let currentDocument = reducer(initialDocument, creators.addTodoItem(firstItemInput));
|
|
96
|
-
|
|
97
|
-
// Now, add the second item
|
|
98
|
-
const secondItemInput = { id: 'task2', text: 'Second task' };
|
|
99
|
-
const action = creators.addTodoItem(secondItemInput);
|
|
100
|
-
currentDocument = reducer(currentDocument, action);
|
|
101
|
-
|
|
102
|
-
const state = currentDocument.state.global;
|
|
103
|
-
expect(state.items).toHaveLength(2);
|
|
104
|
-
expect(state.items[1]).toEqual({
|
|
105
|
-
id: 'task2',
|
|
106
|
-
text: 'Second task',
|
|
107
|
-
checked: false,
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Test suite for UPDATE_TODO_ITEM operation
|
|
113
|
-
describe('updateTodoItemOperation', () => {
|
|
114
|
-
let docWithItem: ToDoListDocument;
|
|
115
|
-
const initialItemId = 'item-to-update';
|
|
30
|
+
describe('Todolist Operations', () => {
|
|
31
|
+
let document: ToDoListDocument;
|
|
116
32
|
|
|
117
33
|
beforeEach(() => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
34
|
+
// REMARKS: We start with a fresh, empty document for each test.
|
|
35
|
+
// The `createDocument` utility initializes the state with an empty 'items' array
|
|
36
|
+
// and a 'stats' object with all counts set to 0.
|
|
37
|
+
document = utils.createDocument();
|
|
121
38
|
});
|
|
122
39
|
|
|
123
|
-
it('should
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
40
|
+
it('should handle addTodoItem operation', () => {
|
|
41
|
+
const input = { id: '1', text: 'Buy milk' };
|
|
42
|
+
|
|
43
|
+
// REMARKS: We apply the 'addTodoItem' operation.
|
|
44
|
+
const updatedDocument = reducer(document, creators.addTodoItem(input));
|
|
45
|
+
|
|
46
|
+
// REMARKS: We verify the operation was recorded in the document's history.
|
|
47
|
+
// Powerhouse records every operation in an array.
|
|
48
|
+
expect(updatedDocument.operations.global).toHaveLength(1);
|
|
49
|
+
expect(updatedDocument.operations.global[0].type).toBe('ADD_TODO_ITEM');
|
|
50
|
+
// REMARKS: We also check that the input data and index are recorded correctly.
|
|
51
|
+
expect(updatedDocument.operations.global[0].input).toStrictEqual(input);
|
|
52
|
+
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
53
|
+
|
|
54
|
+
// REMARKS: Finally, we verify the state was updated according to our reducer logic.
|
|
55
|
+
expect(updatedDocument.state.global.items).toHaveLength(1);
|
|
56
|
+
expect(updatedDocument.state.global.stats.total).toBe(1);
|
|
57
|
+
expect(updatedDocument.state.global.stats.unchecked).toBe(1);
|
|
137
58
|
});
|
|
138
59
|
|
|
139
|
-
it('should
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
60
|
+
it('should handle updateTodoItem operation', () => {
|
|
61
|
+
// REMARKS: For an update, we first need to add an item.
|
|
62
|
+
const addInput = { id: '1', text: 'Buy milk' };
|
|
63
|
+
const updateInput = { id: '1', checked: true }; // We'll test checking the item.
|
|
64
|
+
|
|
65
|
+
// REMARKS: Operations are applied sequentially to build up document state.
|
|
66
|
+
const createdDocument = reducer(document, creators.addTodoItem(addInput));
|
|
67
|
+
const updatedDocument = reducer(createdDocument, creators.updateTodoItem(updateInput));
|
|
68
|
+
|
|
69
|
+
// REMARKS: Now we should have 2 operations in the history.
|
|
70
|
+
expect(updatedDocument.operations.global).toHaveLength(2);
|
|
71
|
+
expect(updatedDocument.operations.global[1].type).toBe('UPDATE_TODO_ITEM');
|
|
72
|
+
expect(updatedDocument.operations.global[1].input).toStrictEqual(updateInput);
|
|
73
|
+
|
|
74
|
+
// REMARKS: We check that the state reflects the update, including our stats.
|
|
75
|
+
expect(updatedDocument.state.global.items[0].checked).toBe(true);
|
|
76
|
+
expect(updatedDocument.state.global.stats.total).toBe(1);
|
|
77
|
+
expect(updatedDocument.state.global.stats.unchecked).toBe(0);
|
|
78
|
+
expect(updatedDocument.state.global.stats.checked).toBe(1);
|
|
149
79
|
});
|
|
150
80
|
|
|
151
|
-
it('should
|
|
152
|
-
|
|
153
|
-
|
|
81
|
+
it('should handle deleteTodoItem operation', () => {
|
|
82
|
+
const addInput = { id: '1', text: 'Buy milk' };
|
|
83
|
+
const deleteInput = { id: '1' };
|
|
154
84
|
|
|
155
|
-
|
|
156
|
-
|
|
85
|
+
const createdDocument = reducer(document, creators.addTodoItem(addInput));
|
|
86
|
+
const updatedDocument = reducer(createdDocument, creators.deleteTodoItem(deleteInput));
|
|
157
87
|
|
|
158
|
-
|
|
159
|
-
|
|
88
|
+
// REMARKS: After deletion, we still have 2 operations in history,
|
|
89
|
+
// but the items array is now empty and the stats are back to zero.
|
|
90
|
+
expect(updatedDocument.operations.global).toHaveLength(2);
|
|
91
|
+
expect(updatedDocument.operations.global[1].type).toBe('DELETE_TODO_ITEM');
|
|
92
|
+
expect(updatedDocument.state.global.items).toHaveLength(0);
|
|
93
|
+
expect(updatedDocument.state.global.stats.total).toBe(0);
|
|
94
|
+
expect(updatedDocument.state.global.stats.unchecked).toBe(0);
|
|
160
95
|
});
|
|
161
|
-
|
|
162
|
-
it('should throw an error or not change state if item to update is not found', () => {
|
|
163
|
-
const updateInput = { id: 'non-existent-id', text: 'Wont matter' };
|
|
164
|
-
const action = creators.updateTodoItem(updateInput);
|
|
165
|
-
|
|
166
|
-
// Depending on your reducer's error handling for "item not found":
|
|
167
|
-
// Option 1: Reducer throws an error (as in 01-GetStarted/03-ImplementOperationReducers.md example)
|
|
168
|
-
expect(() => reducer(docWithItem, action)).toThrowError('Item with id non-existent-id not found');
|
|
169
|
-
|
|
170
|
-
// Option 2: Reducer returns original state (if it handles errors gracefully by not changing state)
|
|
171
|
-
// const updatedDocument = reducer(docWithItem, action);
|
|
172
|
-
// expect(updatedDocument.state.global).toEqual(docWithItem.state.global);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Test suite for DELETE_TODO_ITEM operation
|
|
177
|
-
describe('deleteTodoItemOperation', () => {
|
|
178
|
-
let docWithItems: ToDoListDocument;
|
|
179
|
-
const item1Id = 'item1';
|
|
180
|
-
const item2Id = 'item2';
|
|
181
|
-
|
|
182
|
-
beforeEach(() => {
|
|
183
|
-
// Setup: add multiple items
|
|
184
|
-
const addAction1 = creators.addTodoItem({ id: item1Id, text: 'Item One' });
|
|
185
|
-
let tempDoc = reducer(initialDocument, addAction1);
|
|
186
|
-
const addAction2 = creators.addTodoItem({ id: item2Id, text: 'Item Two' });
|
|
187
|
-
docWithItems = reducer(tempDoc, addAction2);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should delete an existing item', () => {
|
|
191
|
-
const deleteInput = { id: item1Id };
|
|
192
|
-
const action = creators.deleteTodoItem(deleteInput);
|
|
193
|
-
|
|
194
|
-
const updatedDocument = reducer(docWithItems, action);
|
|
195
|
-
const state = updatedDocument.state.global;
|
|
196
|
-
|
|
197
|
-
expect(state.items).toHaveLength(1);
|
|
198
|
-
expect(state.items.find(item => item.id === item1Id)).toBeUndefined();
|
|
199
|
-
expect(state.items[0].id).toBe(item2Id);
|
|
200
|
-
// Verify immutability
|
|
201
|
-
expect(state.items).not.toBe(docWithItems.state.global.items);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('should not change state if item to delete is not found', () => {
|
|
205
|
-
const deleteInput = { id: 'non-existent-id' };
|
|
206
|
-
const action = creators.deleteTodoItem(deleteInput);
|
|
207
|
-
|
|
208
|
-
const updatedDocument = reducer(docWithItems, action);
|
|
209
|
-
expect(updatedDocument.state.global.items).toHaveLength(2);
|
|
210
|
-
expect(updatedDocument.state.global).toEqual(docWithItems.state.global); // Or check for array instance equality
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// Testing Operation History (as shown in 01-GetStarted/03-ImplementOperationReducers.md)
|
|
215
|
-
it('should record operations in the document history', () => {
|
|
216
|
-
const addAction = creators.addTodoItem({ id: 'hist1', text: 'History Test' });
|
|
217
|
-
let doc = reducer(initialDocument, addAction);
|
|
218
|
-
|
|
219
|
-
expect(doc.operations.global).toHaveLength(1);
|
|
220
|
-
expect(doc.operations.global[0].type).toBe('ADD_TODO_ITEM'); // Ensure this matches actual operation type string
|
|
221
|
-
|
|
222
|
-
const updateAction = creators.updateTodoItem({ id: 'hist1', checked: true });
|
|
223
|
-
doc = reducer(doc, updateAction);
|
|
224
|
-
|
|
225
|
-
expect(doc.operations.global).toHaveLength(2);
|
|
226
|
-
expect(doc.operations.global[1].type).toBe('UPDATE_TODO_ITEM');
|
|
227
|
-
});
|
|
228
96
|
});
|
|
229
97
|
```
|
|
230
98
|
|
|
231
|
-
###
|
|
99
|
+
### 2. Run the Tests
|
|
232
100
|
|
|
233
|
-
|
|
234
|
-
* Verify that the relevant parts of the state are updated correctly (e.g., an item is added to an array, a field is changed).
|
|
235
|
-
* Use `toEqual()` for deep equality checks on objects and arrays.
|
|
236
|
-
* Check array lengths, specific property values, etc.
|
|
101
|
+
Now, run the tests from your project's root directory to verify your implementation.
|
|
237
102
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
* For arrays: `expect(newState.items).not.toBe(oldState.items);`
|
|
242
|
-
* For modified items within an array: `expect(updatedItem).not.toBe(originalItem);`
|
|
103
|
+
```bash
|
|
104
|
+
pnpm run test
|
|
105
|
+
```
|
|
243
106
|
|
|
244
|
-
|
|
245
|
-
* As seen in `01-GetStarted/03-ImplementOperationReducers.md`, the top-level reducer provided by Powerhouse might also update an `operations` log in the document. If your tests involve this encompassing reducer, you can assert that operations are correctly recorded.
|
|
246
|
-
* `expect(updatedDocument.operations.global).toHaveLength(expectedCount);`
|
|
247
|
-
* `expect(updatedDocument.operations.global[index].type).toBe('EXPECTED_OPERATION_TYPE');`
|
|
107
|
+
If all tests pass, you have successfully verified the core logic of your `ToDoList` document model. This ensures that the reducers you wrote behave exactly as expected.
|
|
248
108
|
|
|
249
|
-
|
|
250
|
-
* Test what happens when an operation receives invalid input (e.g., trying to update/delete an item that doesn't exist).
|
|
251
|
-
* If your reducer is designed to throw errors, use `expect(() => reducer(...)).toThrowError('Expected error message');`.
|
|
252
|
-
* If it handles errors by returning the original state or a specific error state, test for that outcome.
|
|
109
|
+
</details>
|
|
253
110
|
|
|
254
111
|
## Best Practices for Document Model Tests
|
|
255
112
|
|
|
256
|
-
|
|
113
|
+
While the tutorial provides a concrete example, keep these general best practices in mind when writing your tests:
|
|
114
|
+
|
|
115
|
+
* **Isolate Tests**: Each `it` block should ideally test one specific aspect or scenario. `beforeEach` is crucial for resetting state between tests.
|
|
257
116
|
* **Descriptive Names**: Name your `describe` and `it` blocks clearly so they explain what's being tested.
|
|
258
117
|
* **AAA Pattern (Arrange, Act, Assert)**:
|
|
259
|
-
* **Arrange**: Set up the initial state and any required test data.
|
|
260
|
-
* **Act**: Execute the operation
|
|
261
|
-
* **Assert**: Check if the outcome is as expected
|
|
262
|
-
* **Test
|
|
263
|
-
* **Cover
|
|
264
|
-
* **Cover All State Transitions**: For each operation, test different scenarios that lead to different state changes or outcomes.
|
|
265
|
-
* **Refactor Tests with Code**: As your reducer logic evolves, update your tests accordingly. Outdated tests are worse than no tests.
|
|
118
|
+
* **Arrange**: Set up the initial state and any required test data (e.g., using `utils.createDocument()` and defining `input` objects).
|
|
119
|
+
* **Act**: Execute the operation by calling the `reducer` with an action from a `creator`.
|
|
120
|
+
* **Assert**: Check if the outcome is as expected using `expect()`.
|
|
121
|
+
* **Test Immutability**: A key assertion is to ensure the state is not mutated directly. You can check that a new array or object was created: `expect(newState.items).not.toBe(oldState.items);`.
|
|
122
|
+
* **Cover Edge Cases**: Test what happens when an operation receives invalid input (e.g., trying to update an item that doesn't exist). Your test should confirm the reducer either throws an error or returns the state unchanged, depending on your implementation.
|
|
266
123
|
* **Run Tests Frequently**: Integrate testing into your development workflow. Run tests after making changes to ensure you haven't broken anything. The `pnpm run test` command is your friend.
|
|
267
124
|
|
|
268
125
|
## Conclusion: The Payoff of Diligent Testing
|
|
@@ -274,4 +131,4 @@ Implementing comprehensive tests for your document model reducers is an investme
|
|
|
274
131
|
* **Easier Debugging**: When tests fail, they pinpoint the exact operation and scenario that's problematic.
|
|
275
132
|
* **Better Collaboration**: Tests clarify the intended behavior of the document model for all team members.
|
|
276
133
|
|
|
277
|
-
By following the
|
|
134
|
+
By following the tutorial and applying these best practices, you can build a strong suite of tests that safeguard the integrity and functionality of your document models. This diligence is a hallmark of a "Mastery Track" developer, ensuring that the solutions you build are not just functional but also stable, maintainable, and trustworthy.
|
|
@@ -337,8 +337,11 @@ The `install` script provides a streamlined way to install the Powerhouse CLI to
|
|
|
337
337
|
```
|
|
338
338
|
You will see that `ph-cli` is not yet installed. This is expected, as it will be installed by the service setup command.
|
|
339
339
|
|
|
340
|
-
4. Create a project with `ph-init <projectname>`.
|
|
341
|
-
|
|
340
|
+
4. Create a project with `ph-init <projectname>`.
|
|
341
|
+
|
|
342
|
+
5. After creation, move into the project with `cd <projectname>`.
|
|
343
|
+
|
|
344
|
+
Up next is the configurations of your services.
|
|
342
345
|
|
|
343
346
|
### Service Configuration
|
|
344
347
|
|
|
@@ -15,19 +15,29 @@ Here are the key points to understand:
|
|
|
15
15
|
- **Custom Scalars:** Besides the built-in scalars, you can define custom scalars (e.g., a Date type) if you need to handle more specific formats or validations. Powerhouse does this specific for the web3 ecosystem.
|
|
16
16
|
:::
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## Scalars vs. General UI Components
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
### Scalar Components
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
2. **Complex Component** has an object/array value
|
|
25
|
-
- Group of components (e.g. sidebar tree view)
|
|
26
|
-
3. **Layout Component** will contain other components
|
|
27
|
-
- Containers for other components, Sections (e.g. list of other components, color layouts, etc.)
|
|
28
|
-
4. **fragments Component**
|
|
22
|
+
Scalars are here to help you define custom fields in your document model schema and speed up the development process.
|
|
23
|
+
There are two applications of scalar components in the document model workflow:
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
1. At the **schema definition** level where you build your schema and write your GraphQL state schema.
|
|
26
|
+
2. At the **frontend / react** level where you import it and place it in your UI to represent the scalar field
|
|
27
|
+
|
|
28
|
+
These are specialized form components, each corresponding to a GraphQL scalar type (e.g., String, Number, Boolean, Currency, PHID). They are built on top of react-hook-form, offering out-of-the-box validation but must be wrapped with a Form component in order to work properly.
|
|
29
|
+
|
|
30
|
+
**Location:** @powerhousedao/document-engineering/scalars
|
|
31
|
+
https://github.com/powerhouse-inc/document-engineering
|
|
32
|
+
|
|
33
|
+
**Key Feature**: Must be used within a Form component provided by this library.
|
|
34
|
+
|
|
35
|
+
### General-Purpose UI Components
|
|
36
|
+
|
|
37
|
+
This category includes a broader range of UI elements such as simplified versions of the Scalar components (which don't require a Form wrapper but lack built-in validation), as well as other versatile components like Dropdown, Tooltip, Sidebar, ObjectSetTable and more. These are designed for crafting diverse and complex user interfaces.
|
|
38
|
+
|
|
39
|
+
**Location:** @powerhousedao/document-engineering/ui
|
|
40
|
+
https://github.com/powerhouse-inc/document-engineering
|
|
31
41
|
|
|
32
42
|
## Exploring Components with Storybook
|
|
33
43
|
|
|
@@ -39,8 +49,15 @@ We use Storybook as an interactive catalog for our design system components. It
|
|
|
39
49
|
2. **Usage Snippet:** Below the demo, you'll typically find a basic code example demonstrating how to include the component in your code (e.g., `<Checkbox defaultValue label="Accept terms and conditions" />`). This provides a starting point for implementation.
|
|
40
50
|
3. **Props Table:** Further down, a table lists the properties (`props`) the component accepts. Props are like settings or configuration options. For the `Checkbox`, this table would show props like `label`, `defaultValue`, `value`, `onChange`, etc., often with descriptions of what they control.
|
|
41
51
|
|
|
52
|
+
## **Storybook vs. Source Code:**
|
|
53
|
+
|
|
54
|
+
Storybook serves as essential documentation and a usage guide. Our developers write Storybook "stories" to demonstrate components and document their common props. However, the **ultimate source of truth** for a component's capabilities is its actual source code (e.g., the `.tsx` file within the `@powerhousedao/document-engineering/scalars` package).
|
|
55
|
+
While Storybook aims for accuracy, there might occasionally be discrepancies or undocumented props.
|
|
56
|
+
|
|
42
57
|
## Implementing a Component
|
|
43
58
|
|
|
59
|
+
@callme-t
|
|
60
|
+
|
|
44
61
|
Let's walk through the typical workflow for using a component from the document-engineering system, using the `Checkbox` from the [To-do List editor](/academy/GetStarted/BuildToDoListEditor).
|
|
45
62
|
|
|
46
63
|
1. **Identify the Need:** While building your feature (e.g., the To-do List editor in `editor.tsx`), you determine the need for a standard UI element, like a checkbox.
|
|
@@ -124,12 +141,6 @@ Within the project, the following import maps are available:
|
|
|
124
141
|
- `#ui` - UI components
|
|
125
142
|
- `#graphql` - GraphQL related utilities
|
|
126
143
|
|
|
127
|
-
|
|
128
|
-
## **Storybook vs. Source Code:**
|
|
129
|
-
|
|
130
|
-
Storybook serves as essential documentation and a usage guide. Our developers write Storybook "stories" to demonstrate components and document their common props. However, the **ultimate source of truth** for a component's capabilities is its actual source code (e.g., the `.tsx` file within the `@powerhousedao/document-engineering/scalars` package).
|
|
131
|
-
While Storybook aims for accuracy, there might occasionally be discrepancies or undocumented props.
|
|
132
|
-
|
|
133
144
|
Please don't hesitate to reach out in our discord channels with any questions.
|
|
134
145
|
Happy designing!
|
|
135
146
|
|
|
@@ -3,13 +3,6 @@
|
|
|
3
3
|
This tutorial provides step-by-step instructions for creating custom scalars & components, and to contributing to the document-engineering project.
|
|
4
4
|
The github repo for the Document-Engineering can be found [here](https://github.com/powerhouse-inc/document-engineering/tree/main)
|
|
5
5
|
|
|
6
|
-
:::info
|
|
7
|
-
When contributing as an open source developer please submit a pull request to the Powerhouse team.
|
|
8
|
-
Although a design or UI for your scalar is not mandatory, it definitely helps reviewers if there's a visual reference.
|
|
9
|
-
That said, not all scalars require a dedicated UI component.
|
|
10
|
-
Some scalars, like scalar Title or scalar Description, might both map to a string and use the same UI, in this case scalars plays more semantic role than a type definition.
|
|
11
|
-
:::
|
|
12
|
-
|
|
13
6
|
## Table of Contents
|
|
14
7
|
|
|
15
8
|
- [Creating New GraphQL Scalars](#creating-new-graphql-scalars)
|
|
@@ -393,6 +386,14 @@ export const type = 'string'
|
|
|
393
386
|
export const schema = z.string().datetime()
|
|
394
387
|
```
|
|
395
388
|
|
|
389
|
+
:::info
|
|
390
|
+
**Contributing and UI for Scalars**
|
|
391
|
+
|
|
392
|
+
- **Open Source**: Please submit contributions as a pull request to the Powerhouse team.
|
|
393
|
+
- **UI is Optional but Helpful**: A design or UI for your scalar isn't required, but it helps reviewers understand its purpose.
|
|
394
|
+
- **Semantic Scalars**: Some scalars don't need a unique UI. For instance, `Title` and `Description` might both use a simple text input but serve a semantic role by adding specific meaning and validation to the schema.
|
|
395
|
+
:::
|
|
396
|
+
|
|
396
397
|
### Tips
|
|
397
398
|
|
|
398
399
|
- Always follow the naming convention: use PascalCase for scalar names
|
package/package.json
CHANGED
package/sidebars.ts
CHANGED
|
@@ -122,7 +122,6 @@ const sidebars = {
|
|
|
122
122
|
},
|
|
123
123
|
'academy/ComponentLibrary/CreateCustomScalars',
|
|
124
124
|
'academy/ComponentLibrary/IntegrateIntoAReactComponent',
|
|
125
|
-
'academy/ComponentLibrary/ScalarVsUIComponents',
|
|
126
125
|
],
|
|
127
126
|
},
|
|
128
127
|
|
|
@@ -134,7 +133,6 @@ const sidebars = {
|
|
|
134
133
|
},
|
|
135
134
|
{ type: 'doc', id: 'academy/Cookbook', label: 'Cookbook' },
|
|
136
135
|
{ type: 'doc', id: 'academy/Glossary', label: 'Glossary' },
|
|
137
|
-
{ type: 'doc', id: 'academy/AIResources', label: 'AI Resources' },
|
|
138
136
|
// ...add more as needed
|
|
139
137
|
],
|
|
140
138
|
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Tabs, TabItem } from '@theme/Tabs';
|
|
2
|
-
|
|
3
|
-
# Scalar vs. UI component
|
|
4
|
-
|
|
5
|
-
Scalars are here to help you define custom fields in your document model schema and speed up the development process.
|
|
6
|
-
There are two applications of scalar components in the document model workflow:
|
|
7
|
-
|
|
8
|
-
1. At the **schema definition** level where you build your schema and write your GraphQL state schema.
|
|
9
|
-
2. At the **frontend / react** level where you import it and place it in your UI to represent the scalar field
|
|
10
|
-
|
|
11
|
-
## Overview
|
|
12
|
-
The Document Engineering library provides two main categories of components.
|
|
13
|
-
|
|
14
|
-
https://github.com/powerhouse-inc/document-engineering
|
|
15
|
-
|
|
16
|
-
### Scalar Components
|
|
17
|
-
|
|
18
|
-
These are specialized form components, each corresponding to a GraphQL scalar type (e.g., String, Number, Boolean, Currency, PHID). They are built on top of react-hook-form, offering out-of-the-box validation but must be wrapped with a Form component in order to work properly.
|
|
19
|
-
|
|
20
|
-
Location: @powerhousedao/document-engineering/scalars
|
|
21
|
-
|
|
22
|
-
**Key Feature**: Must be used within a Form component provided by this library.
|
|
23
|
-
|
|
24
|
-
### General-Purpose UI Components
|
|
25
|
-
|
|
26
|
-
This category includes a broader range of UI elements such as simplified versions of the Scalar components (which don't require a Form wrapper but lack built-in validation), as well as other versatile components like Dropdown, Tooltip, Sidebar, ObjectSetTable and more. These are designed for crafting diverse and complex user interfaces.
|
|
27
|
-
|
|
28
|
-
Location: @powerhousedao/document-engineering/ui
|
|
File without changes
|