Giter Site home page Giter Site logo

yahoo / jafar Goto Github PK

View Code? Open in Web Editor NEW
109.0 10.0 9.0 39.32 MB

๐ŸŒŸ!(Just another form application renderer)

Home Page: https://yahoo.github.io/jafar

License: MIT License

JavaScript 99.26% CSS 0.13% HTML 0.51% Shell 0.10%
form forms ui layout react reactjs web form-validation persistence ui-components

jafar's Introduction

JAFAR

Jafar - not Just another form application renderer, a set of tools which implement form capabilities using a simple JSON form object, containing fields and plenty of advanced features.

Table Of Content

Background & Usage

Managing complicated forms is a hard task for developers. Dealing with field validations, dependencies, disable or exclude fields in some conditions and more can make the code complicated, hard to maintain and hard to write to begin with.

Jafar let developers build forms easily by defining a readable and intuitive form definition (model json & resources json) that represent the entire form lifescycle - such as fields and their corresponding data path, initial data, validators, dto conversions and more. It's based on a pure javascript, ui free form class which handles the form's definition, lifecycle and data manipulations. With the basic form class, any ui library (such as react, angular and vue) can easily use it to expose Form and Field components.

Supported Form Products

Javascript Form Class

Javascript Form class which manage fields and data manipulations. More info

React

Supplies 3 products to manage forms in react applications. More info

  • React Form & Field components based on Form class.
  • Common components for usage such as Text, Number, Select and more, based on Material UI
  • Layout components to build form pages / peaces with the same UI / UX experience such as Item component which contain header, sections, footer actions and menu actions.

Potentially a single page (edit / create / details / list) can be implemented using these 3 packages

Highlights

  • Manage Complicated Forms
  • Framework Agnostic
  • High Performance
  • Form Persistency
  • Full Lifecycle Log
  • Replay Client Actions For Debug
  • Form Snapshots and Undo Operations
  • Server Side Validation
  • Grid Usage
  • UI Components And Layout Supply
  • Vast Documentation And Demos
  • Low Dependencies Number
  • Small Package Size
  • High Test Coverage

Install

To install one of our consumable packages:

  • form - Javascript Form class
  • react-form - Form & Field components
  • react-components - Text, Number, Checkbox and more
  • react-layout - Item, Sections and more layout components

Run:

using npm - npm install --save @jafar/{package-name-here}

using yarn - yarn add @jafar/{package-name-here}

Examples

The following is a simple javascript Form class test example:

Javascript Form Class

import Form from '@jafar/form';
import UserService from './UserService';

// define form model object that will be the initial state of the form
const model = {
  id: 'user-form',
  fields: {
    firstName: {
      label: 'First Name',
      path: 'firstName',
      required: true,
      validators: [{
        name: 'minLength'
        args: {
          value: 2
        }
      },
    },
    lastName: {
      label: 'Last Name',
      path: 'lastName',
    },
    email: {
      label: 'Email',
      path: 'email',
      validators: [{
        name: 'email',
      }, {
        name: 'uniqueField',
        args: { serverField: 'email' }
      }],
    },
  },
  data: {
    firstName: 'Ross',
    lastName: 'Geller',
    email: '[email protected]',
  },
};

// define form resources object that contains all the handlers that the model needs
const resources = {
  validators: {
    uniqueField: {
      func: async ({ value, args }) => {
        return await UserService.isFieldUnique(args.serverField, value);
      },
      message: ({ value }) => `${ value } is already taken`,
    }
  },
  hooks: {
    submit: async ({ data }) => {
      return await UserService.save(data);
    }
  }
};

// create user form instance
const form = new Form();
await form.init(model, resources);

// verify form is valid
expect(form.invalid).toBeFalsy();

// change field firstName
await form.changeValue('firstName', 'Monica');

// verify form is valid
expect(form.invalid).toBeFalsy();

// change field firstName to undefined
await form.changeValue('firstName', '');

// verify form is invalid (since field 'firstName' is required and has minimum length)
expect(form.invalid).toBeTruthy();

// verify errors
expect(form.fields.firstName.errors).toEqual([
  { name: 'required', message: 'Field required' }, 
  { name: 'minLength', message: 'Minimum length is 2' }
]);

// make form valid again
await form.changeValue('firstName', 'Monica');

// submit the form
const success = await form.submit();

// verify submit success
expect(success).toEqual(true);

React Form Component

The following is a simple react Form & Field components (based on Form class) example:

import { Form, Field } from '@jafar/react-form';

const model = { /*...*/ };
const resources = { /*...*/ };

<Form model={model} resources={resources}>
  <h2>Basic Info</h2>
  <Field id="firstName" />
  <Field id="lastName" />
  <h2>Contact Info</h2>
  <Field id="email" />
</Form>

Docs & Demos

Jafar's full docs and demos are available here.

Run Docs & Demos Locally

Clone repository

git clone https://github.com/yahoo/jafar.git

Install packages and link them

using npm - cd /jafar && npm run bootstrap

using yarn - cd /jafar && yarn run bootstrap

  • Alternatively, run npm install (or yarn install) in the desired sub-package (under jafar/packages folder) to install it without links.

Run website locally

To run demos and docs locally for one of react-form, react-components, react-layout, react-editor and documentation packages:

using npm - cd /jafar/packages/{package-name-here} && npm start

using yarn - cd /jafar/packages/{package-name-here} && yarn start

Contribute

Please refer to the CONTRIBUTING.md file for information about how to get involved. We welcome issues, questions, and pull requests. Pull Requests are welcome.

Licence

Jafar is MIT licensed.

jafar's People

Contributors

dependabot[bot] avatar galhavivi avatar rgisrael avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jafar's Issues

Logger don't work.

Describe the bug
In react-editor, set log-leverl to debug, the console of browser display nothing.

To Reproduce

  1. In react-editor packages: edit src/website/Root.jsx
  2. Add:
import { setLogLevel, logLevels } from '@jafar/form';
setLogLevel(logLevels.DEBUG);
  1. In browser: console window display nothing

Expected behavior
In other packages except 'form', logger should also work.

Additional context
I think this problem come from the configure of package dependencies.
React-editor or react-form etc, configure '@jafar/form' as 'dependencies', not 'peerDependencies'.
The result is there are two '@jafar/form' in builded bundle.

Value or State change is not sync, so update data in react component is wrong in some scenes

In react-form of jafar, update value or state through onValueChange or onStateChange function, then react pass props to component, reflect this change.

But this progress is not sync, so in some scenes the update logic is wrong.
for example, Validators.js in react-editor :

export default withTheme(({ value = [], state = {}, onValueChange, onStateChange }) => {
    ....
    const onValidatorStateChange = (validatorState, index) => {
        ....
        onStateChange(newState);
     };
}

If there are two validators, when the second child call onStateChange function, the state prop has not updated in this time. So the 'state' in Validators will miss the update of first child.

Add component view/Labels

Add view component of Label to react-components package.

  • expected value - array of strings.

  • should have a state option of "inline" (default is true).

  • if inline = true, render the array of string as arr.join(', ').
    Should also have a "maxItems" (default to 20), and if has more items than 20 show a link of "Show all". When clicking it - it should open a modal that shows all the labels in a scrollable div, each item in one line.

  • if inline=false, render items in a scrollable div (max-height default to 150px). Each array item in one line

  • Add also an .md file similar to other components

Use css grid to layout form

Is your feature request related to a problem? Please describe.
Current react-layout use Section and Box to align component, which is flexible. But I feel it is not Intuitive๏ผŒspecially Box nested Box in source code.

Describe the solution you'd like
Css grid is modern layout, which is intuitive and powerful. Specially grid-area is suit to layout form.

Describe alternatives you've considered
For example, we can define form layout like this:

// CSS
.container {
  grid-template-areas: 
    "name    name    age age"      // <<---------- define component occupy which cell, can span rows or cols
    "phone   phone   "                   //
}
.name {
  grid-area: name;
}
.age {
  grid-area: age;
}
.phone {
 grid-area: phone;
}

// JS
<Field id="name" className="name" />
<Field id="age" className="age" />
<Field id="phone" className="phone" />
...

Changing grid-template-areas will change layout. Grid-template-areas string is just like EXCEL, can span cols and rows, is very flexible.

Add layout editor

Add to react-editor the ability to add Item layout and download files.

Add component view/KeyValue

Add view component of KeyValue to react-components package.

  • Should represent object (json), where the key is string and value is string (if can implement that value can be other types like number / date for example it will be great).
  • Each pair is in a different line
    Add also an .md file similar to other components

Add component edit/ColorPicker

Add edit component of ColorPicker to react-components package.

  • Consider using this as an underline component (to be aligned with the rest of the component that are using material-ui):
    https://www.npmjs.com/package/material-ui-color-picker

  • also add an .md file similar to other components

  • add test under the tests folder

  • Add a record in the CHANGELOG.nd file under "Unreleased" section -> React components

react-components - update components to use toJafar hoc util

Update components in @jafar/react-components to use toJafar mapper util As described in
#86

A good reference -
https://github.com/yahoo/jafar/tree/master/packages/react-components/src/components/edit/Switch

Test
also update tests similar to - https://github.com/yahoo/jafar/blob/master/packages/react-components/src/test/edit/Switch.spec.js

md file - verify demos of the components:

  • all examples of the component work as expected
  • no console errors or warnings
  • description and import with link appear in the top of all examples
  • props & methods appear and show correct data - props and default props

Add component view/Color

Add view component of Color to react-components package.

  • Should present the color string next to a round / square div that shows the color itself.
  • also add an .md file similar to other components

Add updater option to changeState and changeValue actions

Jafar maintain a process queue - meaning in you call 3 times "onStateChange" - only after one action is done processing - the next one will start - https://yahoo.github.io/jafar/docs/pending-actions.html.
onStateChange receive an entire NEW state object and replace it completely for the field (not merge with current state object - replace) just like setState of react - https://reactjs.org/docs/react-component.html#setstate.

We should also support 'updater' option like react's 'setState' to enable updating state based on the current existing field state. i.e :

  1. changeState(fieldId, entireNewStateObject) // currently supported
  2. changeState(fieldId, (currentState) => { return { ...currentState, newData: 12345 } })

That will enable Jafar react components to call "onStateChange" multiple times and the same time (for example from few different underline components of a field ) without waiting first for the Jafar lifecycle to finish previous changeState action digest to get the updated state.

Issue example mentioned in - #50

Add component JsonEditor

Add edit component JsonEditor to @jafar-org/react-components - to allow editing json objects in a form.

Support shorthand definitions of validators

Is your feature request related to a problem? Please describe.
Current validators of Jafar are defined by object, which is appropriate for designer and builder tools. But sometimes it is not for programmer.

Describe the solution you'd like
Shorthand definitions are necessary.

Describe alternatives you've considered
Moleculer has a validator, which support shorthand definitions.

const schema = {
    password: "string|min:6",
    age: "number|optional|integer|positive|min:0|max:99", // additional properties
    state: ["boolean", "number|min:0|max:1"] // multiple types
}

Add validator - "typeof"

Add a new validator to packages/form/src/validators.js file of "typeof".

Motivation
Fields can define a validation of their value type, to be verified when field value changes. For example to verify that the value of the field is "string".

Code

  • args (and defaultArgs) should include:
  • "type" - default undefined
  • func: checks if : typeof value === args.type

  • message: should have a matching error message depend on the args of
    'Invalid type. Expected type of "{args.type}"'

Tests

Docs
should add documentation in Form -> Validators -> Build in validators:
https://yahoo.github.io/jafar/docs/validators.html#built-in-validators

CHANGELOG

Add classes to the default FieldView

We should add classes to the default FieldView (in react-form) to indicate fields statues.

Question to figure before implementing:
should we use aria- instead? And is all the alternative aria-* relevant for non input elements such a container div? is there a better way to describe this on the DOM?

To the root div of the field

  • jafar-field (always exists)
  • jafar-required (only if required=true)
  • jafar-empty (only if empty=true)
  • jafar-disabled (only if disabled=true)
  • jafar-invalid (only if invalid=true)
  • jafar-dirty (only if dirty=true)

To the div that wraps the component

  • jafar-component

To the div that wraps the error

  • jafar-error

To the header

  • jafar-header

To the div that wraps the label

  • jafar-label

To the div that wraps the description

  • jafar-description

To the div that wraps the asterisk of the required

  • jafar-required-indicator

Exclude field should keep required value

Bug:
Field that is both "required=true" and has exclude term, when field is excluded - original "required" value turn to false.

Expected:
Field that is both "required=true" and has exclude term should keep original required value when changing to excluded and back to included.

AsyncMultiSelect display 'no options'

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to 'https://yahoo.github.io/jafar/demo-react-components.html'
  2. Click AsyncMultiSelect on left navmenu
  3. Click ' simple select' on right panel
  4. It will display 'no options' firstly.
  5. Then click it again, will display correct data.

Expected behavior
It should display correct data firstly, or display loading indicator.

Screenshots
Snip20200526_1

Add component edit/KeyValue

Add edit component of KeyValue to react-components package.

  • Should represent object (json), where the key is string and value is string (if can implement that value can be other types like number / date for example it will be great).
  • Should be able to add key value, and then see it in a list of key-values. each row of key value is also editable, and can be removed.
  • Add also an .md file similar to other components

Add component mapper util

To use existing components library in a form (such as Material UI) - a map from the Jafar component generic props to the existing component props is needed.

Example - mapping props using react functional component

/* Input.js */
import React from 'react';
import Input from '@material-ui/core/Input';

export default ({ value = '', state = {}, disabled = false, onValueChange }) => <Input
  type={state.type}
  placeholder={state.placeholder}
  value={value}
  disabled={disabled}
  onChange={(e) => onValueChange(e.target.value)}
/>

Example - mapping props using a desired toJafar HOC

/* Input.js */
import { toJafar } from '@jafar/react-components/utils';
import Input from '@material-ui/core/Input';

export const mapper = ({ value = '', disabled = false, state = {}, onValueChange }) => ({
  type: state.type
  placeholder: state.placeholder,
  value,
  disabled,
  onChange: (e) => onValueChange(e.target.value),
});

export default toJafar(Input, mapper);

Using the above HOC saves the react import as well as simplify tests. The following code tests mapper function with a simple javascript test.

/* Input.spec.js */
import React from 'react';
import { shallow } from 'enzyme';
import Input, { mapper } from './Input.js';

describe('Input', () => {
  const jafarProps = {
    state: {
      type: 'text',
      placeholder: 'Enter name...',
    },
    value: 'Rachel',
    disabled: false,
    onValueChange: jest.fn(),
  };

  const expectedInputProps = {
    type: 'text',
    placeholder: 'Enter name...',
    value: 'Rachel Green',
    disabled: false,
    onChange: expect.any(Function),
  };

  let inputProps;
  
  beforeEach(() => {
    inputProps = mapper(jafarProps);
  });
  
  describe('mapper', () => {
    it('return correct props', () => {
      expect(inputProps).toEqual(expectedInputProps);
    });

    it('call onValueChange with correct value', () => {
      const mockEvent = { target: { value: 'Ross' } };
      inputProps.onChange(mockEvent);
      expect(jafarProps.onValueChange).toHaveBeenCalledWith('Ross');
    });
  });

  describe('component', () => {
    it('renders ok', () => {
      const component = shallow(<Input {...jafarProps} />);
      expect(component.props()).toEqual(expectedInputProps);
    });
  });
});

The description of 'Path' in document is wrong.

In https://yahoo.github.io/jafar/docs/path.html, the following description is wrong, which is about 'Dirty' actually.

Path
Form / Field dirty flags are calculate on each relevant action such as init, change value and change data.
This does not need to be passed by default in the initial form model json. You can pass it in the initial form model json, if you are currently loading a form model from persist model (i.e you saved the form model before user did page reload - and want to reload the form with the exact same model state that it was before the refresh)

Bug - form gets new actions after destroy causing page to crash

Bug:
go to editor page: https://yahoo.github.io/jafar/demo-react-editor.html -> Forms -> edit form -> change only data and then click save right after the change -> page crashes

Reason:
Some components like JsonEditor and Select call "onValueChange" on blur, and when we clicked "save" right after we edit them it's activates the "onChangeValue" after we redirect to list page and destroyed the form

Solution:

  • Should fix in core packages/form - after get destroy action - don't get any more actions to the queue (i.e close queue)
  • Backup with tests

Support dense layout

Is your feature request related to a problem? Please describe.
Current style of layout and component looks too disperse.
Snip20200709_2

Describe the solution you'd like
We should support dense style.

Describe alternatives you've considered
Support variant prop in ItemView component of react-layout.

Add component view/JsonViewer

Add view component of JsonViewer to react-components package.

  • Use the same component that "edit/JsonEditor" uses, only with readOnly always true.
  • Add also an .md file similar to other components

Support wizard form?

Is your feature request related to a problem? Please describe.
I have a form with lots of fields, which be filled with some peoples, everyone can only fill limited fields.

Describe the solution you'd like
I wish jafar can support wizard form, can define each step of form, which control validations, required etc.

Thanks for your great library. Clean code and good document.

Who uses Jafar? ๐ŸŒŸ

Do you use Jafar in your project?

Share it with us :) ๐ŸŽ‰๐ŸŽ‰

by:

  1. Comment on this thread with any details you like: screenshot, link, etc.
  2. Join our users list.

Add validator - "password"

Add a new validator to packages/form/src/validators.js file of "password".

rect-editor - support fields library

Is your feature request related to a problem? Please describe.
Forms often have similar fields, which have some same infos. Currently we need define fields from zero, which is not handy.

Describe the solution you'd like
If having a field library, we can import infos of field from it, then generate form quickly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.