diff --git a/package.json b/package.json
index 96845820e..b742014f4 100644
--- a/package.json
+++ b/package.json
@@ -101,7 +101,6 @@
},
"patchedDependencies": {
"@mendix/pluggable-widgets-tools@11.8.0": "patches/@mendix+pluggable-widgets-tools+11.8.0.patch",
- "@ptomasroos/react-native-multi-slider@1.0.0": "patches/@ptomasroos+react-native-multi-slider+1.0.0.patch",
"react-native-action-button@2.8.5": "patches/react-native-action-button+2.8.5.patch",
"react-native-gesture-handler@2.30.0": "patches/react-native-gesture-handler+2.30.0.patch",
"react-native-slider@0.11.0": "patches/react-native-slider+0.11.0.patch",
diff --git a/packages/pluggableWidgets/range-slider-native/package.json b/packages/pluggableWidgets/range-slider-native/package.json
index 7295f23ca..dd396161e 100644
--- a/packages/pluggableWidgets/range-slider-native/package.json
+++ b/packages/pluggableWidgets/range-slider-native/package.json
@@ -21,11 +21,9 @@
"dependencies": {
"@mendix/piw-native-utils-internal": "*",
"@mendix/piw-utils-internal": "*",
- "@ptomasroos/react-native-multi-slider": "^1.0.0",
- "prop-types": "^15.7.2"
+ "@miblanchard/react-native-slider": "^2.6.0"
},
"devDependencies": {
- "@mendix/pluggable-widgets-tools": "*",
- "@types/ptomasroos__react-native-multi-slider": "^0.0.1"
+ "@mendix/pluggable-widgets-tools": "*"
}
}
diff --git a/packages/pluggableWidgets/range-slider-native/src/Marker.tsx b/packages/pluggableWidgets/range-slider-native/src/Marker.tsx
deleted file mode 100644
index b2a02bfcf..000000000
--- a/packages/pluggableWidgets/range-slider-native/src/Marker.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { MarkerProps } from "@ptomasroos/react-native-multi-slider";
-import { ReactElement } from "react";
-import { Platform, StyleSheet, TouchableHighlight, View } from "react-native";
-
-export function Marker(props: MarkerProps & { testID: string }): ReactElement {
- return (
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- markerStyle: {
- ...Platform.select({
- ios: {
- height: 30,
- width: 30,
- borderRadius: 30,
- borderWidth: 1,
- borderColor: "#DDDDDD",
- backgroundColor: "#FFFFFF",
- shadowColor: "#000000",
- shadowOffset: {
- width: 0,
- height: 3
- },
- shadowRadius: 1,
- shadowOpacity: 0.2
- },
- android: {
- height: 12,
- width: 12,
- borderRadius: 12,
- backgroundColor: "#0D8675"
- }
- })
- },
- pressedMarkerStyle: {
- ...Platform.select({
- ios: {},
- android: {
- height: 20,
- width: 20,
- borderRadius: 20
- }
- })
- },
- disabled: {
- backgroundColor: "#d3d3d3"
- }
-});
diff --git a/packages/pluggableWidgets/range-slider-native/src/RangeSlider.tsx b/packages/pluggableWidgets/range-slider-native/src/RangeSlider.tsx
index 368b53595..37a48d26b 100644
--- a/packages/pluggableWidgets/range-slider-native/src/RangeSlider.tsx
+++ b/packages/pluggableWidgets/range-slider-native/src/RangeSlider.tsx
@@ -1,18 +1,16 @@
import { available, flattenStyles, toNumber, unavailable } from "@mendix/piw-native-utils-internal";
-import MultiSlider, { MarkerProps } from "@ptomasroos/react-native-multi-slider";
-import { ReactElement, useCallback, useRef, useState, JSX } from "react";
-import { LayoutChangeEvent, Text, View } from "react-native";
+import { Slider } from "@miblanchard/react-native-slider";
+import { ReactElement, useCallback, useRef } from "react";
+import { Text, View } from "react-native";
import { Big } from "big.js";
import { RangeSliderProps } from "../typings/RangeSliderProps";
-import { Marker } from "./Marker";
import { defaultRangeSliderStyle, RangeSliderStyle } from "./ui/Styles";
import { executeAction } from "@mendix/piw-utils-internal";
export type Props = RangeSliderProps;
export function RangeSlider(props: Props): ReactElement {
- const [width, setWidth] = useState();
const styles = flattenStyles(defaultRangeSliderStyle, props.style);
const lastLowerValue = useRef(toNumber(props.lowerValueAttribute));
@@ -23,27 +21,13 @@ export function RangeSlider(props: Props): ReactElement {
const validationMessages = validate(props);
const validProps = validationMessages.length === 0;
const editable = props.editable !== "never" && validProps;
- const enabledOne = editable && lowerValue !== undefined && !props.lowerValueAttribute.readOnly;
- const enabledTwo = editable && upperValue !== undefined && !props.upperValueAttribute.readOnly;
+ const enabledLower = editable && lowerValue !== undefined && !props.lowerValueAttribute.readOnly;
+ const enabledUpper = editable && upperValue !== undefined && !props.upperValueAttribute.readOnly;
+ const isEnabled = enabledLower || enabledUpper;
- const customMarker =
- (markerEnabled: boolean, testID: string) =>
- (markerProps: MarkerProps): JSX.Element =>
- (
-
- );
-
- const onLayout = useCallback((event: LayoutChangeEvent): void => {
- setWidth(event.nativeEvent.layout.width);
- }, []);
-
- const onSlide = useCallback(
+ const onValueChange = useCallback(
(values: number[]): void => {
- if (values[0] === null || values[1] === null) {
+ if (values[0] === null || values[0] === undefined || values[1] === null || values[1] === undefined) {
return;
}
props.lowerValueAttribute.setValue(new Big(values[0]));
@@ -52,11 +36,13 @@ export function RangeSlider(props: Props): ReactElement {
[props.lowerValueAttribute, props.upperValueAttribute]
);
- const onChange = useCallback(
+ const onSlidingComplete = useCallback(
(values: number[]): void => {
if (
values[0] === null ||
+ values[0] === undefined ||
values[1] === null ||
+ values[1] === undefined ||
(lastLowerValue.current === values[0] && lastUpperValue.current === values[1])
) {
return;
@@ -73,24 +59,19 @@ export function RangeSlider(props: Props): ReactElement {
);
return (
-
-
+
{props.lowerValueAttribute.validation && (
{props.lowerValueAttribute.validation}
diff --git a/packages/pluggableWidgets/range-slider-native/src/__tests__/RangeSlider.spec.tsx b/packages/pluggableWidgets/range-slider-native/src/__tests__/RangeSlider.spec.tsx
index df54a1088..cd19d45e5 100644
--- a/packages/pluggableWidgets/range-slider-native/src/__tests__/RangeSlider.spec.tsx
+++ b/packages/pluggableWidgets/range-slider-native/src/__tests__/RangeSlider.spec.tsx
@@ -1,11 +1,23 @@
import { actionValue, dynamicValue, EditableValueBuilder } from "@mendix/piw-utils-internal";
import { Big } from "big.js";
-import { View } from "react-native";
-import { fireEvent, render, RenderAPI } from "@testing-library/react-native";
-import { ReactTestInstance } from "react-test-renderer";
+import { fireEvent, render } from "@testing-library/react-native";
import { ValueStatus, DynamicValue } from "mendix";
import { Props, RangeSlider } from "../RangeSlider";
+jest.mock("@miblanchard/react-native-slider", () => {
+ const { View } = require("react-native");
+ return {
+ Slider: (props: any) => (
+ props.onValueChange?.(values)}
+ onSlidingComplete={(values: number[]) => props.onSlidingComplete?.(values)}
+ />
+ )
+ };
+});
+
describe("RangeSlider", () => {
const noValue: DynamicValue = { status: ValueStatus.Unavailable, value: undefined };
let defaultProps: Props;
@@ -32,7 +44,7 @@ describe("RangeSlider", () => {
const component = render(
().isLoading().build()} />
);
- expect(component.queryByTestId(`${defaultProps.name}-validation-message`)).toBeNull();
+ expect(component.queryByTestId(`${defaultProps.name}-validation-messages`)).toBeNull();
});
it("renders an error when no minimum value is provided", () => {
@@ -102,29 +114,6 @@ describe("RangeSlider", () => {
expect(component.queryByText("The upper value must be equal or less than the maximum value.")).not.toBeNull();
});
- it("renders with the width of the parent view", () => {
- const component = render(
-
- );
- fireEvent(component.getByTestId("range-slider-test"), "layout", { nativeEvent: { layout: { width: 100 } } });
- expect(component.getByTestId("range-slider-test").findByProps({ sliderLength: 100 })).not.toBeNull();
- });
-
it("renders a validation message", () => {
const value = new EditableValueBuilder().withValidation("Invalid").build();
const component = render(
@@ -134,71 +123,85 @@ describe("RangeSlider", () => {
expect(component.getAllByText("Invalid")).toHaveLength(2);
});
- it("changes the lower value when swiping", () => {
- const onChangeAction = actionValue();
- const component = render();
+ it("renders as disabled when editable is never", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+ expect(slider.props.disabled).toBe(true);
+ });
- fireEvent(getHandle(component), "responderGrant", { touchHistory: { touchBank: [] } });
- fireEvent(getHandle(component), "responderMove", responderMove(50));
+ it("renders as enabled when editable is default", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+ expect(slider.props.disabled).toBe(false);
+ });
- expect(onChangeAction.execute).not.toHaveBeenCalled();
+ it("passes correct min/max/step to the slider", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+ expect(slider.props.minimumValue).toBe(0);
+ expect(slider.props.maximumValue).toBe(280);
+ expect(slider.props.step).toBe(1);
+ });
- fireEvent(getHandle(component), "responderRelease", {});
+ it("passes range values as array", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+ expect(slider.props.value).toEqual([70, 210]);
+ });
+
+ it("calls onValueChange when sliding", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+
+ fireEvent(slider, "onValueChange", [100, 210]);
- expect(defaultProps.lowerValueAttribute.setValue).toHaveBeenCalledWith(new Big(120));
+ expect(defaultProps.lowerValueAttribute.setValue).toHaveBeenCalledWith(new Big(100));
expect(defaultProps.upperValueAttribute.setValue).toHaveBeenCalledWith(new Big(210));
- expect(onChangeAction.execute).toHaveBeenCalledTimes(1);
});
- it("changes the upper value when swiping", () => {
+ it("calls onChange action on sliding complete", () => {
const onChangeAction = actionValue();
const component = render();
+ const slider = component.getByTestId("mocked-slider");
- fireEvent(getHandle(component, 1), "responderGrant", { touchHistory: { touchBank: [] } });
- fireEvent(getHandle(component, 1), "responderMove", responderMove(-50));
-
- expect(onChangeAction.execute).not.toHaveBeenCalled();
-
- fireEvent(getHandle(component, 1), "responderRelease", {});
+ fireEvent(slider, "onSlidingComplete", [100, 250]);
- expect(defaultProps.lowerValueAttribute.setValue).toHaveBeenCalledWith(new Big(70));
- expect(defaultProps.upperValueAttribute.setValue).toHaveBeenCalledWith(new Big(160));
+ expect(defaultProps.lowerValueAttribute.setValue).toHaveBeenCalledWith(new Big(100));
+ expect(defaultProps.upperValueAttribute.setValue).toHaveBeenCalledWith(new Big(250));
expect(onChangeAction.execute).toHaveBeenCalledTimes(1);
});
- it("does not change the value when non editable", () => {
+ it("does not call onChange when values haven't changed", () => {
const onChangeAction = actionValue();
- const component = render();
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
- fireEvent(getHandle(component), "responderGrant", { touchHistory: { touchBank: [] } });
- fireEvent(getHandle(component), "responderMove", responderMove(50));
- fireEvent(getHandle(component), "responderRelease", {});
+ fireEvent(slider, "onSlidingComplete", [70, 210]);
expect(onChangeAction.execute).not.toHaveBeenCalled();
- expect(defaultProps.lowerValueAttribute.setValue).not.toHaveBeenCalled();
- expect(defaultProps.upperValueAttribute.setValue).not.toHaveBeenCalled();
});
-});
-
-function getHandle(component: RenderAPI, index = 0): ReactTestInstance {
- return component.UNSAFE_getAllByType(View).filter(instance => instance.props.onMoveShouldSetResponder)[index];
-}
-function responderMove(dx: number): any {
- return {
- touchHistory: {
- numberActiveTouches: 1,
- indexOfSingleActiveTouch: 0,
- touchBank: [
- {
- touchActive: true,
- currentTimeStamp: Date.now(),
- currentPageX: dx,
- currentPageY: 0,
- previousPageX: 0,
- previousPageY: 0
- }
- ]
- }
- };
-}
+ it("applies custom styles", () => {
+ const component = render(
+
+ );
+ expect(component.toJSON()).toMatchSnapshot("with custom styles");
+ });
+});
diff --git a/packages/pluggableWidgets/range-slider-native/src/__tests__/__snapshots__/RangeSlider.spec.tsx.snap b/packages/pluggableWidgets/range-slider-native/src/__tests__/__snapshots__/RangeSlider.spec.tsx.snap
index d73f70cd6..27d18dcfd 100644
--- a/packages/pluggableWidgets/range-slider-native/src/__tests__/__snapshots__/RangeSlider.spec.tsx.snap
+++ b/packages/pluggableWidgets/range-slider-native/src/__tests__/__snapshots__/RangeSlider.spec.tsx.snap
@@ -1,267 +1,99 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`RangeSlider applies custom styles: with custom styles 1`] = `
+
+
+
+`;
+
exports[`RangeSlider renders 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ />
`;
diff --git a/packages/pluggableWidgets/range-slider-native/src/ui/Styles.ts b/packages/pluggableWidgets/range-slider-native/src/ui/Styles.ts
index c77e1cf49..945f99f92 100644
--- a/packages/pluggableWidgets/range-slider-native/src/ui/Styles.ts
+++ b/packages/pluggableWidgets/range-slider-native/src/ui/Styles.ts
@@ -5,11 +5,13 @@ export interface RangeSliderStyle extends Style {
container: ViewStyle;
track: ViewStyle;
trackDisabled: ViewStyle;
- highlight: ViewStyle;
- highlightDisabled: ViewStyle;
- marker: ViewStyle;
- markerActive: ViewStyle;
- markerDisabled: ViewStyle;
+ minimumTrack: ViewStyle;
+ minimumTrackDisabled: ViewStyle;
+ maximumTrack: ViewStyle;
+ maximumTrackDisabled: ViewStyle;
+ thumb: ViewStyle;
+ thumbActive: ViewStyle;
+ thumbDisabled: ViewStyle;
validationMessage: TextStyle;
}
@@ -22,34 +24,67 @@ const purpleLightest = "rgba(98,0,238, 0.1)";
export const defaultRangeSliderStyle: RangeSliderStyle = {
container: {},
track: {
- backgroundColor: Platform.select({ ios: blueLighter, android: purpleLighter })
+ height: 4,
+ borderRadius: 2
},
trackDisabled: {
+ height: 4,
+ borderRadius: 2,
...Platform.select({
- ios: {
- opacity: 0.4,
- backgroundColor: blueLighter
- },
- android: {
- backgroundColor: "#EEE"
- }
+ ios: { opacity: 0.4 },
+ android: {}
})
},
- highlight: {
+ minimumTrack: {
backgroundColor: Platform.select({ ios: blue, android: purple })
},
- highlightDisabled: {
+ minimumTrackDisabled: {
backgroundColor: Platform.select({ ios: blue, android: "#AAA" })
},
- marker: {
+ maximumTrack: {
+ backgroundColor: Platform.select({ ios: blueLighter, android: purpleLighter })
+ },
+ maximumTrackDisabled: {
...Platform.select({
+ ios: { backgroundColor: blueLighter },
+ android: { backgroundColor: "#EEE" }
+ })
+ },
+ thumb: {
+ ...Platform.select({
+ ios: {
+ height: 30,
+ width: 30,
+ borderRadius: 30,
+ borderWidth: 1,
+ borderColor: "#DDDDDD",
+ backgroundColor: "#FFFFFF",
+ shadowColor: "#000000",
+ shadowOffset: { width: 0, height: 3 },
+ shadowRadius: 1,
+ shadowOpacity: 0.2
+ },
android: {
- borderColor: purple,
+ height: 12,
+ width: 12,
+ borderRadius: 12,
backgroundColor: purple
}
})
},
- markerDisabled: {
+ thumbActive: {
+ ...Platform.select({
+ android: {
+ height: 20,
+ width: 20,
+ borderRadius: 20,
+ borderWidth: 5,
+ borderColor: purpleLightest,
+ backgroundColor: purple
+ }
+ })
+ },
+ thumbDisabled: {
...Platform.select({
ios: {
backgroundColor: "#FFF",
@@ -62,14 +97,6 @@ export const defaultRangeSliderStyle: RangeSliderStyle = {
}
})
},
- markerActive: {
- ...Platform.select({
- android: {
- borderWidth: 5,
- borderColor: purpleLightest
- }
- })
- },
validationMessage: {
color: "#ed1c24"
}
diff --git a/packages/pluggableWidgets/slider-native/package.json b/packages/pluggableWidgets/slider-native/package.json
index a881290b6..ee6efcbbb 100644
--- a/packages/pluggableWidgets/slider-native/package.json
+++ b/packages/pluggableWidgets/slider-native/package.json
@@ -21,11 +21,9 @@
"dependencies": {
"@mendix/piw-native-utils-internal": "*",
"@mendix/piw-utils-internal": "*",
- "@ptomasroos/react-native-multi-slider": "^1.0.0",
- "prop-types": "^15.7.2"
+ "@miblanchard/react-native-slider": "^2.6.0"
},
"devDependencies": {
- "@mendix/pluggable-widgets-tools": "*",
- "@types/ptomasroos__react-native-multi-slider": "^0.0.1"
+ "@mendix/pluggable-widgets-tools": "*"
}
}
diff --git a/packages/pluggableWidgets/slider-native/src/Marker.tsx b/packages/pluggableWidgets/slider-native/src/Marker.tsx
deleted file mode 100644
index b2a02bfcf..000000000
--- a/packages/pluggableWidgets/slider-native/src/Marker.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { MarkerProps } from "@ptomasroos/react-native-multi-slider";
-import { ReactElement } from "react";
-import { Platform, StyleSheet, TouchableHighlight, View } from "react-native";
-
-export function Marker(props: MarkerProps & { testID: string }): ReactElement {
- return (
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- markerStyle: {
- ...Platform.select({
- ios: {
- height: 30,
- width: 30,
- borderRadius: 30,
- borderWidth: 1,
- borderColor: "#DDDDDD",
- backgroundColor: "#FFFFFF",
- shadowColor: "#000000",
- shadowOffset: {
- width: 0,
- height: 3
- },
- shadowRadius: 1,
- shadowOpacity: 0.2
- },
- android: {
- height: 12,
- width: 12,
- borderRadius: 12,
- backgroundColor: "#0D8675"
- }
- })
- },
- pressedMarkerStyle: {
- ...Platform.select({
- ios: {},
- android: {
- height: 20,
- width: 20,
- borderRadius: 20
- }
- })
- },
- disabled: {
- backgroundColor: "#d3d3d3"
- }
-});
diff --git a/packages/pluggableWidgets/slider-native/src/Slider.tsx b/packages/pluggableWidgets/slider-native/src/Slider.tsx
index 685b71d53..5a50a5531 100644
--- a/packages/pluggableWidgets/slider-native/src/Slider.tsx
+++ b/packages/pluggableWidgets/slider-native/src/Slider.tsx
@@ -1,20 +1,17 @@
import { available, flattenStyles, toNumber, unavailable } from "@mendix/piw-native-utils-internal";
import { executeAction } from "@mendix/piw-utils-internal";
import { ValueStatus, Option } from "mendix";
-import MultiSlider, { MarkerProps } from "@ptomasroos/react-native-multi-slider";
-import { ReactElement, useCallback, useRef, useState, JSX } from "react";
-import { LayoutChangeEvent, Text, View } from "react-native";
+import { Slider as RNSlider } from "@miblanchard/react-native-slider";
+import { ReactElement, useCallback, useRef } from "react";
+import { Text, View } from "react-native";
import { Big } from "big.js";
import { SliderProps } from "../typings/SliderProps";
-import { Marker } from "./Marker";
import { defaultSliderStyle, SliderStyle } from "./ui/Styles";
export type Props = SliderProps;
export function Slider(props: Props): ReactElement {
- const [width, setWidth] = useState();
-
const lastValue = useRef(toNumber(props.valueAttribute));
const value = toNumber(props.valueAttribute);
@@ -22,24 +19,12 @@ export function Slider(props: Props): ReactElement {
const validProps = validationMessages.length === 0;
const editable = props.editable !== "never" && !props.valueAttribute.readOnly && validProps;
const styles = flattenStyles(defaultSliderStyle, props.style);
- // We have to fix the decimal count ourselves because of an unresolved bug in the library: https://github.com/ptomasroos/react-native-multi-slider/issues/211
- const decimalCount = useCallback(
- (value: Option): number => value?.toString().split(".")?.[1]?.length || 0,
- []
- );
-
- const customMarker =
- () =>
- (markerProps: MarkerProps): JSX.Element =>
- ;
- const onLayout = useCallback((event: LayoutChangeEvent): void => {
- setWidth(event.nativeEvent.layout.width);
- }, []);
+ const decimalCount = useCallback((val: Option): number => val?.toString().split(".")?.[1]?.length || 0, []);
- const onSlide = useCallback(
+ const onValueChange = useCallback(
(values: number[]): void => {
- if (values[0] === null) {
+ if (values[0] === null || values[0] === undefined) {
return;
}
@@ -50,9 +35,9 @@ export function Slider(props: Props): ReactElement {
[props.valueAttribute, props.stepSize, decimalCount]
);
- const onChange = useCallback(
+ const onSlidingComplete = useCallback(
(values: number[]): void => {
- if (values[0] === null || lastValue.current === values[0]) {
+ if (values[0] === null || values[0] === undefined || lastValue.current === values[0]) {
return;
}
@@ -67,22 +52,19 @@ export function Slider(props: Props): ReactElement {
);
return (
-
-
+
{!validProps && {validationMessages.join("\n")}}
{props.valueAttribute.validation && (
diff --git a/packages/pluggableWidgets/slider-native/src/__tests__/Slider.spec.tsx b/packages/pluggableWidgets/slider-native/src/__tests__/Slider.spec.tsx
index bf74fc3e6..fbd18fad3 100644
--- a/packages/pluggableWidgets/slider-native/src/__tests__/Slider.spec.tsx
+++ b/packages/pluggableWidgets/slider-native/src/__tests__/Slider.spec.tsx
@@ -1,12 +1,24 @@
import { actionValue, dynamicValue, EditableValueBuilder } from "@mendix/piw-utils-internal";
import { Big } from "big.js";
-import { View } from "react-native";
-import { fireEvent, render, RenderAPI } from "@testing-library/react-native";
-import { ReactTestInstance } from "react-test-renderer";
+import { fireEvent, render } from "@testing-library/react-native";
import { ValueStatus, DynamicValue } from "mendix";
import { Props, Slider } from "../Slider";
+jest.mock("@miblanchard/react-native-slider", () => {
+ const { View } = require("react-native");
+ return {
+ Slider: (props: any) => (
+ props.onValueChange?.(values)}
+ onSlidingComplete={(values: number[]) => props.onSlidingComplete?.(values)}
+ />
+ )
+ };
+});
+
describe("Slider", () => {
const noValue: DynamicValue = { status: ValueStatus.Unavailable, value: undefined };
let defaultProps: Props;
@@ -93,29 +105,6 @@ describe("Slider", () => {
expect(component.queryByText("The current value can not be greater than the maximum value.")).not.toBeNull();
});
- it("renders with the width of the parent view", () => {
- const component = render(
-
- );
- fireEvent(component.getByTestId("slider-test"), "layout", { nativeEvent: { layout: { width: 100 } } });
- expect(component.getByTestId("slider-test").findByProps({ sliderLength: 100 })).not.toBeNull();
- });
-
it("renders a validation message", () => {
const value = new EditableValueBuilder().withValidation("Invalid").build();
const component = render();
@@ -123,61 +112,77 @@ describe("Slider", () => {
expect(component.queryByText("Invalid")).not.toBeNull();
});
- it.skip("handles an invalid step size", () => {
- const component = render();
- expect(component.getByTestId("slider-test").findByProps({ step: 1 })).not.toBeNull();
+ it("renders as disabled when editable is never", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+ expect(slider.props.disabled).toBe(true);
});
- it("changes the value when swiping", () => {
- const onChangeAction = actionValue();
- const component = render();
+ it("renders as enabled when editable is default", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+ expect(slider.props.disabled).toBe(false);
+ });
- fireEvent(getHandle(component), "responderGrant", { touchHistory: { touchBank: [] } });
- fireEvent(getHandle(component), "responderMove", responderMove(50));
+ it("passes correct min/max/step to the slider", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+ expect(slider.props.minimumValue).toBe(0);
+ expect(slider.props.maximumValue).toBe(280);
+ expect(slider.props.step).toBe(1);
+ });
- expect(onChangeAction.execute).not.toHaveBeenCalled();
+ it("calls onValueChange when sliding", () => {
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
+
+ fireEvent(slider, "onValueChange", [200]);
+
+ expect(defaultProps.valueAttribute.setValue).toHaveBeenCalledWith(new Big(200));
+ });
+
+ it("calls onChange action on sliding complete", () => {
+ const onChangeAction = actionValue();
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
- fireEvent(getHandle(component), "responderRelease", {});
+ fireEvent(slider, "onSlidingComplete", [200]);
- expect(defaultProps.valueAttribute.setValue).toHaveBeenCalledWith(new Big(190));
+ expect(defaultProps.valueAttribute.setValue).toHaveBeenCalledWith(new Big(200));
expect(onChangeAction.execute).toHaveBeenCalledTimes(1);
});
- it("does not change the value when non editable", () => {
+ it("does not call onChange when value hasn't changed", () => {
const onChangeAction = actionValue();
- const component = render();
+ const component = render();
+ const slider = component.getByTestId("mocked-slider");
- fireEvent(getHandle(component), "responderGrant", { touchHistory: { touchBank: [] } });
- fireEvent(getHandle(component), "responderMove", responderMove(50));
- fireEvent(getHandle(component), "responderRelease", {});
+ fireEvent(slider, "onSlidingComplete", [140]);
expect(onChangeAction.execute).not.toHaveBeenCalled();
- expect(defaultProps.valueAttribute.setValue).not.toHaveBeenCalled();
});
-});
-function getHandle(component: RenderAPI): ReactTestInstance {
- return component
- .getByTestId("slider-test")
- .findAllByType(View)
- .filter(instance => instance.props.onMoveShouldSetResponder)[0];
-}
-
-function responderMove(dx: number): any {
- return {
- touchHistory: {
- numberActiveTouches: 1,
- indexOfSingleActiveTouch: 0,
- touchBank: [
- {
- touchActive: true,
- currentTimeStamp: Date.now(),
- currentPageX: dx,
- currentPageY: 0,
- previousPageX: 0,
- previousPageY: 0
- }
- ]
- }
- };
-}
+ it("applies custom styles", () => {
+ const component = render(
+
+ );
+ expect(component.toJSON()).toMatchSnapshot("with custom styles");
+ });
+});
diff --git a/packages/pluggableWidgets/slider-native/src/__tests__/__snapshots__/Slider.spec.tsx.snap b/packages/pluggableWidgets/slider-native/src/__tests__/__snapshots__/Slider.spec.tsx.snap
index 927cf2876..c5a6486ec 100644
--- a/packages/pluggableWidgets/slider-native/src/__tests__/__snapshots__/Slider.spec.tsx.snap
+++ b/packages/pluggableWidgets/slider-native/src/__tests__/__snapshots__/Slider.spec.tsx.snap
@@ -1,163 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`Slider applies custom styles: with custom styles 1`] = `
+
+
+
+`;
+
exports[`Slider renders 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
+ }
+ minimumValue={0}
+ onSlidingComplete={[Function]}
+ onValueChange={[Function]}
+ step={1}
+ testID="mocked-slider"
+ thumbStyle={
+ {
+ "backgroundColor": "rgb(98,0,238)",
+ "borderRadius": 12,
+ "height": 12,
+ "width": 12,
+ }
+ }
+ trackStyle={
+ {
+ "borderRadius": 2,
+ "height": 4,
+ }
+ }
+ value={140}
+ />
`;
diff --git a/packages/pluggableWidgets/slider-native/src/ui/Styles.ts b/packages/pluggableWidgets/slider-native/src/ui/Styles.ts
index 2edd936cd..cafbb3b07 100644
--- a/packages/pluggableWidgets/slider-native/src/ui/Styles.ts
+++ b/packages/pluggableWidgets/slider-native/src/ui/Styles.ts
@@ -5,11 +5,13 @@ export interface SliderStyle extends Style {
container: ViewStyle;
track: ViewStyle;
trackDisabled: ViewStyle;
- highlight: ViewStyle;
- highlightDisabled: ViewStyle;
- marker: ViewStyle;
- markerActive: ViewStyle;
- markerDisabled: ViewStyle;
+ minimumTrack: ViewStyle;
+ minimumTrackDisabled: ViewStyle;
+ maximumTrack: ViewStyle;
+ maximumTrackDisabled: ViewStyle;
+ thumb: ViewStyle;
+ thumbActive: ViewStyle;
+ thumbDisabled: ViewStyle;
validationMessage: TextStyle;
}
@@ -22,34 +24,67 @@ const purpleLightest = "rgba(98,0,238, 0.1)";
export const defaultSliderStyle: SliderStyle = {
container: {},
track: {
- backgroundColor: Platform.select({ ios: blueLighter, android: purpleLighter })
+ height: 4,
+ borderRadius: 2
},
trackDisabled: {
+ height: 4,
+ borderRadius: 2,
...Platform.select({
- ios: {
- opacity: 0.4,
- backgroundColor: blueLighter
- },
- android: {
- backgroundColor: "#EEE"
- }
+ ios: { opacity: 0.4 },
+ android: {}
})
},
- highlight: {
+ minimumTrack: {
backgroundColor: Platform.select({ ios: blue, android: purple })
},
- highlightDisabled: {
+ minimumTrackDisabled: {
backgroundColor: Platform.select({ ios: blue, android: "#AAA" })
},
- marker: {
+ maximumTrack: {
+ backgroundColor: Platform.select({ ios: blueLighter, android: purpleLighter })
+ },
+ maximumTrackDisabled: {
...Platform.select({
+ ios: { backgroundColor: blueLighter },
+ android: { backgroundColor: "#EEE" }
+ })
+ },
+ thumb: {
+ ...Platform.select({
+ ios: {
+ height: 30,
+ width: 30,
+ borderRadius: 30,
+ borderWidth: 1,
+ borderColor: "#DDDDDD",
+ backgroundColor: "#FFFFFF",
+ shadowColor: "#000000",
+ shadowOffset: { width: 0, height: 3 },
+ shadowRadius: 1,
+ shadowOpacity: 0.2
+ },
android: {
- borderColor: purple,
+ height: 12,
+ width: 12,
+ borderRadius: 12,
backgroundColor: purple
}
})
},
- markerDisabled: {
+ thumbActive: {
+ ...Platform.select({
+ android: {
+ height: 20,
+ width: 20,
+ borderRadius: 20,
+ borderWidth: 5,
+ borderColor: purpleLightest,
+ backgroundColor: purple
+ }
+ })
+ },
+ thumbDisabled: {
...Platform.select({
ios: {
backgroundColor: "#FFF",
@@ -62,14 +97,6 @@ export const defaultSliderStyle: SliderStyle = {
}
})
},
- markerActive: {
- ...Platform.select({
- android: {
- borderWidth: 5,
- borderColor: purpleLightest
- }
- })
- },
validationMessage: {
color: "#ed1c24"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 519a53900..600ced6c3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -28,9 +28,6 @@ patchedDependencies:
'@mendix/pluggable-widgets-tools@11.8.0':
hash: 00a33321fd423a98cae205a3f6952aa8c0c0e0aad58aa6568d1b39346a5f04a0
path: patches/@mendix+pluggable-widgets-tools+11.8.0.patch
- '@ptomasroos/react-native-multi-slider@1.0.0':
- hash: b5e11465e4305f5284e90a78fc4575401f791921f34dbbafb9831f19ecae94da
- path: patches/@ptomasroos+react-native-multi-slider+1.0.0.patch
react-native-action-button@2.8.5:
hash: 593bb64b27425a7f3805ad9567928d1369fd4cf939ab5d3eb43411a759565c48
path: patches/react-native-action-button+2.8.5.patch
@@ -775,19 +772,13 @@ importers:
'@mendix/piw-utils-internal':
specifier: '*'
version: link:../../tools/piw-utils-internal
- '@ptomasroos/react-native-multi-slider':
- specifier: ^1.0.0
- version: 1.0.0(patch_hash=b5e11465e4305f5284e90a78fc4575401f791921f34dbbafb9831f19ecae94da)(prop-types@15.8.1)(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)
- prop-types:
- specifier: ^15.7.2
- version: 15.8.1
+ '@miblanchard/react-native-slider':
+ specifier: ^2.6.0
+ version: 2.6.0(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)
devDependencies:
'@mendix/pluggable-widgets-tools':
specifier: 11.8.0
version: 11.8.0(patch_hash=00a33321fd423a98cae205a3f6952aa8c0c0e0aad58aa6568d1b39346a5f04a0)(@jest/transform@29.7.0)(@jest/types@30.0.1)(@types/babel__core@7.20.5)(@types/node@25.5.0)(encoding@0.1.13)(jest-util@30.0.2)(picomatch@4.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(tslib@2.8.1)
- '@types/ptomasroos__react-native-multi-slider':
- specifier: ^0.0.1
- version: 0.0.1(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(react@19.2.4)
packages/pluggableWidgets/rating-native:
dependencies:
@@ -873,19 +864,13 @@ importers:
'@mendix/piw-utils-internal':
specifier: '*'
version: link:../../tools/piw-utils-internal
- '@ptomasroos/react-native-multi-slider':
- specifier: ^1.0.0
- version: 1.0.0(patch_hash=b5e11465e4305f5284e90a78fc4575401f791921f34dbbafb9831f19ecae94da)(prop-types@15.8.1)(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)
- prop-types:
- specifier: ^15.7.2
- version: 15.8.1
+ '@miblanchard/react-native-slider':
+ specifier: ^2.6.0
+ version: 2.6.0(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)
devDependencies:
'@mendix/pluggable-widgets-tools':
specifier: 11.8.0
version: 11.8.0(patch_hash=00a33321fd423a98cae205a3f6952aa8c0c0e0aad58aa6568d1b39346a5f04a0)(@jest/transform@29.7.0)(@jest/types@30.0.1)(@types/babel__core@7.20.5)(@types/node@25.5.0)(encoding@0.1.13)(jest-util@30.0.2)(picomatch@4.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(tslib@2.8.1)
- '@types/ptomasroos__react-native-multi-slider':
- specifier: ^0.0.1
- version: 0.0.1(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(react@19.2.4)
packages/pluggableWidgets/switch-native:
dependencies:
@@ -2233,6 +2218,12 @@ packages:
engines: {node: '>=20'}
hasBin: true
+ '@miblanchard/react-native-slider@2.6.0':
+ resolution: {integrity: sha512-o7hk/f/8vkqh6QNR5L52m+ws846fQeD/qNCC9CCSRdBqjq66KiCgbxzlhRzKM/gbtxcvMYMIEEJ1yes5cr6I3A==}
+ peerDependencies:
+ react: 19.2.4
+ react-native: 0.83.3
+
'@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
@@ -2348,13 +2339,6 @@ packages:
'@prettier/plugin-xml@2.2.0':
resolution: {integrity: sha512-UWRmygBsyj4bVXvDiqSccwT1kmsorcwQwaIy30yVh8T+Gspx4OlC0shX1y+ZuwXZvgnafmpRYKks0bAu9urJew==}
- '@ptomasroos/react-native-multi-slider@1.0.0':
- resolution: {integrity: sha512-NpX22rQLArg9widwMzGf7XsInTDf6mfY/D1XaDVjglNkVphj3NSN37+nF6MofArCxC++1P+jHv0SGWbmJQwy4g==}
- peerDependencies:
- prop-types: '*'
- react: 19.2.4
- react-native: 0.83.3
-
'@react-native-async-storage/async-storage@2.2.0':
resolution: {integrity: sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==}
peerDependencies:
@@ -2886,9 +2870,6 @@ packages:
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
- '@types/ptomasroos__react-native-multi-slider@0.0.1':
- resolution: {integrity: sha512-Jn+VZEYHAt8V797mMSABg+qCiU+pBklHxxI5bq294cKFSNa6eN5LIojyvaZBHCns0kY5viiTquCKthRwI+zybQ==}
-
'@types/querystringify@2.0.2':
resolution: {integrity: sha512-7d6OQK6pJ//zE32XLK3vI6GHYhBDcYooaRco9cKFGNu59GVatL5+u7rkiAViq44DxDTd/7QQNBWSDHfJGBz/Pw==}
@@ -9806,6 +9787,11 @@ snapshots:
- tslib
- utf-8-validate
+ '@miblanchard/react-native-slider@2.6.0(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)':
+ dependencies:
+ react: 19.2.4
+ react-native: 0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4)
+
'@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
dependencies:
eslint-scope: 5.1.1
@@ -9895,12 +9881,6 @@ snapshots:
'@xml-tools/parser': 1.0.11
prettier: 2.8.8
- '@ptomasroos/react-native-multi-slider@1.0.0(patch_hash=b5e11465e4305f5284e90a78fc4575401f791921f34dbbafb9831f19ecae94da)(prop-types@15.8.1)(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)':
- dependencies:
- prop-types: 15.8.1
- react: 19.2.4
- react-native: 0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4)
-
'@react-native-async-storage/async-storage@2.2.0(react-native@0.83.3(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4))':
dependencies:
merge-options: 3.0.4
@@ -10663,19 +10643,6 @@ snapshots:
'@types/parse-json@4.0.2': {}
- '@types/ptomasroos__react-native-multi-slider@0.0.1(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(react@19.2.4)':
- dependencies:
- '@types/react': 19.2.14
- '@types/react-native': 0.73.0(@babel/core@7.28.0)(@react-native-community/cli@14.1.0(typescript@5.9.3))(@types/react@19.2.14)(react@19.2.4)
- transitivePeerDependencies:
- - '@babel/core'
- - '@react-native-community/cli'
- - '@react-native/metro-config'
- - bufferutil
- - react
- - supports-color
- - utf-8-validate
-
'@types/querystringify@2.0.2': {}
'@types/react-dom@19.2.3(@types/react@19.2.14)':