settings: Change save and discard button look and feel.

This commit changes the way the save and discard buttons on the
organization profile, settings and permissions tabs look and fades
them out after a delay. It also cleans up the code a bit in the
settings_org.js file. It introduces changes to the css in
settings.css as well as the template for save-discard buttons.

It also fixes a bug on the user settings whereby if an option
that requires reload is clicked before clicking an option that does
not require reload, the reload message is erased. This could create
an issue where the user is not aware that a reload is required.
The loader is also changed to using fa-icon as loading spinner on
user settings and the colors are tweaked a little bit.
This commit is contained in:
Armaan Ahluwalia
2018-03-29 05:32:01 +05:30
committed by Tim Abbott
parent 9a0fdf5b8d
commit 58d07fabef
8 changed files with 261 additions and 76 deletions

View File

@@ -5,6 +5,7 @@ zrequire('stream_data');
zrequire('settings_account');
zrequire('settings_org');
zrequire('settings_ui');
zrequire('settings_ui');
var noop = function () {};
@@ -136,12 +137,63 @@ function test_realms_domain_modal(add_realm_domain) {
error_callback({});
assert.equal(info.val(), 'translated: Failed');
}
function createSaveButtons() {
var stub_save_button_header = $('.subsection-header');
var save_btn_controls = $.create('.save-btn-controls');
var stub_save_button = $('#org-submit-msg-editing');
var stub_save_button_text = $.create('.icon-button-text');
stub_save_button_header.prevAll = function () {
return $.create('<stub failed alert status element>');
};
stub_save_button.closest = function () {
return stub_save_button_header;
};
save_btn_controls.set_find_results(
'.save-button', stub_save_button
);
stub_save_button.set_find_results(
'.icon-button-text', stub_save_button_text
);
stub_save_button_header.set_find_results(
'.save-button-controls', save_btn_controls
);
stub_save_button_header.set_find_results(
'.subsection-changes-discard .button', $.create('#org-discard-msg-editing')
);
var props = {};
props.hidden = false;
props.status = "";
stub_save_button.attr = function (name, val) {
if (name === "data-status") {
if (val !== null) {
props.status = val;
return;
}
return props.status;
} else if (name === "id") {
return 'org-submit-msg-editing';
}
};
save_btn_controls.animate = function (obj) {
if (obj.opacity === 0) {
props.hidden = true;
} else {
props.hidden = false;
}
};
return {
props: props,
save_button: stub_save_button,
save_button_header: stub_save_button_header,
save_button_controls: save_btn_controls,
save_button_text: stub_save_button_text,
};
}
function test_submit_settings_form(submit_form) {
var ev = {
preventDefault: noop,
stopPropagation: noop,
target: '#org-submit-msg-editing',
currentTarget: '#org-submit-msg-editing',
};
$('#id_realm_default_language').val('fr');
@@ -155,20 +207,7 @@ function test_submit_settings_form(submit_form) {
success_callback = req.success;
};
var stub_save_button = $('#org-submit-msg-editing');
stub_save_button.attr = function () {
return 'org-submit-msg-editing';
};
var stub_save_button_header = $('.subsection-header');
stub_save_button_header.prevAll = function () {
return $.create('<stub failed alert status element>');
};
stub_save_button.closest = function () {
return stub_save_button_header;
};
stub_save_button_header.set_find_results(
'.subsection-changes-discard button', $.create('#org-discard-msg-editing')
);
createSaveButtons();
submit_form(ev);
assert(patched);
@@ -177,7 +216,6 @@ function test_submit_settings_form(submit_form) {
allow_message_editing: true,
message_content_edit_limit_seconds: 210,
};
success_callback(response_data);
var updated_value_from_response = $('#id_realm_message_content_edit_limit_minutes').val();
@@ -195,6 +233,38 @@ function test_submit_settings_form(submit_form) {
assert(updated_value_from_response, 0);
}
function test_change_save_button_state() {
set_global('$', global.make_zjquery());
var stubs = createSaveButtons();
var $save_btn_controls = stubs.save_button_controls;
var $save_btn_text = stubs.save_button_text;
var $save_btn = stubs.save_button;
var props = stubs.props;
settings_org.change_save_button_state($save_btn_controls, "unsaved");
assert.equal($save_btn_text.text(), 'translated: Save changes');
assert.equal(props.hidden, false);
assert.equal(props.status, "unsaved");
settings_org.change_save_button_state($save_btn_controls, "saved");
assert.equal($save_btn_text.text(), 'translated: Save changes');
assert.equal(props.hidden, true);
assert.equal(props.status, "");
settings_org.change_save_button_state($save_btn_controls, "saving");
assert.equal($save_btn_text.text(), 'translated: Saving');
assert.equal(props.status, "saving");
assert.equal($save_btn.hasClass('saving'), true);
settings_org.change_save_button_state($save_btn_controls, "discarded");
assert.equal(props.hidden, true);
assert.equal($save_btn.hasClass('saving'), false);
settings_org.change_save_button_state($save_btn_controls, "succeeded");
assert.equal(props.hidden, true);
assert.equal(props.status, "saved");
assert.equal($save_btn_text.text(), 'translated: Saved');
settings_org.change_save_button_state($save_btn_controls, "failed");
assert.equal(props.hidden, false);
assert.equal(props.status, "failed");
assert.equal($save_btn_text.text(), 'translated: Save changes');
}
function test_upload_realm_icon(upload_realm_icon) {
var form_data = {
append: function (field, val) {
@@ -400,7 +470,7 @@ function test_extract_property_name() {
var submit_settings_form;
$('.organization').on = function (action, selector, f) {
if (selector === '.subsection-header .subsection-changes-save button') {
if (selector === '.subsection-header .subsection-changes-save .button') {
assert.equal(action, 'click');
submit_settings_form = f;
}
@@ -441,8 +511,8 @@ function test_extract_property_name() {
test_disable_signup_notifications_stream(callbacks.disable_signup_notifications_stream);
test_change_allow_subdomains(change_allow_subdomains);
test_extract_property_name();
settings_org.render_notifications_stream_ui = stub_render_notifications_stream_ui;
test_change_save_button_state();
}());
(function test_misc() {

View File

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="96px" height="96px" viewBox="21 0 75 75" enable-background="new 0 0 96 96" xml:space="preserve">
<g>
<path fill="#00cbc3" d="M49.844,68.325c-1.416,0-2.748-0.554-3.75-1.557L27.523,48.191c-1.003-1.002-1.555-2.334-1.555-3.75 s0.552-2.749,1.555-3.75c1.001-1.001,2.333-1.552,3.75-1.552s2.75,0.551,3.753,1.553l14.019,14.017L82.14,5.504 c0.989-1.468,2.639-2.345,4.412-2.345c1.054,0,2.075,0.312,2.956,0.902c2.424,1.631,3.07,4.934,1.439,7.361L54.25,65.98 c-0.892,1.316-2.312,2.162-3.895,2.314C50.17,68.315,50.01,68.325,49.844,68.325z"/>
<path fill="#5bbd95" d="M49.844,68.325c-1.416,0-2.748-0.554-3.75-1.557L27.523,48.191c-1.003-1.002-1.555-2.334-1.555-3.75 s0.552-2.749,1.555-3.75c1.001-1.001,2.333-1.552,3.75-1.552s2.75,0.551,3.753,1.553l14.019,14.017L82.14,5.504 c0.989-1.468,2.639-2.345,4.412-2.345c1.054,0,2.075,0.312,2.956,0.902c2.424,1.631,3.07,4.934,1.439,7.361L54.25,65.98 c-0.892,1.316-2.312,2.162-3.895,2.314C50.17,68.315,50.01,68.325,49.844,68.325z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 680 B

View File

@@ -7,10 +7,18 @@ var meta = {
};
function change_display_setting(data, status_element, success_msg, sticky) {
var $status_el = $(status_element);
var status_is_sticky = $status_el.data('is_sticky');
var display_message = (status_is_sticky) ? $status_el.data('sticky_msg') : success_msg;
var opts = {
success_msg: success_msg,
sticky: sticky,
success_msg: display_message,
sticky: status_is_sticky || sticky,
};
if (sticky) {
$status_el.data('is_sticky', true);
$status_el.data('sticky_msg', success_msg);
}
settings_ui.do_settings_change(channel.patch, '/json/settings/display', data, status_element, opts);
}

View File

@@ -323,6 +323,46 @@ exports.sync_realm_settings = function (property) {
}
};
exports.change_save_button_state = function ($element, state) {
var show_hide_element = function (state) {
if (state === 'show') {
$element.removeClass('hide').addClass('show').animate({opacity: 1}, 100);
} else {
$element.animate({opacity: 0});
}
};
var $saveBtn = $element.find('.save-button');
var $textEl = $saveBtn.find('.icon-button-text');
if (state !== "saving") {
$saveBtn.removeClass('saving');
}
if (state === "unsaved") {
$textEl.text(i18n.t("Save changes"));
$saveBtn.attr("data-status", "unsaved");
show_hide_element('show');
} else if (state === "saved") {
$textEl.text(i18n.t("Save changes"));
$saveBtn.attr("data-status", "");
show_hide_element('hide');
} else if (state === "discarded") {
$element.removeClass('saving');
show_hide_element('hide');
} else if (state === "saving") {
$saveBtn.addClass('saving');
$textEl.text(i18n.t("Saving"));
$saveBtn.attr("data-status", "saving");
show_hide_element('show');
} else if (state === "failed") {
show_hide_element('show');
$textEl.text(i18n.t("Save changes"));
$saveBtn.attr("data-status", "failed");
} else if (state === 'succeeded') {
show_hide_element('hide');
$textEl.text(i18n.t("Saved"));
$saveBtn.attr("data-status", "saved");
}
};
function _set_up() {
meta.loaded = true;
@@ -422,46 +462,38 @@ function _set_up() {
}
});
var change_process_button = subsection.find('.subsection-header .button');
change_process_button.first().text(i18n.t("Save"));
if (show_change_process_button) {
change_process_button.removeClass('hide').addClass('show');
} else {
change_process_button.removeClass('show').addClass('hide');
}
var save_btn_controls = subsection.find('.subsection-header .save-button-controls');
var button_state = (show_change_process_button) ? "unsaved" : "saved";
exports.change_save_button_state(save_btn_controls, button_state);
});
$('.organization').on('click', '.subsection-header .subsection-changes-discard button', function (e) {
$('.organization').on('click', '.subsection-header .subsection-changes-discard .button', function (e) {
e.preventDefault();
e.stopPropagation();
_.each(get_subsection_property_elements(e.target), discard_property_element_changes);
var subsection = $(e.target).closest('.org-subsection-parent');
var change_process_buttons = subsection.find('.subsection-header .button');
change_process_buttons.removeClass('show').addClass('hide');
var save_btn_controls = $(e.target).closest('.save-button-controls');
exports.change_save_button_state(save_btn_controls, "discarded");
});
exports.save_organization_settings = function (data, save_button, success_continuation) {
var subsection_parent = save_button.closest('.org-subsection-parent');
var discard_button = subsection_parent.find('.subsection-changes-discard button');
var save_btn_container = subsection_parent.find('.save-button-controls');
var failed_alert_elem = subsection_parent.prevAll('.admin-realm-failed-change-status:first').expectOne();
save_button.text(i18n.t("Saving"));
save_button.attr("data-status", "saving");
exports.change_save_button_state(save_btn_container, "saving");
channel.patch({
url: "/json/realm",
data: data,
success: function (response_data) {
discard_button.removeClass('show').addClass('hide');
failed_alert_elem.hide();
save_button.attr("data-status", "saved");
save_button.text(i18n.t("Saved"));
save_button.removeClass('hide').addClass('show').stop(true).fadeTo(0, 1);
setTimeout(function () {
exports.change_save_button_state(save_btn_container, "succeeded");
}, 500);
if (success_continuation !== undefined) {
success_continuation(response_data);
}
},
error: function (xhr) {
save_button.attr("data-status", "failed");
save_button.text(i18n.t("Save"));
exports.change_save_button_state(save_btn_container, "failed");
ui_report.error(i18n.t("Failed"), xhr, failed_alert_elem);
},
});
@@ -541,10 +573,10 @@ function _set_up() {
return opts;
}
$(".organization").on("click", ".subsection-header .subsection-changes-save button", function (e) {
$(".organization").on("click", ".subsection-header .subsection-changes-save .button", function (e) {
e.preventDefault();
e.stopPropagation();
var save_button = $(e.target);
var save_button = $(e.currentTarget);
var subsection_id = save_button.attr('id').replace("org-submit-", "");
var subsection = subsection_id.split('-').join('_');

View File

@@ -29,6 +29,7 @@ exports.initialize = function () {
// direct calls to `ui_report`.
exports.do_settings_change = function (request_method, url, data, status_element, opts) {
var spinner = $(status_element).expectOne();
spinner.fadeTo(0, 1);
loading.make_indicator(spinner, {text: exports.strings.saving});
var success_msg;
var success_continuation;

View File

@@ -351,6 +351,87 @@ input[type=checkbox] + .inline-block {
margin-bottom: 0px;
}
#settings_page .icon-button {
min-width: auto;
border-width: 2px;
-moz-border-radius: 28px;
-webkit-border-radius: 28px;
border-radius: 5px;
border: 1px solid hsl(0,0%,80%);
display: inline-block;
cursor: pointer;
font-size: 14px;
padding: 3px 12px 4px 10px;
text-decoration: none;
color: hsl(0, 0%, 43%);
font-weight: 400;
}
#settings_page .icon-button:hover {
background: hsl(0, 0%, 100%);
border: 1px solid hsl(0, 0%, 61%);
color: hsl(0, 0%, 18%);
}
#settings_page .icon-button:hover .icon-button-icon {
color: hsl(0, 0%, 47%);
}
#settings_page .icon-button.primary:hover {
background-color: hsl(166, 35%, 57%);
border: 1px solid hsl(166, 35%, 57%);
}
#settings_page .icon-button.primary {
background-color: hsl(156, 30%, 50%);
color: hsl(0, 0%, 100%);
border: 1px solid hsl(155, 30%, 50%);
}
#settings_page .icon-button .icon-button-icon {
color: #969696;
}
#settings_page .icon-button.primary .icon-button-icon {
color: white;
}
#settings_page .icon-button .icon-button-text,
#settings_page .icon-button .icon-button-icon {
vertical-align: baseline;
margin-right: 2px;
}
#settings_page .save-button-controls {
display: inline;
margin-left: 15px;
}
#settings_page .save-button-controls.hide {
display: none;
}
#settings_page .save-button {
margin-right: 5px;
}
#settings_page .icon-button.save-button.saving {
background: hsl(156, 14%, 40%);
border-color: hsl(156, 14%, 40%);
}
#settings_page .save-button.saving .icon-button-icon {
display: none;
}
#settings_page .save-button.saving .icon-button-loading {
display: inline-block;
}
#settings_page .save-button .icon-button-loading {
display: none;
}
.organization-settings-parent div:first-of-type {
margin-top: 10px;
}
@@ -471,14 +552,13 @@ input[type=checkbox].inline-block {
width: auto !important;
background: transparent;
border: 1px solid hsl(178, 100%, 40%);
color: hsl(178, 100%, 40%);
padding: 2px 5px;
border-radius: 4px;
margin-top: 14px;
margin-left: 10px;
border: 1px solid hsl(156, 30%, 50%);
color: hsl(156, 30%, 50%);
padding: 3px 10px;
font-size: 15px;
}
#settings_page .alert-notification.alert-error {
@@ -492,11 +572,6 @@ input[type=checkbox].inline-block {
margin: 0;
}
/* make the spinner green like the text and box. */
#settings_page .alert-notification .loading_indicator_spinner svg path {
fill: hsl(178, 100%, 40%);
}
#settings_page .alert-notification .loading_indicator_text {
margin-top: 0px;
font-size: inherit;
@@ -505,6 +580,8 @@ input[type=checkbox].inline-block {
#settings_page .alert-notification img {
margin-right: 5px;
vertical-align: middle;
margin-top: -1px;
}
#notification-settings .notification-reminder {

View File

@@ -1,13 +1 @@
<svg width='100%' height='100%' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-ring">
<rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect>
<defs>
<filter id="uil-ring-shadow" x="-100%" y="-100%" width="300%" height="300%">
<feOffset result="offOut" in="SourceGraphic" dx="0" dy="0"></feOffset>
<feGaussianBlur result="blurOut" in="offOut" stdDeviation="0"></feGaussianBlur>
<feBlend in="SourceGraphic" in2="blurOut" mode="normal"></feBlend>
</filter>
</defs>
<path d="M10,50c0,0,0,0.5,0.1,1.4c0,0.5,0.1,1,0.2,1.7c0,0.3,0.1,0.7,0.1,1.1c0.1,0.4,0.1,0.8,0.2,1.2c0.2,0.8,0.3,1.8,0.5,2.8 c0.3,1,0.6,2.1,0.9,3.2c0.3,1.1,0.9,2.3,1.4,3.5c0.5,1.2,1.2,2.4,1.8,3.7c0.3,0.6,0.8,1.2,1.2,1.9c0.4,0.6,0.8,1.3,1.3,1.9 c1,1.2,1.9,2.6,3.1,3.7c2.2,2.5,5,4.7,7.9,6.7c3,2,6.5,3.4,10.1,4.6c3.6,1.1,7.5,1.5,11.2,1.6c4-0.1,7.7-0.6,11.3-1.6 c3.6-1.2,7-2.6,10-4.6c3-2,5.8-4.2,7.9-6.7c1.2-1.2,2.1-2.5,3.1-3.7c0.5-0.6,0.9-1.3,1.3-1.9c0.4-0.6,0.8-1.3,1.2-1.9 c0.6-1.3,1.3-2.5,1.8-3.7c0.5-1.2,1-2.4,1.4-3.5c0.3-1.1,0.6-2.2,0.9-3.2c0.2-1,0.4-1.9,0.5-2.8c0.1-0.4,0.1-0.8,0.2-1.2 c0-0.4,0.1-0.7,0.1-1.1c0.1-0.7,0.1-1.2,0.2-1.7C90,50.5,90,50,90,50s0,0.5,0,1.4c0,0.5,0,1,0,1.7c0,0.3,0,0.7,0,1.1 c0,0.4-0.1,0.8-0.1,1.2c-0.1,0.9-0.2,1.8-0.4,2.8c-0.2,1-0.5,2.1-0.7,3.3c-0.3,1.2-0.8,2.4-1.2,3.7c-0.2,0.7-0.5,1.3-0.8,1.9 c-0.3,0.7-0.6,1.3-0.9,2c-0.3,0.7-0.7,1.3-1.1,2c-0.4,0.7-0.7,1.4-1.2,2c-1,1.3-1.9,2.7-3.1,4c-2.2,2.7-5,5-8.1,7.1 c-0.8,0.5-1.6,1-2.4,1.5c-0.8,0.5-1.7,0.9-2.6,1.3L66,87.7l-1.4,0.5c-0.9,0.3-1.8,0.7-2.8,1c-3.8,1.1-7.9,1.7-11.8,1.8L47,90.8 c-1,0-2-0.2-3-0.3l-1.5-0.2l-0.7-0.1L41.1,90c-1-0.3-1.9-0.5-2.9-0.7c-0.9-0.3-1.9-0.7-2.8-1L34,87.7l-1.3-0.6 c-0.9-0.4-1.8-0.8-2.6-1.3c-0.8-0.5-1.6-1-2.4-1.5c-3.1-2.1-5.9-4.5-8.1-7.1c-1.2-1.2-2.1-2.7-3.1-4c-0.5-0.6-0.8-1.4-1.2-2 c-0.4-0.7-0.8-1.3-1.1-2c-0.3-0.7-0.6-1.3-0.9-2c-0.3-0.7-0.6-1.3-0.8-1.9c-0.4-1.3-0.9-2.5-1.2-3.7c-0.3-1.2-0.5-2.3-0.7-3.3 c-0.2-1-0.3-2-0.4-2.8c-0.1-0.4-0.1-0.8-0.1-1.2c0-0.4,0-0.7,0-1.1c0-0.7,0-1.2,0-1.7C10,50.5,10,50,10,50z" fill="#444" filter="url(#uil-ring-shadow)">
<animateTransform attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" repeatCount="indefinite" dur="1s"></animateTransform>
</path>
</svg>
<span class="fa fa-spinner fa-spin icon-button-loading"></span>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 64 B

View File

@@ -1,12 +1,21 @@
{{#if is_admin }}
<div class="input-group organization-submission subsection-changes-save">
<button type="button" id="org-submit-{{section_name}}" class="button rounded sea-green hide" data-status="save">
{{t 'Save' }}
</button>
</div>
<div class="input-group subsection-changes-discard">
<button type="button" id="org-discard-{{section_name}}" class="button rounded hide">
{{t 'Discard changes' }}
</button>
<div class="save-button-controls hide">
<div class="input-group organization-submission subsection-changes-save">
<div class="icon-button button primary save-button" type="button" id="org-submit-{{section_name}}" data-status="save">
<span class="fa fa-spinner fa-spin icon-button-loading"></span>
<span class="fa fa-check icon-button-icon"></span>
<span class="icon-button-text">
{{t 'Save changes' }}
</span>
</div>
</div>
<div class="input-group subsection-changes-discard">
<div class="icon-button button discard-button" type="button" id="org-discard-{{section_name}}">
<span class="fa fa-times icon-button-icon"></span>
<span class="icon-button-text">
{{t 'Discard' }}
</span>
</div>
</div>
</div>
{{/if}}