andrewhathaway / winterfell Goto Github PK
View Code? Open in Web Editor NEWGenerate complex, validated and extendable JSON-based forms in React.
Home Page: http://winterfell.andrewhathaway.net
License: MIT License
Generate complex, validated and extendable JSON-based forms in React.
Home Page: http://winterfell.andrewhathaway.net
License: MIT License
Well so far unsuccessful to get how to manipulate the form.
Let's say you'd want to hide/remove your form after a successful submit
I guess that's pretty simple; but my Winterfell.getDOMNode()'s object remains undefined wherever I check it
If you use the "browserify.transform" feature, it will make it easier for those who also use browserify at the app level to use your npm package. The two changes required to your package.json are: add a dependency on reactify (you currently only have a dev dependency), add at the below line.
"browserify": { "transform": [ ["reactify",{"es6":true}] ] }
You may also wish to consider using babelify instead. It is based on the babel project which is replacing the react-tools project that reactify is based on. If you do, you can use this line instead (as well as a dependency on babelify):
"browserify": { "transform": [ "babelify" ] }
Hey!
I'm working on adding some custom validation. Here's my setup:
import React from 'react';
import Winterfell from 'winterfell';
import SignupSchema from '../forms/signup.js';
import 'moment';
import { default as isPhone } from 'is-phone-number';
export default class SignUpForm extends React.Component {
constructor (props) {
super(props);
Winterfell.addValidationMethods({
isPhoneNumber: value => isPhone(value)
});
}
…
It's throwing:
TypeError: _winterfell2['default'].addValidationMethods is not a function. (In '_winterfell2['default'].addValidationMethods', '_winterfell2['default'].addValidationMethods' is undefined)
and if I console.log(Winterfell)
function Winterfell(props) {
_classCallCheck(this, Winterfell);
_get(Object.getPrototypeOf(Winterfell.prototype), 'constructor', this).call(this, props);
this.panelHistory = [];
var schema = _.extend({
classes: {},
formPanels: [],
questionPanels: [],
questionSets: []
}, this.props.schema);
schema.formPanels = schema.formPanels.sort(function (a, b) {
return a.index > b.index;
});
var panelId = typeof this.props.panelId !== 'undefined' ? this.props.panelId : schema.formPanels.length > 0 ? schema.formPanels[0].panelId : undefined;
var currentPanel = typeof schema !== 'undefined' && typeof schema.formPanels !== 'undefined' && typeof panelId !== 'undefined' ? _.find(schema.formPanels, function (panel) {
return panel.panelId == panelId;
}) : undefined;
if (!currentPanel) {
throw new Error('Winterfell: Could not find initial panel and failed to render.');
}
this.state = {
schema: schema,
currentPanel: currentPanel,
action: this.props.action,
questionAnswers: this.props.questionAnswers
};
}
Do i need to import the validation utils from somewhere else?
Hi andrew, It's not a criticism but I've found a bit complex understand how define my schemas, basically I feel than there are some incidental complexity in the way how build this (again it's not a criticism and I wish know your point of vie)
"formPanels" : [{
"index" : 1,
"panelId" : "intro-panel"
}, {
"index" : 2,
"panelId" : "register-panel"
}, {
"index" : 3,
"panelId" : "final-panel"
}]
can be reduced to
"formPanels" : [ "intro-panel", "register-panel","final-panel"]
I feel than it's not only more readable but than also give the feels about I'm indicating which panels has my component instead of defining panels, in the original way I feel like if I've define my panels in two different places
"action" : {
"conditions" : [{
"questionId" : "existing-user",
"value" : "no",
"action" : "GOTO",
"target" : "register-panel"
}],
"default" : {
"action" : "GOTO",
"target" : "final-panel"
}
},
"button" : {
"text" : "Next"
},
for me it's not clear the relationship between default and button, my understand after read the doc is than button call the default action, but in the def seems like if they're not related
maybe something like this could be better
"action" : {
"conditions" : [{
"questionId" : "existing-user",
"value" : "no",
"action" : "GOTO",
"target" : "register-panel"
}],
"default" : {
"button" : "Next",
"action" : "GOTO",
"target" : "final-panel"
}
},
Hope you feel than this can be an improve, I really like many ideas behind this lib and I found I nice way of build forms, thanks
I'd love to be able to use custom classes on a single question. is this possible? This is how I would expect it to work:
… }, {
panelId : 'register-panel',
panelHeader : 'My Awesome Form',
panelText : 'We don\'t have a survey for you yet, how about we get you subscribed?',
classes: {
radioList : 'Field--paired Grid Grid--alignMiddle Grid--withGutters',
radioListItem : 'Grid-cell u-size1of4'
}, …
Is there a way to create fields that can be added or removed dynamically?
Example: A group field to enter an invoice line (product and price) and a '+' button to add a new invoice line. A '-' button to remove the new added line.
Thank you Andrew for the great work with Winterfell!
First off - this looks real good and I am quite interested in using it in my personal projects.
Now - in addition to the existing demo, I think another with a smaller schema associated with it might help people get started up faster. The current demo is very useful but might work better as the second example.
Just my opinion. Good luck!
Any future plan to add test? Maybe use something like https://github.com/json-schema/JSON-Schema-Test-Suite ? Thanks.
With the built in multi-page forms functionality is there is a way to have async actions occur between steps (like server side validation - proceed to next step) and also any way to tie in routes to the steps? I'd also like to send new data to the next step from the previous async action.
I'm trying to decide whether I should continue to use the built in Multi page form or have separate forms and wire in all the advanced step logic and routes myself. I imagine this sort of logic would need to be tied to the schema so maybe it's just better to use separate forms for each step and not try to tackle this at a schema level.
Hi great plugin, currently evaluating it to use in a project. If it works for our basic needs I'm more than happy to help with the project.
I would like to use the google places react autosuggest component in a form. Including it as a custom input in the schema renders the component fine but I need to capture the onSuggestSelect event provided by google autosuggest and map it to the onChange event that Winterfell is expecting for the input and pass in a placeholder prop like:
<Geosuggest placeholder="" onSuggestSelect={(suggest) => address.onChange(suggest)} {...address} />
What's the best approach to passing props and handling the mapping of events to custom input types with Winterfell?
Help/information tooltips per question would be a good thing to have.
Test suite needs setting up and tests need to be written for everything. Also run tests on prepublish
.
The order of formPanels, questionSets, and questions seems to be inconsistent. Questions are ordered by their array order. QuestionSets, seem to be controlled by their array order in the questionPanels.questionSets array as well, rather than by the index property shown in the examples. As for formPanels, looking at the code, I find that they do get sorted by their index property (index.js line 21).
This is inconsistent, but may be intentional for some reason. If so, the index property of the questionSet should be omitted from documentation as it is misleading and never used. Personally, I'd rather see the ordering of everything be based on the actual array order rather then an index property. However, it could also be configurable, or index could be used if present with fallback to array order. Most importantly, this should be implemented consistently.
A progress indicator that will work out the users steps to complete the form.
When focussed on an input, the enter key will submit the form (expected behaviour). However, if you have disabled submissions this should not be the case. It also submits the form on a panel that does not have a submission action, it should attempt to follow the action (IE: Next/Submit button click).
Using the example schema provided, a la:
"action" : {
"default" : {
"action" : "SUBMIT",
"target": "www.google.com",
},
},
"button" : {
"text" : "Add",
},
I'm getting the following error when I hit the Submit button:
Uncaught TypeError: React.findDOMNode is not a function(anonymous function)
@ index.js:116notifyAll
@ CallbackQueue.js:67close
@ ReactUpdates.js:60closeAll
@ Transaction.js:204perform
@ Transaction.js:151perform
@ ReactUpdates.js:90flushBatchedUpdates
@ ReactUpdates.js:173closeAll
@ Transaction.js:204perform
@ Transaction.js:151batchedUpdates
@ ReactDefaultBatchingStrategy.js:63batchedUpdates
@ ReactUpdates.js:98dispatchEvent
@ ReactEventListener.js:150
Does this have to do with the switch from React to ReactDOM? I couldn't find an open issue with this here on GH.
Using "react": "^15.2.1", "react-dom": "^15.2.1",
on the project in question.
Is there a way to reset the form once it is submitted?
After I submit a form, I'm redirecting to another page. But, once I go to the form again, all the values previously typed in/selected(in case of radio buttons) are already present. I need the values to be cleared from the form.
Edit : I believe what I basically need is to refresh the questionAnswers object.
Hi!
I can't seem to get questionPanelText
to actually set a className
.
given this Schema:
{
classes : {
input : 'Field-value',
label : 'Field-label blank-u-mgBmd',
select : 'Field-value',
question : 'Field blank-u-mgBlg',
questionPanel : '',
questionPanelText : 'This one is not working',
questionPanelHeaderText : 'Nor this one',
questionText : 'blank-u-mgBlg',
radioListItem : '',
radioList : 'Field--paired',
radioLabel: 'Field--paired-label',
radio: 'Field-value',
checkboxInput : 'Field-value',
checkboxListItem : '',
checkboxList : 'Field--paired',
controlButton : 'Button Button--positive',
backButton : 'Button Button--neutral',
errorMessage : 'alert alert-danger',
buttonBar : 'button-bar'
},
formPanels : [{
index : 1,
panelId : 'intro-panel'
}],
questionPanels : [{
panelId : 'intro-panel',
panelHeader : 'My Awesome Form',
panelText : 'Play around, you might find some surprises depending on what you answer!',
…
the node from panelText
or panelHeader
doesn't get any className
. Am I misunderstaning something? Other elements are correctly getting classes.
Thanks!
Hi, I need to display result in real time according to values in the form.
What's the best way to do that?
Thank you again :-)
Hi!
I really like where you're headed with this. To make adoption easy, you should set up a version
task in the package.json
that precompiles this package so it can be used without having the same build pipeline. See https://github.com/rackt/redux/blob/master/package.json for an example on how to do this well.
Keep up the good work!
Any thought about add a custom theming feature so one could slip in UI frameworks like Material-UI or even (god forbid) Bootstrap.
I would mark this as a question, not issue, but I don't know how to do so
For example, in the Winterfell demo, if you choose No on the first question and press next, enter your email address and go back and select yes, the email address previously entered in the No panel will still be there.
I guess the idea would be to delete any answers from that panel, but if the user wanted to go back to that panel, they'd have lost their input. Is that expected functionality or?
Hi @andrewhathaway, I've been struggling to get the examples up and running via all sorts of methods. I've tried npm run, npm build, gulping, grunting and the custom npm commands you put together and for whatever reason, can't seem to compile the app.js to the same build state as the running examples here: http://winterfell.andrewhathaway.net/.
The reason I'd like to have the examples running is because I'm only able to wire into the onSwitchPanel event. Something is off in my attempts to build locally. I really want to move ahead with Winterfell, but I'm currently blocked under a time constraint. Thanks again for the awesome OSS contribution that is Winterfell!
Maybe I am missing something, but there doesn't seem to be an unminified copy of winterfell. I'm not sure if you mean for us to build it if we want it, but many libraries include both versions of code.
Hey up it's Barn, lib looks really good btw!
Just a minor UX thing I noticed when trying the demo, email box jumps up and down as my input goes from empty -> invalid -> valid.
I guess you may want to either display validation below, add a delay which resets on input, or add a validateOn option which would let me validate email on blur.
My custom type is another react component. Is there a way to send the default value as a prop?
"valiue" instead of "value" src/lib/errors.js:24
hiddenInput sets the default props as follows:
HiddenInput.defaultProps = {
name : undefined,
value : undefined
};
So this would appear to be the way to define a question for the hiddenInput
{
"questionId" : "func",
"question" : "",
"input" : {
"type" : "hiddenInput",
"name" : "hiddenname",
"value" : "hiddenvalue"
}
}
However the value prop does not make it into the rendered as this implies. Looking at the render method of question.js it appears as though moving the value property to the question might do what we want
var value = typeof this.props.value !== 'undefined'
? this.props.value
: typeof this.props.input.default !== 'undefined'
? this.props.input.default
: undefined;
This definition did not work either since the value prop of the questions comes from the questionAnswers prop on the set and that seems like a pretty convoluted way to set a simple hidden input.
{
"questionId" : "func",
"question" : "",
"input" : {
"type" : "hiddenInput",
"name" : "hiddenname",
}
"value" : "hiddenvalue"
}
There is a simple but not obvious workaround by setting the default prop on the input:
{
"questionId" : "func",
"question" : "",
"input" : {
"type" : "hiddenInput",
"name" : "hiddenname",
"default" : "hiddenvalue"
}
}
I am not sure if this should be seen as a documentation or a code issue. Perhaps an additional level in the Question Render command might be the easiest solution:
var value = typeof this.props.value !== 'undefined'
? this.props.value
: typeof this.props.input.value!== 'undefined'
? this.props.input.value
: typeof this.props.input.default !== 'undefined'
? this.props.input.default
: undefined;
When displaying two forms, I would expect both to be separated. Instead it seems that the state is shared.
How to reproduce
Insert two forms with the same schema into your page. Fill out the first and submit the second without any inputs. The second form will submit the inputs of the first form.
Possible solutions
I suspect that this is due to the way how default values are taken into the state. The constructor of the Winterfell
class does this:
this.state = {
schema : schema,
currentPanel : currentPanel,
action : this.props.action,
questionAnswers : this.props.questionAnswers
};
When initializing the form, the Winterfell.defaultProps
are passed into the constructor. After the code above this.state.questionAnswers
is the same object as Winterfell.defaultProps.questionAnswers
. Every change to this state also causes the prototype to change. Every form, even if displayed one after another, shares the same questionAnswers
.
Generating the default properties within the constructor instead of holding them statically on the class could help.
Possible workaround
Passing the questionAnswers
as a property while holding them in the state of the surrounding component. And listening to any updates.
Hello, I hope this makes some sense, I am a bit new to React and webpack.
I have a local clone of the repo I am trying to modify for my project. With a fresh copy of the repo, everything is okay and Winterfell renders properly.
However, when I modify one of the winterfell/src/*.js
files, I start running into problems. After making changes and running npm run build
I get these errors in the browser:
Warning: Winterfell(...): React component classes must extend React.Component.
Uncaught Error: Invariant Violation: Winterfell.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.
I am not making any breaking changes to the source files (I have tried very trivial changes), and Winterfell still extends from React.Component
, I am not messing with the class definition.
Does anybody have any ideas what's causing this?
I am using react 0.14.0 in my project, would this matter?
Thanks in advance for any input!
Hi, I got rid of all submit button related things in this schema, but there is still a submit link on the page, does Winterfell internally generate the submit for all schemas?
Thanks!
Hello,
i am getting this answer when entering an email in the example form (My Awesome Form). Can you help me here?
Thanks in advance
Being based on React, there could be a way to retrieve the choices as a single parsible object. I don't know if this would require using one of the data store libraries, or just creating an object with nested data.
It would be incredibly useful to have something, either way.
I noticed problems when using the checkboxOptionsInput
(and maybe with others, too) and defining your options using numbers (not numbers in strings). When checking a checkbox, the value that is registered is the number as string. This seems to lead to multiple problems:
questionsAnswers
holds duplicate values if the checkbox was clicked multiple times.isAllIn
validation does not validate if given as numbers as well.Workaround
Not using numbers or convert numbers to strings in the schema.
Why I think this is a bug
This library aims to provide "JSON-based forms in React". The JSON format allows numbers, so the expected behaviour would be that numbers work as options.
Possible solutions
The source of this problem could be that there is no distinction between numbers and strings in HTML and that the options are used as value of the checkbox. Simply converting numbers to strings would fix most of the issues. But it would not allow that for example 42
and "42"
could both be values. So another solution would be to use proxy-options as values for the checkboxes and then map the proxy-options to the real options.
Example broken question
{
"questionId": "example-broken-question",
"question": "Please select any number of elements",
"input": {
"type": "checkboxOptionsInput",
"required": false,
"options": [
{
"text": "Option one",
"value": 1
},
{
"text": "Option two",
"value": 2
},
{
"text": "Option three",
"value": 3
}
]
},
"validations": [
{
"type": "isAllIn",
"params": [
[1, 2, 3]
]
}
]
}
Is there a way to make sure the input type is only integers?
Hi, we are trying to add the password confirmation to the schema, but it doesn't seem like the two password fields talk to each other. We currently have it like following:
"validations" : [{
"type" : "isEmail"
}]
},{
"questionId" : "password",
"question" : "Password",
"input" : {
"type" : "passwordInput",
"placeholder" : "8 characters minimum",
"required" : true
},
// Make sure password is entered
"validations" : [{
"type" : "isLength",
"params" : [1]
}]
},{
"questionId" : "passwordConfirm",
"question" : "Password Confirm",
"input" : {
"type" : "passwordInput",
"placeholder" : "confirm password"
},
"validations" :[{
"type" : "equals",
"params" : ["12345678"]
//"matches" : "({passwordConfirm}, {password} )"
//"message" : "Confirm password must match password field"
}]
Thanks!
Hi
I've tried for several time to get your example working.
I can see that there is a "gulp build-example"...
All though when I run this, I get the following:
[14:41:50] Starting 'build-examples'...
events.js:85
throw er; // Unhandled 'error' event
^
Error: Cannot find module 'react' from 'c:\Users\User\Downloads\Winterfell-master\Winterfell-master\examples'
Other than that, I have tried just to find a precompiled version of "Winterfell", to use with my app, but it seems I really have to build it....
Can you supply a production ready precompiled version of your plugin, ready to use?
Regards
Hi, I read the documentation but I didn't find a way to display a conditional question on a different panel.
Example: I answer on panel 1 and 2 and I want a new question on panel 3 according to the previous answers.
Is there a way to do that?
Thanks :-)
Might have missed it maybe; how do I implement a blank default option ?
That works with adding an empty object for sure; I was thinking about the rails select helper that has this specific option which makes it clear and separated from the real selectable ones
The ability to add more items of a questionSet
for example needs to be built in with an "add another" button etc with a remove button.
I created a tool based on the form and when I do a submit in IE, I see that it is not submitting. Clicking enter works. It does not log any error to console.
It would be great if you can help in root causing the issue.
Render an asterisk for fields that contain a new required
property on a question
object. Support a custom class. Possibly a custom render handler?
Winterfell should support compound conditionals, IE answerOne == this && answerTwo == that, both in the questionPanel
objects and the question
objects.
I'm not exactly sure what the schema should be like for this, will spend some time thinking about it. Suggestions welcome.
When validating a form, it would be convenient if there were a way to validate against multiple different fields. An example might be a date-range input with a start date and an end date. The form would be invalid if the end date came before the start date, even though both fields might be valid independently.
When using emailInput it defaulted to TextInput which threw an error from questions.js
I am having an issue that when I have numerous questions that use the input type checkboxOptionsInput the checked answers are added the state including one from previous questions, thereby causing the answer to include all checked boxes from all of the different questions that use an checkboxOptionsInput.
I think it might be due to the fact that the constructor set the state to the props.value and when the state is updated it also resets the props.value.
After some tests, I am unable to set a checkbox as checked by default. I am reading the code and I don't see any way the checkboxInput component can accomplish that.
Am I missing anything? Should I try to do it and send a PR?
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.