Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/pluggableWidgets/range-slider-web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Fixed

- Mark labels and numeric tooltips now respect the configured `decimalPlaces` and the user's session locale (decimal separator and thousands grouping). Previously, marks and tooltips rendered raw numbers via `.toString()`, ignoring locale and decimal precision settings.

## [3.0.1] - 2026-02-10

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ test.describe("Range Slider", () => {
});

test("renders slider min value text", async ({ page }) => {
await expect(page.locator(".mx-name-rangeSlider1 .rc-slider-mark-text").first()).toHaveText("0");
await expect(page.locator(".mx-name-rangeSlider1 .rc-slider-mark-text").first()).toHaveText("0.0");
});

test("renders slider max value text", async ({ page }) => {
await expect(page.locator(".mx-name-rangeSlider1 .rc-slider-mark-text").nth(1)).toHaveText("100");
await expect(page.locator(".mx-name-rangeSlider1 .rc-slider-mark-text").nth(1)).toHaveText("100.0");
});

test("upper bound value is higher than lower bound value", async ({ page }) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/pluggableWidgets/range-slider-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
},
"dependencies": {
"@mendix/widget-plugin-component-kit": "workspace:*",
"@mendix/widget-plugin-platform": "workspace:*",
"@rc-component/slider": "^1.0.1",
"@rc-component/tooltip": "^1.3.3",
"classnames": "^2.5.1"
Expand All @@ -55,7 +56,6 @@
"@mendix/prettier-config-web-widgets": "workspace:*",
"@mendix/run-e2e": "workspace:*",
"@mendix/widget-plugin-hooks": "workspace:*",
"@mendix/widget-plugin-platform": "workspace:*",
"@types/rc-slider": "^8.6.6",
"@types/rc-tooltip": "^3.7.7",
"cross-env": "^7.0.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ReactNode } from "react";
import { createMarks } from "@mendix/widget-plugin-platform/utils/slider-marks";
import { RangeSliderPreviewProps } from "../typings/RangeSliderProps";
import { RangeSlider } from "./components/RangeSlider";
import { createMarks } from "./utils/marks";
import { getPreviewValues } from "./utils/getPreviewValues";
import { getStyleProp, isVertical } from "./utils/prop-utils";

Expand All @@ -11,11 +11,13 @@ export function getPreviewCss(): string {

export function preview(props: RangeSliderPreviewProps): ReactNode {
const { min, max, step, value } = getPreviewValues(props);
const decimalPlaces = props.decimalPlaces ?? 0;
const marks = createMarks({
min,
max,
numberOfMarks: props.noOfMarkers ?? 1,
decimalPlaces: props.decimalPlaces ?? 0
decimalPlaces,
format: (v: number) => v.toFixed(decimalPlaces)
});

const style = getStyleProp({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { NumberFormatter } from "mendix";
import { ReactElement, useMemo, useRef } from "react";
import { useScheduleUpdateOnce } from "@mendix/widget-plugin-hooks/useScheduleUpdateOnce";
import { createValueFormatter } from "@mendix/widget-plugin-platform/utils/number-formatter";
import { RangeSlider as RangeComponent } from "./RangeSlider";
import { HandleTooltip } from "./TooltipHandler";
import { RangeSliderContainerProps } from "../../typings/RangeSliderProps";
import { getStyleProp, isVertical, maxProp, minProp, stepProp } from "../utils/prop-utils";
import { useMarks } from "../utils/useMarks";
import { useNumber } from "../utils/useNumber";
import { RangeSlider as RangeComponent } from "./RangeSlider";
import { useOnChangeDebounced } from "../utils/useOnChangeDebounced";
import { useMarks } from "../utils/useMarks";
import { getStyleProp, isVertical, maxProp, minProp, stepProp } from "../utils/prop-utils";
import { useScheduleUpdateOnce } from "@mendix/widget-plugin-hooks/useScheduleUpdateOnce";
import { HandleTooltip } from "./TooltipHandler";

export function Container(props: RangeSliderContainerProps): ReactElement {
const min = useNumber(minProp(props));
Expand Down Expand Up @@ -41,9 +43,16 @@ function InnerContainer(props: InnerContainerProps): ReactElement {

const { onChange } = useOnChangeDebounced({ lowerBoundAttribute, upperBoundAttribute, onChange: props.onChange });

const format = useMemo(
() => createValueFormatter(lowerBoundAttribute.formatter as NumberFormatter, props.decimalPlaces),
// eslint-disable-next-line react-hooks/exhaustive-deps
[lowerBoundAttribute.formatter, props.decimalPlaces]
);

const marks = useMarks({
noOfMarkers: props.noOfMarkers,
decimalPlaces: props.decimalPlaces,
format,
min,
max
});
Expand Down Expand Up @@ -73,7 +82,9 @@ function InnerContainer(props: InnerContainerProps): ReactElement {
max={props.max}
handleRender={(node, handleProps) => {
const isCustomText = tooltipTypeCheck[handleProps.index] === "customText";
const displayValue = isCustomText ? (tooltipValue[handleProps.index]?.value ?? "") : handleProps.value;
const displayValue = isCustomText
? (tooltipValue[handleProps.index]?.value ?? "")
: format(handleProps.value);
return (
<HandleTooltip
value={displayValue}
Expand Down
32 changes: 0 additions & 32 deletions packages/pluggableWidgets/range-slider-web/src/utils/marks.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { useMemo } from "react";
import { createMarks } from "./marks";
import { type ValueFormatter } from "@mendix/widget-plugin-platform/utils/number-formatter";
import { createMarks } from "@mendix/widget-plugin-platform/utils/slider-marks";

type UseMarksParams = {
noOfMarkers: number;
decimalPlaces: number;
format: ValueFormatter;
min?: number;
max?: number;
};

export function useMarks(props: UseMarksParams): ReturnType<typeof createMarks> {
const { noOfMarkers, decimalPlaces, min = 0, max = 100 } = props;
const { noOfMarkers, decimalPlaces, format, min = 0, max = 100 } = props;

return useMemo(
() =>
createMarks({
numberOfMarks: noOfMarkers,
decimalPlaces,
format,
min,
max
}),
[min, max, noOfMarkers, decimalPlaces]
[min, max, noOfMarkers, decimalPlaces, format]
);
}
4 changes: 4 additions & 0 deletions packages/pluggableWidgets/slider-web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 respecting the decimal places and locale settings configured on the value attribute. Numbers now display with the correct decimal separator, digit grouping, and number of decimal places.

## [3.0.2] - 2026-02-19

### Fixed
Expand Down
12 changes: 5 additions & 7 deletions packages/pluggableWidgets/slider-web/e2e/Slider.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@ test.describe("Slider", () => {
await page.goto("/");
await waitForMendixApp(page);

const minimumValue = await page.inputValue(".mx-name-textBoxMinimumValue input");
const minimumValueText = await page
.locator(".mx-name-sliderContext .rc-slider-mark > span")
.first()
.textContent();
await expect(minimumValueText).toBe(minimumValue);
await expect(minimumValueText).toBe("0.0");

const maximumValue = await page.inputValue(".mx-name-textBoxMaximumValue input");
const maximumValueText = await page
.locator(".mx-name-sliderContext .rc-slider-mark > span")
.nth(2)
.textContent();
await expect(maximumValueText).toBe(maximumValue);
await expect(maximumValueText).toBe("20.0");

const value = await page.inputValue(".mx-name-textBoxValue input");
await expect(value).toContain("10");
Expand All @@ -38,13 +36,13 @@ test.describe("Slider", () => {
.locator(".mx-name-sliderNoContext .rc-slider-mark > span")
.first()
.textContent();
await expect(minimumValueText).toBe("0");
await expect(minimumValueText).toBe("0.0");

const maximumValueText = await page
.locator(".mx-name-sliderNoContext .rc-slider-mark > span")
.nth(2)
.textContent();
await expect(maximumValueText).toBe("100");
await expect(maximumValueText).toBe("100.0");

const handleStyle = await page.locator(".mx-name-sliderNoContext .rc-slider-handle").getAttribute("style");
await expect(handleStyle).toContain("left: 0%;");
Expand Down Expand Up @@ -173,7 +171,7 @@ test.describe("Slider", () => {
await waitForMendixApp(page);

await expect(page.locator(".mx-name-slider")).toBeVisible();
await expect(page.locator(".mx-name-slider .rc-slider-mark > span").nth(1)).toHaveText("140000");
await expect(page.locator(".mx-name-slider .rc-slider-mark > span").nth(1)).toHaveText("140000.0");
await expect(page.locator(".mx-name-slider .rc-slider-mark > span").nth(1)).toHaveAttribute(
"style",
/left: 33.3333%;/
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/pluggableWidgets/slider-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
},
"dependencies": {
"@mendix/widget-plugin-component-kit": "workspace:*",
"@mendix/widget-plugin-platform": "workspace:*",
"@rc-component/slider": "^1.0.1",
"@rc-component/tooltip": "^1.3.3",
"classnames": "^2.5.1"
Expand All @@ -55,7 +56,6 @@
"@mendix/prettier-config-web-widgets": "workspace:*",
"@mendix/run-e2e": "workspace:*",
"@mendix/widget-plugin-hooks": "workspace:*",
"@mendix/widget-plugin-platform": "workspace:*",
"cross-env": "^7.0.3"
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ReactNode } from "react";
import { createMarks } from "@mendix/widget-plugin-platform/utils/slider-marks";
import { Slider } from "./components/Slider";
import { SliderPreviewProps } from "../typings/SliderProps";
import { getPreviewValues } from "./utils/getPreviewValues";
import { createMarks } from "./utils/marks";
import { getStyleProp, isVertical } from "./utils/prop-utils";

export function getPreviewCss(): string {
Expand All @@ -11,11 +11,13 @@ export function getPreviewCss(): string {

export function preview(props: SliderPreviewProps): ReactNode {
const values = getPreviewValues(props);
const decimalPlaces = props.decimalPlaces ?? 2;
const marks = createMarks({
min: values.min,
max: values.max,
numberOfMarks: props.noOfMarkers ?? 2,
decimalPlaces: props.decimalPlaces ?? 2
decimalPlaces,
format: (v: number) => v.toFixed(decimalPlaces)
});
const style = getStyleProp({
orientation: props.orientation,
Expand Down
37 changes: 25 additions & 12 deletions packages/pluggableWidgets/slider-web/src/components/Container.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { NumberFormatter } from "mendix";
import { ReactElement, useMemo, useRef } from "react";
import { createValueFormatter } from "@mendix/widget-plugin-platform/utils/number-formatter";
import { Slider as SliderComponent } from "./Slider";
import { SliderContainerProps } from "../../typings/SliderProps";

import { createHandleRender } from "../utils/createHandleRender";
import { getSliderLabel } from "../utils/helpers";
import { getStyleProp, isVertical, maxProp, minProp, stepProp } from "../utils/prop-utils";
import { useMarks } from "../utils/useMarks";
import { useNumber } from "../utils/useNumber";
import { getSliderLabel } from "../utils/helpers";
import { useOnChangeDebounced } from "../utils/useOnChangeDebounced";

import { Slider as SliderComponent } from "./Slider";

export function Container(props: SliderContainerProps): ReactElement {
const min = useNumber(minProp(props));
const max = useNumber(maxProp(props));
Expand All @@ -30,19 +30,32 @@ interface InnerContainerProps extends SliderContainerProps {

function InnerContainer(props: InnerContainerProps): ReactElement {
const sliderRef = useRef<HTMLDivElement>(null);
const handleRender = props.showTooltip
? createHandleRender({
tooltip: props.tooltip,
tooltipType: props.tooltipType,
tooltipAlwaysVisible: props.tooltipAlwaysVisible,
sliderRef
})
: undefined;

const format = useMemo(
() => createValueFormatter(props.valueAttribute.formatter as NumberFormatter, props.decimalPlaces),
// eslint-disable-next-line react-hooks/exhaustive-deps
[props.valueAttribute.formatter, props.decimalPlaces]
);

const handleRender = useMemo(
() =>
props.showTooltip
? createHandleRender({
tooltip: props.tooltip,
tooltipType: props.tooltipType,
tooltipAlwaysVisible: props.tooltipAlwaysVisible,
sliderRef,
format
})
: undefined,
[props.showTooltip, props.tooltip, props.tooltipType, props.tooltipAlwaysVisible, format]
);

const { onChange } = useOnChangeDebounced({ valueAttribute: props.valueAttribute, onChange: props.onChange });
const marks = useMarks({
noOfMarkers: props.noOfMarkers,
decimalPlaces: props.decimalPlaces,
format,
min: props.min,
max: props.max
});
Expand Down
Loading
Loading