Skip to content

Commit c633bbe

Browse files
authored
Merge pull request #185 from NMFS-RADFish/documentation/storage-1.1
Add documentation for v1.1.0 storage
2 parents 140d9c7 + 2aaba25 commit c633bbe

File tree

2 files changed

+388
-1
lines changed

2 files changed

+388
-1
lines changed

docs/design-system/storage.md

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
---
2+
sidebar_position: 2
3+
---
4+
5+
# Storage
6+
7+
The RADFish storage system provides a robust and flexible solution for managing data in your applications. It's designed to work seamlessly with offline-first applications, providing a consistent API for storing and retrieving data from device storage.
8+
9+
## Overview
10+
11+
The storage system is built around several key components that work together:
12+
13+
1. **Connector** - Connects to a specific storage backend (e.g., IndexedDB).
14+
2. **Schema** - Defines the structure and validation rules for your data.
15+
3. **Store** - Manages collections and provides access to data.
16+
4. **Collection** - Handles CRUD operations for a specific type of data.
17+
18+
## Setting Up Storage in an Application
19+
20+
The most common way to set up storage in a RADFish application is through the `Application` constructor. This approach automatically handles connecting to the storage backend and setting up collections.
21+
22+
```jsx
23+
import { Application } from "@nmfs-radfish/radfish";
24+
import { IndexedDBConnector } from "@nmfs-radfish/radfish/storage";
25+
26+
const app = new Application({
27+
stores: {
28+
weatherSurvey: {
29+
connector: new IndexedDBConnector("weather-survey-app"),
30+
collections: {
31+
Form: {
32+
schema: {
33+
fields: {
34+
id: {
35+
type: "string",
36+
primaryKey: true,
37+
},
38+
surveyDate: {
39+
type: "string",
40+
required: true,
41+
},
42+
submittedBy: {
43+
type: "string",
44+
required: true,
45+
},
46+
submittedAt: {
47+
type: "timestamp",
48+
},
49+
status: {
50+
type: "string",
51+
required: true,
52+
},
53+
},
54+
},
55+
},
56+
},
57+
},
58+
}
59+
});
60+
```
61+
62+
## Key Components
63+
64+
### Connector
65+
66+
A Connector is responsible for connecting to a specific storage backend and providing an interface for collections to interact with that backend.
67+
68+
RADFish provides two built-in connectors:
69+
70+
1. **IndexedDBConnector** - Uses IndexedDB for storage (recommended for most applications)
71+
2. **LocalStorageConnector** - Uses localStorage for storage (limited capacity, but simpler)
72+
73+
```jsx
74+
import { IndexedDBConnector } from "@nmfs-radfish/radfish/storage";
75+
76+
const connector = new IndexedDBConnector("weather-survey-app");
77+
```
78+
79+
### Schema
80+
81+
A Schema defines the structure and validation rules for a collection of data. It defines all the fields, their types, and constraints for a particular type of data.
82+
83+
```jsx
84+
const formSchema = {
85+
fields: {
86+
id: {
87+
type: "string",
88+
primaryKey: true,
89+
},
90+
surveyDate: {
91+
type: "string",
92+
required: true,
93+
},
94+
submittedBy: {
95+
type: "string",
96+
required: true,
97+
},
98+
submittedAt: {
99+
type: "timestamp",
100+
},
101+
// ... other fields
102+
},
103+
};
104+
```
105+
106+
#### Schema Field Properties
107+
108+
| Property | Type | Description |
109+
| ------------ | ------- | --------------------------------------------------------- |
110+
| `type` | string | The data type of the field (see supported types below) |
111+
| `required` | boolean | Indicates if the field is required |
112+
| `primaryKey` | boolean | Marks the field as the primary key for the collection |
113+
| `default` | any | Default value for the field if not provided |
114+
| `minLength` | number | Minimum length for string fields |
115+
| `minimum` | number | Minimum value for number fields |
116+
| `maximum` | number | Maximum value for number fields |
117+
| `pattern` | RegExp | Regular expression pattern for string validation |
118+
| `unique` | boolean | Indicates if the field value must be unique in collection |
119+
120+
#### Supported Data Types
121+
122+
The Schema system supports the following data types:
123+
124+
| Type | Description | Valid Values |
125+
| ---------------- | ------------------------------------- | -------------------------------------------------------- |
126+
| `string` | Text values | Any string |
127+
| `number` | Numeric values (integers or decimals) | Any number |
128+
| `boolean` | Boolean values | `true` or `false` |
129+
| `timestamp` | Date and time values with timezone | Date object or ISO string (e.g., `2025-05-16T14:23:00Z`) |
130+
| `date` | Date values (without time) | Date object or ISO date string (e.g., `2025-05-16`) |
131+
| `time` | Time values (without date) | String in 24-hour format (e.g., `14:23:00`) |
132+
| `datetime-local` | Date and time values without timezone | Date object or ISO string (e.g., `2025-05-16T14:23:00`) |
133+
134+
#### Schema Validation
135+
136+
The Schema class provides a `validate` method to validate data against the schema rules. It can be used to check if data is valid before attempting to save it to a collection.
137+
138+
```jsx
139+
// Get a collection's schema
140+
const schema = formCollection.schema;
141+
142+
// Data to validate
143+
const data = {
144+
surveyDate: "2025-05-16",
145+
submittedBy: "John Smith",
146+
status: "draft",
147+
};
148+
149+
// Validate data (non-strict mode)
150+
const validation = schema.validate(data);
151+
152+
if (!validation.isValid) {
153+
console.error("Validation errors:", validation.errors);
154+
// Example output: [{field: "id", error: "Field is required"}]
155+
} else {
156+
// Data is valid
157+
await formCollection.create(data);
158+
}
159+
```
160+
161+
The `validate` method returns an object with two properties:
162+
- `isValid` (boolean): Indicates if the data is valid
163+
- `errors` (array): List of validation error objects if any
164+
165+
Each error object in the errors array has the following format:
166+
- `field` (string): The name of the field that failed validation
167+
- `error` (string): A description of the validation error
168+
169+
##### Strict Mode Validation
170+
171+
You can also use strict mode, which throws a ValidationError instead of returning an error object:
172+
173+
```jsx
174+
try {
175+
// Passing 'true' as the second parameter enables strict mode
176+
schema.validate(data, true);
177+
178+
// If validation passes, create the record
179+
await formCollection.create(data);
180+
} catch (error) {
181+
if (error.name === "ValidationError") {
182+
console.error("Validation errors:", error.errors);
183+
// Handle validation errors
184+
} else {
185+
// Handle other errors
186+
console.error("Error:", error.message);
187+
}
188+
}
189+
```
190+
191+
### Store
192+
193+
A Store manages collections for a specific domain of your application. Each store has a connector and can have multiple collections.
194+
195+
```jsx
196+
import { Store } from "@nmfs-radfish/radfish/storage";
197+
198+
const store = new Store({
199+
name: "weatherSurvey",
200+
connector: connector
201+
});
202+
```
203+
204+
### Collection
205+
206+
A Collection provides methods for creating, reading, updating, and deleting data for a specific schema. Collections validate data against their schema before performing any operations.
207+
208+
```jsx
209+
// Get a collection from the Application
210+
const formCollection = app.stores.weatherSurvey.getCollection("Form");
211+
212+
// Create a new form
213+
const newForm = await formCollection.create({
214+
surveyDate: "2025-05-16",
215+
submittedBy: "John Smith",
216+
status: "draft"
217+
});
218+
219+
// Find forms
220+
const draftForms = await formCollection.find({ status: "draft" });
221+
222+
// Update a form
223+
const updatedForm = await formCollection.update({
224+
id: newForm.id,
225+
status: "submitted",
226+
submittedAt: new Date().toISOString()
227+
});
228+
229+
// Delete a form
230+
await formCollection.delete({ id: newForm.id });
231+
```
232+
233+
## Working with Collections
234+
235+
### Creating Records
236+
237+
To create a new record, call the `create` method on a collection:
238+
239+
```jsx
240+
const newForm = await formCollection.create({
241+
surveyDate: "2025-05-16",
242+
submittedBy: "John Smith",
243+
status: "draft"
244+
});
245+
246+
console.log("Created form with ID:", newForm.id);
247+
```
248+
249+
### Finding Records
250+
251+
To retrieve records from a collection, use the `find` method:
252+
253+
```jsx
254+
// Find all draft forms
255+
const draftForms = await formCollection.find({ status: "draft" });
256+
257+
// Find a specific form by ID
258+
const form = await formCollection.find({ id: "form-123" });
259+
```
260+
261+
### Updating Records
262+
263+
To update a record, use the `update` method:
264+
265+
```jsx
266+
const updatedForm = await formCollection.update({
267+
id: "form-123",
268+
status: "submitted",
269+
submittedAt: new Date().toISOString()
270+
});
271+
```
272+
273+
### Deleting Records
274+
275+
To delete records, use the `delete` method:
276+
277+
```jsx
278+
// Delete a specific form
279+
await formCollection.delete({ id: "form-123" });
280+
281+
// Delete all draft forms
282+
await formCollection.delete({ status: "draft" });
283+
```
284+
285+
## Event Handling
286+
287+
The storage system emits events during various operations, allowing you to hook into the lifecycle of data operations.
288+
289+
```jsx
290+
// Listen for events at the collection level
291+
formCollection.addEventListener('beforeCreate', (event) => {
292+
// Add defaults if needed
293+
if (!event.detail.data.status) {
294+
event.detail.data.status = "draft";
295+
}
296+
297+
console.log('About to create form:', event.detail.data);
298+
});
299+
300+
formCollection.addEventListener('create', (event) => {
301+
console.log('Form created:', event.detail.data);
302+
});
303+
304+
// Listen for events at the connector level (across all collections)
305+
connector.addEventListener('create', (event) => {
306+
console.log(`Created record in ${event.detail.schema.name}:`, event.detail.data);
307+
});
308+
```
309+
310+
## Best Practices
311+
312+
1. **Define Clear Schemas**: Well-defined schemas ensure data integrity and make your application more maintainable.
313+
314+
2. **Use IndexedDB for Larger Applications**: IndexedDB provides more storage space and better performance than LocalStorage.
315+
316+
3. **Handle Offline Scenarios**: Implement sync strategies to handle data that needs to be synchronized with a server when connectivity is restored.
317+
318+
4. **Use Events for Side Effects**: Use event listeners for logging, caching, and other side effects in response to changes to your data.
319+
320+
5. **Validate User Input**: While collections validate data against schemas, it's still good practice to validate user input before calling collection methods.
321+
322+
## Example: Complete Weather Survey Application
323+
324+
Here's an example of how to set up a complete weather survey application with forms:
325+
326+
```jsx
327+
import { Application } from "@nmfs-radfish/radfish";
328+
import { IndexedDBConnector } from "@nmfs-radfish/radfish/storage";
329+
330+
// Initialize the application with storage
331+
const app = new Application({
332+
stores: {
333+
weatherSurvey: {
334+
connector: new IndexedDBConnector("weather-survey-app"),
335+
collections: {
336+
Form: {
337+
schema: {
338+
fields: {
339+
id: {
340+
type: "string",
341+
primaryKey: true,
342+
},
343+
surveyDate: {
344+
type: "string",
345+
required: true,
346+
},
347+
submittedBy: {
348+
type: "string",
349+
required: true,
350+
},
351+
submittedAt: {
352+
type: "timestamp",
353+
},
354+
status: {
355+
type: "string",
356+
required: true,
357+
},
358+
},
359+
},
360+
},
361+
},
362+
},
363+
}
364+
});
365+
366+
// When the application is ready
367+
app.on("ready", async () => {
368+
// Get collections
369+
const formCollection = app.stores.weatherSurvey.getCollection("Form");
370+
371+
// Create a new form
372+
const newForm = await formCollection.create({
373+
surveyDate: "2025-05-16",
374+
submittedBy: "John Smith",
375+
status: "draft"
376+
});
377+
378+
// Mark the form as submitted
379+
await formCollection.update({
380+
id: newForm.id,
381+
status: "submitted",
382+
submittedAt: new Date().toISOString()
383+
});
384+
});
385+
```
386+
387+
By following these patterns, you can create powerful applications that work seamlessly online and offline, providing a consistent user experience regardless of network conditions.

docs/design-system/us-web-design-system.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 2
2+
sidebar_position: 3
33
---
44

55
# U.S. Web Design System

0 commit comments

Comments
 (0)