diff --git a/packages/pluggableWidgets/slider-web/CHANGELOG.md b/packages/pluggableWidgets/slider-web/CHANGELOG.md index 20079df8d5..0753d662c3 100644 --- a/packages/pluggableWidgets/slider-web/CHANGELOG.md +++ b/packages/pluggableWidgets/slider-web/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Fixed + +- We fixed mark labels and tooltip values not preserving trailing zeros when decimal places are configured (e.g., `10` now displays as `10.00` and `9.2` as `9.20` when two decimal places are set). + ## [3.0.2] - 2026-02-19 ### Fixed diff --git a/packages/pluggableWidgets/slider-web/src/components/Container.tsx b/packages/pluggableWidgets/slider-web/src/components/Container.tsx index b26135235b..9a708ec1f0 100644 --- a/packages/pluggableWidgets/slider-web/src/components/Container.tsx +++ b/packages/pluggableWidgets/slider-web/src/components/Container.tsx @@ -30,14 +30,19 @@ interface InnerContainerProps extends SliderContainerProps { function InnerContainer(props: InnerContainerProps): ReactElement { const sliderRef = useRef(null); - const handleRender = props.showTooltip - ? createHandleRender({ - tooltip: props.tooltip, - tooltipType: props.tooltipType, - tooltipAlwaysVisible: props.tooltipAlwaysVisible, - sliderRef - }) - : undefined; + const handleRender = useMemo( + () => + props.showTooltip + ? createHandleRender({ + tooltip: props.tooltip, + tooltipType: props.tooltipType, + tooltipAlwaysVisible: props.tooltipAlwaysVisible, + sliderRef, + decimalPlaces: props.decimalPlaces + }) + : undefined, + [props.showTooltip, props.tooltip, props.tooltipType, props.tooltipAlwaysVisible, props.decimalPlaces] + ); const { onChange } = useOnChangeDebounced({ valueAttribute: props.valueAttribute, onChange: props.onChange }); const marks = useMarks({ diff --git a/packages/pluggableWidgets/slider-web/src/utils/__tests__/createHandleRender.spec.tsx b/packages/pluggableWidgets/slider-web/src/utils/__tests__/createHandleRender.spec.tsx new file mode 100644 index 0000000000..d149907aa0 --- /dev/null +++ b/packages/pluggableWidgets/slider-web/src/utils/__tests__/createHandleRender.spec.tsx @@ -0,0 +1,51 @@ +import { createRef, ReactElement } from "react"; +import { createHandleRender } from "../createHandleRender"; + +const defaultRenderProps = { + dragging: false, + index: 0, + prefixCls: "rc-slider-handle", + draggingDelete: false, + onFocus: jest.fn(), + onBlur: jest.fn() +}; + +const mockNode =
; + +function buildHandleRender(decimalPlaces: number, tooltipType: "value" | "customText" = "value") { + const sliderRef = createRef(); + return createHandleRender({ tooltipType, tooltipAlwaysVisible: true, sliderRef, decimalPlaces })!; +} + +describe("createHandleRender tooltip value formatting", () => { + it("formats whole number with trailing zeros when decimalPlaces=2", () => { + const result = buildHandleRender(2)(mockNode, { ...defaultRenderProps, value: 10 } as any) as ReactElement; + expect(result.props.overlay).toBe("10.00"); + }); + + it("formats partial decimal with trailing zero when decimalPlaces=2", () => { + const result = buildHandleRender(2)(mockNode, { + ...defaultRenderProps, + value: 9.2 + } as any) as ReactElement; + expect(result.props.overlay).toBe("9.20"); + }); + + it("formats value without decimals when decimalPlaces=0", () => { + const result = buildHandleRender(0)(mockNode, { ...defaultRenderProps, value: 10 } as any) as ReactElement; + expect(result.props.overlay).toBe("10"); + }); + + it("renders custom text tooltip ignoring decimalPlaces", () => { + const sliderRef = createRef(); + const handleRender = createHandleRender({ + tooltip: { value: "custom label" } as any, + tooltipType: "customText", + tooltipAlwaysVisible: true, + sliderRef, + decimalPlaces: 2 + })!; + const result = handleRender(mockNode, { ...defaultRenderProps, value: 10 } as any) as ReactElement; + expect(result.props.overlay.props.children).toBe("custom label"); + }); +}); diff --git a/packages/pluggableWidgets/slider-web/src/utils/__tests__/marks.spec.ts b/packages/pluggableWidgets/slider-web/src/utils/__tests__/marks.spec.ts new file mode 100644 index 0000000000..8bc3458350 --- /dev/null +++ b/packages/pluggableWidgets/slider-web/src/utils/__tests__/marks.spec.ts @@ -0,0 +1,34 @@ +import { createMarks } from "../marks"; + +describe("createMarks", () => { + it("forces trailing zeros when decimalPlaces > 0 and value is whole number", () => { + const marks = createMarks({ numberOfMarks: 2, decimalPlaces: 2, min: 0, max: 10 }); + expect(marks).toBeDefined(); + expect(marks![0]).toBe("0.00"); + expect(marks![5]).toBe("5.00"); + expect(marks![10]).toBe("10.00"); + }); + + it("forces trailing zeros when decimalPlaces > 0 and value has fewer decimals", () => { + const marks = createMarks({ numberOfMarks: 2, decimalPlaces: 2, min: 0, max: 9.2 }); + expect(marks).toBeDefined(); + expect(marks![4.6]).toBe("4.60"); + expect(marks![9.2]).toBe("9.20"); + }); + + it("does not add decimal places when decimalPlaces is 0", () => { + const marks = createMarks({ numberOfMarks: 4, decimalPlaces: 0, min: 0, max: 100 }); + expect(marks).toBeDefined(); + expect(marks![0]).toBe("0"); + expect(marks![25]).toBe("25"); + expect(marks![100]).toBe("100"); + }); + + it("returns undefined when numberOfMarks is 0", () => { + expect(createMarks({ numberOfMarks: 0, decimalPlaces: 2, min: 0, max: 100 })).toBeUndefined(); + }); + + it("returns undefined when min equals max", () => { + expect(createMarks({ numberOfMarks: 4, decimalPlaces: 2, min: 5, max: 5 })).toBeUndefined(); + }); +}); diff --git a/packages/pluggableWidgets/slider-web/src/utils/createHandleRender.tsx b/packages/pluggableWidgets/slider-web/src/utils/createHandleRender.tsx index 5b1a86bdc4..d5a9760a55 100644 --- a/packages/pluggableWidgets/slider-web/src/utils/createHandleRender.tsx +++ b/packages/pluggableWidgets/slider-web/src/utils/createHandleRender.tsx @@ -10,13 +10,15 @@ type CreateHandleRenderProps = { tooltipType: "value" | "customText"; tooltipAlwaysVisible: boolean; sliderRef: RefObject; + decimalPlaces: number; }; export function createHandleRender({ tooltip, tooltipType, tooltipAlwaysVisible, - sliderRef + sliderRef, + decimalPlaces }: CreateHandleRenderProps): RcSliderProps["handleRender"] | undefined { const isCustomText = tooltipType === "customText"; @@ -29,7 +31,7 @@ export function createHandleRender({ getTooltipContainer={() => sliderRef.current ?? document.body} defaultVisible prefixCls="rc-slider-tooltip" - overlay={isCustomText ? overlay : restProps.value} + overlay={isCustomText ? overlay : restProps.value.toFixed(decimalPlaces)} trigger={["hover", "click", "focus"]} visible={tooltipAlwaysVisible || dragging} placement="top" diff --git a/packages/pluggableWidgets/slider-web/src/utils/marks.ts b/packages/pluggableWidgets/slider-web/src/utils/marks.ts index 2c27eba8e6..c70e24cf10 100644 --- a/packages/pluggableWidgets/slider-web/src/utils/marks.ts +++ b/packages/pluggableWidgets/slider-web/src/utils/marks.ts @@ -24,8 +24,9 @@ export function createMarks(params: CreateMarksParams): Marks | undefined { const interval = (max - min) / numberOfMarks; for (let i = 0; i <= numberOfMarks; i++) { - const value = parseFloat((min + i * interval).toFixed(decimalPlaces)); - marks[value] = value.toString(); + const label = (min + i * interval).toFixed(decimalPlaces); + const key = parseFloat(label); + marks[key] = label; } return marks;