From 4afdeb4dd3381ac84693e36381c5f21e11d1a4ff Mon Sep 17 00:00:00 2001 From: Ejaaz Khan Date: Thu, 4 Dec 2025 01:02:37 +0530 Subject: [PATCH 1/5] feat: show search on Nav on mobile devices --- frappe/desk/page/desktop/desktop.css | 6 +++--- frappe/desk/page/desktop/desktop.html | 2 +- frappe/public/js/frappe/ui/sidebar/sidebar.js | 10 ++++++---- frappe/public/js/frappe/ui/toolbar/navbar.html | 15 +++++++++++++++ frappe/public/scss/desk/navbar.scss | 17 +++++++++-------- 5 files changed, 34 insertions(+), 16 deletions(-) diff --git a/frappe/desk/page/desktop/desktop.css b/frappe/desk/page/desktop/desktop.css index 5437905d59..be29818896 100644 --- a/frappe/desk/page/desktop/desktop.css +++ b/frappe/desk/page/desktop/desktop.css @@ -41,9 +41,9 @@ .desktop-navbar-modal-search{ background-color: var(--control-bg); - border-radius: var(--border-radius-sm); - padding: 6px 10px; - width: 100%; + border-radius: var(--border-radius-sm); + padding: 6px 10px; + width: 100%; } #brand-logo{ width: auto; diff --git a/frappe/desk/page/desktop/desktop.html b/frappe/desk/page/desktop/desktop.html index 3be439ba07..8606cf3bfd 100644 --- a/frappe/desk/page/desktop/desktop.html +++ b/frappe/desk/page/desktop/desktop.html @@ -16,7 +16,7 @@ > - Search + {{ _("Search") }} {{ "⌘ K" if is_mac else "Ctrl K" }} diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar.js b/frappe/public/js/frappe/ui/sidebar/sidebar.js index 1b4a37b301..80a7004b73 100644 --- a/frappe/public/js/frappe/ui/sidebar/sidebar.js +++ b/frappe/public/js/frappe/ui/sidebar/sidebar.js @@ -234,8 +234,9 @@ frappe.ui.Sidebar = class Sidebar { } add_standard_items(items) { if (this.standard_items_setup) return; - this.standard_items = [ - { + this.standard_items = []; + if (!frappe.is_mobile()) { + this.standard_items.push({ label: "Search", icon: "search", type: "Button", @@ -243,8 +244,9 @@ frappe.ui.Sidebar = class Sidebar { suffix: { keyboard_shortcut: "CtrlK", }, - }, - ]; + class: "navbar-search-bar hidden", + }); + } this.standard_items.forEach((w) => { this.add_item(this.$standard_items_sections, w); }); diff --git a/frappe/public/js/frappe/ui/toolbar/navbar.html b/frappe/public/js/frappe/ui/toolbar/navbar.html index 51987e2053..d2facd7485 100644 --- a/frappe/public/js/frappe/ui/toolbar/navbar.html +++ b/frappe/public/js/frappe/ui/toolbar/navbar.html @@ -10,6 +10,21 @@ {% } else { %} {% let icon = item.icon %} From 75855b1ddaae181592c0e4dd679a604ad38033c7 Mon Sep 17 00:00:00 2001 From: sokumon Date: Thu, 4 Dec 2025 02:49:46 +0530 Subject: [PATCH 4/5] feat: add support for condition in menu --- frappe/public/js/frappe/ui/menu.js | 9 ++++- .../js/frappe/ui/sidebar/sidebar_header.js | 36 +++++++++++-------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/frappe/public/js/frappe/ui/menu.js b/frappe/public/js/frappe/ui/menu.js index 93d846feaf..330095832f 100644 --- a/frappe/public/js/frappe/ui/menu.js +++ b/frappe/public/js/frappe/ui/menu.js @@ -14,7 +14,14 @@ frappe.ui.menu = class ContextMenu { this.template.empty(); this.menu_items.forEach((f) => { - this.add_menu_item(f); + f.condition = + f.condition || + function () { + return true; + }; + if (f.condition()) { + this.add_menu_item(f); + } }); // if (!$.contains(document.body, this.template[0])) { diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar_header.js b/frappe/public/js/frappe/ui/sidebar/sidebar_header.js index ae19db7ef6..84f0b5d3ed 100644 --- a/frappe/public/js/frappe/ui/sidebar/sidebar_header.js +++ b/frappe/public/js/frappe/ui/sidebar/sidebar_header.js @@ -5,13 +5,16 @@ frappe.ui.SidebarHeader = class SidebarHeader { this.drop_down_expanded = false; this.title = this.sidebar.sidebar_title; const me = this; - this.fetch; + this.sibling_workspaces = this.fetch_sibling_workspaces(); this.dropdown_items = [ { name: "workspaces", label: "Workspaces", icon: "wallpaper", - items: this.fetch_sibling_workspaces(), + condition: function () { + return me.sibling_workspaces.length > 0; + }, + items: this.sibling_workspaces, }, { name: "desktop", @@ -46,18 +49,23 @@ frappe.ui.SidebarHeader = class SidebarHeader { } fetch_sibling_workspaces() { let sibling_workspaces = []; - let workspaces = frappe.current_app.workspaces; - workspaces.splice(workspaces.indexOf(this.title), 1); - workspaces.forEach((w) => { - let item = { - name: w.toLowerCase(), - label: w, - icon: "wallpaper", - url: frappe.utils.generate_route({ type: "Workspace", route: w.toLowerCase() }), - }; - sibling_workspaces.push(item); - }); - return sibling_workspaces; + if (frappe.current_app) { + let workspaces = [...frappe.current_app.workspaces]; + workspaces.splice(workspaces.indexOf(this.title), 1); + workspaces.forEach((w) => { + let item = { + name: w.toLowerCase(), + label: w, + icon: "wallpaper", + url: frappe.utils.generate_route({ + type: "Workspace", + route: w.toLowerCase(), + }), + }; + sibling_workspaces.push(item); + }); + return sibling_workspaces; + } } make() { $(".sidebar-header").remove(); From 8b8c2c800e25c69d2fc03313ba659dced422dbe3 Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Thu, 4 Dec 2025 10:51:07 +0530 Subject: [PATCH 5/5] chore: remove extra parameter `form_doctype` from `search_link` --- frappe/desk/search.py | 14 ++++---------- frappe/public/js/frappe/form/controls/link.js | 1 - frappe/tests/test_search.py | 14 +++++++------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/frappe/desk/search.py b/frappe/desk/search.py index f928aadf7c..dc23bf5778 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -44,7 +44,6 @@ def search_link( reference_doctype: str | None = None, ignore_user_permissions: bool = False, *, - form_doctype: str | None = None, link_fieldname: str | None = None, ) -> list[LinkSearchResults]: results = search_widget( @@ -56,7 +55,6 @@ def search_link( filters=filters, reference_doctype=reference_doctype, ignore_user_permissions=ignore_user_permissions, - form_doctype=form_doctype, link_fieldname=link_fieldname, ) return build_for_autosuggest(results, doctype=doctype) @@ -77,18 +75,15 @@ def search_widget( reference_doctype: str | None = None, ignore_user_permissions: bool = False, *, - form_doctype: str | None = None, link_fieldname: str | None = None, ): if ignore_user_permissions: - if form_doctype and link_fieldname: - validate_ignore_user_permissions(form_doctype, link_fieldname, doctype) + if reference_doctype and link_fieldname: + validate_ignore_user_permissions(reference_doctype, link_fieldname, doctype) else: frappe.logger().error( - "setting ignore_user_permissions=True in search_link requires " - "form_doctype and link_fieldname to be set. " - f"Got form_doctype={form_doctype}, link_fieldname={link_fieldname}. " - "Ignoring flag." + "setting ignore_user_permissions=True requires reference_doctype and link_fieldname to be set. " + f"Got reference_doctype={reference_doctype}, link_fieldname={link_fieldname}. Ignoring flag." ) ignore_user_permissions = False @@ -122,7 +117,6 @@ def search_widget( as_dict=as_dict, reference_doctype=reference_doctype, ignore_user_permissions=ignore_user_permissions, - form_doctype=form_doctype, link_fieldname=link_fieldname, ) except (frappe.PermissionError, frappe.AppNotInstalledError, ImportError): diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index abb5e6ae21..b04a535b1e 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -359,7 +359,6 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat ignore_user_permissions: this.df.ignore_user_permissions, reference_doctype: this.get_reference_doctype() || "", page_length: cint(frappe.boot.sysdefaults?.link_field_results_limit) || 10, - form_doctype: this.doctype, link_fieldname: this.df.fieldname, }; diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py index 6689fc400d..ad42bc3abb 100644 --- a/frappe/tests/test_search.py +++ b/frappe/tests/test_search.py @@ -261,25 +261,25 @@ class TestSearch(IntegrationTestCase): self.assertIn(allowed_doc.name, result_values) self.assertNotIn(restricted_doc.name, result_values) - # With ignore_user_permissions + form_doctype + link_fieldname, both should be returned + # With ignore_user_permissions + reference_doctype + link_fieldname, both should be returned results_with_ignore = search_link( doctype="Test Search Linked", txt="Document", ignore_user_permissions=True, - form_doctype="Test Search Form", + reference_doctype="Test Search Form", link_fieldname="linked_doc", ) result_values = [r["value"] for r in results_with_ignore] self.assertIn(allowed_doc.name, result_values) self.assertIn(restricted_doc.name, result_values) - # With ignore_user_permissions=True but WITHOUT form_doctype/link_fieldname, + # With ignore_user_permissions=True but WITHOUT reference_doctype/link_fieldname, # the flag should be silently ignored and user permissions should apply results_without_context = search_link( doctype="Test Search Linked", txt="Document", ignore_user_permissions=True, - # form_doctype and link_fieldname not provided + # reference_doctype and link_fieldname not provided ) result_values = [r["value"] for r in results_without_context] self.assertIn(allowed_doc.name, result_values) @@ -334,7 +334,7 @@ class TestSearch(IntegrationTestCase): doctype="Test Search Linked2", txt="test", ignore_user_permissions=True, - form_doctype="Test Search Form No Ignore", + reference_doctype="Test Search Form No Ignore", link_fieldname="linked_doc", ) @@ -345,7 +345,7 @@ class TestSearch(IntegrationTestCase): doctype="Test Search Linked2", txt="test", ignore_user_permissions=True, - form_doctype="Test Search Form No Ignore", + reference_doctype="Test Search Form No Ignore", link_fieldname="nonexistent_field", ) @@ -370,7 +370,7 @@ class TestSearch(IntegrationTestCase): doctype="Test Search Linked2", txt="test", ignore_user_permissions=True, - form_doctype="Test Search Form Wrong Link", + reference_doctype="Test Search Form Wrong Link", link_fieldname="wrong_link", )