From d74acdaa0ef38d09b235c1d3928bedee0e6f0194 Mon Sep 17 00:00:00 2001 From: Emmad Usmani <emmadusmani@berkeley.edu> Date: Wed, 4 Sep 2024 11:49:23 -0700 Subject: [PATCH] remove static viz import from pie chart option (#47159) --- ...ptop_static_viz_Gauge_Truncated_Labels.png | Bin 0 -> 26421 bytes .../components/FunnelChart/utils/funnel.ts | 29 +++++++++++-- .../components/Gauge/Gauge.stories.tsx | 4 ++ .../components/Gauge/GaugeContainer.tsx | 12 ++++- .../components/Gauge/stories-data.ts | 40 +++++++++++++++++ .../static-viz/components/Legend/Legend.tsx | 12 +++-- .../static-viz/components/Legend/utils.ts | 12 +++-- frontend/src/metabase/static-viz/lib/text.ts | 21 --------- .../metabase/static-viz/lib/text.unit.spec.js | 16 +------ .../visualizations/echarts/pie/option.ts | 14 ++++-- .../src/metabase/visualizations/lib/text.ts | 23 ++++++++++ .../visualizations/lib/text.unit.spec.ts | 41 ++++++++++++++++++ 12 files changed, 173 insertions(+), 51 deletions(-) create mode 100644 .loki/reference/chrome_laptop_static_viz_Gauge_Truncated_Labels.png create mode 100644 frontend/src/metabase/visualizations/lib/text.ts create mode 100644 frontend/src/metabase/visualizations/lib/text.unit.spec.ts diff --git a/.loki/reference/chrome_laptop_static_viz_Gauge_Truncated_Labels.png b/.loki/reference/chrome_laptop_static_viz_Gauge_Truncated_Labels.png new file mode 100644 index 0000000000000000000000000000000000000000..7d5a4b0beca413a1d8d8ff7e0c7dfbcee0e1f3af GIT binary patch literal 26421 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 A!vFvP literal 0 HcmV?d00001 diff --git a/frontend/src/metabase/static-viz/components/FunnelChart/utils/funnel.ts b/frontend/src/metabase/static-viz/components/FunnelChart/utils/funnel.ts index 77aa661e018..883c4c25d43 100644 --- a/frontend/src/metabase/static-viz/components/FunnelChart/utils/funnel.ts +++ b/frontend/src/metabase/static-viz/components/FunnelChart/utils/funnel.ts @@ -1,8 +1,11 @@ import type { PolygonProps } from "@visx/shape/lib/shapes/Polygon"; import { isNotNull } from "metabase/lib/types"; +import { CHAR_SIZES_FONT_WEIGHT } from "metabase/static-viz/constants/char-sizes"; import { formatNumber, formatPercent } from "metabase/static-viz/lib/numbers"; -import { truncateText } from "metabase/static-viz/lib/text"; +import { measureTextWidth } from "metabase/static-viz/lib/text"; +import { truncateText } from "metabase/visualizations/lib/text"; +import type { TextWidthMeasurer } from "metabase/visualizations/shared/types/measure-text"; import type { FunnelDatum, FunnelSettings, FunnelStep, Step } from "../types"; @@ -63,10 +66,21 @@ export const getFormattedStep = ( typeof step.step === "number" ? formatNumber(step.step, settings?.step?.format) : step.step; + + const textMeasurer: TextWidthMeasurer = (text, style) => + measureTextWidth(text, Number(style.size), Number(style.weight)); + + const fontStyle = { + size: stepFontSize, + weight: CHAR_SIZES_FONT_WEIGHT, + family: "Lato", + }; + const stepName = truncateText( formattedStepName, maxStepTextWidth, - stepFontSize, + textMeasurer, + fontStyle, ); const formattedMeasure = formatNumber( @@ -75,12 +89,19 @@ export const getFormattedStep = ( ); const measure = isFirst ? formattedMeasure - : truncateText(formattedMeasure, maxStepTextWidth, measureFontSize); + : truncateText(formattedMeasure, maxStepTextWidth, textMeasurer, { + ...fontStyle, + size: measureFontSize, + }); const percent = truncateText( formatPercent(step.percent), maxStepTextWidth, - percentFontSize, + textMeasurer, + { + ...fontStyle, + size: percentFontSize, + }, ); return { diff --git a/frontend/src/metabase/static-viz/components/Gauge/Gauge.stories.tsx b/frontend/src/metabase/static-viz/components/Gauge/Gauge.stories.tsx index 31677b8f8c6..687c02ca8ff 100644 --- a/frontend/src/metabase/static-viz/components/Gauge/Gauge.stories.tsx +++ b/frontend/src/metabase/static-viz/components/Gauge/Gauge.stories.tsx @@ -4,6 +4,7 @@ import { color } from "metabase/lib/colors"; import GaugeContainer from "metabase/static-viz/components/Gauge/GaugeContainer"; import { DEFAULT, + TRUNCATED_LABELS, WITH_FORMATTING, } from "metabase/static-viz/components/Gauge/stories-data"; @@ -21,3 +22,6 @@ Default.args = { ...DEFAULT, getColor: color }; export const WithFormatting = Template.bind({}); WithFormatting.args = { ...WITH_FORMATTING, getColor: color }; + +export const TruncatedLabels = Template.bind({}); +TruncatedLabels.args = { ...TRUNCATED_LABELS, getColor: color }; diff --git a/frontend/src/metabase/static-viz/components/Gauge/GaugeContainer.tsx b/frontend/src/metabase/static-viz/components/Gauge/GaugeContainer.tsx index 92ac7d6ad08..e45f1f978de 100644 --- a/frontend/src/metabase/static-viz/components/Gauge/GaugeContainer.tsx +++ b/frontend/src/metabase/static-viz/components/Gauge/GaugeContainer.tsx @@ -1,5 +1,7 @@ +import { CHAR_SIZES_FONT_WEIGHT } from "metabase/static-viz/constants/char-sizes"; import { formatNumber } from "metabase/static-viz/lib/numbers"; -import { truncateText } from "metabase/static-viz/lib/text"; +import { measureTextWidth } from "metabase/static-viz/lib/text"; +import { truncateText } from "metabase/visualizations/lib/text"; import type { ColorGetter } from "metabase/visualizations/types"; import Gauge from "./Gauge"; @@ -126,7 +128,13 @@ export default function GaugeContainer({ value: truncateText( segment.label, MAX_SEGMENT_VALUE_WIDTH, - SEGMENT_LABEL_FONT_SIZE, + (text, style) => + measureTextWidth(text, Number(style.size), Number(style.weight)), + { + size: SEGMENT_LABEL_FONT_SIZE, + family: "Lato", + weight: CHAR_SIZES_FONT_WEIGHT, + }, ), color: getColor("text-dark"), }; diff --git a/frontend/src/metabase/static-viz/components/Gauge/stories-data.ts b/frontend/src/metabase/static-viz/components/Gauge/stories-data.ts index 2271862d785..322f4ae23ab 100644 --- a/frontend/src/metabase/static-viz/components/Gauge/stories-data.ts +++ b/frontend/src/metabase/static-viz/components/Gauge/stories-data.ts @@ -69,3 +69,43 @@ export const WITH_FORMATTING: Omit<GaugeContainerProps, "getColor"> = { rows: [[18760]], }, }; + +export const TRUNCATED_LABELS: Omit<GaugeContainerProps, "getColor"> = { + card: { + visualization_settings: { + "gauge.segments": [ + { + min: 0, + max: 9380, + color: "#ED6E6E", + label: "The quick brown fox jumps over the lazy dog.", + }, + { + min: 9380, + max: 18760, + color: "#F9CF48", + label: "The quick brown fox jumps over the lazy dog.", + }, + { + min: 18760, + max: 37520, + color: "#84BB4C", + label: "The quick brown fox jumps over the lazy dog.", + }, + ], + column_settings: { + '["name","count"]': { + number_style: "currency", + number_separators: ".’", + scale: 2, + prefix: "<", + suffix: ">", + decimals: 1, + }, + }, + }, + }, + data: { + rows: [[18760]], + }, +}; diff --git a/frontend/src/metabase/static-viz/components/Legend/Legend.tsx b/frontend/src/metabase/static-viz/components/Legend/Legend.tsx index d0926d43933..27ec09a82e3 100644 --- a/frontend/src/metabase/static-viz/components/Legend/Legend.tsx +++ b/frontend/src/metabase/static-viz/components/Legend/Legend.tsx @@ -1,7 +1,8 @@ import { Group } from "@visx/group"; import { Text } from "metabase/static-viz/components/Text"; -import { measureTextWidth, truncateText } from "metabase/static-viz/lib/text"; +import { measureTextWidth } from "metabase/static-viz/lib/text"; +import { truncateText } from "metabase/visualizations/lib/text"; import { DEFAULT_LEGEND_FONT_SIZE, @@ -49,8 +50,13 @@ export const Legend = ({ LEGEND_CIRCLE_SIZE - LEGEND_CIRCLE_MARGIN_RIGHT - legendItemMarginRight, - fontSize, - fontWeight, + (text, style) => + measureTextWidth( + text, + Number(style.size), + Number(style.weight), + ), + { size: fontSize, weight: fontWeight, family: "Lato" }, ) : originalName; diff --git a/frontend/src/metabase/static-viz/components/Legend/utils.ts b/frontend/src/metabase/static-viz/components/Legend/utils.ts index 66719517da3..5549bf88edb 100644 --- a/frontend/src/metabase/static-viz/components/Legend/utils.ts +++ b/frontend/src/metabase/static-viz/components/Legend/utils.ts @@ -1,5 +1,6 @@ -import { measureTextWidth, truncateText } from "metabase/static-viz/lib/text"; +import { measureTextWidth } from "metabase/static-viz/lib/text"; import type { LegendItem } from "metabase/visualizations/echarts/cartesian/model/types"; +import { truncateText } from "metabase/visualizations/lib/text"; import { DEFAULT_LEGEND_FONT_SIZE, @@ -105,8 +106,13 @@ export const calculateLegendRows = ({ name: truncateText( item.name, availableTotalWidth, - fontSize, - fontWeight, + (text, style) => + measureTextWidth(text, Number(style.size), Number(style.weight)), + { + size: fontSize, + weight: fontWeight, + family: "Lato", + }, ), left: horizontalPadding, top: currentRowIndex * lineHeight + verticalPadding, diff --git a/frontend/src/metabase/static-viz/lib/text.ts b/frontend/src/metabase/static-viz/lib/text.ts index 0c92d1b6e4d..0beb06dc5c3 100644 --- a/frontend/src/metabase/static-viz/lib/text.ts +++ b/frontend/src/metabase/static-viz/lib/text.ts @@ -6,7 +6,6 @@ import { CHAR_SIZES_FONT_WEIGHT, } from "../constants/char-sizes"; -const CHAR_ELLIPSES = "…"; const FONT_WEIGHT_WIDTH_FACTOR = 0.039; export const { getTextWidth } = init(CHAR_SIZES); @@ -34,26 +33,6 @@ export const measureTextHeight = (fontSize: number) => { return fontSize * 1.3; }; -export const truncateText = ( - text: string, - width: number, - fontSize: number, - fontWeight = CHAR_SIZES_FONT_WEIGHT, -) => { - if (measureTextWidth(text, fontSize, fontWeight) <= width) { - return text; - } - - while ( - text.length && - measureTextWidth(text + CHAR_ELLIPSES, fontSize, fontWeight) > width - ) { - text = text.substring(0, text.length - 1).trim(); - } - - return text + CHAR_ELLIPSES; -}; - const parseEChartsFontString = (fontString: string) => { const parts = fontString.split(/\s+/); diff --git a/frontend/src/metabase/static-viz/lib/text.unit.spec.js b/frontend/src/metabase/static-viz/lib/text.unit.spec.js index 3468a2b8b88..e5d5d5fa696 100644 --- a/frontend/src/metabase/static-viz/lib/text.unit.spec.js +++ b/frontend/src/metabase/static-viz/lib/text.unit.spec.js @@ -1,4 +1,4 @@ -import { measureTextWidth, truncateText } from "./text"; +import { measureTextWidth } from "./text"; const fontSize = 11; @@ -7,17 +7,3 @@ describe("measureTextWidth", () => { expect(Math.round(measureTextWidth("abc", fontSize))).toBe(17); }); }); - -describe("truncateText", () => { - it("should not truncate text with ellipses if there is no overflow", () => { - expect(truncateText("John Doe", 48, fontSize)).toBe("John Doe"); - }); - - it("should truncate text with ellipses if there is overflow", () => { - expect(truncateText("John Doe", 48, fontSize)).toBe("John Doe"); - }); - - it("should use ellipses in case there is no space for text at all", () => { - expect(truncateText("John Doe", 0, fontSize)).toBe("…"); - }); -}); diff --git a/frontend/src/metabase/visualizations/echarts/pie/option.ts b/frontend/src/metabase/visualizations/echarts/pie/option.ts index 006753df1f8..0afec622d99 100644 --- a/frontend/src/metabase/visualizations/echarts/pie/option.ts +++ b/frontend/src/metabase/visualizations/echarts/pie/option.ts @@ -2,7 +2,7 @@ import Color from "color"; import type { EChartsOption } from "echarts"; import { getTextColorForBackground } from "metabase/lib/colors"; -import { truncateText } from "metabase/static-viz/lib/text"; +import { truncateText } from "metabase/visualizations/lib/text"; import type { ComputedVisualizationSettings, RenderingContext, @@ -52,17 +52,25 @@ function getTotalGraphicOption( weight: DIMENSIONS.total.fontWeight, }) > outerRadius; // innerRadius technically makes more sense, but looks too narrow in practice + const fontStyle = { + size: DIMENSIONS.total.valueFontSize, + weight: DIMENSIONS.total.fontWeight, + family: renderingContext.fontFamily, + }; + valueText = truncateText( formatters.formatMetric(sliceValueOrTotal, valueWillOverflow), outerRadius, - DIMENSIONS.total.valueFontSize, + renderingContext.measureText, + fontStyle, ); labelText = truncateText( hoveredIndex != null ? chartModel.slices[hoveredIndex].data.name.toUpperCase() : TOTAL_TEXT, outerRadius, - DIMENSIONS.total.labelFontSize, + renderingContext.measureText, + fontStyle, ); } diff --git a/frontend/src/metabase/visualizations/lib/text.ts b/frontend/src/metabase/visualizations/lib/text.ts new file mode 100644 index 00000000000..20c9aa5b3f6 --- /dev/null +++ b/frontend/src/metabase/visualizations/lib/text.ts @@ -0,0 +1,23 @@ +import type { + FontStyle, + TextWidthMeasurer, +} from "../shared/types/measure-text"; + +const CHAR_ELLIPSES = "…"; + +export function truncateText( + text: string, + width: number, + measurer: TextWidthMeasurer, + fontStyle: FontStyle, +) { + if (measurer(text, fontStyle) <= width) { + return text; + } + + while (text.length && measurer(text + CHAR_ELLIPSES, fontStyle) > width) { + text = text.substring(0, text.length - 1).trim(); + } + + return text + CHAR_ELLIPSES; +} diff --git a/frontend/src/metabase/visualizations/lib/text.unit.spec.ts b/frontend/src/metabase/visualizations/lib/text.unit.spec.ts new file mode 100644 index 00000000000..24eaa868acf --- /dev/null +++ b/frontend/src/metabase/visualizations/lib/text.unit.spec.ts @@ -0,0 +1,41 @@ +import { measureTextWidth as measureDynamic } from "metabase/lib/measure-text"; +import { measureTextWidth as measureStatic } from "metabase/static-viz/lib/text"; + +import type { TextWidthMeasurer } from "../shared/types/measure-text"; + +import { truncateText } from "./text"; + +const fontStyle = { + size: 11, + weight: 400, + family: "Lato", +}; + +const tests: { name: string; measurer: TextWidthMeasurer }[] = [ + { name: "truncateText - dynamic viz measurer", measurer: measureDynamic }, + { + name: "truncateText - static viz measurer", + measurer: (text, style) => + measureStatic(text, Number(style.size), Number(style.weight)), + }, +]; + +tests.map(test => { + describe(test.name, () => { + it("should not truncate text with ellipses if there is no overflow", () => { + expect(truncateText("John Doe", 48, test.measurer, fontStyle)).toBe( + "John Doe", + ); + }); + + it("should truncate text with ellipses if there is overflow", () => { + expect(truncateText("John Doe", 48, test.measurer, fontStyle)).toBe( + "John Doe", + ); + }); + + it("should use ellipses in case there is no space for text at all", () => { + expect(truncateText("John Doe", 0, test.measurer, fontStyle)).toBe("…"); + }); + }); +}); -- GitLab