From f2b5c3f60fd16bbdc3a9eb08b2a33eea70785265 Mon Sep 17 00:00:00 2001 From: sokumon Date: Fri, 6 Feb 2026 15:17:59 +0530 Subject: [PATCH] feat: show description on click --- frappe/core/doctype/docfield/docfield.json | 11 +++- frappe/core/doctype/docfield/docfield.py | 3 +- .../js/frappe/form/controls/base_input.js | 59 ++++++++++++++++++- .../js/frappe/ui/sidebar/sidebar_card.html | 13 ++++ .../js/frappe/ui/sidebar/sidebar_card.js | 3 + frappe/public/scss/common/global.scss | 12 ++++ frappe/public/scss/desk/sidebar_card.scss | 9 +++ 7 files changed, 105 insertions(+), 5 deletions(-) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 3acf1d5ea8..fe7f5e78ef 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -82,7 +82,8 @@ "documentation_url", "placeholder", "oldfieldname", - "oldfieldtype" + "oldfieldtype", + "show_description_on_click" ], "fields": [ { @@ -633,6 +634,12 @@ "fieldtype": "Select", "label": "Button Color", "options": "\nDefault\nPrimary\nInfo\nSuccess\nWarning\nDanger" + }, + { + "default": "0", + "fieldname": "show_description_on_click", + "fieldtype": "Check", + "label": "Show Description on Click" } ], "grid_page_length": 50, @@ -640,7 +647,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2026-01-06 01:37:29.723265", + "modified": "2026-02-06 15:13:03.688027", "modified_by": "Administrator", "module": "Core", "name": "DocField", diff --git a/frappe/core/doctype/docfield/docfield.py b/frappe/core/doctype/docfield/docfield.py index 94d99fe5e9..b97291b754 100644 --- a/frappe/core/doctype/docfield/docfield.py +++ b/frappe/core/doctype/docfield/docfield.py @@ -14,10 +14,10 @@ class DocField(Document): if TYPE_CHECKING: from frappe.types import DF + alignment: DF.Literal["", "Left", "Center", "Right"] allow_bulk_edit: DF.Check allow_in_quick_entry: DF.Check allow_on_submit: DF.Check - alignment: DF.Literal["", "Left", "Center", "Right"] bold: DF.Check button_color: DF.Literal["", "Default", "Primary", "Info", "Success", "Warning", "Danger"] collapsible: DF.Check @@ -117,6 +117,7 @@ class DocField(Document): search_index: DF.Check set_only_once: DF.Check show_dashboard: DF.Check + show_description_on_click: DF.Check show_on_timeline: DF.Check sort_options: DF.Check sticky: DF.Check diff --git a/frappe/public/js/frappe/form/controls/base_input.js b/frappe/public/js/frappe/form/controls/base_input.js index 3cd277a239..7daa4ce30b 100644 --- a/frappe/public/js/frappe/form/controls/base_input.js +++ b/frappe/public/js/frappe/form/controls/base_input.js @@ -1,3 +1,4 @@ +import { createPopper } from "@popperjs/core"; frappe.ui.form.ControlInput = class ControlInput extends frappe.ui.form.Control { static horizontal = true; make() { @@ -7,7 +8,7 @@ frappe.ui.form.ControlInput = class ControlInput extends frappe.ui.form.Control // set description this.set_max_width(); - + this.info_card_display = false; // set initial value if set if (this.df.initial_value) { this.set_value(this.df.initial_value); @@ -147,6 +148,7 @@ frappe.ui.form.ControlInput = class ControlInput extends frappe.ui.form.Control me.set_description(); me.set_label(); me.set_doc_url(); + // me.set_info_url(); me.set_mandatory(me.value); me.set_bold(); me.set_required(); @@ -189,6 +191,7 @@ frappe.ui.form.ControlInput = class ControlInput extends frappe.ui.form.Control } } set_label(label) { + const me = this; if (label) this.df.label = label; if (this.only_input || this.df.label == this._label) return; @@ -197,14 +200,64 @@ frappe.ui.form.ControlInput = class ControlInput extends frappe.ui.form.Control this.label_span.innerHTML = (icon ? ' ' : "") + __(this.df.label, null, this.df.parent) || " "; + if (this.df.show_description_on_click) { + $(`${frappe.utils.icon("message-circle-question-mark", "sm")}`).appendTo( + $(this.label_span) + ); + this.$info_card = $("
").appendTo(this.label_span); + $(this.label_area).css({ + display: "flex", + gap: "6px", + "align-items": "center", + "white-space": "nowrap", + }); + let popper = createPopper( + $(this.label_span).find("a").get(0), + this.$info_card.get(0), + { + modifiers: [ + { + name: "offset", + options: { + offset: [0, 8], + }, + }, + ], + } + ); + $(this.label_span) + .find("a") + .on("click", (event) => { + event.preventDefault(); + me.$info_card.html(""); + let card = new frappe.ui.SidebarCard({ + // title: "Trial ends in 3 days", + // icon: "info", + message: me.df.description, + parent: me.$info_card, + // primary_action_icon: "zap", + // primary_action_label: "Upgrade", + close_button: true, + // primary_action: () => {}, + }); + if (me.info_card_display) { + me.info_card_display = false; + me.$info_card.removeAttr("data-show"); + } else { + me.info_card_display = true; + me.$info_card.attr("data-show", ""); + popper.update(); + } + }); + } this._label = this.df.label; } set_doc_url() { + if (this.df.show_description_on_click) return; let unsupported_fieldtypes = frappe.model.no_value_type.filter( (x) => frappe.model.table_fields.indexOf(x) === -1 ); - if ( !this.df.label || !this.df?.documentation_url || @@ -214,6 +267,7 @@ frappe.ui.form.ControlInput = class ControlInput extends frappe.ui.form.Control let $help = this.$wrapper.find("span.help"); $help.empty(); + $(` + {% if(!card.close_button) { %}
{%= frappe.utils.icon(card.icon, "sm", "", "", "card-icon") %} @@ -6,5 +7,17 @@
+ {% } else { %} +
+ {%= frappe.utils.icon(card.icon, "sm", "", "", "card-icon") %} + + + + + {%= frappe.utils.icon("x","sm", "", "", "card-icon") %} +
+ {% } %} + {% if(card.primary_action_label) { %} + {% } %} \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar_card.js b/frappe/public/js/frappe/ui/sidebar/sidebar_card.js index feb9005e1c..1fed679626 100644 --- a/frappe/public/js/frappe/ui/sidebar/sidebar_card.js +++ b/frappe/public/js/frappe/ui/sidebar/sidebar_card.js @@ -8,6 +8,9 @@ frappe.ui.SidebarCard = class SidebarCard { this.setup(); } make() { + if (!this.icon) { + this.icon = "info"; + } this.card = $( frappe.render_template("sidebar_card", { card: this, diff --git a/frappe/public/scss/common/global.scss b/frappe/public/scss/common/global.scss index cc18d7622a..4e80af9ecc 100644 --- a/frappe/public/scss/common/global.scss +++ b/frappe/public/scss/common/global.scss @@ -174,3 +174,15 @@ body { .margin-right { margin-right: var(--margin-sm); } +.info-card { + display: none; +} + +.info-card[data-show] { + display: block; + z-index: 1; + .sidebar-card { + box-shadow: 0px 18px 22px -6px rgba(0, 0, 0, 0.1), 0px 0px 6px 3px rgba(0, 0, 0, 0.03), + 0px 0px 1.5px 0px rgba(0, 0, 0, 0.18); + } +} diff --git a/frappe/public/scss/desk/sidebar_card.scss b/frappe/public/scss/desk/sidebar_card.scss index ecf2a595df..b888803cbb 100644 --- a/frappe/public/scss/desk/sidebar_card.scss +++ b/frappe/public/scss/desk/sidebar_card.scss @@ -10,6 +10,14 @@ @include get_textstyle("base", "medium"); } } +.card-close-button { + align-items: normal; + .card-icon { + flex-shrink: 0; + margin-top: 2px; + margin-bottom: 2px; + } +} .sidebar-card { width: 100%; background-color: var(--surface-modal); @@ -19,6 +27,7 @@ } .sidebar-card-description { @include get_textstyle("sm", "regular"); + white-space: wrap; } .sidebar-card-button { display: flex;