From 97387b3dbc1a15cbfe8d6c07a14b5299e2f63055 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 22 Feb 2022 13:06:21 +0530 Subject: [PATCH 01/23] feat: grid search --- frappe/public/js/frappe/form/grid.js | 129 +++++++++++++++++--- frappe/public/js/frappe/form/grid_row.js | 144 +++++++++++++++++++++-- frappe/public/js/frappe/utils/utils.js | 12 +- frappe/public/scss/common/grid.scss | 24 ++++ 4 files changed, 285 insertions(+), 24 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 8b615f3c59..e806e46ebc 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -35,7 +35,7 @@ export default class Grid { && this.frm.meta.__form_grid_templates[this.df.fieldname]) { this.template = this.frm.meta.__form_grid_templates[this.df.fieldname]; } - + this.filter = {}; this.is_grid = true; this.debounced_refresh = this.refresh.bind(this); this.debounced_refresh = frappe.utils.debounce(this.debounced_refresh, 100); @@ -274,6 +274,8 @@ export default class Grid { } make_head() { + if (this.prevent_build) return; + // labels if (this.header_row) { $(this.parent).find(".grid-heading-row .grid-row").remove(); @@ -286,12 +288,42 @@ export default class Grid { grid: this, configure_columns: true }); + + this.header_search = new GridRow({ + parent: $(this.parent).find(".grid-heading-row"), + parent_df: this.df, + docfields: this.docfields, + frm: this.frm, + grid: this, + show_search: true + }); + + Object.keys(this.filter).length !== 0 && + this.update_search_columns(); + } + + update_search_columns() { + for (const field in this.filter) { + if (this.filter[field] && !this.header_search.search_columns[field]) { + delete this.filter[field]; + this.data = this.get_data(Object.keys(this.filter).length !== 0); + break; + } + + if (this.filter[field] && this.filter[field].value) { + let $input = this.header_search.row_index.find('input'); + if (field && field !== 'row-index') { + $input = this.header_search.search_columns[field].find('input'); + } + $input.val(this.filter[field].value); + } + }; } - refresh(force) { + refresh() { if (this.frm && this.frm.setting_dependency) return; - this.data = this.get_data(); + this.data = this.get_data(Object.keys(this.filter).length !== 0); !this.wrapper && this.make(); let $rows = $(this.parent).find('.rows'); @@ -453,7 +485,7 @@ export default class Grid { } make_sortable($rows) { - new Sortable($rows.get(0), { + this.grid_sortable = new Sortable($rows.get(0), { group: { name: this.df.fieldname }, handle: '.sortable-handle', draggable: '.grid-row', @@ -484,14 +516,74 @@ export default class Grid { $(this.frm.wrapper).trigger("grid-make-sortable", [this.frm]); } - get_data() { - var data = this.frm ? - this.frm.doc[this.df.fieldname] || [] - : this.df.data || this.get_modal_data(); - // data.sort(function(a, b) { return a.idx - b.idx}); + get_data(filter_field) { + let data = []; + if (filter_field) { + data = this.get_filtered_data(); + } else { + data = this.frm ? + this.frm.doc[this.df.fieldname] || [] + : this.df.data || this.get_modal_data(); + } return data; } + get_filtered_data() { + if (!this.frm) return; + + let all_data = this.frm.doc[this.df.fieldname]; + + for (const field in this.filter) { + all_data = all_data.filter(data => { + let {df, value} = this.filter[field]; + + if (["Check"].includes(df.fieldtype)) { + return (data[df.fieldname] === parseInt(value || 0)) && data; + } else if (df.fieldtype === "Sr No" && data.idx.toString().indexOf(value) > -1) { + return data; + } else if (["Currency", "Float", "Int", "Percent", "Rating"].includes(df.fieldtype)) { + let num = data[df.fieldname] || 0; + + if (df.fieldtype === "Rating") { + let out_of_rating = parseInt(df.options) || 5; + num = data[df.fieldname] * out_of_rating; + } + + if (num.toString().indexOf(value) > -1) { + return data; + } + } else if (["Datetime", "Date"].includes(df.fieldtype) && data[df.fieldname]) { + let user_formatted_date = frappe.datetime.str_to_user(data[df.fieldname]); + + if (user_formatted_date.includes(value)) { + return data; + } + } else if (df.fieldtype === "Duration" && data[df.fieldname]) { + let formatted_duration = frappe.utils.get_formatted_duration(data[df.fieldname]); + + if (formatted_duration.includes(value.toLowerCase())) { + return data; + } + } else if (df.fieldtype === "Barcode" && data[df.fieldname]) { + let svg = data[df.fieldname]; + + if (svg.startsWith(' { if (!this.deleted_docs || !in_list(this.deleted_docs, data.name)) { @@ -701,7 +793,7 @@ export default class Grid { if (this.visible_columns && this.visible_columns.length > 0) return; this.user_defined_columns = []; - this.setup_user_defined_columns(); + this.setup_user_settings(); var total_colsize = 1, fields = (this.user_defined_columns && this.user_defined_columns.length > 0) ? this.user_defined_columns : this.editable_fields || this.docfields; @@ -775,12 +867,16 @@ export default class Grid { df.colsize = colsize; } - setup_user_defined_columns() { - if (this.frm) { - let user_settings = frappe.get_user_settings(this.frm.doctype, 'GridView'); - if (user_settings && user_settings[this.doctype] && user_settings[this.doctype].length) { - this.user_defined_columns = user_settings[this.doctype].map(row => { + setup_user_settings() { + if (!this.frm) return; + + let user_settings = frappe.get_user_settings(this.frm.doctype, 'GridView'); + + if (user_settings && user_settings[this.doctype] && user_settings[this.doctype]) { + if (user_settings[this.doctype]['columns'] && user_settings[this.doctype]['columns'].length) { + this.user_defined_columns = user_settings[this.doctype]['columns'].map(row => { let column = frappe.meta.get_docfield(this.doctype, row.fieldname); + if (column) { column.in_list_view = 1; column.columns = row.columns; @@ -788,6 +884,9 @@ export default class Grid { } }); } + + this.show_search = this.frm.doc[this.df.fieldname].length >= + (user_settings[this.doctype]['enable_search_count'] || 15); } } diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index a40f428969..97263d2529 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -12,7 +12,7 @@ export default class GridRow { } this.columns = {}; this.columns_list = []; - this.row_check_html = ''; + this.row_check_html = ''; this.make(); } make() { @@ -192,23 +192,67 @@ export default class GridRow { this.set_row_index(); // index (1, 2, 3 etc) - if(!this.row_index) { + if(!this.row_index && !this.show_search) { // REDESIGN-TODO: Make translation contextual, this No is Number var txt = (this.doc ? this.doc.idx : __("No.")); - this.row_index = $( - `
+ + this.row_check = $( + `
${this.row_check_html} -
`) +
`) + .appendTo(this.row); + + this.row_index = $( + ``) .appendTo(this.row) .on('click', function(e) { if(!$(e.target).hasClass('grid-row-check')) { me.toggle_view(); } }); + } else if (this.show_search) { + let timer = null; + this.row_check = $( + `` + ).appendTo(this.row); + + this.row_index = $( + `` + ).appendTo(this.row); + + this.row_index.find('input').on('keyup', (e) => { + clearTimeout(timer); + timer = setTimeout(() => { + let df = { + fieldtype: "Sr No" + }; + + this.grid.filter['row-index'] = { + df: df, + value: e.target.value + } + + if(e.target.value == "") { + delete this.grid.filter['row-index']; + } + + this.grid.grid_sortable + .option('disabled', Object.keys(this.grid.filter).length !== 0); + + this.grid.prevent_build = true; + me.grid.refresh(); + this.grid.prevent_build = false; + }, 500); + }); + frappe.utils.only_allow_num_decimal(this.row_index.find('input')); } else { this.row_index.find('span').html(txt); } - + this.show_search && this.show_search_columns(); this.setup_columns(); this.add_open_form_button(); this.add_column_configure_button(); @@ -266,14 +310,26 @@ export default class GridRow { } configure_dialog_for_columns_selector() { + let user_settings = frappe.get_user_settings(this.frm.doctype, 'GridView'); + let enable_search_count = user_settings[this.grid.doctype] && + user_settings[this.grid.doctype]["enable_search_count"] || 15; + this.grid_settings_dialog = new frappe.ui.Dialog({ title: __("Configure Columns"), fields: [{ 'fieldtype': 'HTML', 'fieldname': 'fields_html' + }, + { + 'label': 'Enable Grid Search Count', + 'fieldtype': 'Data', + 'fieldname': 'enable_search', + 'default': enable_search_count, + 'description': __("Enable grid search if the grid row's are greater than or equal to the entered number") }] }); + this.enable_search_count = this.grid_settings_dialog.fields_dict.enable_search; this.grid.setup_visible_columns(); this.setup_columns_for_dialog(); this.prepare_wrapper_for_columns(); @@ -512,7 +568,10 @@ export default class GridRow { } let value = {}; - value[this.grid.doctype] = this.selected_columns_for_grid; + value[this.grid.doctype] = {}; + value[this.grid.doctype]['columns'] = this.selected_columns_for_grid; + value[this.grid.doctype]['enable_search_count'] = this.enable_search_count.get_value(); + frappe.model.user_settings.save(this.frm.doctype, 'GridView', value) .then((r) => { frappe.model.user_settings[this.frm.doctype] = r.message || r; @@ -530,6 +589,7 @@ export default class GridRow { setup_columns() { this.focus_set = false; + this.search_columns = {}; this.grid.setup_visible_columns(); this.grid.visible_columns.forEach((col, ci) => { @@ -545,8 +605,10 @@ export default class GridRow { txt = __(txt); } let column; - if (!this.columns[df.fieldname]) { + if (!this.columns[df.fieldname] && !this.show_search) { column = this.make_column(df, colsize, txt, ci); + } else if (!this.columns[df.fieldname] && this.show_search) { + column = this.make_search_column(df, colsize); } else { column = this.columns[df.fieldname]; this.refresh_field(df.fieldname, txt); @@ -564,6 +626,72 @@ export default class GridRow { } } }); + + if (this.show_search) { + // last empty column + $(`
`) + .appendTo(this.row) + } + } + + show_search_columns() { + // show or remove search columns based on Grid Search Count + this.grid.setup_user_settings(); + !this.grid.show_search && this.wrapper.remove(); + } + + make_search_column(df, colsize) { + let timer = null; + let title = ""; + let input_class = ""; + let is_disabled = ""; + + if (["Text", "Small Text"].includes(df.fieldtype)) { + input_class = "grid-overflow-no-ellipsis"; + } else if (["Int", "Currency", "Float", "Percent"].includes(df.fieldtype)) { + input_class = "text-right"; + } else if (df.fieldtype === "Check") { + title = __("1 = True & 0 = False"); + input_class = "text-center"; + } else if (df.fieldtype === 'Password') { + is_disabled = 'disabled' + title = __('Password cannot be filtered') + } + + let $col = $('') + .appendTo(this.row); + + let $search_input = $(` + + `).appendTo($col); + + this.search_columns[df.fieldname] = $col; + + $search_input.on('keyup', (e) => { + clearTimeout(timer); + timer = setTimeout(() => { + this.grid.filter[df.fieldname] = { + df: df, + value: e.target.value + } + + if(e.target.value == '') { + delete this.grid.filter[df.fieldname]; + } + + this.grid.grid_sortable + .option('disabled', Object.keys(this.grid.filter).length !== 0); + + this.grid.prevent_build = true; + this.grid.refresh(); + this.grid.prevent_build = false; + }, 500); + }); + + ["Currency", "Float", "Int", "Percent", "Rating", "Check"].includes(df.fieldtype) && + frappe.utils.only_allow_num_decimal($search_input); + + return $col; } make_column(df, colsize, txt, ci) { diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index dc75239ed5..ae63f79e82 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -1093,7 +1093,7 @@ Object.assign(frappe.utils, { seconds: round(seconds % 60) }; - if (duration_options.hide_days) { + if (duration_options && duration_options.hide_days) { total_duration.hours = round(seconds / 3600); total_duration.days = 0; } @@ -1453,5 +1453,15 @@ Object.assign(frappe.utils, { console.log(error); // eslint-disable-line return Promise.resolve(name); } + }, + + only_allow_num_decimal(input) { + input.on('input', (e) => { + let self = $(e.target); + self.val(self.val().replace(/[^0-9\.]/g, '')); + if ((e.which != 46 || self.val().indexOf('.') != -1) && (e.which < 48 || e.which > 57)) { + e.preventDefault(); + } + }); } }); diff --git a/frappe/public/scss/common/grid.scss b/frappe/public/scss/common/grid.scss index 1903413fbb..d5c9ae8d6b 100644 --- a/frappe/public/scss/common/grid.scss +++ b/frappe/public/scss/common/grid.scss @@ -82,6 +82,29 @@ height: 34px; padding: 8px; max-height: 200px; + + &.search { + padding: 7px !important; + + input { + height: -webkit-fill-available; + padding: 3px 7px; + } + } +} + +.row-check { + height: 34px; + padding: 8px 3px !important; + text-align: center; + + input { + margin-right: 0 !important; + } + + &.search { + padding: 0 !important; + } } .grid-row-check { @@ -409,6 +432,7 @@ } .page-number { + background-color: var(--fg-color); padding: 0 3px; } From cf4f35e8deb7a5e576e2b45ae86482ee46a960de Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 22 Feb 2022 15:51:54 +0530 Subject: [PATCH 02/23] fix(sider): missing semicolons --- frappe/public/js/frappe/form/grid.js | 2 +- frappe/public/js/frappe/form/grid_row.js | 16 ++++++++-------- frappe/public/js/frappe/utils/utils.js | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index e806e46ebc..adbaa5bcad 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -317,7 +317,7 @@ export default class Grid { } $input.val(this.filter[field].value); } - }; + } } refresh() { diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 97263d2529..789114572b 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -192,7 +192,7 @@ export default class GridRow { this.set_row_index(); // index (1, 2, 3 etc) - if(!this.row_index && !this.show_search) { + if (!this.row_index && !this.show_search) { // REDESIGN-TODO: Make translation contextual, this No is Number var txt = (this.doc ? this.doc.idx : __("No.")); @@ -234,9 +234,9 @@ export default class GridRow { this.grid.filter['row-index'] = { df: df, value: e.target.value - } + }; - if(e.target.value == "") { + if (e.target.value == "") { delete this.grid.filter['row-index']; } @@ -630,7 +630,7 @@ export default class GridRow { if (this.show_search) { // last empty column $(`
`) - .appendTo(this.row) + .appendTo(this.row); } } @@ -654,8 +654,8 @@ export default class GridRow { title = __("1 = True & 0 = False"); input_class = "text-center"; } else if (df.fieldtype === 'Password') { - is_disabled = 'disabled' - title = __('Password cannot be filtered') + is_disabled = 'disabled'; + title = __('Password cannot be filtered'); } let $col = $('') @@ -673,9 +673,9 @@ export default class GridRow { this.grid.filter[df.fieldname] = { df: df, value: e.target.value - } + }; - if(e.target.value == '') { + if (e.target.value == '') { delete this.grid.filter[df.fieldname]; } diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index ae63f79e82..759b7b5499 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -1458,7 +1458,7 @@ Object.assign(frappe.utils, { only_allow_num_decimal(input) { input.on('input', (e) => { let self = $(e.target); - self.val(self.val().replace(/[^0-9\.]/g, '')); + self.val(self.val().replace(/[^0-9.]/g, '')); if ((e.which != 46 || self.val().indexOf('.') != -1) && (e.which < 48 || e.which > 57)) { e.preventDefault(); } From 058d89312b63cc39535eded7f638e2d061e68b01 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 24 Feb 2022 12:15:02 +0530 Subject: [PATCH 03/23] revert: search row visiblitity is customizable (made it hard coded) --- frappe/public/js/frappe/form/grid.js | 18 +++++-------- frappe/public/js/frappe/form/grid_row.js | 34 ++++++++---------------- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index adbaa5bcad..61a5309b39 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -793,7 +793,7 @@ export default class Grid { if (this.visible_columns && this.visible_columns.length > 0) return; this.user_defined_columns = []; - this.setup_user_settings(); + this.setup_user_defined_columns(); var total_colsize = 1, fields = (this.user_defined_columns && this.user_defined_columns.length > 0) ? this.user_defined_columns : this.editable_fields || this.docfields; @@ -867,14 +867,11 @@ export default class Grid { df.colsize = colsize; } - setup_user_settings() { - if (!this.frm) return; - - let user_settings = frappe.get_user_settings(this.frm.doctype, 'GridView'); - - if (user_settings && user_settings[this.doctype] && user_settings[this.doctype]) { - if (user_settings[this.doctype]['columns'] && user_settings[this.doctype]['columns'].length) { - this.user_defined_columns = user_settings[this.doctype]['columns'].map(row => { + setup_user_defined_columns() { + if (this.frm) { + let user_settings = frappe.get_user_settings(this.frm.doctype, 'GridView'); + if (user_settings && user_settings[this.doctype] && user_settings[this.doctype].length) { + this.user_defined_columns = user_settings[this.doctype].map(row => { let column = frappe.meta.get_docfield(this.doctype, row.fieldname); if (column) { @@ -884,9 +881,6 @@ export default class Grid { } }); } - - this.show_search = this.frm.doc[this.df.fieldname].length >= - (user_settings[this.doctype]['enable_search_count'] || 15); } } diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 789114572b..554af0c436 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -188,7 +188,9 @@ export default class GridRow { })); } render_row(refresh) { - var me = this; + if (this.show_search && !this.show_search_row()) return; + + let me = this; this.set_row_index(); // index (1, 2, 3 etc) @@ -252,7 +254,7 @@ export default class GridRow { } else { this.row_index.find('span').html(txt); } - this.show_search && this.show_search_columns(); + this.setup_columns(); this.add_open_form_button(); this.add_column_configure_button(); @@ -310,26 +312,14 @@ export default class GridRow { } configure_dialog_for_columns_selector() { - let user_settings = frappe.get_user_settings(this.frm.doctype, 'GridView'); - let enable_search_count = user_settings[this.grid.doctype] && - user_settings[this.grid.doctype]["enable_search_count"] || 15; - this.grid_settings_dialog = new frappe.ui.Dialog({ title: __("Configure Columns"), fields: [{ 'fieldtype': 'HTML', 'fieldname': 'fields_html' - }, - { - 'label': 'Enable Grid Search Count', - 'fieldtype': 'Data', - 'fieldname': 'enable_search', - 'default': enable_search_count, - 'description': __("Enable grid search if the grid row's are greater than or equal to the entered number") }] }); - this.enable_search_count = this.grid_settings_dialog.fields_dict.enable_search; this.grid.setup_visible_columns(); this.setup_columns_for_dialog(); this.prepare_wrapper_for_columns(); @@ -568,10 +558,7 @@ export default class GridRow { } let value = {}; - value[this.grid.doctype] = {}; - value[this.grid.doctype]['columns'] = this.selected_columns_for_grid; - value[this.grid.doctype]['enable_search_count'] = this.enable_search_count.get_value(); - + value[this.grid.doctype] = this.selected_columns_for_grid; frappe.model.user_settings.save(this.frm.doctype, 'GridView', value) .then((r) => { frappe.model.user_settings[this.frm.doctype] = r.message || r; @@ -634,10 +621,11 @@ export default class GridRow { } } - show_search_columns() { - // show or remove search columns based on Grid Search Count - this.grid.setup_user_settings(); - !this.grid.show_search && this.wrapper.remove(); + show_search_row() { + // show or remove search columns based on grid rows + this.show_search = this.frm.doc[this.grid.df.fieldname].length >= 15; + !this.show_search && this.wrapper.remove(); + return this.show_search; } make_search_column(df, colsize) { @@ -668,7 +656,7 @@ export default class GridRow { this.search_columns[df.fieldname] = $col; $search_input.on('keyup', (e) => { - clearTimeout(timer); + clearTimeout(timer); timer = setTimeout(() => { this.grid.filter[df.fieldname] = { df: df, From 3dda9aeb8354ac81e6c89f1c5066da794ee8c402 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 24 Feb 2022 15:43:43 +0530 Subject: [PATCH 04/23] chore: created function for code readability --- frappe/public/js/frappe/form/grid.js | 106 ++++++++++++++------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 61a5309b39..0a6211767e 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -536,52 +536,58 @@ export default class Grid { for (const field in this.filter) { all_data = all_data.filter(data => { let {df, value} = this.filter[field]; + return this.get_data_based_on_fieldtype(df, data, value.toLowerCase()); + }); + } - if (["Check"].includes(df.fieldtype)) { - return (data[df.fieldname] === parseInt(value || 0)) && data; - } else if (df.fieldtype === "Sr No" && data.idx.toString().indexOf(value) > -1) { - return data; - } else if (["Currency", "Float", "Int", "Percent", "Rating"].includes(df.fieldtype)) { - let num = data[df.fieldname] || 0; - - if (df.fieldtype === "Rating") { - let out_of_rating = parseInt(df.options) || 5; - num = data[df.fieldname] * out_of_rating; - } + return all_data; + } - if (num.toString().indexOf(value) > -1) { - return data; - } - } else if (["Datetime", "Date"].includes(df.fieldtype) && data[df.fieldname]) { - let user_formatted_date = frappe.datetime.str_to_user(data[df.fieldname]); + get_data_based_on_fieldtype(df, data, value) { + let fieldname = df.fieldname; + let fieldtype = df.fieldtype; + let fieldvalue = data[fieldname]; - if (user_formatted_date.includes(value)) { - return data; - } - } else if (df.fieldtype === "Duration" && data[df.fieldname]) { - let formatted_duration = frappe.utils.get_formatted_duration(data[df.fieldname]); + if (fieldtype === "Check") { + return (fieldvalue === parseInt(value || 0)) && data; + } else if (fieldtype === "Sr No" && data.idx.toString().includes(value)) { + return data; + } else if (fieldtype === "Duration" && fieldvalue) { + let formatted_duration = frappe.utils.get_formatted_duration(fieldvalue); - if (formatted_duration.includes(value.toLowerCase())) { - return data; - } - } else if (df.fieldtype === "Barcode" && data[df.fieldname]) { - let svg = data[df.fieldname]; + if (formatted_duration.includes(value)) { + return data; + } + } else if (fieldtype === "Barcode" && fieldvalue) { + let svg = fieldvalue; - if (svg.startsWith(' -1) { + return data; + } + } else if (fieldvalue && fieldvalue.toLowerCase().includes(value)) { + return data; + } } get_modal_data() { @@ -868,19 +874,19 @@ export default class Grid { } setup_user_defined_columns() { - if (this.frm) { - let user_settings = frappe.get_user_settings(this.frm.doctype, 'GridView'); - if (user_settings && user_settings[this.doctype] && user_settings[this.doctype].length) { - this.user_defined_columns = user_settings[this.doctype].map(row => { - let column = frappe.meta.get_docfield(this.doctype, row.fieldname); - - if (column) { - column.in_list_view = 1; - column.columns = row.columns; - return column; - } - }); - } + if (!this.frm) return; + + let user_settings = frappe.get_user_settings(this.frm.doctype, 'GridView'); + if (user_settings && user_settings[this.doctype] && user_settings[this.doctype].length) { + this.user_defined_columns = user_settings[this.doctype].map(row => { + let column = frappe.meta.get_docfield(this.doctype, row.fieldname); + + if (column) { + column.in_list_view = 1; + column.columns = row.columns; + return column; + } + }); } } From e1ada33cc56b0243c7266580e0573a75a3e306f7 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 24 Feb 2022 16:40:26 +0530 Subject: [PATCH 05/23] fix: show grid search row if rows are >= 20 --- frappe/public/js/frappe/form/grid_row.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 9b270fb8b0..e40fc3ba64 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -640,7 +640,7 @@ export default class GridRow { show_search_row() { // show or remove search columns based on grid rows - this.show_search = this.frm.doc[this.grid.df.fieldname].length >= 15; + this.show_search = this.frm.doc[this.grid.df.fieldname].length >= 20; !this.show_search && this.wrapper.remove(); return this.show_search; } From d800495810f813462172559a232a8ff37c9c8ddc Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 24 Feb 2022 19:36:59 +0530 Subject: [PATCH 06/23] fix: added fieldtype attribute on search field --- frappe/public/js/frappe/form/grid_row.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index e40fc3ba64..509875d8eb 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -667,7 +667,13 @@ export default class GridRow { .appendTo(this.row); let $search_input = $(` - + `).appendTo($col); this.search_columns[df.fieldname] = $col; From 1e68cca66330c9ace32102382c42f04a97d985de Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 24 Feb 2022 19:37:52 +0530 Subject: [PATCH 07/23] fix: allow both svg and text in barcode --- frappe/public/js/frappe/form/grid.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 385bd15cdf..99d9bbe0fc 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -559,14 +559,11 @@ export default class Grid { return data; } } else if (fieldtype === "Barcode" && fieldvalue) { - let svg = fieldvalue; + let barcode = fieldvalue.startsWith(' Date: Thu, 24 Feb 2022 19:44:19 +0530 Subject: [PATCH 08/23] test: UI test for grid search --- cypress/fixtures/child_table_doctype_1.js | 59 +++++++++++ cypress/fixtures/doctype_with_child_table.js | 6 ++ cypress/integration/grid_search.js | 104 +++++++++++++++++++ frappe/tests/ui_test_helpers.py | 45 +++++++- 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 cypress/fixtures/child_table_doctype_1.js create mode 100644 cypress/integration/grid_search.js diff --git a/cypress/fixtures/child_table_doctype_1.js b/cypress/fixtures/child_table_doctype_1.js new file mode 100644 index 0000000000..4657d63e2e --- /dev/null +++ b/cypress/fixtures/child_table_doctype_1.js @@ -0,0 +1,59 @@ +export default { + name: "Child Table Doctype 1", + actions: [], + custom: 1, + autoname: "format: Test-{####}", + creation: "2022-02-09 20:15:21.242213", + doctype: "DocType", + editable_grid: 1, + engine: "InnoDB", + fields: [ + { + fieldname: "data", + fieldtype: "Data", + in_list_view: 1, + label: "Data" + }, + { + fieldname: "barcode", + fieldtype: "Barcode", + in_list_view: 1, + label: "Barcode" + }, + { + fieldname: "check", + fieldtype: "Check", + in_list_view: 1, + label: "Check" + }, + { + fieldname: "rating", + fieldtype: "Rating", + in_list_view: 1, + label: "Rating" + }, + { + fieldname: "duration", + fieldtype: "Duration", + in_list_view: 1, + label: "Duration" + }, + { + fieldname: "date", + fieldtype: "Date", + in_list_view: 1, + label: "Date" + } + ], + links: [], + istable: 1, + modified: "2022-02-10 12:03:12.603763", + modified_by: "Administrator", + module: "Custom", + naming_rule: "By fieldname", + owner: "Administrator", + permissions: [], + sort_field: 'modified', + sort_order: 'ASC', + track_changes: 1 +}; \ No newline at end of file diff --git a/cypress/fixtures/doctype_with_child_table.js b/cypress/fixtures/doctype_with_child_table.js index bbb2127448..014074b0b5 100644 --- a/cypress/fixtures/doctype_with_child_table.js +++ b/cypress/fixtures/doctype_with_child_table.js @@ -20,6 +20,12 @@ export default { label: "Child Table", options: "Child Table Doctype", reqd: 1 + }, + { + fieldname: "child_table_1", + fieldtype: "Table", + label: "Child Table 1", + options: "Child Table Doctype 1" } ], links: [], diff --git a/cypress/integration/grid_search.js b/cypress/integration/grid_search.js new file mode 100644 index 0000000000..6ccf3e37f0 --- /dev/null +++ b/cypress/integration/grid_search.js @@ -0,0 +1,104 @@ +import doctype_with_child_table from '../fixtures/doctype_with_child_table'; +import child_table_doctype from '../fixtures/child_table_doctype'; +import child_table_doctype_1 from '../fixtures/child_table_doctype_1'; +const doctype_with_child_table_name = doctype_with_child_table.name; + +context('Grid Search', () => { + before(() => { + cy.visit('/login'); + cy.login(); + cy.insert_doc('DocType', child_table_doctype, true); + cy.insert_doc('DocType', child_table_doctype_1, true); + cy.insert_doc('DocType', doctype_with_child_table, true); + return cy.window().its('frappe').then(frappe => { + frappe.model.user_settings.save('Doctype With Child Table', 'GridView', { + 'Child Table Doctype 1': [ + {'fieldname': 'data', 'columns': 2}, + {'fieldname': 'barcode', 'columns': 1}, + {'fieldname': 'check', 'columns': 1}, + {'fieldname': 'rating', 'columns': 2}, + {'fieldname': 'duration', 'columns': 2}, + {'fieldname': 'date', 'columns': 2} + ] + }); + + return frappe.xcall("frappe.tests.ui_test_helpers.insert_doctype_with_child_table_record", { + name: doctype_with_child_table_name + }); + }); + }); + + it('Test search row visibility', () => { + cy.visit(`/app/doctype-with-child-table/Test Grid Search`); + + cy.get('[title="child_table_1"]').as('table'); + cy.get('@table').find('.grid-row-check:last').click(); + cy.get('@table').find('.grid-footer').contains('Delete').click(); + cy.get('.grid-heading-row .grid-row .search').should('not.exist'); + }); + + it('test search field for different fieldtypes', () => { + cy.visit(`/app/doctype-with-child-table/Test Grid Search`); + + cy.get('[title="child_table_1"]').as('table'); + + // Index Column + cy.get('@table').find('.grid-heading-row .row-index.search input').type('3'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 2); + cy.get('@table').find('.grid-heading-row .row-index.search input').clear(); + + // Data Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Data"]').type('Data'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 1); + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Data"]').clear(); + + // Barcode Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Barcode"]').type('092'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 4); + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Barcode"]').clear(); + + // Check Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Check"]').type('1'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 9); + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Check"]').clear(); + + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Check"]').type('0'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 11); + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Check"]').clear(); + + // Rating Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Rating"]').type('3'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 3); + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Rating"]').clear(); + + // Duration Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Duration"]').type('3d'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 3); + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Duration"]').clear(); + + // Date Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').type('2022'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 4); + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').clear(); + }); + + it('test with multiple filter', () => { + cy.get('[title="child_table_1"]').as('table'); + + // Data Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Data"]').type('a'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 10); + + // Barcode Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Barcode"]').type('0'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 8); + + // Duration Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Duration"]').type('d'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 5); + + // Date Column + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').type('-02'); + cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 2); + }) +}); \ No newline at end of file diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index 26c20f3d18..ca41615ca1 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -268,4 +268,47 @@ def update_child_table(name): 'options': 'Doctype to Link' }) - doc.save() \ No newline at end of file + doc.save() + + +@frappe.whitelist() +def insert_doctype_with_child_table_record(name): + if frappe.db.get_all(name, {'title': 'Test Grid Search'}): + return + + def insert_child(doc, data, barcode, check, rating, duration, date): + doc.append('child_table_1', { + 'data': data, + 'barcode': barcode, + 'check': check, + 'rating': rating, + 'duration': duration, + 'date': date, + }) + + doc = frappe.new_doc(name) + doc.title = 'Test Grid Search' + doc.append('child_table', {'title': 'Test Grid Search'}) + + insert_child(doc, 'Data', '09709KJKKH2432', 1, 0.5, 266851, "2022-02-21") + insert_child(doc, 'Test', '09209KJHKH2432', 1, 0.8, 547877, "2021-05-27") + insert_child(doc, 'New', '09709KJHYH1132', 0, 0.1, 3, "2019-03-02") + insert_child(doc, 'Old', '09701KJHKH8750', 0, 0, 127455, "2022-01-11") + insert_child(doc, 'Alpha', '09204KJHKH2432', 0, 0.6, 364, "2019-12-31") + insert_child(doc, 'Delta', '09709KSPIO2432', 1, 0.9, 1242000, "2020-04-21") + insert_child(doc, 'Update', '76989KJLVA2432', 0, 1, 183845, "2022-02-10") + insert_child(doc, 'Delete', '29189KLHVA1432', 0, 0, 365647, "2021-05-07") + insert_child(doc, 'Make', '09689KJHAA2431', 0, 0.3, 24, "2020-11-11") + insert_child(doc, 'Create', '09709KLKKH2432', 1, 0.3, 264851, "2021-02-21") + insert_child(doc, 'Group', '09209KJLKH2432', 1, 0.8, 537877, "2020-03-15") + insert_child(doc, 'Slide', '01909KJHYH1132', 0, 0.5, 9, "2018-03-02") + insert_child(doc, 'Drop', '09701KJHKH8750', 1, 0, 127255, "2018-01-01") + insert_child(doc, 'Beta', '09204QJHKN2432', 0, 0.6, 354, "2017-12-30") + insert_child(doc, 'Flag', '09709KXPIP2432', 1, 0, 1241000, "2021-04-21") + insert_child(doc, 'Upgrade', '75989ZJLVA2432', 0.8, 1, 183645, "2020-08-13") + insert_child(doc, 'Down', '28189KLHRA1432', 1, 0, 362647, "2020-06-17") + insert_child(doc, 'Note', '09689DJHAA2431', 0, 0.1, 29, "2021-09-11") + insert_child(doc, 'Click', '08189DJHAA2431', 1, 0.3, 209, "2020-07-04") + insert_child(doc, 'Drag', '08189DIHAA2981', 0, 0.7, 342628, "2022-05-04") + + doc.insert() From daa2b7921c2dfff5996e7607565e031d1888000e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 24 Feb 2022 19:48:10 +0530 Subject: [PATCH 09/23] fix(sider): missing semicolon --- cypress/integration/grid_search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/grid_search.js b/cypress/integration/grid_search.js index 6ccf3e37f0..dfb153f67b 100644 --- a/cypress/integration/grid_search.js +++ b/cypress/integration/grid_search.js @@ -100,5 +100,5 @@ context('Grid Search', () => { // Date Column cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').type('-02'); cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 2); - }) + }); }); \ No newline at end of file From 839f488c9185fb7a963102abcc9d36b5c873d0be Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 25 Feb 2022 11:50:55 +0530 Subject: [PATCH 10/23] fix: failing grid search UI test --- cypress/integration/grid_search.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cypress/integration/grid_search.js b/cypress/integration/grid_search.js index dfb153f67b..10444b2d2a 100644 --- a/cypress/integration/grid_search.js +++ b/cypress/integration/grid_search.js @@ -7,10 +7,19 @@ context('Grid Search', () => { before(() => { cy.visit('/login'); cy.login(); + cy.visit('/app/website'); cy.insert_doc('DocType', child_table_doctype, true); cy.insert_doc('DocType', child_table_doctype_1, true); cy.insert_doc('DocType', doctype_with_child_table, true); return cy.window().its('frappe').then(frappe => { + return frappe.xcall("frappe.tests.ui_test_helpers.insert_doctype_with_child_table_record", { + name: doctype_with_child_table_name + }); + }); + }); + + it('Test search row visibility', () => { + cy.window().its('frappe').then(frappe => { frappe.model.user_settings.save('Doctype With Child Table', 'GridView', { 'Child Table Doctype 1': [ {'fieldname': 'data', 'columns': 2}, @@ -21,14 +30,8 @@ context('Grid Search', () => { {'fieldname': 'date', 'columns': 2} ] }); - - return frappe.xcall("frappe.tests.ui_test_helpers.insert_doctype_with_child_table_record", { - name: doctype_with_child_table_name - }); }); - }); - it('Test search row visibility', () => { cy.visit(`/app/doctype-with-child-table/Test Grid Search`); cy.get('[title="child_table_1"]').as('table'); From 5a0dc5d7c80d3977a706482f4e36160d0488a2a0 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 7 Mar 2022 16:08:03 +0530 Subject: [PATCH 11/23] fix(linter): Trim Trailing Whitespace --- frappe/public/js/frappe/form/grid.js | 2 +- frappe/public/js/frappe/form/grid_row.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 99d9bbe0fc..36461fb671 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -559,7 +559,7 @@ export default class Grid { return data; } } else if (fieldtype === "Barcode" && fieldvalue) { - let barcode = fieldvalue.startsWith(' { - clearTimeout(timer); + clearTimeout(timer); timer = setTimeout(() => { let df = { fieldtype: "Sr No" @@ -666,12 +666,12 @@ export default class GridRow { .appendTo(this.row); let $search_input = $(` - `).appendTo($col); From d2e656eff52967952b5a95eb21ef1e6edffc05e4 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 7 Mar 2022 16:11:23 +0530 Subject: [PATCH 12/23] fix(linter): Trim Trailing Whitespace --- frappe/public/js/frappe/form/grid_row.js | 4 ++-- package.json | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 7f039ebb06..1054c863d9 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -205,7 +205,7 @@ export default class GridRow { } render_row(refresh) { if (this.show_search && !this.show_search_row()) return; - + let me = this; this.set_row_index(); @@ -257,7 +257,7 @@ export default class GridRow { if (e.target.value == "") { delete this.grid.filter['row-index']; } - + this.grid.grid_sortable .option('disabled', Object.keys(this.grid.filter).length !== 0); diff --git a/package.json b/package.json index 259a157311..4326ddb226 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,9 @@ }, "homepage": "https://frappeframework.com", "dependencies": { + "@cypress/code-coverage": "^3", "@editorjs/editorjs": "2.20.0", + "@testing-library/cypress": "^8", "ace-builds": "^1.4.8", "air-datepicker": "github:frappe/air-datepicker", "autoprefixer": "^9.8.6", @@ -31,6 +33,8 @@ "cookie": "^0.4.0", "cropperjs": "^1.5.12", "cssnano": "^5.0.0", + "cypress": "^6", + "cypress-file-upload": "^5", "driver.js": "^0.9.8", "editorjs-undo": "0.1.6", "express": "^4.17.1", From 9c2feb802378d51f3d0fefbe4e2a47e95add5395 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 7 Mar 2022 18:03:50 +0530 Subject: [PATCH 13/23] test: fix failing grid search UI test: --- cypress/integration/grid_search.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cypress/integration/grid_search.js b/cypress/integration/grid_search.js index 10444b2d2a..45473bdd5c 100644 --- a/cypress/integration/grid_search.js +++ b/cypress/integration/grid_search.js @@ -34,7 +34,7 @@ context('Grid Search', () => { cy.visit(`/app/doctype-with-child-table/Test Grid Search`); - cy.get('[title="child_table_1"]').as('table'); + cy.get('.frappe-control[data-fieldname="child_table_1"]').as('table'); cy.get('@table').find('.grid-row-check:last').click(); cy.get('@table').find('.grid-footer').contains('Delete').click(); cy.get('.grid-heading-row .grid-row .search').should('not.exist'); @@ -43,7 +43,7 @@ context('Grid Search', () => { it('test search field for different fieldtypes', () => { cy.visit(`/app/doctype-with-child-table/Test Grid Search`); - cy.get('[title="child_table_1"]').as('table'); + cy.get('.frappe-control[data-fieldname="child_table_1"]').as('table'); // Index Column cy.get('@table').find('.grid-heading-row .row-index.search input').type('3'); @@ -86,7 +86,7 @@ context('Grid Search', () => { }); it('test with multiple filter', () => { - cy.get('[title="child_table_1"]').as('table'); + cy.get('.frappe-control[data-fieldname="child_table_1"]').as('table'); // Data Column cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Data"]').type('a'); From eb0c01e5085d26722691974bae28c212253ef68b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 7 Mar 2022 18:24:47 +0530 Subject: [PATCH 14/23] fix: don't show search row if table is empty --- frappe/public/js/frappe/form/grid_row.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 1054c863d9..c07c52678b 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -639,7 +639,8 @@ export default class GridRow { show_search_row() { // show or remove search columns based on grid rows - this.show_search = this.frm.doc[this.grid.df.fieldname].length >= 20; + this.show_search = this.frm.doc[this.grid.df.fieldname] && + this.frm.doc[this.grid.df.fieldname].length >= 20; !this.show_search && this.wrapper.remove(); return this.show_search; } From d9a2b57c44d2c2797351060020131e835a855907 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 7 Mar 2022 18:47:30 +0530 Subject: [PATCH 15/23] test: fixed test with multiple filter UI test --- cypress/integration/dashboard_links.js | 2 ++ cypress/integration/grid_search.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cypress/integration/dashboard_links.js b/cypress/integration/dashboard_links.js index 93d10cf1fd..019de1991d 100644 --- a/cypress/integration/dashboard_links.js +++ b/cypress/integration/dashboard_links.js @@ -1,5 +1,6 @@ import doctype_with_child_table from '../fixtures/doctype_with_child_table'; import child_table_doctype from '../fixtures/child_table_doctype'; +import child_table_doctype_1 from '../fixtures/child_table_doctype_1'; import doctype_to_link from '../fixtures/doctype_to_link'; const doctype_to_link_name = doctype_to_link.name; const child_table_doctype_name = child_table_doctype.name; @@ -9,6 +10,7 @@ context('Dashboard links', () => { cy.visit('/login'); cy.login(); cy.insert_doc('DocType', child_table_doctype, true); + cy.insert_doc('DocType', child_table_doctype_1, true); cy.insert_doc('DocType', doctype_with_child_table, true); cy.insert_doc('DocType', doctype_to_link, true); return cy.window().its('frappe').then(frappe => { diff --git a/cypress/integration/grid_search.js b/cypress/integration/grid_search.js index 45473bdd5c..d30545a2e1 100644 --- a/cypress/integration/grid_search.js +++ b/cypress/integration/grid_search.js @@ -101,7 +101,7 @@ context('Grid Search', () => { cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 5); // Date Column - cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').type('-02'); + cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').type('02-'); cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 2); }); }); \ No newline at end of file From 7d14c26a9571fd6f68c252dbb8a0ee9623e7e9d2 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 7 Mar 2022 18:54:33 +0530 Subject: [PATCH 16/23] revert: dashboard_links UI test fix --- cypress/integration/dashboard_links.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/cypress/integration/dashboard_links.js b/cypress/integration/dashboard_links.js index 019de1991d..93d10cf1fd 100644 --- a/cypress/integration/dashboard_links.js +++ b/cypress/integration/dashboard_links.js @@ -1,6 +1,5 @@ import doctype_with_child_table from '../fixtures/doctype_with_child_table'; import child_table_doctype from '../fixtures/child_table_doctype'; -import child_table_doctype_1 from '../fixtures/child_table_doctype_1'; import doctype_to_link from '../fixtures/doctype_to_link'; const doctype_to_link_name = doctype_to_link.name; const child_table_doctype_name = child_table_doctype.name; @@ -10,7 +9,6 @@ context('Dashboard links', () => { cy.visit('/login'); cy.login(); cy.insert_doc('DocType', child_table_doctype, true); - cy.insert_doc('DocType', child_table_doctype_1, true); cy.insert_doc('DocType', doctype_with_child_table, true); cy.insert_doc('DocType', doctype_to_link, true); return cy.window().its('frappe').then(frappe => { From 6526e48e0a2a2081d0d7980de6632f8e99d45c3b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 7 Mar 2022 18:57:23 +0530 Subject: [PATCH 17/23] revert: package.json file updated with cypress --- package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/package.json b/package.json index 4326ddb226..259a157311 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,7 @@ }, "homepage": "https://frappeframework.com", "dependencies": { - "@cypress/code-coverage": "^3", "@editorjs/editorjs": "2.20.0", - "@testing-library/cypress": "^8", "ace-builds": "^1.4.8", "air-datepicker": "github:frappe/air-datepicker", "autoprefixer": "^9.8.6", @@ -33,8 +31,6 @@ "cookie": "^0.4.0", "cropperjs": "^1.5.12", "cssnano": "^5.0.0", - "cypress": "^6", - "cypress-file-upload": "^5", "driver.js": "^0.9.8", "editorjs-undo": "0.1.6", "express": "^4.17.1", From 9ec61293c0c11a06fc6ea909b908f6e9226010d8 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 7 Mar 2022 19:24:18 +0530 Subject: [PATCH 18/23] test: failing dashboard_links UI test fix --- cypress/integration/dashboard_links.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cypress/integration/dashboard_links.js b/cypress/integration/dashboard_links.js index 93d10cf1fd..019de1991d 100644 --- a/cypress/integration/dashboard_links.js +++ b/cypress/integration/dashboard_links.js @@ -1,5 +1,6 @@ import doctype_with_child_table from '../fixtures/doctype_with_child_table'; import child_table_doctype from '../fixtures/child_table_doctype'; +import child_table_doctype_1 from '../fixtures/child_table_doctype_1'; import doctype_to_link from '../fixtures/doctype_to_link'; const doctype_to_link_name = doctype_to_link.name; const child_table_doctype_name = child_table_doctype.name; @@ -9,6 +10,7 @@ context('Dashboard links', () => { cy.visit('/login'); cy.login(); cy.insert_doc('DocType', child_table_doctype, true); + cy.insert_doc('DocType', child_table_doctype_1, true); cy.insert_doc('DocType', doctype_with_child_table, true); cy.insert_doc('DocType', doctype_to_link, true); return cy.window().its('frappe').then(frappe => { From 533434d1aec82ba54cf6b71888a82856f502b558 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 10 Mar 2022 14:31:32 +0530 Subject: [PATCH 19/23] fix: Last grid column has unnecessary margin --- frappe/public/js/frappe/form/grid_row.js | 3 ++- frappe/public/scss/common/grid.scss | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index c07c52678b..be14bcdff7 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -639,7 +639,8 @@ export default class GridRow { show_search_row() { // show or remove search columns based on grid rows - this.show_search = this.frm.doc[this.grid.df.fieldname] && + this.show_search = this.frm && this.frm.doc && + this.frm.doc[this.grid.df.fieldname] && this.frm.doc[this.grid.df.fieldname].length >= 20; !this.show_search && this.wrapper.remove(); return this.show_search; diff --git a/frappe/public/scss/common/grid.scss b/frappe/public/scss/common/grid.scss index 926dcc8923..d1f89abbcd 100644 --- a/frappe/public/scss/common/grid.scss +++ b/frappe/public/scss/common/grid.scss @@ -147,7 +147,6 @@ .grid-row > .row { .col:last-child { - margin-right: calc(-1 * var(--margin-sm)); border-right: none; } From 54c0fee9a228374359d2dde1f7d83308894f1301 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 10 Mar 2022 14:35:05 +0530 Subject: [PATCH 20/23] fix(linter): Trim Trailing Whitespace --- frappe/public/js/frappe/form/grid_row.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index be14bcdff7..403807eaff 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -639,7 +639,7 @@ export default class GridRow { show_search_row() { // show or remove search columns based on grid rows - this.show_search = this.frm && this.frm.doc && + this.show_search = this.frm && this.frm.doc && this.frm.doc[this.grid.df.fieldname] && this.frm.doc[this.grid.df.fieldname].length >= 20; !this.show_search && this.wrapper.remove(); From cd3950401c06fba274f96025608cb09fc889c128 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 15 Mar 2022 17:22:03 +0530 Subject: [PATCH 21/23] fix: using debounce instead of setTimeout() --- frappe/public/js/frappe/form/grid_row.js | 72 +++++++++++------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 403807eaff..c12ac23319 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -231,7 +231,6 @@ export default class GridRow { } }); } else if (this.show_search) { - let timer = null; this.row_check = $( `` ).appendTo(this.row); @@ -242,30 +241,27 @@ export default class GridRow { ` ).appendTo(this.row); - this.row_index.find('input').on('keyup', (e) => { - clearTimeout(timer); - timer = setTimeout(() => { - let df = { - fieldtype: "Sr No" - }; + this.row_index.find('input').on('keyup', frappe.utils.debounce((e) => { + let df = { + fieldtype: "Sr No" + }; - this.grid.filter['row-index'] = { - df: df, - value: e.target.value - }; + this.grid.filter['row-index'] = { + df: df, + value: e.target.value + }; - if (e.target.value == "") { - delete this.grid.filter['row-index']; - } + if (e.target.value == "") { + delete this.grid.filter['row-index']; + } - this.grid.grid_sortable - .option('disabled', Object.keys(this.grid.filter).length !== 0); + this.grid.grid_sortable + .option('disabled', Object.keys(this.grid.filter).length !== 0); - this.grid.prevent_build = true; - me.grid.refresh(); - this.grid.prevent_build = false; - }, 500); - }); + this.grid.prevent_build = true; + me.grid.refresh(); + this.grid.prevent_build = false; + }, 500)); frappe.utils.only_allow_num_decimal(this.row_index.find('input')); } else { this.row_index.find('span').html(txt); @@ -647,7 +643,6 @@ export default class GridRow { } make_search_column(df, colsize) { - let timer = null; let title = ""; let input_class = ""; let is_disabled = ""; @@ -679,28 +674,25 @@ export default class GridRow { this.search_columns[df.fieldname] = $col; - $search_input.on('keyup', (e) => { - clearTimeout(timer); - timer = setTimeout(() => { - this.grid.filter[df.fieldname] = { - df: df, - value: e.target.value - }; + $search_input.on('keyup', frappe.utils.debounce((e) => { + this.grid.filter[df.fieldname] = { + df: df, + value: e.target.value + }; - if (e.target.value == '') { - delete this.grid.filter[df.fieldname]; - } + if (e.target.value == '') { + delete this.grid.filter[df.fieldname]; + } - this.grid.grid_sortable - .option('disabled', Object.keys(this.grid.filter).length !== 0); + this.grid.grid_sortable + .option('disabled', Object.keys(this.grid.filter).length !== 0); - this.grid.prevent_build = true; - this.grid.refresh(); - this.grid.prevent_build = false; - }, 500); - }); + this.grid.prevent_build = true; + this.grid.refresh(); + this.grid.prevent_build = false; + }, 500)); - ["Currency", "Float", "Int", "Percent", "Rating", "Check"].includes(df.fieldtype) && + ["Currency", "Float", "Int", "Percent", "Rating"].includes(df.fieldtype) && frappe.utils.only_allow_num_decimal($search_input); return $col; From 5a1bc4b1d61db755b2196da98bd9cea7144a198e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 15 Mar 2022 17:23:46 +0530 Subject: [PATCH 22/23] fix: use boolean strings (T, F, true, false, 1, 0 etc) for Check fieldtype --- frappe/public/js/frappe/form/grid.js | 3 ++- frappe/public/js/frappe/utils/utils.js | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 36461fb671..be20676183 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -549,7 +549,8 @@ export default class Grid { let fieldvalue = data[fieldname]; if (fieldtype === "Check") { - return (fieldvalue === parseInt(value || 0)) && data; + value = frappe.utils.string_to_boolean(value); + return (Boolean(fieldvalue) === value) && data; } else if (fieldtype === "Sr No" && data.idx.toString().includes(value)) { return data; } else if (fieldtype === "Duration" && fieldvalue) { diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 339917ed77..b253f4da54 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -1463,5 +1463,13 @@ Object.assign(frappe.utils, { e.preventDefault(); } }); + }, + + string_to_boolean(string) { + switch(string.toLowerCase().trim()){ + case "t": case "true": case "y": case "yes": case "1": return true; + case "f": case "false": case "n": case "no": case "0": case null: return false; + default: return string; + } } }); From cac1fd40d4d74508294896743603cd883235dbf2 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 15 Mar 2022 17:39:02 +0530 Subject: [PATCH 23/23] fix(sider): expected space(s) --- frappe/public/js/frappe/utils/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index b253f4da54..03f3204abb 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -1466,7 +1466,7 @@ Object.assign(frappe.utils, { }, string_to_boolean(string) { - switch(string.toLowerCase().trim()){ + switch (string.toLowerCase().trim()) { case "t": case "true": case "y": case "yes": case "1": return true; case "f": case "false": case "n": case "no": case "0": case null: return false; default: return string;