Skip to content

Commit

Permalink
Make wizard workflow work when non-modal
Browse files Browse the repository at this point in the history
The JavaScript code that adds the wizard functionality was initialized
only when the page was displayed as a modal.  I moved the call to that
initialization function into the template, so that it is called always
when a wizard is displayed.

Change-Id: Id73116392158ea08b4f04062f4f926286b1f7973
Closes: bug 1273608
  • Loading branch information
deshipu committed Mar 27, 2014
1 parent 857ccf9 commit 44ab363
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 110 deletions.
219 changes: 110 additions & 109 deletions horizon/static/horizon/js/horizon.modals.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,116 @@ horizon.modals.modal_spinner = function (text) {
horizon.modals.spinner.spin(horizon.conf.spinner_options.modal);
};

horizon.modals.init_wizard = function () {
// If workflow is in wizard mode, initialize wizard.
var _max_visited_step = 0;
var _validate_steps = function (start, end) {
var $form = $('.workflow > form'),
response = {};

if (typeof end === 'undefined') {
end = start;
}

// Clear old errors.
$form.find('td.actions div.alert-error').remove();
$form.find('.control-group.error').each(function () {
var $group = $(this);
$group.removeClass('error');
$group.find('span.help-inline.error').remove();
});

// Send the data for validation.
$.ajax({
type: 'POST',
url: $form.attr('action'),
headers: {
'X-Horizon-Validate-Step-Start': start,
'X-Horizon-Validate-Step-End': end
},
data: $form.serialize(),
dataType: 'json',
async: false,
success: function (data) { response = data; }
});

// Handle errors.
if (response.has_errors) {
var first_field = true;

$.each(response.errors, function (step_slug, step_errors) {
var step_id = response.workflow_slug + '__' + step_slug,
$fieldset = $form.find('#' + step_id);
$.each(step_errors, function (field, errors) {
var $field;
if (field === '__all__') {
// Add global errors.
$.each(errors, function (index, error) {
$fieldset.find('td.actions').prepend(
'<div class="alert alert-message alert-error">' +
error + '</div>');
});
$fieldset.find('input, select, textarea').first().focus();
return;
}
// Add field errors.
$field = $fieldset.find('[name="' + field + '"]');
$field.closest('.control-group').addClass('error');
$.each(errors, function (index, error) {
$field.before(
'<span class="help-inline error">' +
error + '</span>');
});
// Focus the first invalid field.
if (first_field) {
$field.focus();
first_field = false;
}
});
});

return false;
}
};

$('.workflow.wizard').bootstrapWizard({
tabClass: 'wizard-tabs',
nextSelector: '.button-next',
previousSelector: '.button-previous',
onTabShow: function (tab, navigation, index) {
var $navs = navigation.find('li');
var total = $navs.length;
var current = index;
var $footer = $('.modal-footer');
_max_visited_step = Math.max(_max_visited_step, current);
if (current + 1 >= total) {
$footer.find('.button-next').hide();
$footer.find('.button-final').show();
} else {
$footer.find('.button-next').show();
$footer.find('.button-final').hide();
}
$navs.each(function(i) {
$this = $(this);
if (i <= _max_visited_step) {
$this.addClass('done');
} else {
$this.removeClass('done');
}
});
},
onNext: function ($tab, $nav, index) {
return _validate_steps(index - 1);
},
onTabClick: function ($tab, $nav, current, index) {
// Validate if moving forward, but move backwards without validation
return (index <= current ||
_validate_steps(current, index - 1) !== false);
}
});
};


horizon.addInitFunction(function() {
// Bind handler for initializing new modals.
$('#modal_wrapper').on('new_modal', function (evt, modal) {
Expand Down Expand Up @@ -155,115 +265,6 @@ horizon.addInitFunction(function() {
$(modal).find(":text, select, textarea").filter(":visible:first").focus();
});

// If workflow id wizard mode, initialize wizard.
horizon.modals.addModalInitFunction(function (modal) {
var _max_visited_step = 0;
var _validate_steps = function (start, end) {
var $form = $('.workflow > form'),
response = {};

if (typeof end === 'undefined') {
end = start;
}

// Clear old errors.
$form.find('td.actions div.alert-error').remove();
$form.find('.control-group.error').each(function () {
var $group = $(this);
$group.removeClass('error');
$group.find('span.help-inline.error').remove();
});

// Send the data for validation.
$.ajax({
type: 'POST',
url: $form.attr('action'),
headers: {
'X-Horizon-Validate-Step-Start': start,
'X-Horizon-Validate-Step-End': end
},
data: $form.serialize(),
dataType: 'json',
async: false,
success: function (data) { response = data; }
});

// Handle errors.
if (response.has_errors) {
var first_field = true;

$.each(response.errors, function (step_slug, step_errors) {
var step_id = response.workflow_slug + '__' + step_slug,
$fieldset = $form.find('#' + step_id);
$.each(step_errors, function (field, errors) {
var $field;
if (field === '__all__') {
// Add global errors.
$.each(errors, function (index, error) {
$fieldset.find('td.actions').prepend(
'<div class="alert alert-message alert-error">' +
error + '</div>');
});
$fieldset.find('input, select, textarea').first().focus();
return;
}
// Add field errors.
$field = $fieldset.find('[name="' + field + '"]');
$field.closest('.control-group').addClass('error');
$.each(errors, function (index, error) {
$field.before(
'<span class="help-inline error">' +
error + '</span>');
});
// Focus the first invalid field.
if (first_field) {
$field.focus();
first_field = false;
}
});
});

return false;
}
};

$('.workflow.wizard').bootstrapWizard({
tabClass: 'wizard-tabs',
nextSelector: '.button-next',
previousSelector: '.button-previous',
onTabShow: function (tab, navigation, index) {
var $navs = navigation.find('li');
var total = $navs.length;
var current = index;
var $footer = $('.modal-footer');
_max_visited_step = Math.max(_max_visited_step, current);
if (current + 1 >= total) {
$footer.find('.button-next').hide();
$footer.find('.button-final').show();
} else {
$footer.find('.button-next').show();
$footer.find('.button-final').hide();
}
$navs.each(function(i) {
$this = $(this);
if (i <= _max_visited_step) {
$this.addClass('done');
} else {
$this.removeClass('done');
}
});
},
onNext: function ($tab, $nav, index) {
return _validate_steps(index - 1);
},
onTabClick: function ($tab, $nav, current, index) {
// Validate if moving forward, but move backwards without validation
return (index <= current ||
_validate_steps(current, index - 1) !== false);
}
});
});

horizon.modals.addModalInitFunction(horizon.datatables.validate_button);

// Load modals for ajax-modal links.
Expand Down
9 changes: 8 additions & 1 deletion horizon/templates/horizon/common/_workflow.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ <h3>{{ workflow.name }}</h3>
</div>
<div class="modal-body clearfix">
{% block modal-body %}
<ul class="nav nav-tabs">
<ul class="nav nav-tabs{% if workflow.wizard %} wizard-tabs{% endif %}">
{% for step in workflow.steps %}
<li class="{% if entry_point == step.slug %}active{% endif %}{% if step.has_errors %} error{% endif %}{% if step.has_required_fields %} required{% endif %}">
<a href="#{{ step.get_id }}" data-toggle="tab" data-target="#{{ step.get_id }}">{{ step }}</a>
Expand Down Expand Up @@ -52,4 +52,11 @@ <h3>{{ workflow.name }}</h3>
</div>
{% endwith %}
{% block modal-js %}
{% if workflow.wizard %}
<script type="text/javascript">
(window.$ || window.addHorizonLoadEvent)(function () {
horizon.modals.init_wizard();
});
</script>
{% endif %}
{% endblock %}

0 comments on commit 44ab363

Please sign in to comment.