Some browsers don't properly support tri-state checkboxes, so create our own control to handle true/false/null. Fixes #2848

This commit is contained in:
Murtuza Zabuawala 2017-11-21 17:22:25 +00:00 committed by Dave Page
parent b284572afe
commit 930dd8af1f
4 changed files with 157 additions and 29 deletions

View File

@ -13,13 +13,14 @@
"10": ["[61,62]", "[61,62]", "json"],
"11": ["", "true", "bool"],
"12": ["", "[null]", "bool"],
"13": ["", "[null]", "text[]"],
"14": ["{}", "{}", "text[]"],
"15": ["{data,,'',\"\",\\'\\',\\\"\\\"}", "{data,[null],,,'',\"\"}", "text[]"],
"16": ["{}", "{}", "int[]"],
"17": ["{123,,456}", "{123,[null],456}", "int[]"],
"18": ["", "[null]", "boolean[]"],
"19": ["{false,,true}", "{false,[null],true}", "boolean[]"]
"13": ["", "false", "bool"],
"14": ["", "[null]", "text[]"],
"15": ["{}", "{}", "text[]"],
"16": ["{data,,'',\"\",\\'\\',\\\"\\\"}", "{data,[null],,,'',\"\"}", "text[]"],
"17": ["{}", "{}", "int[]"],
"18": ["{123,,456}", "{123,[null],456}", "int[]"],
"19": ["", "[null]", "boolean[]"],
"20": ["{false,,true}", "{false,[null],true}", "boolean[]"]
}
}
}

View File

@ -65,8 +65,9 @@ CREATE TABLE public.defaults
text_null4 text COLLATE pg_catalog."default",
json_defaults json DEFAULT '[51, 52]'::json,
json_null json,
boolean_defaults boolean DEFAULT true,
boolean_true boolean DEFAULT true,
boolean_null boolean,
boolean_false boolean,
text_arr text[],
text_arr_empty text[],
text_arr_null text[],
@ -194,12 +195,17 @@ CREATE TABLE public.defaults
self.page.find_by_xpath("//*[contains(@class, 'pg_text_editor')]"
"//button[contains(@class, 'fa-save')]").click()
else:
# Boolean editor test for to True click
if data[1] == 'true':
checkbox_el = cell_el.find_element_by_xpath(".//input")
checkbox_el = cell_el.find_element_by_xpath(".//*[contains(@class, 'multi-checkbox')]")
checkbox_el.click()
ActionChains(self.driver).move_to_element(checkbox_el).double_click(
checkbox_el
).perform()
# Boolean editor test for to False click
elif data[1] == 'false':
checkbox_el = cell_el.find_element_by_xpath(".//*[contains(@class, 'multi-checkbox')]")
# Sets true
checkbox_el.click()
# Sets false
ActionChains(self.driver).click(checkbox_el).perform()
def _tables_node_expandable(self):
self.page.toggle_open_tree_item(self.server['name'])

View File

@ -1399,3 +1399,31 @@ body {
font-size: 8px;
color: #888888;
}
/* CSS for custom checkbox editor in SlickGrid */
.multi-checkbox .check {
display: inline-block;
vertical-align: top;
width: 15px;
height: 15px;
border: 1px solid #333;
margin: 3px;
text-align: center;
line-height: 15px;
}
.multi-checkbox .check.unchecked {
background: #fff;
}
.multi-checkbox .check.partial {
background: #cccccc;
}
.multi-checkbox .check.checked:after {
content: "\2713";
}
.multi-checkbox .check.partial:after {
content: "\fe56";
}

View File

@ -12,10 +12,10 @@
"pgText": pgTextEditor,
"JsonText": JsonTextEditor,
"CustomNumber": CustomNumberEditor,
"Checkbox": pgCheckboxEditor,
// Below editor will read only editors, Just to display data
"ReadOnlyText": ReadOnlyTextEditor,
"ReadOnlyCheckbox": ReadOnlyCheckboxEditor,
"Checkbox": CheckboxEditor, // Override editor to implement checkbox with three states
"ReadOnlypgText": ReadOnlypgTextEditor,
"ReadOnlyJsonText": ReadOnlyJsonTextEditor
}
@ -545,7 +545,7 @@
*/
function CheckboxEditor(args) {
var $select, el;
var defaultValue;
var defaultValue, previousState;
var scope = this;
this.init = function () {
@ -564,22 +564,31 @@
checkbox_status = 1;
}
switch(checkbox_status) {
// unchecked, going indeterminate
// State 0 will come when we had indeterminate state
case 0:
el.prop('indeterminate', true);
el.data('checked', 2); // determines next checkbox status
// We will check now
el.prop('checked', true);
el.data('checked', 1);
break;
// indeterminate, going checked
// State 1 will come when we had checked state
case 1:
el.prop('checked', true);
// We will uncheck now
el.prop('checked', false);
el.data('checked', 2);
break;
// State 2 will come when we had unchecked state
case 2:
// We will set to indeterminate state
el.prop('indeterminate', true);
el.data('checked', 0);
break;
// checked, going unchecked
// Default, Set to indeterminate state
default:
el.prop('checked', false);
el.data('checked', 1);
el.prop('indeterminate', true);
el.data('checked', 0);
}
});
};
@ -594,18 +603,21 @@
this.loadValue = function (item) {
defaultValue = item[args.column.field];
previousState = 0;
if (_.isNull(defaultValue)||_.isUndefined(defaultValue)) {
$select.prop('indeterminate', true);
$select.data('checked', 2);
$select.data('checked', 0);
}
else {
defaultValue = !!item[args.column.field];
if (defaultValue) {
$select.prop('checked', true);
$select.data('checked', 0);
$select.data('checked', 1);
previousState = 1;
} else {
$select.prop('checked', false);
$select.data('checked', 1);
$select.data('checked', 2);
previousState = 2;
}
}
};
@ -622,10 +634,8 @@
};
this.isValueChanged = function () {
// var select_value = this.serializeValue();
var select_value = $select.data('checked');
return (!(select_value === 2 && (defaultValue == null || defaultValue == undefined))) &&
(select_value !== defaultValue);
var currentState = $select.data('checked');
return currentState !== previousState;
};
this.validate = function () {
@ -1023,4 +1033,87 @@
this.init();
}
// Custom checkbox editor, We need it for runtime as it does not render
// indeterminate checkbox state
function pgCheckboxEditor(args) {
var $select, el;
var defaultValue, previousState;
var scope = this;
this.init = function () {
$select = $("<div class='multi-checkbox'><span class='check' hideFocus></span></div>");
$select.appendTo(args.container);
$select.focus();
// The following code is taken from https://css-tricks.com/indeterminate-checkboxes/
$select.bind("click", function (e) {
el = $(this);
var states = ["unchecked", "partial", "checked"];
var curState = el.find(".check").data("state");
curState++;
el.find(".check")
.removeClass("unchecked partial checked")
.addClass(states[curState % states.length])
.data("state", curState % states.length);
});
};
this.destroy = function () {
$select.remove();
};
this.focus = function () {
$select.focus();
};
this.loadValue = function (item) {
defaultValue = item[args.column.field];
previousState = 1;
if (_.isNull(defaultValue)||_.isUndefined(defaultValue)) {
$select.find(".check").data("state", 1).addClass("partial");
}
else {
defaultValue = !!item[args.column.field];
if (defaultValue) {
$select.find(".check").data("state", 2).addClass("checked");
previousState = 2;
} else {
$select.find(".check").data("state", 0).addClass("unchecked");
previousState = 0;
}
}
};
this.serializeValue = function () {
if ($select.find(".check").data("state") == 1) {
return null;
}
return $select.find(".check").data("state") == 2 ? true : false;
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
var currentState = $select.find(".check").data("state");
return currentState !== previousState;
};
this.validate = function () {
if (args.column.validator) {
var validationResults = args.column.validator(this.serializeValue());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null
};
};
this.init();
}
})(jQuery);