Skip to content

Commit

Permalink
feat(core): add title truncation to tooltip
Browse files Browse the repository at this point in the history
  • Loading branch information
natashadecoste committed Oct 29, 2019
1 parent e515734 commit bdd677c
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 105 deletions.
21 changes: 12 additions & 9 deletions packages/core/src/components/essentials/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,31 @@ export class Title extends Component {
.text("...");

// get the bounding width including the elipses '...'
const tspanLength = DOMUtils.appendOrSelect(title, "tspan").node().getComputedTextLength()
const tspanLength = DOMUtils.appendOrSelect(title, "tspan").node().getComputedTextLength();
const truncatedSize = Math.floor(containerWidth - tspanLength);
const titleString = this.model.getOptions().title;

// get the index for creating the max length substring that fit within the svg
// use one less than the index to avoid crowding (the elipsis)
const substringIndex = this._getSubstringIndex(title.node(), 0, titleString.length - 1, truncatedSize);

// use the substring as the title
title.text(titleString.substring(0, substringIndex - 1))
.append("tspan")
.text("...");

// add events for displaying the tooltip with the title
const self = this;
title.on("mouseover mouseenter", function() {
title.on("mouseenter", function() {
self.services.events.dispatchEvent("show-tooltip", {
title,
hoveredElement: title,
type: TooltipTypes.TITLE
});
})
.on("mouseout", function() {
self.services.events.dispatchEvent("hide-tooltip", {
hoveredElement: title,
});
});
}
}
Expand All @@ -78,12 +86,7 @@ export class Title extends Component {
.attr("height", 20)
.attr("fill", "none");

// title needs to first render so that we can check for overflow
this.truncateTitle();

text
.on("mouseover", function() {

});

}
}
75 changes: 18 additions & 57 deletions packages/core/src/components/essentials/tooltip-bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class TooltipBar extends Tooltip {

// Apply html content to the tooltip
const tooltipTextContainer = DOMUtils.appendOrSelect(this.tooltip, "div.content-box");
this.tooltip.style("max-width", null);

// listen to show-tooltip Custom Events to render the tooltip
this.services.events.addEventListener("show-tooltip", e => {
Expand Down Expand Up @@ -50,9 +51,24 @@ export class TooltipBar extends Tooltip {
const position = this.getTooltipPosition(hoveredElement);
// Position the tooltip relative to the bars
this.positionTooltip(e.detail.multidata ? undefined : position );
// Fade in
this.tooltip.classed("hidden", false);

} else if (e.detail.type === TooltipTypes.TITLE) {
// use the chart size to enforce a max width on the tooltip
const chart = DOMUtils.appendOrSelect(holder, `svg.${settings.prefix}--${chartprefix}--chart-svg`);
// use the configs to determine how large the tooltip should be
const tooltipMax = DOMUtils.getSVGElementSize(chart).width * Tools.getProperty(this.model.getOptions(), "tooltip", "title", "width");
this.tooltip.style("max-width", tooltipMax);

// use tooltip.ts to get the tooltip html for titles
tooltipTextContainer.html(super.getTooltipHTML(e.detail.hoveredElement, TooltipTypes.TITLE));

// get the position based on the title positioning (static)
const position = super.getTooltipPosition(e.detail.hoveredElement.node());
this.positionTooltip(position);
}

// Fade in
this.tooltip.classed("hidden", false);
});

// listen to hide-tooltip Custom Events to hide the tooltip
Expand Down Expand Up @@ -144,59 +160,4 @@ export class TooltipBar extends Tooltip {
</li>
</ul>`;
}

positionTooltip(positionOverride?: any) {
const holder = this.services.domUtils.getHolder();
const target = this.tooltip.node();
const mouseRelativePos = mouse(holder);
let pos;

// override position to place tooltip at {placement:.., position:{top:.. , left:..}}
if (positionOverride) {
// placement determines whether the tooltip is centered above or below the position provided
const placement = positionOverride.placement === TooltipPosition.TOP ? PLACEMENTS.TOP : PLACEMENTS.BOTTOM;

pos = this.positionService.findPositionAt(
positionOverride.position,
target,
placement
);
} else {
// Find out whether tooltip should be shown on the left or right side
const bestPlacementOption = this.positionService.findBestPlacementAt(
{
left: mouseRelativePos[0],
top: mouseRelativePos[1]
},
target,
[
PLACEMENTS.RIGHT,
PLACEMENTS.LEFT,
PLACEMENTS.TOP,
PLACEMENTS.BOTTOM
],
() => ({
width: holder.offsetWidth,
height: holder.offsetHeight
})
);

let { horizontalOffset } = this.model.getOptions().tooltip.datapoint;
if (bestPlacementOption === PLACEMENTS.LEFT) {
horizontalOffset *= -1;
}

// Get coordinates to where tooltip should be positioned
pos = this.positionService.findPositionAt(
{
left: mouseRelativePos[0] + horizontalOffset,
top: mouseRelativePos[1]
},
target,
bestPlacementOption
);
}

this.positionService.setElement(target, pos);
}
}
8 changes: 7 additions & 1 deletion packages/core/src/components/essentials/tooltip-scatter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Tooltip } from "./tooltip";
import { Tools } from "../../tools";
import { TooltipTypes } from "./../../interfaces";


export class TooltipScatter extends Tooltip {
getTooltipHTML(data: any) {
getTooltipHTML(data: any, type: TooltipTypes) {
if (type === TooltipTypes.TITLE) {
// the main tooltip component handles title styles
return super.getTooltipHTML(data, type);
}

const formattedValue = Tools.getProperty(this.model.getOptions(), "tooltip", "valueFormatter") ?
this.model.getOptions().tooltip.valueFormatter(data.value) : data.value.toLocaleString("en");

Expand Down
126 changes: 88 additions & 38 deletions packages/core/src/components/essentials/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import settings from "carbon-components/src/globals/js/settings";

// D3 Imports
import { select, mouse, event } from "d3-selection";
import { TooltipTypes, ScaleTypes } from "../../interfaces";
import { TooltipTypes, ScaleTypes, TooltipPosition } from "../../interfaces";

export class Tooltip extends Component {
type = "tooltip";
Expand All @@ -34,6 +34,7 @@ export class Tooltip extends Component {

// Apply html content to the tooltip
const tooltipTextContainer = DOMUtils.appendOrSelect(this.tooltip, "div.content-box");
this.tooltip.style("max-width", null);

// listen to show-tooltip Custom Events to render the tooltip
this.services.events.addEventListener("show-tooltip", e => {
Expand All @@ -50,8 +51,9 @@ export class Tooltip extends Component {
data = e.detail.multidata;
defaultTooltip = this.getMultilineTooltipHTML(data);
} else {
defaultTooltip = this.getTooltipHTML(data);
defaultTooltip = this.getTooltipHTML(data, TooltipTypes.DATAPOINT);
}

// if there is a provided tooltip HTML function call it
if (Tools.getProperty(this.model.getOptions(), "tooltip", "customHTML")) {
tooltipTextContainer.html(this.model.getOptions().tooltip.customHTML(data, defaultTooltip));
Expand All @@ -63,9 +65,23 @@ export class Tooltip extends Component {
// Position the tooltip
this.positionTooltip();

// Fade in
this.tooltip.classed("hidden", false);
} else if (e.detail.type === TooltipTypes.TITLE) {
const chart = DOMUtils.appendOrSelect(holder, `svg.${settings.prefix}--${chartprefix}--chart-svg`);
const chartWidth = DOMUtils.getSVGElementSize(chart).width * Tools.getProperty(this.model.getOptions(), "tooltip", "title", "width");

this.tooltip.style("max-width", chartWidth);


tooltipTextContainer.html(this.getTooltipHTML(e.detail.hoveredElement, TooltipTypes.TITLE));

// get the position based on the title positioning (static)
const position = this.getTooltipPosition(e.detail.hoveredElement.node());
this.positionTooltip(position);

}

// Fade in
this.tooltip.classed("hidden", false);
});

// listen to hide-tooltip Custom Events to hide the tooltip
Expand All @@ -74,7 +90,12 @@ export class Tooltip extends Component {
});
}

getTooltipHTML(data: any) {
getTooltipHTML(data: any, type: TooltipTypes) {
// check if it is getting styles for a tooltip type
if (type === TooltipTypes.TITLE) {
const title = this.model.getOptions().title;
return `<div class="title-tooltip"><text>${title}</text></div>`;
}
// this cleans up the data item, pie slices have the data within the data.data but other datapoints are self contained within data
const dataVal = Tools.getProperty(data, "data") ? data.data : data;

Expand Down Expand Up @@ -128,45 +149,74 @@ export class Tooltip extends Component {
this.tooltip.classed("hidden", true);
}

positionTooltip() {
// returns static position based on the element
getTooltipPosition(hoveredElement) {
const holderPosition = select(this.services.domUtils.getHolder()).node().getBoundingClientRect();
const elementPosition = hoveredElement.getBoundingClientRect();

// get the vertical offset
const { verticalOffset } = this.model.getOptions().tooltip.title;

const tooltipPos = {
left: (elementPosition.left - holderPosition.left) + elementPosition.width / 2,
top: (elementPosition.top - holderPosition.top - verticalOffset)
};

return { placement: TooltipPosition.BOTTOM, position: tooltipPos };
}

positionTooltip(positionOverride?: any) {
const holder = this.services.domUtils.getHolder();
const target = this.tooltip.node();
const mouseRelativePos = mouse(holder);
let pos;

// override position to place tooltip at {placement:.., position:{top:.. , left:..}}
if (positionOverride) {
// placement determines whether the tooltip is centered above or below the position provided
const placement = positionOverride.placement === TooltipPosition.TOP ? PLACEMENTS.TOP : PLACEMENTS.BOTTOM;

pos = this.positionService.findPositionAt(
positionOverride.position,
target,
placement
);
} else {
// Find out whether tooltip should be shown on the left or right side
const bestPlacementOption = this.positionService.findBestPlacementAt(
{
left: mouseRelativePos[0],
top: mouseRelativePos[1]
},
target,
[
PLACEMENTS.RIGHT,
PLACEMENTS.LEFT,
PLACEMENTS.TOP,
PLACEMENTS.BOTTOM
],
() => ({
width: holder.offsetWidth,
height: holder.offsetHeight
})
);

let { horizontalOffset } = this.model.getOptions().tooltip.datapoint;
if (bestPlacementOption === PLACEMENTS.LEFT) {
horizontalOffset *= -1;
}

// Find out whether tooltip should be shown on the left or right side
const bestPlacementOption = this.positionService.findBestPlacementAt(
{
left: mouseRelativePos[0],
top: mouseRelativePos[1]
},
target,
[
PLACEMENTS.RIGHT,
PLACEMENTS.LEFT,
PLACEMENTS.TOP,
PLACEMENTS.BOTTOM
],
() => ({
width: holder.offsetWidth,
height: holder.offsetHeight
})
);

let { horizontalOffset } = this.model.getOptions().tooltip.datapoint;
if (bestPlacementOption === PLACEMENTS.LEFT) {
horizontalOffset *= -1;
// Get coordinates to where tooltip should be positioned
pos = this.positionService.findPositionAt(
{
left: mouseRelativePos[0] + horizontalOffset,
top: mouseRelativePos[1]
},
target,
bestPlacementOption
);
}

// Get coordinates to where tooltip should be positioned
const pos = this.positionService.findPositionAt(
{
left: mouseRelativePos[0] + horizontalOffset,
top: mouseRelativePos[1]
},
target,
bestPlacementOption
);

this.positionService.setElement(target, pos);
}
}
4 changes: 4 additions & 0 deletions packages/core/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ export const baseTooltip: TooltipOptions = {
horizontalOffset: 10,
enabled: true,
},
title: {
verticalOffset: .75,
width: .4
}
};

export const axisChartTooltip: AxisTooltipOptions = Tools.merge({}, baseTooltip, {
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/interfaces/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ export interface TooltipOptions {
*/
verticalOffset?: number
};
title?: {
/**
* vertical offset for title tooltip placement. < 0 will shift the tooltip above title, > 0 shifts vertically down
*/
verticalOffset?: number
/**
* max width of title tooltip relative to the width of the chart-svg (percentage should be < 1)
*/
width?: number
};
}

/**
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/styles/components/_tooltip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
.content-box {
color: $text-01;

.title-tooltip {
line-height: 16px;
font-size: 12px;
padding: 4px;
min-width: 20px;
}

.datapoint-tooltip, .total-val {
display: flex;
padding: 4px;
Expand Down

0 comments on commit bdd677c

Please sign in to comment.