diff --git a/web/src/components/traces/FlameGraphView.spec.ts b/web/src/components/traces/FlameGraphView.spec.ts index 4f8b914925..0cce033f4e 100644 --- a/web/src/components/traces/FlameGraphView.spec.ts +++ b/web/src/components/traces/FlameGraphView.spec.ts @@ -70,6 +70,8 @@ vi.mock("@/utils/traces/serviceColorRegistry", () => ({ "service-1": "#4caf50", "service-2": "#2196f3", "service-3": "#ff9800", + "lime-service": "#84cc16", + "violet-service": "#7c3aed", }; return colorMap[name] || "#9CA3AF"; }), @@ -1483,6 +1485,21 @@ describe("FlameGraphView span event markers", () => { expect(result.children.slice(1).every((c: any) => c.silent)).toBe(true); }); + it.each([ + ["lime-service", "#000000"], + ["violet-service", "#ffffff"], + ])("picks a label colour that contrasts with the %s bar", (service, expected) => { + const wrapper = mount(FlameGraphView, { + props: { + spans: [{ ...spanWithEvents, resolvedIdentity: service, events: [] }], + traceDuration: 100, + selectedSpanId: null, + }, + }); + + expect(renderBlock(wrapper, 100).textContent.style.fill).toBe(expected); + }); + it("returns a bare rect for a span with no events", () => { const wrapper = mount(FlameGraphView, { props: { diff --git a/web/src/components/traces/FlameGraphView.vue b/web/src/components/traces/FlameGraphView.vue index a53e5d3a34..bd5e008cb0 100644 --- a/web/src/components/traces/FlameGraphView.vue +++ b/web/src/components/traces/FlameGraphView.vue @@ -163,6 +163,7 @@ import { type EnrichedSpan } from "@/ts/interfaces/traces/span.types"; import { formatDuration } from "@/composables/traces/useTraceProcessing"; import { maxOf } from "@/plugins/traces/threadView.utils"; import { getOrSetServiceColor } from "@/utils/traces/serviceColorRegistry"; +import { getContrastTextColor } from "@/utils/traces/traceColors"; import { escapeHtml } from "@/utils/html"; import { normalizeSpanEvents, @@ -649,7 +650,10 @@ const chartOptions = computed(() => { type: "text", style: { text: data[params.dataIndex].spanData.operationName, - fill: "#ffffff", + fill: + getContrastTextColor(data[params.dataIndex].itemStyle.color) === "black" + ? "#000000" + : "#ffffff", font: "0.6875rem Inter, sans-serif", overflow: "truncate", width: rectWidth - 8, diff --git a/web/src/plugins/traces/TraceDetails.spec.ts b/web/src/plugins/traces/TraceDetails.spec.ts index c345fbb21d..0b79a9d413 100644 --- a/web/src/plugins/traces/TraceDetails.spec.ts +++ b/web/src/plugins/traces/TraceDetails.spec.ts @@ -1153,6 +1153,14 @@ describe("TraceDetails", () => { expect(closeBtn.exists()).toBe(true); }); + it("renders the close button as outline with a Close tooltip", () => { + const closeBtn = wrapper.findComponent('[data-test="trace-details-close-btn"]'); + expect(closeBtn.exists()).toBe(true); + expect(closeBtn.attributes("data-o2-variant")).toBe("outline"); + const tooltip = closeBtn.findComponent({ name: "OTooltip" }); + expect(tooltip.props("content")).toBe(i18n.global.t("common.close")); + }); + it("should show share button when showShareButton is true in standalone mode", () => { const shareBtn = wrapper.find('[data-test="trace-details-share-link-btn"]'); expect(shareBtn.exists()).toBe(true); diff --git a/web/src/plugins/traces/TraceDetails.vue b/web/src/plugins/traces/TraceDetails.vue index 0fae8b8ba7..b6f0753bbd 100644 --- a/web/src/plugins/traces/TraceDetails.vue +++ b/web/src/plugins/traces/TraceDetails.vue @@ -215,12 +215,12 @@ along with this program. If not, see . - + @@ -392,13 +392,13 @@ along with this program. If not, see . - + diff --git a/web/src/plugins/traces/metrics/TracesComparisonField.spec.ts b/web/src/plugins/traces/metrics/TracesComparisonField.spec.ts index 84b28af2cf..933d8ed96f 100644 --- a/web/src/plugins/traces/metrics/TracesComparisonField.spec.ts +++ b/web/src/plugins/traces/metrics/TracesComparisonField.spec.ts @@ -164,6 +164,14 @@ describe("TracesComparisonField", () => { expect(o.series[1].itemStyle.color).toBe("#0a4ce8"); }); + it("renders the histogram tooltip on the body, so the card does not clip it", async () => { + wrapper = mountField(numeric()); + await settle(); + const o = chartOptions(wrapper); + expect(o.tooltip.appendToBody).toBe(true); + expect(o.tooltip.className).toBe("o2-echarts-tooltip"); + }); + it("pre-selects the most different bin and marks it", async () => { const field = numeric(); wrapper = mountField(field); diff --git a/web/src/plugins/traces/metrics/TracesComparisonField.vue b/web/src/plugins/traces/metrics/TracesComparisonField.vue index bd7b21ec0d..c2e825036a 100644 --- a/web/src/plugins/traces/metrics/TracesComparisonField.vue +++ b/web/src/plugins/traces/metrics/TracesComparisonField.vue @@ -210,6 +210,9 @@ const options = computed(() => { grid: { left: 4, right: 4, top: 6, bottom: 20 }, tooltip: { trigger: "axis", + // An overflow-hidden card ancestor clips a tooltip rendered inside the chart. + appendToBody: true, + className: "o2-echarts-tooltip", axisPointer: { type: "shadow" }, backgroundColor: chartColor("--color-tooltip-bg"), borderWidth: 0, diff --git a/web/src/plugins/traces/metrics/TracesLatencyHeatmap.spec.ts b/web/src/plugins/traces/metrics/TracesLatencyHeatmap.spec.ts index 8b184a66e0..7cdc53b401 100644 --- a/web/src/plugins/traces/metrics/TracesLatencyHeatmap.spec.ts +++ b/web/src/plugins/traces/metrics/TracesLatencyHeatmap.spec.ts @@ -276,6 +276,13 @@ describe("TracesLatencyHeatmap", () => { expect(o.series[0].type).toBe("heatmap"); }); + it("renders the tooltip on the body, so the card does not clip it", async () => { + wrapper = await mountHeatmap(); + const o = options(wrapper); + expect(o.tooltip.appendToBody).toBe(true); + expect(o.tooltip.className).toBe("o2-echarts-tooltip"); + }); + it("draws cells without borders, so a dense grid is not striped", async () => { wrapper = await mountHeatmap(); expect(options(wrapper).series[0].itemStyle.borderWidth).toBe(0); diff --git a/web/src/plugins/traces/metrics/TracesLatencyHeatmap.vue b/web/src/plugins/traces/metrics/TracesLatencyHeatmap.vue index c5b27b0faf..b1b1fa5c48 100644 --- a/web/src/plugins/traces/metrics/TracesLatencyHeatmap.vue +++ b/web/src/plugins/traces/metrics/TracesLatencyHeatmap.vue @@ -238,6 +238,9 @@ const options = computed(() => { grid: { left: 52, right: 8, top: 8, bottom: 22 }, tooltip: { trigger: "item", + // An overflow-hidden card ancestor clips a tooltip rendered inside the chart. + appendToBody: true, + className: "o2-echarts-tooltip", backgroundColor: chartColor("--color-tooltip-bg"), borderWidth: 0, textStyle: { color: chartColor("--color-tooltip-text"), fontSize: 12 },