xpfw
xpfw is a collection of packages geared towards turning your JSON-Schema into a React UI. Check out the live demo to get a quick practical overview.
- @xpfw/form turns your JSON-Schema Input Components just like https://github.com/rjsf-team/react-jsonschema-form
- @xpfw/data lets you turn JSON-Schema into a functional backend independent CRUD-UI
Customization
xpfw is all about custimziation. You can connect @xpfw/data to any backend. @xpfw/form and @xpfw/data also both provide React Hooks for easy themability.
React Native
XPFW supports react native. An example application can be found here.
Usage
To render a field use the SharedField component and pass it your JSON-Schema.
Click here to see a live demo of
@xpfw/form
import { SharedField } from "@xpfw/form"
import { registerComponents } from "@xpfw/form-bulma"
// Making sure that @xpfw/form-shared can find components
registerComponents()
const StaticFieldRenderer = () => <SharedField field={{mapTo: "myField", type: FieldType.Text}} />
If you require readily usable create / edit pages and more check out @xpfw/data.
Usage
To render a field use the SharedField component and pass it your JSON-Schema.
Click here to see a live demo of
@xpfw/form
import { SharedField } from "@xpfw/form"
import { registerComponents } from "@xpfw/form-bulma"
// Making sure that @xpfw/form-shared can find components
registerComponents()
const StaticFieldRenderer = () => <SharedField field={{mapTo: "myField", type: FieldType.Text}} />
If you require readily usable create / edit pages and more check out @xpfw/data.
Theming
Theming a validation type is possible by writing a React Component and registering it in the ComponentRegistry via registerComponent
import { ComponentRegistry } from "@xpfw/form"
ComponentRegistry.registerComponent("number", "guided", GuidedNumbersField)
With this we have registered a theme named guided for the Number-FieldType.
To render a number field with said theme pass the theme-property to a SharedField.
import { SharedField } from "@xpfw/form"
const RenderThemed = () => <SharedField
schema={{title: "myGuidedNumber", type: "number"}}
theme="guided"
/>
Click here to see a live demo of this example field component
This is the code for the registered component.
import { ComponentRegistry, IFieldProps, memo, useFieldWithValidation } from "@xpfw/form"
import { observer } from "mobx-react"
import * as React from "react"
const GuidedNumbersField: React.FunctionComponent<IFieldProps> = observer((props) => {
const fieldProps = useFieldWithValidation(props.schema, props.mapTo, props.prefix)
const memoVals = [props.mapTo, props.prefix, JSON.stringify(props.schema)]
const randomize = memo(() => () => fieldProps.setValue(Math.round(Math.random() * 100)), memoVals)
React.useEffect(randomize, memoVals)
return (
<div className="is-flex centerJustify has-text-centered is-size-5 marginTop marginBottom">
<a className="button" onClick={() => {
fieldProps.setValue(fieldProps.value - 1)
}}>Decrease</a>
<span style={{marginRight: "1rem", marginLeft: "1rem"}}>Value of <i>{props.schema.title}</i> is: <b>{fieldProps.value}</b></span>
<a className="button" onClick={() => {
fieldProps.setValue(fieldProps.value + 1)
}}>Increase</a><a style={{marginLeft: "1rem"}} className="button" onClick={randomize}>Randomize</a>
</div>
)
})
Usage
@xpfw/data packages provide 4 hooks. One for each of the CRUD operations.
Click here to see a live demo of
@xpfw/data
Quick Start
If you want a full CRUD UI from a JSON-Schema out of the box you can use use @xpfw/data-bulma.
For custom styling you can use the hooks in your own components!
The parameters are minimal: They all require an ExtendedJSONSchema and optionally take a prefix. If it's an edit, show or delete you also need to supply an id. If it's a show you don't even need a form just the collection name.
import {
BulmaCreate, BulmaEdit, BulmaDelete, BulmaShow, BulmaList
} from "@xpfw/data-bulma"
// replace with your own
const form = {
model: "testModel",
collection: "testCol",
sections: [{fields: [{
mapTo: "myString",
type: FieldType.Text,
validate: {required: {type: RequiredType.Always}}
}]}]
}
<BulmaCreate schema={schema} prefix="optionalPrefix" />
<BulmaEdit schema={schema} prefix="optionalPrefix" id="idOfEditedItem" />
<BulmaDelete schema={schema} prefix="optionalPrefix" id="idOfEditedItem" />
<BulmaShow schema={schema} id="idOfItem" />
<BulmaList schema={schema} prefix="optionalPrefix" />
Hooks
The @xpfw/data-bulma package was built to provide a quick start. Most probably you will want to customize the components and let @xpfw/data only focus on providing the data.
For every of the 4 CRUD operation's there is a React hook as well as for lists and authentication (login / registration / logout):
Create, Read, Update, Remove, List, Authentication
For complete code examples of wrapped components you can look at the implementations used for the demo site:
Create, Read, Update, Remove, List
Create
Example usage of the useCreate Hook can be found here
Expects:
schema=>ExtendedJSONSchemaconform JSON-Schema- (optional)
mapTo=> use for FormStore path instead ofschema.title - (optional)
prefix=> prepend to FormStore path Provides the following properties: submitCreate=> Call this and thecreatecall will be called.state,loadinganderrorwill update respectivelystate=> Status of the creationloading=> boolean indicating businesserror=> null if none otherwise array of invalid fieldsuser=> currently logged in user
Read
Example usage of the useGet hook can be found here
Expects:
id=> The ID of the requested recordcollection=> The name of the collection to fetch the record from Provides the following properties:item=> either null or the requested object fromcollectionandidcombinationloading=> boolean indicating business
Edit
Example usage of the useEdit hook can be found here
Expects:
id=> The ID of the record to editschema=>ExtendedJSONSchemaconform JSON-Schema. Thecollectionproperty is required if used in this hook!- (optional)
mapTo=> use for FormStore path instead ofschema.title - (optional)
prefix=> prepend to FormStore path Provides the following properties: submitEdit=> Call this and theeditcall will be called.state,loadinganderrorwill update respectivelystate=> Status of the creationloading=> boolean indicating businesserror=> null if none otherwise array of invalid fieldsuser=> currently logged in user
Delete
useRemove
Example usage of the useRemove hook can be found here
Expects:
id=> The ID of the record to removeschema=>ExtendedJSONSchemaconform JSON-Schema. Thecollectionproperty is required if used in this hook! Provides the following properties:submitRemove=> Call this and theremovecall will be called.state,loadinganderrorwill update respectivelystate=> Status of the creationloading=> boolean indicating businesserror=> null if none otherwise array of invalid fields
List
Example usage of the useList Hook can be found here
Expects:
schema=>ExtendedJSONSchemaconform JSON-Schema- (optional)
mapTo=> use for FormStore path instead ofschema.title - (optional)
prefix=> prepend to FormStore path - (optional)
options=> optional advanced options Provides the following properties: nextPage=> Increase page count, load content of next page and hence change content oflistprevPage=> Decrease page count, load content of previous page and hence change content oflistlist=> Array of found items you can rendercurrentPage=> null if none otherwise array of invalid fieldsmaxPage=> null if none otherwise array of invalid fieldsshowNextPage=> convenience prop to know whether there are more pages available or not based on comparison of currentPage and maxPageshowPrevPage=> convenience prop to know whether there are earlier pages available or not based on comparison of currentPage and maxPage
Authentication
The useAuth hook provides information about the current user.
It provides the following properties:
submitLogin=> Call this and theloginwill be called with the value of MailField and PwField.error,userandloadingwill update respectivelysubmitLogout=> Call this and thelogoutwill be called with the value of MailField and PwField.error,userandloadingwill update respectivelysubmitRegister=> Call this and theregistrationwill be called with the value of MailField and PwField.error,userandloadingwill update respectivelyloggedIn=> true if successfully logged inconnected=> Whether an active connection to the server existsloading=> whether user login/out/register related activites are ongoinguser=> Currently logged in user. Null if not logged inerror=> null if none otherwise array of invalid fields
Usage
@xpfw/data packages provide 4 hooks. One for each of the CRUD operations.
Click here to see a live demo of
@xpfw/data
Quick Start
If you want a full CRUD UI from a JSON-Schema out of the box you can use use @xpfw/data-bulma.
For custom styling you can use the hooks in your own components!
The parameters are minimal: They all require an ExtendedJSONSchema and optionally take a prefix. If it's an edit, show or delete you also need to supply an id. If it's a show you don't even need a form just the collection name.
import {
BulmaCreate, BulmaEdit, BulmaDelete, BulmaShow, BulmaList
} from "@xpfw/data-bulma"
// replace with your own
const form = {
model: "testModel",
collection: "testCol",
sections: [{fields: [{
mapTo: "myString",
type: FieldType.Text,
validate: {required: {type: RequiredType.Always}}
}]}]
}
<BulmaCreate schema={schema} prefix="optionalPrefix" />
<BulmaEdit schema={schema} prefix="optionalPrefix" id="idOfEditedItem" />
<BulmaDelete schema={schema} prefix="optionalPrefix" id="idOfEditedItem" />
<BulmaShow schema={schema} id="idOfItem" />
<BulmaList schema={schema} prefix="optionalPrefix" />
Hooks
The @xpfw/data-bulma package was built to provide a quick start. Most probably you will want to customize the components and let @xpfw/data only focus on providing the data.
For every of the 4 CRUD operation's there is a React hook as well as for lists and authentication (login / registration / logout):
Create, Read, Update, Remove, List, Authentication
For complete code examples of wrapped components you can look at the implementations used for the demo site:
Create, Read, Update, Remove, List
Create
Example usage of the useCreate Hook can be found here
Expects:
schema=>ExtendedJSONSchemaconform JSON-Schema- (optional)
mapTo=> use for FormStore path instead ofschema.title - (optional)
prefix=> prepend to FormStore path Provides the following properties: submitCreate=> Call this and thecreatecall will be called.state,loadinganderrorwill update respectivelystate=> Status of the creationloading=> boolean indicating businesserror=> null if none otherwise array of invalid fieldsuser=> currently logged in user
Read
Example usage of the useGet hook can be found here
Expects:
id=> The ID of the requested recordcollection=> The name of the collection to fetch the record from Provides the following properties:item=> either null or the requested object fromcollectionandidcombinationloading=> boolean indicating business
Edit
Example usage of the useEdit hook can be found here
Expects:
id=> The ID of the record to editschema=>ExtendedJSONSchemaconform JSON-Schema. Thecollectionproperty is required if used in this hook!- (optional)
mapTo=> use for FormStore path instead ofschema.title - (optional)
prefix=> prepend to FormStore path Provides the following properties: submitEdit=> Call this and theeditcall will be called.state,loadinganderrorwill update respectivelystate=> Status of the creationloading=> boolean indicating businesserror=> null if none otherwise array of invalid fieldsuser=> currently logged in user
Delete
useRemove
Example usage of the useRemove hook can be found here
Expects:
id=> The ID of the record to removeschema=>ExtendedJSONSchemaconform JSON-Schema. Thecollectionproperty is required if used in this hook! Provides the following properties:submitRemove=> Call this and theremovecall will be called.state,loadinganderrorwill update respectivelystate=> Status of the creationloading=> boolean indicating businesserror=> null if none otherwise array of invalid fields
List
Example usage of the useList Hook can be found here
Expects:
schema=>ExtendedJSONSchemaconform JSON-Schema- (optional)
mapTo=> use for FormStore path instead ofschema.title - (optional)
prefix=> prepend to FormStore path - (optional)
options=> optional advanced options Provides the following properties: nextPage=> Increase page count, load content of next page and hence change content oflistprevPage=> Decrease page count, load content of previous page and hence change content oflistlist=> Array of found items you can rendercurrentPage=> null if none otherwise array of invalid fieldsmaxPage=> null if none otherwise array of invalid fieldsshowNextPage=> convenience prop to know whether there are more pages available or not based on comparison of currentPage and maxPageshowPrevPage=> convenience prop to know whether there are earlier pages available or not based on comparison of currentPage and maxPage
Authentication
The useAuth hook provides information about the current user.
It provides the following properties:
submitLogin=> Call this and theloginwill be called with the value of MailField and PwField.error,userandloadingwill update respectivelysubmitLogout=> Call this and thelogoutwill be called with the value of MailField and PwField.error,userandloadingwill update respectivelysubmitRegister=> Call this and theregistrationwill be called with the value of MailField and PwField.error,userandloadingwill update respectivelyloggedIn=> true if successfully logged inconnected=> Whether an active connection to the server existsloading=> whether user login/out/register related activites are ongoinguser=> Currently logged in user. Null if not logged inerror=> null if none otherwise array of invalid fields
IBackendClient
Connecting your own backend to xpfw is done by implementing the IBackendClient interface.
The implementation of the NeDB and feathers client offer a great example on how to do on and offline backends!
For an offline plugin you only need to implement
getcreateremovefindpatch
For an online plugin you also need to implement
connectTodisconnectloginregisterlogout
Since xpfw is strongly typed your IDE should tell you what parameters to expect. In case you are not using typescript see an abstract from the API docs below.
Stacks
XPFW was mainly developed with feathers and NeDB in mind.
Nevertheless it was written so that it can be easily connected to any backend via the IBackendClient interface.
The implementation of the NeDB and feathers client offer a great example on how to do on and offline backends!
Frontend
xpfw was initially written for feathers, but later this was exchanged by IBackendClient to prevent lock-in.
If you have a working feathers backend you can use @xpfw/data-feathers like this
import { BackendClient, UserStore } from "@xpfw/data"
import { FeathersClient } from "@xpfw/data-feathers"
BackendClient.client = FeathersClient
BackendClient.client.connectTo("yourBackend.com", {
// For react-native replace with AsyncStorage
authOptions: {storage: get(global, "window.localStorage")},
userStore: UserStore
})
Backend
If you want to use feathers as backend with xpfw we provide the @xpfw/feathers-hooks package.
Add the permission and validate hooks to your database services and you're all set!
import { permission, validate } from "@xpfw/feathers-hooks"
import { Application } from "@feathersjs/feathers"
// use with app.configure(xpfwConfiguration)
const xpfwConfiguration = (app: Application) => {
const service = app.service("users")
service.hooks({
before: {create: [
permission.create(permissions),
validate.general(schema, "create")
]}})
}
import { IPermissionSchema, Permission } from "@xpfw/permission"
import { ExtendedJSONSchema } from "@xpfw/form"
const permissions: IPermissionSchema = {
required:{
create:Permission.Public,
update:Permission.User
}
}
const schema: ExtendedJSONSchema = {
title: "userModel",
collection: "users",
properties: {
"email": {type: "string"},
"password": {type: "string"}
}
}
NeDB
If you want to write an offline application @xpfw/data-nedb is the adapter you are looking for.
Overwrite the client property of BackendClient and your xpfw powered offline application is ready to go.
import { BackendClient } from '@xpfw/data';
import NedbClient from '@xpfw/data-nedb';
BackendClient.client = NedbClient
Testing
xpfw delivers premade tests which you can include to get increase code coverage out of your custom styled components!
All tests follow the same "API". It is an exported function, which you give your readily usable React Component. That will be rendered then.
Form Tests
For tests regarding form component themes there is @xpfw/form-tests It contains the following tests:
- booleanTest
- stringTest
- arrayTest
- dateTest
- locationTest
- objectTest
- selectTest
Data Tests
For tests regarding data / CRUD components there is @xpfw/data-tests It provides the following tests:
- authTest
- createTest
- editTest
- removeTest
- showTest
- listTest
- relationshipTest
Caution! MongoDB required
A running MongoDB instance on localhost is required to run the data tests!