@powerhousedao/academy 2.5.0-dev.4 → 2.5.0-dev.40

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +237 -0
  2. package/docs/academy/01-GetStarted/00-ExploreDemoPackage.md +19 -15
  3. package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +39 -40
  4. package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +22 -7
  5. package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +9 -4
  6. package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +146 -422
  7. package/docs/academy/01-GetStarted/_04-BuildToDoListEditor +360 -0
  8. package/docs/academy/01-GetStarted/home.mdx +16 -24
  9. package/docs/academy/01-GetStarted/styles.module.css +31 -0
  10. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/01-Prerequisites.md +0 -18
  11. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +10 -6
  12. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +1 -1
  13. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +33 -16
  14. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +73 -0
  15. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +59 -4
  16. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +32 -12
  17. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +103 -38
  18. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +90 -228
  19. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +41 -1
  20. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +342 -67
  21. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/02-ConfiguringDrives.md +5 -3
  22. package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +70 -5
  23. package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +162 -73
  24. package/docs/academy/02-MasteryTrack/05-Launch/{03-RunOnACloudServer.md → _03-RunOnACloudServer} +8 -5
  25. package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +10 -9
  26. package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +3 -4
  27. package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +1 -1
  28. package/docs/academy/03-ExampleUsecases/Chatroom/_category_.json +1 -1
  29. package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +13 -49
  30. package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +3 -0
  31. package/docs/academy/05-Architecture/images/PowerhouseArchitecture.png +0 -0
  32. package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +85 -30
  33. package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +382 -0
  34. package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +124 -0
  35. package/docs/academy/07-Cookbook.md +252 -4
  36. package/docs/academy/08-Glossary.md +20 -18
  37. package/docs/academy/09-AIResources +131 -0
  38. package/docusaurus.config.ts +4 -0
  39. package/package.json +1 -1
  40. package/sidebars.ts +3 -45
  41. package/src/css/custom.css +23 -1
  42. package/docs/academy/03-ExampleUsecases/Chatroom/01-SetupBuilderEnvironment.md +0 -216
  43. package/docs/academy/06-ComponentLibrary/02-BuildingWithScalars.md +0 -54
  44. package/docs/academy/06-ComponentLibrary/03-Scalar-Components/01-phid-field.mdx +0 -72
  45. package/docs/academy/06-ComponentLibrary/03-Scalar-Components/02-input-field.mdx +0 -0
  46. package/docs/academy/06-ComponentLibrary/04-Complex-Components/01-sidebar.mdx +0 -36
  47. package/docs/academy/06-ComponentLibrary/05-Layout-Components/01-test-toupdate.mdx +0 -61
  48. package/docs/academy/06-ComponentLibrary/06-Fragments/01-test-toupdate.mdx +0 -61
  49. /package/docs/academy/02-MasteryTrack/05-Launch/{02-IntroductionToPackages.md → 01-IntroductionToPackages.md} +0 -0
  50. /package/docs/academy/02-MasteryTrack/05-Launch/{00-IntegrateInAFront-End → _00-IntegrateInAFront-End} +0 -0
  51. /package/docs/academy/02-MasteryTrack/05-Launch/{01-IntroducingFusion → _01-IntroducingFusion} +0 -0
  52. /package/docs/academy/02-MasteryTrack/05-Launch/{04-GraphQLNamespacing → _04-GraphQLNamespacing} +0 -0
  53. /package/docs/academy/02-MasteryTrack/05-Launch/{05-LaunchYourBackend.md → _05-LaunchYourBackend} +0 -0
  54. /package/docs/academy/02-MasteryTrack/05-Launch/{06-LaunchYourFrontend.md → _06-LaunchYourFrontend} +0 -0
  55. /package/docs/academy/04-APIReferences/{01-ReactHooks.md → 01-ReactHooks} +0 -0
  56. /package/docs/academy/04-APIReferences/{02-ReactorAPI.md → 02-ReactorAPI} +0 -0
  57. /package/docs/academy/04-APIReferences/{03-Configuration.md → 03-Configuration} +0 -0
@@ -1,8 +1,8 @@
1
1
  # Document-Engineering
2
2
 
3
- The reusable components in the Document-Engineering design system are a set of of front-end components based on graphQL scalars.
3
+ The reusable components in the Document-Engineering system are a set of of front-end components based on graphQL scalars.
4
4
  Powerhouse also has a set of custom scalars that are not part of the graphQL standard but are specific to the web3 ecosystem.
5
- These components are offered through the **Powerhouse Document-Engineering design system** with the help of storybook & the Academy documentation.
5
+ These components are offered through the **Powerhouse Document-Engineering system** with the help of storybook & the Academy documentation.
6
6
 
7
7
  It provides a collection of pre-built, reusable UI components designed for consistency and efficiency across Powerhouse applications and editors. Think of it as a toolkit of standard UI elements like buttons, inputs, and checkboxes with many of these components based on graphql scalars.
8
8
 
@@ -15,33 +15,33 @@ 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
- In the next few chapters of our documentation, we will start with the simplest scalar components first, then move on to more complex, Powerhouse-specific components, and combine these in the Layout components.
18
+ ## Scalars vs. General UI Components
19
19
 
20
- ## **3 types of components**
20
+ ### Scalar Components
21
21
 
22
- 1. **Simple Component** has a scalar value as input
23
- - Component, Composition of smaller UI controls (e.g. Scalar component)
24
-
25
- 2. **Complex Component** has an object/array value
26
- - Group of components (e.g. sidebar tree view)
27
-
28
- 3. **Layout Component** will contain other components
29
- - Containers for other components, Sections (e.g. list of other components, color layouts, etc.)
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:
30
24
 
31
- The documentation of each of the reusable components in the Academy documentation will be structured as follows:
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
32
27
 
33
- 1. **Component Context**
34
- 2. **Scalar Definition**
35
- 3. **Component Storybook Base Example**
36
- - Component Code
37
- - Component Default Props
38
- 4. **Component Storybook Usage Examples**
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.
39
29
 
40
- ## Exploring Components with Storybook
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.
41
38
 
42
- We use Storybook as an interactive catalog for our design system components. It allows you to visually explore each component, interact with different states, and understand how to integrate them into your projects.
39
+ **Location:** @powerhousedao/document-engineering/ui
40
+ https://github.com/powerhouse-inc/document-engineering
43
41
 
44
- [https://storybook.powerhouse.academy](https://storybook.powerhouse.academy)
42
+ ## Exploring Components with Storybook
43
+
44
+ We use Storybook as an interactive catalog for our design system components. It allows you to visually explore each component, interact with different states, and understand how to integrate them into your projects. [https://storybook.powerhouse.academy](https://storybook.powerhouse.academy)
45
45
 
46
46
  **Understanding the Storybook Interface:**
47
47
 
@@ -49,13 +49,18 @@ We use Storybook as an interactive catalog for our design system components. It
49
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.
50
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.
51
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
+
52
57
  ## Implementing a Component
53
58
 
54
- 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).
59
+ 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/MasteryTrack/BuildingUserExperiences/BuildingDocumentEditors).
55
60
 
56
61
  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.
57
- 2. **Consult the Resuable Components in Academy or in Storybook:**
58
- * Open the Powerhouse Storybook instance.
62
+ 2. **Consult the Document Engineering Components in Storybook:**
63
+ * Open the Powerhouse Storybook instance. [https://storybook.powerhouse.academy](https://storybook.powerhouse.academy)
59
64
  * Navigate or search to find the `Checkbox` component.
60
65
  * Review the visual examples and interactive demo.
61
66
  * Examine the "Usage" snippet and the **Props table** to understand the basic implementation and available configuration options (`label`, `value`, `onChange`, etc.).
@@ -85,10 +90,60 @@ Let's walk through the typical workflow for using a component from the document-
85
90
  You configure the component's appearance and behavior by passing the appropriate values to its props.
86
91
  5. **Test and Refine:** Run your application (e.g., using `ph connect`) to see the component in context. Verify its appearance and functionality.
87
92
 
88
- **Storybook vs. Source Code:**
93
+ ## Usage
94
+
95
+ The Document Engineering package provides several entry points for different use cases in your powerhouse project:
96
+
97
+ ### Main Package
98
+
99
+ ```typescript
100
+ import { ... } from '@powerhousedao/document-engineering';
101
+ ```
102
+
103
+ ### UI Components
104
+
105
+ ```typescript
106
+ import { ... } from '@powerhousedao/document-engineering/ui';
107
+ ```
108
+
109
+ ### Scalars
110
+
111
+ For data manipulation and transformation utilities:
112
+
113
+ ```typescript
114
+ import { ... } from '@powerhousedao/document-engineering/scalars';
115
+ ```
116
+
117
+ ### GraphQL
118
+
119
+ For GraphQL related utilities and schema definitions:
120
+
121
+ ```typescript
122
+ import { ... } from '@powerhousedao/document-engineering/graphql';
123
+ ```
124
+
125
+ ### Styles
126
+
127
+ To include the package's styles:
128
+
129
+ ```typescript
130
+ import '@powerhousedao/document-engineering/style.css';
131
+ ```
132
+
133
+ ## Import Maps
134
+
135
+ Within the project, the following import maps are available:
136
+
137
+ - `#assets` - Assets utilities and components
138
+ - `#scalars` - Scalar transformations and utilities
139
+ - `#ui` - UI components
140
+ - `#graphql` - GraphQL related utilities
141
+
142
+ Please don't hesitate to reach out in our discord channels with any questions.
143
+ Happy designing!
144
+
145
+ ### Up next: Create Custom Scalars
146
+
147
+ You can learn how to do so in our guide on [Creating Custom Scalars](/academy/ComponentLibrary/CreateCustomScalars).
89
148
 
90
- 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).
91
- While Storybook aims for accuracy, there might occasionally be discrepancies or undocumented props.
92
149
 
93
- Please don't hesitate to reach out in our discord channels with any questions.
94
- Happy designing!
@@ -0,0 +1,382 @@
1
+ # Step 1: Create Custom Scalars
2
+
3
+ This tutorial provides step-by-step instructions for creating custom scalars & components, and to contributing to the document-engineering project.
4
+ The github repo for the Document-Engineering can be found [here](https://github.com/powerhouse-inc/document-engineering/tree/main)
5
+
6
+ ### Creating New GraphQL Scalars
7
+
8
+ GraphQL scalars are custom data types that define how data is validated, serialized, and parsed. This guide will walk you through creating a new scalar in the `src/scalars/graphql/` directory.
9
+
10
+ ## Step 1: Create the Scalar File
11
+
12
+ Create a new TypeScript file in `src/scalars/graphql/` for your scalar. Use `EmailAddress.ts` as a reference.
13
+
14
+ **Example: Creating a `PhoneNumber.ts` scalar**
15
+
16
+ ```typescript
17
+ import { GraphQLError, GraphQLScalarType, type GraphQLScalarTypeConfig, Kind } from 'graphql'
18
+ import { z } from 'zod'
19
+
20
+ export interface ScalarType {
21
+ input: string
22
+ output: string
23
+ }
24
+
25
+ export const type = 'string' // TS type in string form
26
+
27
+ export const typedef = 'scalar PhoneNumber'
28
+
29
+ export const schema = z.string().regex(/^\+?[1-9]\d{1,14}$/, 'Invalid phone number format')
30
+
31
+ export const stringSchema = 'z.string().regex(/^\\+?[1-9]\\d{1,14}$/, "Invalid phone number format")'
32
+
33
+ const phoneValidation = (value: unknown): string => {
34
+ if (typeof value !== 'string') {
35
+ throw new GraphQLError(`Value is not string: ${JSON.stringify(value)}`)
36
+ }
37
+
38
+ const result = schema.safeParse(value)
39
+
40
+ if (result.success) return result.data
41
+ throw new GraphQLError(result.error.message)
42
+ }
43
+
44
+ export const config: GraphQLScalarTypeConfig<string, string> = {
45
+ name: 'PhoneNumber',
46
+ description: 'A field whose value conforms to international phone number format.',
47
+ serialize: phoneValidation,
48
+ parseValue: phoneValidation,
49
+ parseLiteral: (value) => {
50
+ if (value.kind !== Kind.STRING) {
51
+ throw new GraphQLError(`Can only validate strings as phone numbers but got a: ${value.kind}`, { nodes: value })
52
+ }
53
+
54
+ return phoneValidation(value.value)
55
+ },
56
+ }
57
+
58
+ export const scalar = new GraphQLScalarType(config)
59
+ ```
60
+
61
+ ### Key Components to Update:
62
+
63
+ 1. **`type`**: The TypeScript type (usually `'string'` for text-based scalars)
64
+ 2. **`typedef`**: The GraphQL type definition (e.g., `'scalar PhoneNumber'`)
65
+ 3. **`schema`**: Zod validation schema for your data type
66
+ 4. **`stringSchema`**: String representation of the zod schema (used for code generation)
67
+ 5. **Validation function**: Custom validation logic for your scalar
68
+ 6. **`config.name`**: The name of your scalar (must match the typedef)
69
+ 7. **`config.description`**: Human-readable description of the scalar
70
+
71
+ ## Step 2: Register the Scalar in `scalars.ts`
72
+
73
+ After creating your scalar file, you need to register it in `src/scalars/graphql/scalars.ts`. This involves updating multiple sections of the file.
74
+ The github repo for the Document-Engineering can be found [here](https://github.com/powerhouse-inc/document-engineering/tree/main)
75
+
76
+ ### 2.1 Add Namespace Import
77
+
78
+ Add your scalar to the namespace imports section (around line 2):
79
+
80
+ ```typescript
81
+ // namespace imports -- DO NOT REMOVE OR EDIT THIS COMMENT
82
+ import * as Amount from './Amount.js'
83
+ import * as AmountCrypto from './AmountCrypto.js'
84
+ // ... other imports ...
85
+ import * as PhoneNumber from './PhoneNumber.js' // ADD THIS LINE
86
+ import * as URLScalar from './URL.js'
87
+ ```
88
+
89
+ ### 2.2 Add Type Export
90
+
91
+ Add the type export (around line 22):
92
+
93
+ ```typescript
94
+ // export types -- DO NOT REMOVE OR EDIT THIS COMMENT
95
+ export type { ScalarType as AmountScalarType } from './Amount.js'
96
+ // ... other type exports ...
97
+ export type { ScalarType as PhoneNumberScalarType } from './PhoneNumber.js' // ADD THIS LINE
98
+ export type { ScalarType as URLScalarType } from './URL.js'
99
+ ```
100
+
101
+ ### 2.3 Add to Export Object
102
+
103
+ Add your scalar to the main export object (around line 40):
104
+
105
+ ```typescript
106
+ export {
107
+ Amount,
108
+ AmountCrypto,
109
+ // ... other exports ...
110
+ PhoneNumber, // ADD THIS LINE
111
+ URLScalar,
112
+ }
113
+ ```
114
+
115
+ ### 2.4 Add to Custom Scalars
116
+
117
+ Add your scalar to the `customScalars` object (around line 54):
118
+
119
+ ```typescript
120
+ export const customScalars: Record<string, BasePHScalar<any>> = {
121
+ // ... other scalars ...
122
+ PhoneNumber, // ADD THIS LINE
123
+ URLScalar,
124
+ } as const
125
+ ```
126
+
127
+ #### 2.5 Add to Resolvers
128
+
129
+ Add your scalar to the `resolvers` object (around line 74):
130
+
131
+ ```typescript
132
+ export const resolvers = {
133
+ // export resolvers -- DO NOT REMOVE OR EDIT THIS COMMENT
134
+ AmountTokens: AmountTokens.scalar,
135
+ // ... other resolvers ...
136
+ PhoneNumber: PhoneNumber.scalar, // ADD THIS LINE
137
+ Amount: Amount.scalar,
138
+ }
139
+ ```
140
+
141
+ ### 2.6 Add to Type Definitions
142
+
143
+ Add your typedef to the `typeDefs` array (around line 90):
144
+
145
+ ```typescript
146
+ export const typeDefs = [
147
+ // export typedefs -- DO NOT REMOVE OR EDIT THIS COMMENT
148
+ AmountTokens.typedef,
149
+ // ... other typedefs ...
150
+ PhoneNumber.typedef, // ADD THIS LINE
151
+ Amount.typedef,
152
+ ]
153
+ ```
154
+
155
+ ### 2.7 Add to Generator Type Definitions
156
+
157
+ Add your scalar to the `generatorTypeDefs` object (around line 105):
158
+
159
+ ```typescript
160
+ export const generatorTypeDefs = {
161
+ // export generator typedefs -- DO NOT REMOVE OR EDIT THIS COMMENT
162
+ [AmountTokens.config.name]: AmountTokens.type,
163
+ // ... other entries ...
164
+ [PhoneNumber.config.name]: PhoneNumber.type, // ADD THIS LINE
165
+ [Amount.config.name]: Amount.type,
166
+ }
167
+ ```
168
+
169
+ ### 2.8 Add to Validation Schema
170
+
171
+ Add your scalar to the `validationSchema` object (around line 120):
172
+
173
+ ```typescript
174
+ export const validationSchema = {
175
+ // export validation schema -- DO NOT REMOVE OR EDIT THIS COMMENT
176
+ [AmountTokens.config.name]: AmountTokens.stringSchema,
177
+ // ... other entries ...
178
+ [PhoneNumber.config.name]: PhoneNumber.stringSchema, // ADD THIS LINE
179
+ [Amount.config.name]: Amount.stringSchema,
180
+ }
181
+ ```
182
+
183
+ ## Step 3: Create Tests for Your Scalar
184
+
185
+ Every scalar must have comprehensive tests to ensure it works correctly. Create a test file in `src/scalars/graphql/test/` following the naming convention `YourScalar.test.ts`.
186
+
187
+ **Example: Creating `PhoneNumber.test.ts`**
188
+
189
+ ```typescript
190
+ import { Kind } from 'graphql'
191
+ import { scalar } from '../PhoneNumber.js'
192
+
193
+ describe('PhoneNumber Scalar', () => {
194
+ it('should serialize a phone number', () => {
195
+ const phoneNumber = '+1234567890'
196
+
197
+ expect(scalar.serialize(phoneNumber)).toBe(phoneNumber)
198
+ })
199
+
200
+ it('should throw an error if the value is not a string', () => {
201
+ const phoneNumber = 123
202
+
203
+ expect(() => scalar.serialize(phoneNumber)).toThrow()
204
+ })
205
+
206
+ it('should throw an error if the value is not a valid phone number', () => {
207
+ const phoneNumber = 'invalid-phone'
208
+
209
+ expect(() => scalar.serialize(phoneNumber)).toThrow()
210
+ })
211
+
212
+ it('should parse a valid phone number', () => {
213
+ const phoneNumber = '+1234567890'
214
+
215
+ expect(scalar.parseValue(phoneNumber)).toBe(phoneNumber)
216
+ })
217
+
218
+ it('should throw an error if parse a value that is not a valid phone number', () => {
219
+ const phoneNumber = 'invalid-phone'
220
+
221
+ expect(() => scalar.parseValue(phoneNumber)).toThrow()
222
+ })
223
+
224
+ it('should throw an error if parse a value that is not a string', () => {
225
+ const phoneNumber = 123
226
+
227
+ expect(() => scalar.parseValue(phoneNumber)).toThrow()
228
+ })
229
+
230
+ it('should parse a valid phone number from a literal', () => {
231
+ const phoneNumber = '+1234567890'
232
+
233
+ expect(
234
+ scalar.parseLiteral({
235
+ kind: Kind.STRING,
236
+ value: phoneNumber,
237
+ })
238
+ ).toBe(phoneNumber)
239
+ })
240
+
241
+ it('should throw an error if parse a literal that is not a valid phone number', () => {
242
+ const phoneNumber = 'invalid-phone'
243
+
244
+ expect(() =>
245
+ scalar.parseLiteral({
246
+ kind: Kind.STRING,
247
+ value: phoneNumber,
248
+ })
249
+ ).toThrow()
250
+ })
251
+
252
+ it('should throw an error if parse a literal that is not a string', () => {
253
+ const phoneNumber = '+1234567890'
254
+
255
+ expect(() =>
256
+ scalar.parseLiteral({
257
+ kind: Kind.INT,
258
+ value: phoneNumber,
259
+ })
260
+ ).toThrow()
261
+ })
262
+ })
263
+ ```
264
+
265
+ #### Required Test Cases
266
+
267
+ Your scalar tests should cover these essential scenarios:
268
+
269
+ ##### Serialization Tests
270
+
271
+ - ✅ **Valid values**: Test that valid inputs are serialized correctly
272
+ - ❌ **Invalid types**: Test that non-string inputs throw errors
273
+ - ❌ **Invalid format**: Test that strings not matching your validation throw errors
274
+
275
+ ##### Parse Value Tests
276
+
277
+ - ✅ **Valid values**: Test that valid inputs are parsed correctly
278
+ - ❌ **Invalid format**: Test that invalid strings throw errors
279
+ - ❌ **Invalid types**: Test that non-string inputs throw errors
280
+
281
+ ##### Parse Literal Tests
282
+
283
+ - ✅ **Valid STRING literals**: Test that valid string literals are parsed correctly
284
+ - ❌ **Invalid STRING literals**: Test that invalid string literals throw errors
285
+ - ❌ **Non-STRING literals**: Test that non-string literal kinds (INT, FLOAT, etc.) throw errors
286
+
287
+ #### Testing Best Practices
288
+
289
+ 1. **Test edge cases**: Include boundary values and common invalid inputs
290
+ 2. **Test multiple valid formats**: If your scalar accepts different valid formats, test them all
291
+ 3. **Use descriptive test names**: Make it clear what each test is validating
292
+ 4. **Follow the naming convention**: `YourScalar.test.ts` in the `test/` directory
293
+
294
+ #### Example Edge Cases for Different Scalar Types
295
+
296
+ **String-based scalars (like PhoneNumber):**
297
+
298
+ ```typescript
299
+ // Test empty string
300
+ expect(() => scalar.parseValue('')).toThrow()
301
+
302
+ // Test too long/short values
303
+ expect(() => scalar.parseValue('123')).toThrow()
304
+ expect(() => scalar.parseValue('+' + '1'.repeat(20))).toThrow()
305
+
306
+ // Test special characters
307
+ expect(() => scalar.parseValue('+1-234-567-890')).not.toThrow()
308
+ ```
309
+
310
+ **Number-based scalars:**
311
+
312
+ ```typescript
313
+ // Test zero
314
+ expect(scalar.parseValue(0)).toBe(0)
315
+
316
+ // Test negative numbers
317
+ expect(() => scalar.parseValue(-1)).toThrow()
318
+
319
+ // Test decimal numbers
320
+ expect(scalar.parseValue(123.45)).toBe(123.45)
321
+ ```
322
+
323
+ **Date-based scalars:**
324
+
325
+ ```typescript
326
+ // Test valid ISO date
327
+ expect(scalar.parseValue('2023-12-25T00:00:00Z')).toBe('2023-12-25T00:00:00Z')
328
+
329
+ // Test invalid date format
330
+ expect(() => scalar.parseValue('25/12/2023')).toThrow()
331
+ ```
332
+
333
+ ## Step 4: Validate Your Implementation
334
+
335
+ After implementing your scalar and tests, make sure to:
336
+
337
+ 1. **Run the tests** to ensure they all pass
338
+ 2. **Build the project** to ensure there are no TypeScript errors
339
+ 3. **Test GraphQL queries** that use your new scalar
340
+ 4. **Verify code generation** works with your new scalar
341
+
342
+ ### Common Scalar Types
343
+
344
+ Here are some common patterns for different types of scalars:
345
+
346
+ #### String-based Scalars
347
+
348
+ ```typescript
349
+ export const type = 'string'
350
+ export const schema = z.string().min(1).max(100)
351
+ ```
352
+
353
+ #### Number-based Scalars
354
+
355
+ ```typescript
356
+ export const type = 'number'
357
+ export const schema = z.number().positive()
358
+ ```
359
+
360
+ #### Date-based Scalars
361
+
362
+ ```typescript
363
+ export const type = 'string'
364
+ export const schema = z.string().datetime()
365
+ ```
366
+
367
+ :::info
368
+ **Contributing and UI for Scalars**
369
+
370
+ - **Open Source**: Please submit contributions as a pull request to the Powerhouse team.
371
+ - **UI is Optional but Helpful**: A design or UI for your scalar isn't required, but it helps reviewers understand its purpose.
372
+ - **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.
373
+ :::
374
+
375
+ ### Tips
376
+
377
+ - Always follow the naming convention: use PascalCase for scalar names
378
+ - Include meaningful validation in your Zod schema
379
+ - Write clear, descriptive error messages
380
+ - Keep the `stringSchema` in sync with your `schema` definition
381
+ - Test edge cases in your validation function
382
+ - Update all required sections in `scalars.ts`
@@ -0,0 +1,124 @@
1
+ # Step 2: Integrate Your Scalar into a React Component
2
+
3
+ This guide explains how to use a custom scalar (created as described in the previous step) within a React component. You'll learn how to leverage the scalar's validation schema for form input, display errors, and ensure a seamless user experience.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Step 1: Import the Scalar and Dependencies](#step-1-import-the-scalar-and-dependencies)
9
+ - [Step 2: Define Component Props](#step-2-define-component-props)
10
+ - [Step 3: Implement the Component](#step-3-implement-the-component)
11
+ - [Step 4: Render the Input and Error](#step-4-render-the-input-and-error)
12
+ - [Step 5: Example Usage](#step-5-example-usage)
13
+ - [Best Practices](#best-practices)
14
+ - [Tips](#tips)
15
+
16
+ ## Overview
17
+
18
+ Custom scalars provide type-safe validation and parsing for your data. Integrating them into React components ensures that user input is validated consistently with your backend and schema definitions. This is especially useful for form fields like email, phone number, Ethereum address, etc.
19
+
20
+ ## Step 1: Import the Scalar and Dependencies
21
+
22
+ Import your scalar and React hooks. You may use any input component to capture user input. In the following example, `FormInput` is used for demonstration purposes, but you can use a standard `<input>`, a custom component, or any UI library input.
23
+
24
+ ```typescript
25
+ import { useState } from "react";
26
+ import { EthereumAddress as EthereumAddressScalar } from "@powerhousedao/document-engineering/graphql";
27
+ // FormInput is just an example. You can use any input component you prefer.
28
+ import { FormInput } from "@powerhousedao/design-system";
29
+ ```
30
+
31
+ Replace `EthereumAddress` with your scalar's name as needed.
32
+
33
+ ## Step 2: Define Component Props
34
+
35
+ Define the props for your component. Typically, you'll want an `onChange` callback to notify the parent of the value and its validity:
36
+
37
+ ```typescript
38
+ export interface EthereumAddressProps {
39
+ onChange?: (address: string, isValidAddress: boolean) => void;
40
+ }
41
+ ```
42
+
43
+ Adapt the prop names and types to your scalar (e.g., `PhoneNumberProps`, `onChange(phone: string, isValid: boolean)`).
44
+
45
+ ## Step 3: Implement the Component
46
+
47
+ Use React state to track the input value. Use the scalar's Zod schema for validation. Call `onChange` with the value and validity whenever the input changes.
48
+
49
+ > **Note:** The input component in this example is `FormInput`, but you can use any input or UI component to capture user input. The key is to validate the value using the scalar's schema.
50
+
51
+ ```typescript
52
+ export const EthereumAddress: React.FC<EthereumAddressProps> = ({ onChange }) => {
53
+ const [address, setAddress] = useState("");
54
+
55
+ // Validate using the scalar's Zod schema
56
+ const result = EthereumAddressScalar.schema.safeParse(address);
57
+ const errors = result.error?.errors.map((error) => error.message).join(", ");
58
+
59
+ // Notify parent of value and validity
60
+ if (onChange) {
61
+ onChange(address, result.success);
62
+ }
63
+
64
+ return (
65
+ <div>
66
+ {/* Replace FormInput with any input component you prefer */}
67
+ <FormInput
68
+ id="eth-address-input"
69
+ value={address}
70
+ onChange={(e) => setAddress(e.target.value)}
71
+ placeholder="0x...."
72
+ aria-label="Ethereum address input"
73
+ />
74
+ <label htmlFor="eth-address-input">
75
+ {address !== "" && errors}
76
+ </label>
77
+ </div>
78
+ );
79
+ };
80
+ ```
81
+
82
+ **Key Points:**
83
+ - Use the scalar's `.schema.safeParse(value)` for validation.
84
+ - Display error messages if validation fails.
85
+ - Call `onChange` with both the value and its validity.
86
+ - Use accessible labels and attributes.
87
+ - The input component is flexible—use what fits your UI best.
88
+
89
+ ## Step 4: Render the Input and Error
90
+
91
+ - Use any form input component (e.g., `FormInput`, `<input>`, or a custom UI input) for the field.
92
+ - Show error messages below the input when validation fails.
93
+ - Add accessibility attributes (`aria-label`, `htmlFor`).
94
+
95
+ ## Step 5: Example Usage
96
+
97
+ Here's how you might use your component in a parent form:
98
+
99
+ ```typescript
100
+ <EthereumAddress
101
+ onChange={(address, isValid) => {
102
+ // Handle the address and its validity
103
+ console.log("Address:", address, "Valid:", isValid);
104
+ }}
105
+ />
106
+ ```
107
+
108
+ Replace `EthereumAddress` with your scalar component as needed.
109
+
110
+ ## Best Practices
111
+
112
+ - **Validation:** Always use the scalar's schema for validation to ensure consistency with your backend.
113
+ - **Accessibility:** Use proper labels, `aria-*` attributes, and keyboard navigation.
114
+ - **Error Handling:** Display clear, user-friendly error messages.
115
+ - **DRY Principle:** Reuse the scalar's schema and avoid duplicating validation logic.
116
+ - **Type Safety:** Use TypeScript types for props and state.
117
+
118
+ ## Tips
119
+
120
+ - Keep your UI clean and intuitive.
121
+ - Sync your component with any updates to the scalar's schema.
122
+ - Test edge cases (empty input, invalid formats, etc.).
123
+ - Use meaningful placeholder text and labels.
124
+ - Consider supporting additional props (e.g., `disabled`, `required`) for flexibility.