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
=>ExtendedJSONSchema
conform 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 thecreate
call will be called.state
,loading
anderror
will 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 fromcollection
andid
combinationloading
=> boolean indicating business
Edit
Example usage of the useEdit
hook can be found here
Expects:
id
=> The ID of the record to editschema
=>ExtendedJSONSchema
conform JSON-Schema. Thecollection
property 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 theedit
call will be called.state
,loading
anderror
will 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
=>ExtendedJSONSchema
conform JSON-Schema. Thecollection
property is required if used in this hook! Provides the following properties:submitRemove
=> Call this and theremove
call will be called.state
,loading
anderror
will 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
=>ExtendedJSONSchema
conform 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 oflist
prevPage
=> Decrease page count, load content of previous page and hence change content oflist
list
=> 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 thelogin
will be called with the value of MailField and PwField.error
,user
andloading
will update respectivelysubmitLogout
=> Call this and thelogout
will be called with the value of MailField and PwField.error
,user
andloading
will update respectivelysubmitRegister
=> Call this and theregistration
will be called with the value of MailField and PwField.error
,user
andloading
will 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
=>ExtendedJSONSchema
conform 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 thecreate
call will be called.state
,loading
anderror
will 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 fromcollection
andid
combinationloading
=> boolean indicating business
Edit
Example usage of the useEdit
hook can be found here
Expects:
id
=> The ID of the record to editschema
=>ExtendedJSONSchema
conform JSON-Schema. Thecollection
property 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 theedit
call will be called.state
,loading
anderror
will 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
=>ExtendedJSONSchema
conform JSON-Schema. Thecollection
property is required if used in this hook! Provides the following properties:submitRemove
=> Call this and theremove
call will be called.state
,loading
anderror
will 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
=>ExtendedJSONSchema
conform 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 oflist
prevPage
=> Decrease page count, load content of previous page and hence change content oflist
list
=> 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 thelogin
will be called with the value of MailField and PwField.error
,user
andloading
will update respectivelysubmitLogout
=> Call this and thelogout
will be called with the value of MailField and PwField.error
,user
andloading
will update respectivelysubmitRegister
=> Call this and theregistration
will be called with the value of MailField and PwField.error
,user
andloading
will 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
get
create
remove
find
patch
For an online plugin you also need to implement
connectTo
disconnect
login
register
logout
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!