Wizard: Server Side Validation + Finished Step

This commit is contained in:
Robin Ward
2016-08-31 13:35:49 -04:00
parent be1d74d207
commit 9f12b571ef
35 changed files with 260 additions and 62 deletions

View File

@@ -64,10 +64,11 @@ export default Ember.Component.extend({
}
const $elem = this.$();
const minimumResultsForSearch = this.capabilities.isIOS ? -1 : 5;
const caps = this.capabilities;
const minimumResultsForSearch = (caps && caps.isIOS) ? -1 : 5;
$elem.select2({
formatResult: this.comboTemplate, minimumResultsForSearch,
width: 'resolve',
width: this.get('width') || 'resolve',
allowClear: true
});

View File

@@ -0,0 +1,3 @@
import { htmlHelper } from 'discourse-common/lib/helpers';
export default htmlHelper((key, params) => I18n.t(key, params.hash));

View File

@@ -1,4 +1,4 @@
import ComboboxView from 'discourse/components/combo-box';
import ComboboxView from 'discourse-common/components/combo-box';
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
import computed from 'ember-addons/ember-computed-decorators';
import { observes, on } from 'ember-addons/ember-computed-decorators';

View File

@@ -1,5 +1,5 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import Combobox from 'discourse/components/combo-box';
import Combobox from 'discourse-common/components/combo-box';
import { on, observes } from 'ember-addons/ember-computed-decorators';
export default Combobox.extend({

View File

@@ -1,3 +1,4 @@
//= require env
//= require jquery_include
//= require ember_include
//= require loader

View File

@@ -60,7 +60,6 @@
//= require ./discourse/views/container
//= require ./discourse/views/modal-body
//= require ./discourse/views/flag
//= require ./discourse/components/combo-box
//= require ./discourse/components/edit-category-panel
//= require ./discourse/views/button
//= require ./discourse/components/dropdown-button

View File

@@ -1,5 +1,4 @@
//= require logster
//= require ./env
//= require ./discourse-objects
//= require probes.js
@@ -38,4 +37,3 @@
//= require virtual-dom
//= require virtual-dom-amd
//= require highlight.js
//= require_tree ./discourse/ember

View File

@@ -1,2 +1,2 @@
//= require env
//= require template_include.js
//= require select2.js

View File

@@ -4,5 +4,8 @@ export default Ember.Component.extend({
classNameBindings: [':wizard-field', ':text-field', 'field.invalid'],
@computed('field.id')
inputClassName: id => `field-${Ember.String.dasherize(id)}`
inputClassName: id => `field-${Ember.String.dasherize(id)}`,
@computed('field.type')
inputComponentName: type => `wizard-field-${type}`
});

View File

@@ -12,6 +12,9 @@ export default Ember.Component.extend({
@computed('step.displayIndex', 'wizard.totalSteps')
showNextButton: (current, total) => current < total,
@computed('step.displayIndex', 'wizard.totalSteps')
showDoneButton: (current, total) => current === total,
@computed('step.index')
showBackButton: index => index > 0,

View File

@@ -8,6 +8,7 @@ export const States = {
export default {
_validState: null,
errorDescription: null,
init() {
this._super();
@@ -23,8 +24,14 @@ export default {
@computed('_validState')
unchecked: state => state === States.UNCHECKED,
setValid(valid) {
setValid(valid, description) {
this.set('_validState', valid ? States.VALID : States.INVALID);
if (!valid && description && description.length) {
this.set('errorDescription', description);
} else {
this.set('errorDescription', null);
}
}
};

View File

@@ -1,7 +1,8 @@
export default Ember.Route.extend({
model(params) {
const allSteps = this.modelFor('application').steps;
return allSteps.findProperty('id', params.step_id);
const step = allSteps.findProperty('id', params.step_id);
return step ? step : allSteps[0];
},
setupController(controller, step) {

View File

@@ -0,0 +1 @@
{{combo-box value=field.value content=field.options nameProperty="label" width="400px"}}

View File

@@ -0,0 +1 @@
{{input value=field.value class=inputClassName placeholder=field.placeholder}}

View File

@@ -2,6 +2,14 @@
<span class='label-value'>{{field.label}}</span>
<div class='input-area'>
{{input value=field.value class=inputClassName placeholder=field.placeholder}}
{{component inputComponentName field=field inputClassName=inputClassName}}
</div>
{{#if field.errorDescription}}
<div class='field-error-description'>{{field.errorDescription}}</div>
{{/if}}
{{#if field.description}}
<div class='field-description'>{{field.description}}</div>
{{/if}}
</label>

View File

@@ -3,7 +3,7 @@
{{/if}}
{{#if step.description}}
<p class='wizard-step-description'>{{step.description}}</p>
<p class='wizard-step-description'>{{{step.description}}}</p>
{{/if}}
{{#wizard-step-form step=step}}
@@ -14,24 +14,33 @@
<div class='wizard-step-footer'>
<div class='wizard-progress'>
<div class='text'>{{i18n "wizard.step" current=step.displayIndex total=wizard.totalSteps}}</div>
<div class='text'>{{bound-i18n "wizard.step" current=step.displayIndex total=wizard.totalSteps}}</div>
<div class='bar-container'>
<div class='bar-contents' style={{barStyle}}></div>
</div>
</div>
{{#if showBackButton}}
<button class='wizard-btn back' {{action "backStep"}} disabled={{saving}}>
{{fa-icon "chevron-left"}}
{{i18n "wizard.back"}}
</button>
{{/if}}
<div class='wizard-buttons'>
{{#if showBackButton}}
<button class='wizard-btn back' {{action "backStep"}} disabled={{saving}}>
{{fa-icon "chevron-left"}}
{{i18n "wizard.back"}}
</button>
{{/if}}
{{#if showNextButton}}
<button class='wizard-btn next' {{action "nextStep"}} disabled={{saving}}>
{{i18n "wizard.next"}}
{{fa-icon "chevron-right"}}
</button>
{{/if}}
{{#if showNextButton}}
<button class='wizard-btn next' {{action "nextStep"}} disabled={{saving}}>
{{i18n "wizard.next"}}
{{fa-icon "chevron-right"}}
</button>
{{/if}}
{{#if showDoneButton}}
<button class='wizard-btn done' {{action "finished"}} disabled={{saving}}>
{{fa-icon "check"}}
{{i18n "wizard.done"}}
</button>
{{/if}}
</div>
</div>

View File

@@ -18,7 +18,9 @@ test("Forum Name Step", assert => {
assert.ok(exists('.wizard-step-title'));
assert.ok(exists('.wizard-step-description'));
assert.ok(!exists('.invalid .field-full-name'), "don't show it as invalid until the user does something");
assert.ok(exists('.wizard-field .field-description'));
assert.ok(!exists('.wizard-btn.back'));
assert.ok(!exists('.wizard-field .field-error-description'));
});
// invalid data
@@ -32,16 +34,19 @@ test("Forum Name Step", assert => {
click('.wizard-btn.next');
andThen(() => {
assert.ok(exists('.invalid .field-full-name'));
assert.ok(exists('.wizard-field .field-error-description'));
});
// server validation ok
fillIn('input.field-full-name', "Evil Trout");
click('.wizard-btn.next');
andThen(() => {
assert.ok(!exists('.wizard-field .field-error-description'));
assert.ok(!exists('.wizard-step-title'));
assert.ok(!exists('.wizard-step-description'));
assert.ok(exists('input.field-email'), "went to the next step");
assert.ok(!exists('.wizard-btn.next'));
assert.ok(exists('.wizard-btn.done'), 'last step shows a done button');
assert.ok(exists('.wizard-btn.back'), 'shows the back button');
});

View File

@@ -40,7 +40,10 @@ export default function() {
title: 'hello there',
index: 0,
description: 'hello!',
fields: [{ id: 'full_name', type: 'text', required: true }],
fields: [{ id: 'full_name',
type: 'text',
required: true,
description: "Your name" }],
next: 'second-step'
},
{