diff --git a/packages/chart/index.html b/packages/chart/index.html
index 4464af916e..df176d63e3 100644
--- a/packages/chart/index.html
+++ b/packages/chart/index.html
@@ -16,7 +16,43 @@
margin-top: 50px;
}
- #placeholder {
+ .controls {
+ max-width: 800px;
+ margin: 20px auto;
+ padding: 15px;
+ background-color: #fff;
+ border-radius: 5px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ }
+
+ .control-group {
+ margin: 10px 0;
+ }
+
+ .control-group label {
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ font-size: 14px;
+ }
+
+ .control-group input[type="checkbox"] {
+ margin-right: 10px;
+ cursor: pointer;
+ width: 18px;
+ height: 18px;
+ }
+
+ .control-group .description {
+ margin-left: 28px;
+ font-size: 12px;
+ color: #666;
+ font-style: italic;
+ }
+
+ #placeholder,
+ #placeholder2,
+ #placeholder3 {
width: 100%;
height: 500px;
background-color: #fff;
@@ -29,9 +65,56 @@
ESM Quick Test
-
+
+
+
+
+
+ When enabled, you can use Tab to navigate through points and Space/Enter to select them
+
+
+
+
+
+
+ Quickly preview how scatter behaves with lines or area fill enabled
+
+
+
+
+
+ When enabled, you can use Tab to navigate through columns and Space/Enter to select them
+
+
+
+
+
+ When enabled, you can use Tab to navigate through slices and Space/Enter to select them
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/chart/src/Column.css b/packages/chart/src/Column.css
index d731d289e1..c9cb02a787 100644
--- a/packages/chart/src/Column.css
+++ b/packages/chart/src/Column.css
@@ -1,17 +1,45 @@
.chart_Column .columnRect {
- fill: steelblue;
- cursor: pointer;
+ fill: steelblue;
+ cursor: pointer;
}
.chart_Column .data.axis path {
- display: none;
+ display: none;
}
.chart_Column .columnRect {
stroke: transparent;
- border-width: 1.5px;
+ border-width: 2px;
}
-.chart_Column .columnRect.selected {
- stroke: red;
+.chart_Column .dataCell.selected .columnRect {
+ stroke: #dc3545 !important;
+ stroke-width: 3px !important;
+ paint-order: fill stroke !important;
+ transition: all 0.2s ease;
}
+
+.chart_Column .dataCell:hover .columnRect {
+ stroke: rgba(108, 117, 125, 0.6);
+ stroke-width: 2px;
+ filter: brightness(1.05);
+}
+
+.chart_Column .dataCell:focus .columnRect {
+ stroke: #007bff !important;
+ stroke-width: 3px !important;
+ paint-order: fill stroke !important;
+ transition: all 0.2s ease;
+}
+
+.chart_Column .dataCell.selected:focus .columnRect {
+ stroke: #6f42c1 !important;
+}
+
+.chart_Column .dataCell:focus-visible {
+ outline: none;
+}
+
+.chart_Column .dataCell:active {
+ outline: none !important;
+}
\ No newline at end of file
diff --git a/packages/chart/src/Column.ts b/packages/chart/src/Column.ts
index db8d6cf35c..ec393b8217 100644
--- a/packages/chart/src/Column.ts
+++ b/packages/chart/src/Column.ts
@@ -1,5 +1,5 @@
import { INDChart, ITooltip } from "@hpcc-js/api";
-import { InputField, Text } from "@hpcc-js/common";
+import { d3Event, InputField, Text } from "@hpcc-js/common";
import { format as d3Format } from "d3-format";
import { scaleBand as d3ScaleBand } from "d3-scale";
import { local as d3Local, select as d3Select } from "d3-selection";
@@ -34,6 +34,7 @@ export class Column extends XYAxis {
layerEnter(host: XYAxis, element, duration: number = 250) {
super.layerEnter(host, element, duration);
+
const context = this;
this
.tooltipHTML(function (d) {
@@ -84,6 +85,18 @@ export class Column extends XYAxis {
this.isHorizontal = isHorizontal;
const context = this;
+ if (this.tabNavigation() && host.parentRelativeDiv) {
+ host.parentRelativeDiv
+ .attr("tabindex", "0")
+ .attr("role", "group")
+ .attr("aria-label", `${this.columns()[0] || "Chart"} data`);
+ } else if (host.parentRelativeDiv) {
+ host.parentRelativeDiv
+ .attr("tabindex", null)
+ .attr("role", null)
+ .attr("aria-label", null);
+ }
+
this._palette = this._palette.switch(this.paletteID());
if (this.useClonedPalette()) {
this._palette = this._palette.cloneNotExists(this.paletteID() + "_" + this.id());
@@ -110,7 +123,7 @@ export class Column extends XYAxis {
const columnScale = d3ScaleBand()
.domain(context.layerColumns(host).filter(function (_d, idx) { return idx > 0; }))
.rangeRound(isHorizontal ? [0, dataLen] : [dataLen, 0])
- .paddingInner(this.xAxisSeriesPaddingInner())
+ .paddingInner(Math.max(this.xAxisSeriesPaddingInner(), 0.05))
.paddingOuter(0)
;
let domainSums = [];
@@ -177,6 +190,15 @@ export class Column extends XYAxis {
.on("dblclick", function (d: any) {
context.dblclick(host.rowToObj(d.origRow), d.column, host._selection.selected(this));
})
+ .on("keydown", function (evt, d: any) {
+ if (context.tabNavigation()) {
+ const event = d3Event();
+ if (event.code === "Space" || event.key === "Enter") {
+ event.preventDefault();
+ host._selection.click(this);
+ }
+ }
+ })
.style("opacity", 0)
.each(function (this: SVGElement, d: any) {
const element = d3Select(this);
@@ -193,266 +215,270 @@ export class Column extends XYAxis {
.style("opacity", 1)
;
const domainLength = host.yAxisStacked() ? dataLen : columnScale.bandwidth();
- columnGEnter.merge(columnGRect as any).each(function (this: SVGElement, d: any) {
- const element = d3Select(this);
- const domainPos = host.dataPos(dataRow[0]) + (host.yAxisStacked() ? 0 : columnScale(d.column)) + offset;
- const upperValue = d.value instanceof Array ? d.value[1] : d.value;
- let valueText = d.origRow[d.idx];
- if (context.showValue()) {
- const dm = context.dataMeta();
- switch (context.showValueAsPercent()) {
- case "series":
- const seriesSum = typeof dm.sum !== "undefined" ? dm.sum : seriesSums[d.idx];
- valueText = formatPct(valueText / seriesSum);
- break;
- case "domain":
- const domainSum = typeof dm.sum !== "undefined" ? dm.sum : domainSums[dataRowIdx];
- valueText = formatPct(valueText / domainSum);
- break;
- case null:
- default:
- valueText = d3Format(context.showValueFormat())(valueText);
- break;
- }
- }
- const upperValuePos = host.valuePos(upperValue);
- const lowerValuePos = host.valuePos(d.value instanceof Array ? d.value[0] : 0);
- const valuePos = Math.min(lowerValuePos, upperValuePos);
- const valueLength = Math.abs(upperValuePos - lowerValuePos);
-
- const innerTextHeight = context.innerTextFontSize();
- const innerTextPadding = context.innerTextPadding_exists() ? context.innerTextPadding() : innerTextHeight / 2.5;
-
- const dataRect = context.intersectRectRect(
- {
- x: isHorizontal ? domainPos : valuePos,
- y: isHorizontal ? valuePos : domainPos,
- width: isHorizontal ? domainLength : valueLength,
- height: isHorizontal ? valueLength : domainLength
- },
- {
- x: 0,
- y: 0,
- width: axisSize.width,
- height: axisSize.height
+ columnGEnter.merge(columnGRect as any)
+ .attr("tabindex", context.tabNavigation() ? 0 : null) // Tabster Groupper manages these inner focusables
+ .attr("role", context.tabNavigation() ? "button" : null) // ARIA role for accessibility
+ .attr("aria-label", context.tabNavigation() ? (d: any) => `${d.origRow[0]} - ${d.column}: ${d.value instanceof Array ? d.value[1] - d.value[0] : d.value}` : null)
+ .each(function (this: SVGElement, d: any) {
+ const element = d3Select(this);
+ const domainPos = host.dataPos(dataRow[0]) + (host.yAxisStacked() ? 0 : columnScale(d.column)) + offset;
+ const upperValue = d.value instanceof Array ? d.value[1] : d.value;
+ let valueText = d.origRow[d.idx];
+ if (context.showValue()) {
+ const dm = context.dataMeta();
+ switch (context.showValueAsPercent()) {
+ case "series":
+ const seriesSum = typeof dm.sum !== "undefined" ? dm.sum : seriesSums[d.idx];
+ valueText = formatPct(valueText / seriesSum);
+ break;
+ case "domain":
+ const domainSum = typeof dm.sum !== "undefined" ? dm.sum : domainSums[dataRowIdx];
+ valueText = formatPct(valueText / domainSum);
+ break;
+ case null:
+ default:
+ valueText = d3Format(context.showValueFormat())(valueText);
+ break;
+ }
}
- );
-
- const _rects = element.select("rect").transition().duration(duration)
- .style("fill", (d: any) => context.fillColor(d.row, d.column, d.value, d.origRow))
- ;
-
- if (isHorizontal) {
- _rects
- .attr("x", domainPos)
- .attr("y", valuePos)
- .attr("width", domainLength)
- .attr("height", valueLength)
+ const upperValuePos = host.valuePos(upperValue);
+ const lowerValuePos = host.valuePos(d.value instanceof Array ? d.value[0] : 0);
+ const valuePos = Math.min(lowerValuePos, upperValuePos);
+ const valueLength = Math.abs(upperValuePos - lowerValuePos);
+
+ const innerTextHeight = context.innerTextFontSize();
+ const innerTextPadding = context.innerTextPadding_exists() ? context.innerTextPadding() : innerTextHeight / 2.5;
+
+ const dataRect = context.intersectRectRect(
+ {
+ x: isHorizontal ? domainPos : valuePos,
+ y: isHorizontal ? valuePos : domainPos,
+ width: isHorizontal ? domainLength : valueLength,
+ height: isHorizontal ? valueLength : domainLength
+ },
+ {
+ x: 0,
+ y: 0,
+ width: axisSize.width,
+ height: axisSize.height
+ }
+ );
+
+ const _rects = element.select("rect").transition().duration(duration)
+ .style("fill", (d: any) => context.fillColor(d.row, d.column, d.value, d.origRow))
;
- } else {
- _rects
- .attr("y", domainPos)
- .attr("x", valuePos)
- .attr("height", domainLength)
- .attr("width", valueLength)
+
+ if (isHorizontal) {
+ _rects
+ .attr("x", domainPos)
+ .attr("y", valuePos)
+ .attr("width", domainLength)
+ .attr("height", valueLength)
+ ;
+ } else {
+ _rects
+ .attr("y", domainPos)
+ .attr("x", valuePos)
+ .attr("height", domainLength)
+ .attr("width", valueLength)
+ ;
+ }
+ const _texts = element.select("text").transition().duration(duration)
+ .style("font-size", innerTextHeight + "px")
+ .style("fill", (d: any) => context.textColor(d.row, d.column, d.value, d.origRow))
;
- }
- const _texts = element.select("text").transition().duration(duration)
- .style("font-size", innerTextHeight + "px")
- .style("fill", (d: any) => context.textColor(d.row, d.column, d.value, d.origRow))
- ;
- _texts.style("font-family", context.innerTextFontFamily_exists() ? context.innerTextFontFamily() : null);
+ _texts.style("font-family", context.innerTextFontFamily_exists() ? context.innerTextFontFamily() : null);
- const padding = context.innerTextPadding_exists() ? context.innerTextPadding() : 8;
+ const padding = context.innerTextPadding_exists() ? context.innerTextPadding() : 8;
- const textHeightOffset = innerTextHeight / 2.7;
+ const textHeightOffset = innerTextHeight / 2.7;
- if (isHorizontal) { // Column
- const y = dataRect.y + dataRect.height - innerTextPadding;
- _texts
- .attr("x", domainPos + (domainLength / 2))
- .attr("y", y + textHeightOffset)
- .attr("transform", `rotate(-90, ${domainPos + (domainLength / 2)}, ${y})`)
- ;
- } else { // Bar
- _texts
- .attr("x", dataRect.x + padding)
- .attr("y", domainPos + (domainLength / 2) + textHeightOffset)
- ;
- }
- _texts
- .attr("height", domainLength)
- .attr("width", valueLength)
- ;
- if (context.showInnerText()) {
+ if (isHorizontal) { // Column
+ const y = dataRect.y + dataRect.height - innerTextPadding;
+ _texts
+ .attr("x", domainPos + (domainLength / 2))
+ .attr("y", y + textHeightOffset)
+ .attr("transform", `rotate(-90, ${domainPos + (domainLength / 2)}, ${y})`)
+ ;
+ } else { // Bar
+ _texts
+ .attr("x", dataRect.x + padding)
+ .attr("y", domainPos + (domainLength / 2) + textHeightOffset)
+ ;
+ }
_texts
- .text((d: any) => {
- const innerText = context.innerText(d.origRow, d.origRow[columnLength], d.idx);
- if (innerText) {
- const clippedValueLength = isHorizontal ? dataRect.height : dataRect.width;
- const innerTextObj = context.calcInnerText(clippedValueLength, innerText, valueText);
- d.innerTextObj = innerTextObj;
-
- return innerTextObj.text;
- }
- return "";
- })
+ .attr("height", domainLength)
+ .attr("width", valueLength)
;
- }
- const dataText = element.selectAll(".dataText").data(context.showValue() ? [`${upperValue}`] : []);
- const dataTextEnter = dataText.enter().append("g")
- .attr("class", "dataText")
- .each(function (this: SVGElement, d) {
- context.textLocal.set(this, new Text().target(this).colorStroke_default("transparent"));
- });
- dataTextEnter.merge(dataText as any)
- .each(function (this: SVGElement) {
- const pos = { x: 0, y: 0 };
- const valueFontFamily = context.valueFontFamily();
- const valueFontSize = context.valueFontSize();
- const textSize = context.textSize(valueText, valueFontFamily, valueFontSize);
-
- const isPositive = parseFloat(valueText) >= 0;
-
- let valueAnchor = context.valueAnchor() ? context.valueAnchor() : isHorizontal ? "middle" : "start";
-
- const leftSpace = dataRect.x;
- const rightSpace = axisSize.width - (dataRect.x + dataRect.width);
- const topSpace = dataRect.y;
- const bottomSpace = axisSize.height - (dataRect.y + dataRect.height);
-
- let noRoomInside;
- let isOutside;
- let noRoomOnExpectedSide;
-
- if (d.innerTextObj) {
- const { padding, valueTextWidth } = d.innerTextObj;
- isOutside = false;
- if (isHorizontal) { // Column
- valueAnchor = "middle";
- pos.x = domainPos + (domainLength / 2);
-
- if (d.innerTextObj.category === 4) {
- isOutside = true;
- pos.y = valuePos - padding - (valueFontSize / 2);
- } else {
- pos.y = valuePos + padding + (valueFontSize / 2);
+ if (context.showInnerText()) {
+ _texts
+ .text((d: any) => {
+ const innerText = context.innerText(d.origRow, d.origRow[columnLength], d.idx);
+ if (innerText) {
+ const clippedValueLength = isHorizontal ? dataRect.height : dataRect.width;
+ const innerTextObj = context.calcInnerText(clippedValueLength, innerText, valueText);
+ d.innerTextObj = innerTextObj;
+
+ return innerTextObj.text;
}
- } else { // Bar
- valueAnchor = "start";
- if (d.innerTextObj.category === 4) {
- isOutside = true;
- pos.x = (valueLength + valuePos) + padding;
- } else {
- pos.x = (valueLength + valuePos) - valueTextWidth - padding;
+ return "";
+ })
+ ;
+ }
+ const dataText = element.selectAll(".dataText").data(context.showValue() ? [`${upperValue}`] : []);
+ const dataTextEnter = dataText.enter().append("g")
+ .attr("class", "dataText")
+ .each(function (this: SVGElement, d) {
+ context.textLocal.set(this, new Text().target(this).colorStroke_default("transparent"));
+ });
+ dataTextEnter.merge(dataText as any)
+ .each(function (this: SVGElement) {
+ const pos = { x: 0, y: 0 };
+ const valueFontFamily = context.valueFontFamily();
+ const valueFontSize = context.valueFontSize();
+ const textSize = context.textSize(valueText, valueFontFamily, valueFontSize);
+
+ const isPositive = parseFloat(valueText) >= 0;
+
+ let valueAnchor = context.valueAnchor() ? context.valueAnchor() : isHorizontal ? "middle" : "start";
+
+ const leftSpace = dataRect.x;
+ const rightSpace = axisSize.width - (dataRect.x + dataRect.width);
+ const topSpace = dataRect.y;
+ const bottomSpace = axisSize.height - (dataRect.y + dataRect.height);
+
+ let noRoomInside;
+ let isOutside;
+ let noRoomOnExpectedSide;
+
+ if (d.innerTextObj) {
+ const { padding, valueTextWidth } = d.innerTextObj;
+ isOutside = false;
+ if (isHorizontal) { // Column
+ valueAnchor = "middle";
+ pos.x = domainPos + (domainLength / 2);
+
+ if (d.innerTextObj.category === 4) {
+ isOutside = true;
+ pos.y = valuePos - padding - (valueFontSize / 2);
+ } else {
+ pos.y = valuePos + padding + (valueFontSize / 2);
+ }
+ } else { // Bar
+ valueAnchor = "start";
+ if (d.innerTextObj.category === 4) {
+ isOutside = true;
+ pos.x = (valueLength + valuePos) + padding;
+ } else {
+ pos.x = (valueLength + valuePos) - valueTextWidth - padding;
+ }
+ pos.y = domainPos + (domainLength / 2);
}
- pos.y = domainPos + (domainLength / 2);
- }
- } else {
- /*
- IF this.valueCentered() and NO ROOM INSIDE
- ...then ASSUME THERES ROOM OUTSIDE
- IF NO ROOM OUTSIDE ON EXPECTED SIDE
- ...then ASSUME THERES ROOM ON THE OPPOSITE SIDE
- */
- if (isHorizontal) { // Column
- noRoomInside = dataRect.height < textSize.height;
- isOutside = !context.valueCentered() || noRoomInside;
-
- pos.x = dataRect.x + (dataRect.width / 2);
-
- if (isOutside) {
- if (isPositive) {
- noRoomOnExpectedSide = topSpace < textSize.height + padding;
- if (noRoomOnExpectedSide) {
- if (!noRoomInside) {
- isOutside = false;
- pos.y = dataRect.y + (dataRect.height / 2);
+ } else {
+ /*
+ IF this.valueCentered() and NO ROOM INSIDE
+ ...then ASSUME THERES ROOM OUTSIDE
+ IF NO ROOM OUTSIDE ON EXPECTED SIDE
+ ...then ASSUME THERES ROOM ON THE OPPOSITE SIDE
+ */
+ if (isHorizontal) { // Column
+ noRoomInside = dataRect.height < textSize.height;
+ isOutside = !context.valueCentered() || noRoomInside;
+
+ pos.x = dataRect.x + (dataRect.width / 2);
+
+ if (isOutside) {
+ if (isPositive) {
+ noRoomOnExpectedSide = topSpace < textSize.height + padding;
+ if (noRoomOnExpectedSide) {
+ if (!noRoomInside) {
+ isOutside = false;
+ pos.y = dataRect.y + (dataRect.height / 2);
+ } else {
+ pos.y = dataRect.y + dataRect.height + textSize.height;
+ }
} else {
- pos.y = dataRect.y + dataRect.height + textSize.height;
+ pos.y = dataRect.y - (textSize.height / 2) - padding;
}
} else {
- pos.y = dataRect.y - (textSize.height / 2) - padding;
- }
- } else {
- noRoomOnExpectedSide = bottomSpace < textSize.height;
- if (noRoomOnExpectedSide) {
- if (!noRoomInside) {
- isOutside = false;
- pos.y = dataRect.y + (dataRect.height / 2);
+ noRoomOnExpectedSide = bottomSpace < textSize.height;
+ if (noRoomOnExpectedSide) {
+ if (!noRoomInside) {
+ isOutside = false;
+ pos.y = dataRect.y + (dataRect.height / 2);
+ } else {
+ pos.y = dataRect.y - (textSize.height / 2) - padding;
+ }
} else {
- pos.y = dataRect.y - (textSize.height / 2) - padding;
+ pos.y = dataRect.y + textSize.height + padding;
}
- } else {
- pos.y = dataRect.y + textSize.height + padding;
}
+ } else {
+ pos.y = dataRect.y + (dataRect.height / 2);
}
- } else {
+ } else { // Bar
+ noRoomInside = dataRect.width < textSize.width;
+ isOutside = !context.valueCentered() || noRoomInside;
+
pos.y = dataRect.y + (dataRect.height / 2);
- }
- } else { // Bar
- noRoomInside = dataRect.width < textSize.width;
- isOutside = !context.valueCentered() || noRoomInside;
-
- pos.y = dataRect.y + (dataRect.height / 2);
-
- if (isOutside) {
- if (isPositive) {
- noRoomOnExpectedSide = rightSpace < textSize.width + padding;
- if (noRoomOnExpectedSide) {
- if (context.showInnerText() || !noRoomInside) {
- isOutside = false;
- pos.x = dataRect.x + (dataRect.width / 2);
+
+ if (isOutside) {
+ if (isPositive) {
+ noRoomOnExpectedSide = rightSpace < textSize.width + padding;
+ if (noRoomOnExpectedSide) {
+ if (context.showInnerText() || !noRoomInside) {
+ isOutside = false;
+ pos.x = dataRect.x + (dataRect.width / 2);
+ } else {
+ pos.x = dataRect.x - (textSize.width - padding);
+ }
} else {
- pos.x = dataRect.x - (textSize.width - padding);
+ pos.x = dataRect.x + dataRect.width + (textSize.width / 2) + padding;
}
} else {
- pos.x = dataRect.x + dataRect.width + (textSize.width / 2) + padding;
- }
- } else {
- noRoomOnExpectedSide = leftSpace < textSize.width;
- if (noRoomOnExpectedSide) {
- if (context.showInnerText() || !noRoomInside) {
- isOutside = false;
- pos.x = dataRect.x + (dataRect.width / 2);
+ noRoomOnExpectedSide = leftSpace < textSize.width;
+ if (noRoomOnExpectedSide) {
+ if (context.showInnerText() || !noRoomInside) {
+ isOutside = false;
+ pos.x = dataRect.x + (dataRect.width / 2);
+ } else {
+ pos.x = dataRect.x + dataRect.width + (textSize.width - padding);
+ }
} else {
- pos.x = dataRect.x + dataRect.width + (textSize.width - padding);
+ pos.x = dataRect.x - (textSize.width - padding);
}
- } else {
- pos.x = dataRect.x - (textSize.width - padding);
}
+ } else {
+ pos.x = dataRect.x + (dataRect.width / 2);
}
- } else {
- pos.x = dataRect.x + (dataRect.width / 2);
}
}
- }
- const textColor = isOutside ? null : context.textColor(d.row, d.column, d.value, d.origRow);
-
- // Prevent overlapping labels on stacked columns
- const columns = context.columns();
- const hideValue = (context.yAxisStacked() && noRoomInside) ||
- (isOutside && context.yAxisStacked() && columns.indexOf(d.column) !== columns.length - 1);
- context.textLocal.get(this)
- .pos(pos)
- .anchor(valueAnchor)
- .fontFamily(valueFontFamily)
- .fontSize(valueFontSize)
- .text(`${valueText}`)
- .colorFill(textColor)
- .visible(context.showValue() && !hideValue)
- .render()
- ;
-
- });
- dataText.exit()
- .each(function (this: SVGElement, d) {
- context.textLocal.get(this).target(null);
- })
- .remove()
- ;
- });
+ const textColor = isOutside ? null : context.textColor(d.row, d.column, d.value, d.origRow);
+
+ // Prevent overlapping labels on stacked columns
+ const columns = context.columns();
+ const hideValue = (context.yAxisStacked() && noRoomInside) ||
+ (isOutside && context.yAxisStacked() && columns.indexOf(d.column) !== columns.length - 1);
+ context.textLocal.get(this)
+ .pos(pos)
+ .anchor(valueAnchor)
+ .fontFamily(valueFontFamily)
+ .fontSize(valueFontSize)
+ .text(`${valueText}`)
+ .colorFill(textColor)
+ .visible(context.showValue() && !hideValue)
+ .render()
+ ;
+
+ });
+ dataText.exit()
+ .each(function (this: SVGElement, d) {
+ context.textLocal.get(this).target(null);
+ })
+ .remove()
+ ;
+ });
columnGRect.exit().transition().duration(duration)
.style("opacity", 0)
.remove()
@@ -517,7 +543,6 @@ export class Column extends XYAxis {
}
calcInnerText(offset, innerText, valueText) {
-
const fontFamily = this.innerTextFontFamily_exists() ? this.innerTextFontFamily() : "Verdana";
const fontSize = this.innerTextFontSize();
const valueFontFamily = this.valueFontFamily_exists() ? this.valueFontFamily() : "Verdana";
@@ -644,7 +669,7 @@ Column.prototype.publish("showValueAsPercentFormat", ".0%", "string", "D3 Format
Column.prototype.publish("showDomainTotal", false, "boolean", "Show Total Value for Stacked Columns", null);
Column.prototype.publish("valueCentered", false, "boolean", "Show Value in center of column");
Column.prototype.publish("valueAnchor", "middle", "set", "text-anchor for shown value text", ["start", "middle", "end"]);
-Column.prototype.publish("xAxisSeriesPaddingInner", 0, "number", "Determines the ratio of the range that is reserved for blank space between band (0->1)");
+Column.prototype.publish("xAxisSeriesPaddingInner", 0.0, "number", "Determines the ratio of the range that is reserved for blank space between band (0->1)");
Column.prototype.publish("tooltipInnerTextEllipsedOnly", false, "boolean", "Show tooltip only when inner text is truncated with an ellipsis");
/*
diff --git a/packages/chart/src/Pie.css b/packages/chart/src/Pie.css
index 3be1ede838..60cce75e21 100644
--- a/packages/chart/src/Pie.css
+++ b/packages/chart/src/Pie.css
@@ -2,18 +2,20 @@
cursor: pointer;
}
-.chart_Pie > g > text {
+.chart_Pie>g>text {
cursor: pointer;
}
.chart_Pie .arc path {
- stroke: white;
- stroke-width: 0.75px;
+ stroke: transparent;
+ stroke-width: 2px;
}
.chart_Pie .arc.selected path {
- stroke: red;
- stroke-width: 1.5px;
+ stroke: #dc3545 !important;
+ stroke-width: 3px !important;
+ paint-order: fill stroke !important;
+ transition: all 0.2s ease;
}
.chart_Pie polyline {
@@ -21,4 +23,29 @@
stroke: black;
stroke-width: 2px;
fill: none;
+}
+
+.chart_Pie .arc:hover path {
+ stroke: rgba(108, 117, 125, 0.6);
+ stroke-width: 2px;
+ filter: brightness(1.05);
+}
+
+.chart_Pie .arc:focus path {
+ stroke: #007bff !important;
+ stroke-width: 3px !important;
+ paint-order: fill stroke !important;
+ transition: all 0.2s ease;
+}
+
+.chart_Pie .arc.selected:focus path {
+ stroke: #6f42c1 !important;
+}
+
+.chart_Pie .arc:focus-visible {
+ outline: none;
+}
+
+.chart_Pie .arc:active {
+ outline: none !important;
}
\ No newline at end of file
diff --git a/packages/chart/src/Pie.ts b/packages/chart/src/Pie.ts
index 65bb825613..b35af3ac14 100644
--- a/packages/chart/src/Pie.ts
+++ b/packages/chart/src/Pie.ts
@@ -1,5 +1,5 @@
import { I2DChart, ITooltip } from "@hpcc-js/api";
-import { InputField, SVGWidget, Utility } from "@hpcc-js/common";
+import { d3Event, InputField, SVGWidget, Utility } from "@hpcc-js/common";
import { degreesToRadians, normalizeRadians } from "@hpcc-js/util";
import { format as d3Format } from "d3-format";
import { interpolate as d3Interpolate } from "d3-interpolate";
@@ -33,6 +33,7 @@ export class Pie extends SVGWidget {
private _maxLabelBottom = 0;
private _seriesValueFormatter;
private _seriesPercentageFormatter;
+
constructor() {
super();
I2DChart.call(this);
@@ -91,6 +92,11 @@ export class Pie extends SVGWidget {
}, 0);
}
+ calcPadAngleRadians(): number {
+ const paddingValue = this.slicePadding();
+ return paddingValue > 0 ? Math.min(paddingValue, 0.05) : 0;
+ }
+
getLabelText(d, truncate?) {
let len;
let label = d.data[0];
@@ -140,12 +146,19 @@ export class Pie extends SVGWidget {
_slices;
_labels;
- enter(_domNode, element) {
- super.enter(_domNode, element);
- this._selection.widgetElement(element);
+
+ enter(domNode, element) {
+ super.enter(domNode, element);
+ this._selection
+ .widgetElement(element)
+ .skipBringToTop(true)
+ ;
+
this._slices = element.append("g");
this._labels = element.append("g");
+
const context = this;
+
this
.tooltipHTML(function (d) {
switch (context.tooltipStyle()) {
@@ -168,8 +181,10 @@ export class Pie extends SVGWidget {
}
update(_domNode, element) {
+ this.selectionGlow(!this.tabNavigation());
super.update(_domNode, element);
const context = this;
+
this.updateD3Pie();
this._palette = this._palette.switch(this.paletteID());
this._seriesValueFormatter = d3Format(this.seriesValueFormat() as string);
@@ -179,13 +194,15 @@ export class Pie extends SVGWidget {
}
this._smallValueLabelHeight = this.calcSmallValueLabelHeight();
this._totalValue = this.calcTotalValue();
- const innerRadius = this.calcInnerRadius();
const outerRadius = this.calcOuterRadius();
+ const innerRadius = Math.max(this.calcInnerRadius(), Math.min(outerRadius / 30, 6));
const labelRadius = outerRadius + 12;
+
this.d3Arc
.innerRadius(innerRadius)
.padRadius(outerRadius)
.outerRadius(outerRadius)
+ .padAngle(this.calcPadAngleRadians())
;
this._quadIdxArr = [[], [], [], []];
@@ -213,6 +230,13 @@ export class Pie extends SVGWidget {
.on("dblclick", function (d) {
context.dblclick(context.rowToObj(d.data), context.columns()[1], context._selection.selected(this));
})
+ .on("keydown", function (evt, d) {
+ const event = d3Event();
+ if (context.tabNavigation() && (event.code === "Space" || event.key === "Enter")) {
+ event.preventDefault();
+ context._selection.click(this);
+ }
+ })
.each(function (d, i) {
d3Select(this).append("path")
.on("mouseout.tooltip", context.tooltip.hide)
@@ -223,6 +247,9 @@ export class Pie extends SVGWidget {
})
.merge(arc).transition()
.attr("opacity", 1)
+ .attr("tabindex", context.tabNavigation() ? "0" : null)
+ .attr("role", context.tabNavigation() ? "button" : null)
+ .attr("aria-label", context.tabNavigation() ? (d: any) => `${d.data[0]}: ${d.data[1]}` : null)
.each(function (d, i) {
const quad = context.getQuadrant(midAngle(d));
context._quadIdxArr[quad].push(i);
@@ -429,7 +456,7 @@ export class Pie extends SVGWidget {
}
this.d3Pie
- .padAngle(0.0025)
+ .padAngle(this.calcPadAngleRadians())
.startAngle(startAngle)
.endAngle(2 * Math.PI + startAngle)
.value(function (d) {
@@ -456,6 +483,8 @@ export interface Pie {
startAngle(_: number): this;
labelHeight(): number;
labelHeight(_: number): this;
+ slicePadding(): number;
+ slicePadding(_: number): this;
seriesPercentageFormat(): string;
seriesPercentageFormat(_: string): this;
showLabels(): boolean;
@@ -463,10 +492,14 @@ export interface Pie {
sortDataByValue(): "none" | "ascending" | "descending";
sortDataByValue(_: "none" | "ascending" | "descending"): this;
- paletteID(_?: string): string | Pie;
- useClonedPalette(_?: boolean): boolean | Pie;
- outerText(_?: boolean): boolean | Pie;
+ paletteID(): string;
+ paletteID(_: string): this;
+ useClonedPalette(): boolean;
+ useClonedPalette(_: boolean): this;
+ outerText(): boolean;
+ outerText(_: boolean): this;
innerRadius(): number;
+ innerRadius(_: number): this;
innerRadius_exists(): boolean;
// I2DChart
@@ -492,6 +525,10 @@ export interface Pie {
// SimpleSelectionMixin
_selection: Utility.SimpleSelection;
+
+ // Tab Navigation
+ tabNavigation(): boolean;
+ tabNavigation(_: boolean): this;
}
Pie.prototype.publish("showLabels", true, "boolean", "If true, wedge labels will display");
Pie.prototype.publish("showSeriesValue", false, "boolean", "Append data series value next to label", null, { disable: w => !w.showLabels() });
@@ -504,4 +541,6 @@ Pie.prototype.publish("innerRadius", 0, "number", "Sets inner pie hole radius as
Pie.prototype.publish("minOuterRadius", 20, "number", "Minimum outer radius (pixels)");
Pie.prototype.publish("startAngle", 0, "number", "Starting angle of the first (and largest) wedge (degrees)");
Pie.prototype.publish("labelHeight", 12, "number", "Font size of labels (pixels)", null, { disable: w => !w.showLabels() });
+Pie.prototype.publish("slicePadding", 0.01, "number", "Padding between pie slices (converted to pixels)", null, { tags: ["Basic"], range: { min: 0, step: 0.01, max: 0.2 } });
Pie.prototype.publish("sortDataByValue", "descending", "set", "Sort data by value", ["none", "ascending", "descending"]);
+Pie.prototype.publish("tabNavigation", false, "boolean", "Enable or disable tab navigation");
diff --git a/packages/chart/src/Scatter.css b/packages/chart/src/Scatter.css
index 234c33283a..be6a73e3b2 100644
--- a/packages/chart/src/Scatter.css
+++ b/packages/chart/src/Scatter.css
@@ -6,11 +6,38 @@
.chart_Scatter .point .pointSelection {
fill: none;
- stroke: none;
+ stroke: transparent;
+ stroke-width: 2px;
pointer-events: all;
+ transition: all 0.2s ease;
}
.chart_Scatter .point .pointSelection.selected {
- fill: none;
- stroke: red;
+ stroke: #dc3545 !important;
+ stroke-width: 3px !important;
+ paint-order: fill stroke !important;
+}
+
+.chart_Scatter .point .pointSelection:hover {
+ stroke: rgba(108, 117, 125, 0.6);
+ stroke-width: 2px;
+ filter: brightness(1.05);
+}
+
+.chart_Scatter .point .pointSelection:focus {
+ stroke: #007bff !important;
+ stroke-width: 3px !important;
+ paint-order: fill stroke !important;
}
+
+.chart_Scatter .point .pointSelection.selected:focus {
+ stroke: #6f42c1 !important;
+}
+
+.chart_Scatter .point .pointSelection:focus-visible {
+ outline: none;
+}
+
+.chart_Scatter .point .pointSelection:active {
+ outline: none !important;
+}
\ No newline at end of file
diff --git a/packages/chart/src/Scatter.ts b/packages/chart/src/Scatter.ts
index 9984a2c3fc..e53d683d7b 100644
--- a/packages/chart/src/Scatter.ts
+++ b/packages/chart/src/Scatter.ts
@@ -1,5 +1,5 @@
import { INDChart, ITooltip } from "@hpcc-js/api";
-import { InputField } from "@hpcc-js/common";
+import { d3Event, InputField } from "@hpcc-js/common";
import { extent as d3Extent } from "d3-array";
import { scaleLinear as d3ScaleLinear, scaleLog as d3ScaleLog, scalePow as d3ScalePow, scaleSqrt as d3ScaleSqrt } from "d3-scale";
import { select as d3Select } from "d3-selection";
@@ -129,6 +129,18 @@ export class Scatter extends XYAxis {
const height = isHorizontal ? this.height() : this.width();
const context = this;
+ if (this.tabNavigation() && host.parentRelativeDiv) {
+ host.parentRelativeDiv
+ .attr("tabindex", "0")
+ .attr("role", "group")
+ .attr("aria-label", `${this.columns()[0] || "Chart"} data`);
+ } else if (host.parentRelativeDiv) {
+ host.parentRelativeDiv
+ .attr("tabindex", null)
+ .attr("role", null)
+ .attr("aria-label", null);
+ }
+
this._palette = this._palette.switch(this.paletteID());
if (this.useClonedPalette()) {
this._palette = this._palette.cloneNotExists(this.paletteID() + "_" + this.id());
@@ -258,6 +270,15 @@ export class Scatter extends XYAxis {
.on("dblclick", function (d: any, _idx) {
context.dblclick(host.rowToObj(host.data()[d.rowIdx]), d.column, host._selection.selected(this));
})
+ .on("keydown", function (evt, d: any) {
+ if (context.tabNavigation()) {
+ const event = d3Event();
+ if (event.code === "Space" || event.key === "Enter") {
+ event.preventDefault();
+ host._selection.click(this);
+ }
+ }
+ })
;
})
.merge(points)
@@ -279,6 +300,9 @@ export class Scatter extends XYAxis {
.attr("cx", function (d) { return context.xPos(host, d); })
.attr("cy", function (d) { return context.yPos(host, d); })
.attr("r", d2.size)
+ .attr("tabindex", context.tabNavigation() ? 0 : null)
+ .attr("role", context.tabNavigation() ? "button" : null)
+ .attr("aria-label", context.tabNavigation() ? (d: any) => `${d.column || "Value"}: ${d.value} @ ${d.label}` : null)
;
const element = d3Select(this).select(".pointShape");
@@ -371,6 +395,7 @@ export interface Scatter {
tooltipHTML(_): string;
tooltipFormat(_): string;
tooltipStyle(): "default" | "none" | "series-table";
+ tooltipStyle(_: "default" | "none" | "series-table"): this;
}
Scatter.prototype.publish("paletteID", "default", "set", "Color palette for this widget", Scatter.prototype._palette.switch(), { tags: ["Basic", "Shared"] });
Scatter.prototype.publish("pointSizeScale", "linear", "set", "pointSizeScale", ["linear", "pow", "log", "sqrt"]);
diff --git a/packages/chart/src/XYAxis.ts b/packages/chart/src/XYAxis.ts
index 936daa6268..f74d646e1c 100644
--- a/packages/chart/src/XYAxis.ts
+++ b/packages/chart/src/XYAxis.ts
@@ -323,6 +323,7 @@ export class XYAxis extends SVGWidget {
protected _prevXAxisType;
update(domNode, element) {
+ this.selectionGlow(!this.tabNavigation());
super.update(domNode, element);
const context = this;
@@ -754,6 +755,10 @@ export interface XYAxis {
xAxisPadding(_: number): this;
yAxisPadding(): number;
yAxisPadding(_: number): this;
+
+ // Tab Navigation
+ tabNavigation(): boolean;
+ tabNavigation(_: boolean): this;
}
XYAxis.prototype.publish("orientation", "horizontal", "set", "Selects orientation for the axis", ["horizontal", "vertical"]);
@@ -801,3 +806,4 @@ XYAxis.prototype.publish("regions", [], "array", "Regions");
XYAxis.prototype.publish("layers", [], "widgetArray", "Layers", null, { render: false });
XYAxis.prototype.publishProxy("xAxisPadding", "domainAxis", "padding");
XYAxis.prototype.publishProxy("yAxisPadding", "valueAxis", "padding");
+XYAxis.prototype.publish("tabNavigation", false, "boolean", "Enable or disable tab navigation");
diff --git a/packages/common/index.html b/packages/common/index.html
index 6eb04986a3..2751ddd5d0 100644
--- a/packages/common/index.html
+++ b/packages/common/index.html
@@ -2,7 +2,8 @@
- Home
+ IconBar Demo
+
- ESM Quick Test
-
+ IconBar Quick Test
+ Buttons are grouped with spacers, hover to explore the built-in styling.
+
diff --git a/packages/common/src/SVGWidget.ts b/packages/common/src/SVGWidget.ts
index 2c5fd7e29b..e7295771a0 100644
--- a/packages/common/src/SVGWidget.ts
+++ b/packages/common/src/SVGWidget.ts
@@ -92,6 +92,7 @@ const intersectCircleLine = function (c: Point, r: number, a1: Point, a2: Point)
};
export class SVGGlowFilter {
+ protected id;
protected filter;
protected feOffset;
protected feColorMatrix;
@@ -99,6 +100,7 @@ export class SVGGlowFilter {
protected feBlend;
constructor(target, id: string) {
+ this.id = id;
this.filter = target.append("filter")
.attr("id", id)
.attr("width", "130%")
@@ -136,8 +138,14 @@ export class SVGGlowFilter {
].join(" ");
}
+ enable(enable: boolean) {
+ this.filter.attr("id", enable ? this.id : `disabled_${this.id}`);
+ return this;
+ }
+
update(color: string) {
this.feColorMatrix.attr("values", this.rgb2ColorMatrix(color));
+ return this;
}
}
@@ -147,7 +155,7 @@ export class SVGWidget extends Widget {
protected _boundingBox;
protected transition;
protected _drawStartPos: "center" | "origin";
- protected _svgSelectionFilter;
+ protected _svgSelectionFilter: SVGGlowFilter;
protected _parentRelativeDiv;
protected _parentOverlay;
@@ -278,6 +286,10 @@ export class SVGWidget extends Widget {
return retVal;
}
+ get parentRelativeDiv() {
+ return this._parentRelativeDiv;
+ }
+
parentOverlay() {
return this._parentOverlay;
}
@@ -289,7 +301,10 @@ export class SVGWidget extends Widget {
update(domNode, element) {
super.update(domNode, element);
if (this._svgSelectionFilter) {
- this._svgSelectionFilter.update(this.selectionGlowColor());
+ this._svgSelectionFilter
+ .enable(this.selectionGlow())
+ .update(this.selectionGlowColor())
+ ;
}
}
@@ -558,8 +573,11 @@ export class SVGWidget extends Widget {
SVGWidget.prototype._class += " common_SVGWidget";
export interface SVGWidget {
+ selectionGlow(): boolean;
+ selectionGlow(_: boolean): this;
selectionGlowColor(): string;
selectionGlowColor(_: string): this;
}
+SVGWidget.prototype.publish("selectionGlow", true, "boolean", "Selection Glow");
SVGWidget.prototype.publish("selectionGlowColor", "red", "html-color", "Selection Glow Color");
diff --git a/packages/common/src/TitleBar.css b/packages/common/src/TitleBar.css
index b002ad1b2a..ea56c1ff3d 100644
--- a/packages/common/src/TitleBar.css
+++ b/packages/common/src/TitleBar.css
@@ -11,27 +11,39 @@
margin: 0px;
white-space: nowrap;
line-height: 28px;
- z-index:1;
+ z-index: 1;
}
.common_IconBar .icon-bar a {
- text-align: center; /* Center-align text */
+ position: relative;
+ z-index: 0;
+ /* Center-align text */
+ text-align: center;
padding-top: 4px;
padding-left: 2px;
padding-right: 2px;
padding-bottom: 4px;
- transition: all 0.3s ease; /* Add transition for hover effects */
- color: darkgray; /* White text color */
+ /* Add transition for hover effects */
+ transition: all 0.3s ease;
+ /* White text color */
+ color: darkgray;
+}
+
+.common_IconBar .icon-bar a:focus,
+.common_IconBar .icon-bar a:focus-visible {
+ outline: 2px solid #0078d4;
+ outline-offset: 2px;
+ z-index: 2;
}
.common_IconBar .icon-bar a.disabled {
opacity: 0.3;
pointer-events: none;
- color: darkgray;
+ color: darkgray;
}
.common_IconBar .icon-bar a:hover {
- background-color: whitesmoke; /* Add a hover color */
+ background-color: whitesmoke;
}
.common_IconBar .icon-bar a {
@@ -39,36 +51,38 @@
}
.common_IconBar .icon-bar a.selected {
- background-color: #efe5e5; /* Add a hover color */
+ /* Add a hover color */
+ background-color: #efe5e5;
}
+
.common_IconBar .icon-bar div.spacer {
- text-align: center; /* Center-align text */
- height:28px;
+ text-align: center;
+ height: 28px;
border-left-style: solid;
border-left-width: 1px;
border-left-color: transparent;
padding-top: 0px;
- padding-left: 2px;
- margin-left: 2px;
+ padding-left: 2px;
+ margin-left: 2px;
padding-bottom: 0px;
}
.common_IconBar .icon-bar div.spacer.vline {
border-left-color: darkgray;
- padding-left: 4px;
- margin-left: 4px;
+ padding-left: 4px;
+ margin-left: 4px;
}
.common_IconBar .icon-bar a.spacer:hover {
background-color: transparent;
}
-.common_TitleBar > .title-title {
+.common_TitleBar>.title-title {
margin: 4px;
}
-.common_TitleBar > .icon-bar {
+.common_TitleBar>.icon-bar {
margin: 4px;
}
@@ -77,7 +91,8 @@
position: static;
}
-.common_TitleBar .data-count{
+
+.common_TitleBar .data-count {
position: absolute;
visibility: hidden;
}
@@ -90,10 +105,11 @@
font-size: 20px;
font-weight: bold;
}
+
.common_TitleBar .description-text {
padding: 0px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-weight: normal;
-}
+}
\ No newline at end of file
diff --git a/packages/common/src/TitleBar.ts b/packages/common/src/TitleBar.ts
index 79c8ad62c4..58ea2d70b7 100644
--- a/packages/common/src/TitleBar.ts
+++ b/packages/common/src/TitleBar.ts
@@ -24,6 +24,12 @@ export class Button extends HTMLWidget {
context.click();
d3Event.preventDefault();
})
+ .on("keydown", function (this: HTMLElement) {
+ if (d3Event.key === " " || d3Event.key === "Spacebar" || d3Event.code === "Space" || d3Event.key === "Enter") {
+ this.click();
+ d3Event.preventDefault();
+ }
+ })
.on("mousemove", this.mouseMove)
.on("mouseout", this.mouseOut)
.append("i")
diff --git a/packages/graph/index.html b/packages/graph/index.html
index acafdaa2ce..1c815ef391 100644
--- a/packages/graph/index.html
+++ b/packages/graph/index.html
@@ -42,6 +42,7 @@ ESM Quick Test
new Test()
.target("placeholder1")
+ .tabNavigation(true)
.render()
;
@@ -52,6 +53,7 @@ ESM Quick Test
new Test()
.target("placeholder2")
+
.render()
;
diff --git a/packages/graph/src/common/graphT.css b/packages/graph/src/common/graphT.css
index d6c8ae8c81..6964a86d5a 100644
--- a/packages/graph/src/common/graphT.css
+++ b/packages/graph/src/common/graphT.css
@@ -30,4 +30,10 @@
.graph_GraphT g.selected circle {
stroke: navy !important;
+}
+
+.graph_GraphT .graphVertex:focus,
+.graph_GraphT .graphVertex:focus-visible {
+ outline: 2px solid #0078d4;
+ outline-offset: 2px;
}
\ No newline at end of file
diff --git a/packages/graph/src/common/graphT.ts b/packages/graph/src/common/graphT.ts
index 492426b67f..7e2ad8df68 100644
--- a/packages/graph/src/common/graphT.ts
+++ b/packages/graph/src/common/graphT.ts
@@ -747,6 +747,34 @@ export class GraphT d.element as any
+ };
+ if (event.ctrlKey || event.metaKey) {
+ if (context._selection.isSelected(selectionItem)) {
+ context._selection.remove(selectionItem);
+ } else {
+ context._selection.append(selectionItem);
+ }
+ } else {
+ context._selection.clear();
+ context._selection.append(selectionItem);
+ }
+
+ context.selectionChanged();
+ const selected = d.element.classed("selected");
+ const eventOrigin = context.resolveEventOrigin();
+ context.vertex_click(d.props.origData || d.props, "", selected, eventOrigin);
+ })
.on("mousein", function (d) {
Utility.safeRaise(this);
context.highlightVertex(d3Select(this), d);
@@ -798,6 +826,10 @@ export class GraphT d.props.centroid)
.attr("opacity", d => d.props.hidden ? 0 : 1)
.attr("filter", d => d.props.centroid ? "url(#" + this.id() + "_glow)" : null)
+ .attr("tabindex", d => context.tabNavigation() && !d.props.hidden ? 0 : null)
+ .attr("role", context.tabNavigation() ? "button" : null)
+ .attr("aria-label", d => context.tabNavigation() ? context.vertexAriaLabel(d) : null)
+ .attr("aria-hidden", d => d.props.hidden ? "true" : null)
.each(function (this: any, d) {
const props = context.calcProps(
{
@@ -815,6 +847,10 @@ export class GraphT): string {
+ return d.props?.text || `Vertex ${d.id}`;
+ }
+
hasSubgraphs() {
switch (this.layout()) {
case "DOT":
@@ -1008,6 +1044,21 @@ export class GraphT> {
constructor() {
diff --git a/packages/graph/tests/test6.ts b/packages/graph/tests/test6.ts
index 3248948c31..0ef31738c1 100644
--- a/packages/graph/tests/test6.ts
+++ b/packages/graph/tests/test6.ts
@@ -1,4 +1,5 @@
import { GraphHtml, VertexProps } from "../src/index.ts";
+import { genData2 } from "./data";
const VERTEX_ARR: VertexProps[] = [{
id: 0,
@@ -71,6 +72,7 @@ export class Test6 extends GraphHtml {
constructor() {
super();
+ // const g = genData2();
this
.data({
vertices: VERTEX_ARR,