diff --git a/web/pgadmin/misc/static/explain/js/explain.js b/web/pgadmin/misc/static/explain/js/explain.js index 0cf78abff..da595e6b3 100644 --- a/web/pgadmin/misc/static/explain/js/explain.js +++ b/web/pgadmin/misc/static/explain/js/explain.js @@ -753,6 +753,12 @@ define('pgadmin.misc.explain', [ return data; }, + getLabel: function() { + return this.get('Schema') == undefined ? + this.get('image_text') : + (this.get('Schema') + '.' + this.get('image_text')); + }, + // Draw image, its name and its tooltip draw: function( s, xpos, ypos, pXpos, pYpos, graphContainer, toolTipContainer, @@ -802,9 +808,7 @@ define('pgadmin.misc.explain', [ // Draw text below the node - var node_label = this.get('Schema') == undefined ? - this.get('image_text') : - (this.get('Schema') + '.' + this.get('image_text')); + var node_label = this.getLabel(); g.multitext( currentXpos + (pWIDTH / 2) + TXT_ALIGN, currentYpos + pHEIGHT - TXT_ALIGN, @@ -943,34 +947,29 @@ define('pgadmin.misc.explain', [ IMAGE_WIDTH, IMAGE_HEIGHT ); + image.attr({style: 'cursor: pointer'}); // Draw tooltip - var image_data = this.toJSON(); - var title = ''; - _.each(image_data, function(value, key) { - if (key !== 'image' && key !== 'Plans' && - key !== 'level' && key !== 'image' && - key !== 'image_text' && key !== 'xpos' && - key !== 'ypos' && key !== 'width' && - key !== 'height') { - title += `${key}: ${value}\n`; - } - }); - - title += ''; - - image.append(Snap.parse(title)); - - image.mouseover(() => { + var image_data = this.toJSON(), + nodeLabel = this.getLabel(); + image.click(() => { // Empty the tooltip content if it has any and add new data - toolTipContainer.empty(); + let toolTipBody = toolTipContainer.find('.details-body'); + let toolTipTitle = toolTipContainer.find('.details-title'); + toolTipTitle.text(nodeLabel); + + toolTipBody.empty(); // Remove the title content so that we can show our custom build tooltips. image.node.textContent = ''; - var tooltip = $('
', { - class: 'pgadmin-tooltip-table', - }).appendTo(toolTipContainer); + var tooltipTable = $(` + + +
` + ).appendTo(toolTipBody); + var tooltip = tooltipTable.find('tbody'); + _.each(image_data, function(value, key) { if (key !== 'image' && key !== 'Plans' && key !== 'level' && key !== 'image' && @@ -987,42 +986,8 @@ define('pgadmin.misc.explain', [ `); } }); - - var zoomFactor = graphContainer.data('zoom-factor'); - - // Calculate co-ordinates for tooltip - var toolTipX = ((currentXpos + pWIDTH) * zoomFactor - graphContainer.scrollLeft()); - var toolTipY = ((currentYpos) * zoomFactor - graphContainer.scrollTop()); - - toolTipX = toolTipX < 0 ? 0 : (toolTipX); - toolTipY = toolTipY < 0 ? 0 : (toolTipY); - - toolTipX = toolTipX > graphContainer.width() - toolTipContainer[0].clientWidth ? toolTipX - (toolTipContainer[0].clientWidth+(pWIDTH* zoomFactor)) : toolTipX; - toolTipY = toolTipY > graphContainer.height() - toolTipContainer[0].clientHeight ? graphContainer.height() - toolTipContainer[0].clientHeight : toolTipY; - - // Show toolTip at respective x,y coordinates - toolTipContainer.css({ - 'opacity': '0.8', - }); - toolTipContainer.css('left', toolTipX); - toolTipContainer.css('top', toolTipY); - - $('.pgadmin-explain-tooltip').css('padding', '5px'); - $('.pgadmin-explain-tooltip').css('border', '1px solid white'); - }); - - // Remove tooltip when mouse is out from node's area - image.mouseout(() => { - /* Append the title again which we have removed on mouse over event, so - * that our custom tooltip should be visible. - */ - image.append(Snap.parse(title)); - toolTipContainer.empty(); - toolTipContainer.css({ - 'opacity': '0', - }); - toolTipContainer.css('left', 0); - toolTipContainer.css('top', 0); + toolTipContainer.removeClass('d-none'); + toolTipBody.scrollTop(0); }); }, }); @@ -1362,16 +1327,23 @@ define('pgadmin.misc.explain', [ // Main div to be drawn all images on var planDiv = $('
', { - class: 'pgadmin-explain-container w-100 h-100 overflow-auto', - }).appendTo(graphicalContainer), - // Div to draw tool-tip on - toolTip = $('
', { - id: 'toolTip', - class: 'pgadmin-explain-tooltip', - }).appendTo(graphicalContainer); - toolTip.empty(); + class: 'pgadmin-explain-container w-100 h-100 overflow-auto', + }).appendTo(graphicalContainer); planDiv.data('zoom-factor', curr_zoom_factor); + var explainDetails = $( + `` + ).appendTo(graphicalContainer); + explainDetails.find('.details-close').on('click', ()=>{ + explainDetails.addClass('d-none'); + }); + var w = 0, h = yMargin; @@ -1420,7 +1392,7 @@ define('pgadmin.misc.explain', [ var s = Snap(w, h), $svg = $(s.node).detach(); planDiv.append($svg); - main_plan.draw(s, w - xMargin, yMargin, planDiv, toolTip, ctx); + main_plan.draw(s, w - xMargin, yMargin, planDiv, explainDetails, ctx); var initPanelWidth = planDiv.width(); diff --git a/web/pgadmin/misc/static/explain/js/explain_statistics.js b/web/pgadmin/misc/static/explain/js/explain_statistics.js index 6f4dd306d..b918fe98f 100644 --- a/web/pgadmin/misc/static/explain/js/explain_statistics.js +++ b/web/pgadmin/misc/static/explain/js/explain_statistics.js @@ -30,12 +30,15 @@ let StatisticsModel = Backbone.Model.extend({ $('.pg-explain-stats-area').removeClass('d-none'); } - var tooltip = $('
', { - class: 'pgadmin-tooltip-table', - }); + var tooltipTable = $(` + + +
` + ); + var tooltip = tooltipTable.find('tbody'); if (Object.keys(jit_stats).length > 0){ - tooltip.append('' + gettext('JIT:') + ''); + tooltip.append('' + gettext('JIT:') + ''); _.each(jit_stats, function(value, key) { key = _.escape(key); value = _.escape(value); @@ -49,7 +52,7 @@ let StatisticsModel = Backbone.Model.extend({ } if (Object.keys(triggers_stats).length > 0){ - tooltip.append('' + gettext('Triggers:') + ''); + tooltip.append('' + gettext('Triggers:') + ''); _.each(triggers_stats, function(triggers, key_id) { if (triggers instanceof Object) { _.each(triggers, function(value, key) { @@ -88,7 +91,7 @@ let StatisticsModel = Backbone.Model.extend({ } if (Object.keys(summary).length > 0){ - tooltip.append('' + gettext('Summary:') + ''); + tooltip.append('' + gettext('Summary:') + ''); _.each(summary, function(value, key) { key = _.escape(key); value = _.escape(value); @@ -101,7 +104,7 @@ let StatisticsModel = Backbone.Model.extend({ }); } - $('.pg-explain-stats-area').off('mouseover').on('mouseover', () => { + $('.pg-explain-stats-area').off('click').on('click', () => { // Empty the tooltip content if it has any and add new data if (Object.keys(jit_stats).length == 0 && @@ -110,30 +113,15 @@ let StatisticsModel = Backbone.Model.extend({ return; } - toolTipContainer.empty(); - toolTipContainer.append(tooltip); + let toolTipBody = toolTipContainer.find('.details-body'); + let toolTipTitle = toolTipContainer.find('.details-title'); + toolTipTitle.text('Statistics'); - // Show toolTip at respective x,y coordinates - toolTipContainer.css({ - 'opacity': '0.8', - 'left': '', - 'right': '65px', - 'top': '15px', - }); + toolTipBody.empty(); + toolTipBody.append(tooltipTable); - $('.pgadmin-explain-tooltip').css('padding', '5px'); - $('.pgadmin-explain-tooltip').css('border', '1px solid white'); - }); - - // Remove tooltip when mouse is out from node's area - $('.pg-explain-stats-area').off('mouseout').on('mouseout', () => { - toolTipContainer.empty(); - toolTipContainer.css({ - 'opacity': '0', - 'left': 0, - 'top': 0, - 'right': '', - }); + toolTipContainer.removeClass('d-none'); + toolTipBody.scrollTop(0); }); }, }); diff --git a/web/pgadmin/misc/static/explain/scss/_explain.scss b/web/pgadmin/misc/static/explain/scss/_explain.scss index 095372ec5..458dca075 100644 --- a/web/pgadmin/misc/static/explain/scss/_explain.scss +++ b/web/pgadmin/misc/static/explain/scss/_explain.scss @@ -1,12 +1,29 @@ -.pgadmin-explain-tooltip { +.pgadmin-explain-details { + min-width: 200px; + max-width: 300px; position: absolute; - opacity: 0; - color: $popover-body-color; - background-color: $popover-bg; + top: 0.25rem; + bottom: 0.25rem; + right: 0.25rem; border-color: $popover-border-color; box-shadow: $popover-box-shadow; + word-break: break-all; + display: flex; + flex-direction: column; + z-index: 99; + + .details-header { + padding: 0.25rem; + font-weight: unset; + } + + .details-body { + overflow: auto; + flex-grow: 1; + } } + .sql-editor-explain { .backform-tab { .tab-content { diff --git a/web/regression/javascript/misc/explain/explain_statistics_spec.js b/web/regression/javascript/misc/explain/explain_statistics_spec.js index e33194bda..5ad146765 100644 --- a/web/regression/javascript/misc/explain/explain_statistics_spec.js +++ b/web/regression/javascript/misc/explain/explain_statistics_spec.js @@ -18,10 +18,14 @@ describe('ExplainStatistics', () => { beforeEach(function() { statsModel = new StatisticsModel(); statsDiv = '
'; - tooltipContainer = $('
', { - id: 'toolTip', - class: 'pgadmin-explain-tooltip', - }); + tooltipContainer = $( + `
+
+
+
+
+
` + ); }); describe('No Statistics', () => { @@ -49,20 +53,12 @@ describe('ExplainStatistics', () => { expect($('.pg-explain-stats-area').hasClass('d-none')).toEqual(false); }); - it('Mouse over event should be trigger', () => { + it('Mouse click event should be trigger', () => { // Trigger mouse over event - var hoverEvent = new $.Event('mouseover'); - $('.pg-explain-stats-area').trigger(hoverEvent); + var clickEvent = new $.Event('click'); + $('.pg-explain-stats-area').trigger(clickEvent); - expect(tooltipContainer.css('opacity')).toEqual('0.8'); - }); - - it('Mouse out event should be trigger', () => { - // Trigger mouse out event - var hoverEvent = new $.Event('mouseout'); - $('.pg-explain-stats-area').trigger(hoverEvent); - - expect(tooltipContainer.css('opacity')).toEqual('0'); + expect(tooltipContainer.hasClass('d-none')).toBe(false); }); }); @@ -78,20 +74,12 @@ describe('ExplainStatistics', () => { expect($('.pg-explain-stats-area').hasClass('d-none')).toEqual(false); }); - it('Mouse over event should be trigger', () => { + it('Mouse click event should be trigger', () => { // Trigger mouse over event - var hoverEvent = new $.Event('mouseover'); - $('.pg-explain-stats-area').trigger(hoverEvent); - - expect(tooltipContainer.css('opacity')).toEqual('0.8'); - }); - - it('Mouse out event should be trigger', () => { - // Trigger mouse out event - var hoverEvent = new $.Event('mouseout'); - $('.pg-explain-stats-area').trigger(hoverEvent); + var clickEvent = new $.Event('click'); + $('.pg-explain-stats-area').trigger(clickEvent); - expect(tooltipContainer.css('opacity')).toEqual('0'); + expect(tooltipContainer.hasClass('d-none')).toBe(false); }); }); @@ -111,20 +99,12 @@ describe('ExplainStatistics', () => { expect($('.pg-explain-stats-area').hasClass('d-none')).toEqual(false); }); - it('Mouse over event should be trigger', () => { + it('Mouse click event should be trigger', () => { // Trigger mouse over event - var hoverEvent = new $.Event('mouseover'); - $('.pg-explain-stats-area').trigger(hoverEvent); - - expect(tooltipContainer.css('opacity')).toEqual('0.8'); - }); - - it('Mouse out event should be trigger', () => { - // Trigger mouse out event - var hoverEvent = new $.Event('mouseout'); - $('.pg-explain-stats-area').trigger(hoverEvent); + var clickEvent = new $.Event('click'); + $('.pg-explain-stats-area').trigger(clickEvent); - expect(tooltipContainer.css('opacity')).toEqual('0'); + expect(tooltipContainer.hasClass('d-none')).toBe(false); }); }); });