Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ jest.mock('react-native-gesture-handler', () => {

describe('BatchSellPercentageSlider', () => {
it.each([
[-10, 0],
[0, 0],
[12, 0],
[-10, 25],
[0, 25],
[12, 25],
[13, 25],
[37, 25],
[38, 50],
Expand Down Expand Up @@ -86,6 +86,23 @@ describe('BatchSellPercentageSlider', () => {
expect(onValueChange).toHaveBeenCalledWith(25);
});

it('does not decrement below the minimum snap point', () => {
const onValueChange = jest.fn();
const { getByTestId } = render(
<BatchSellPercentageSlider
value={25}
onValueChange={onValueChange}
testID={SLIDER_TEST_ID}
/>,
);

fireEvent(getByTestId(SLIDER_TEST_ID), 'accessibilityAction', {
nativeEvent: { actionName: 'decrement' },
});

expect(onValueChange).toHaveBeenCalledWith(25);
});

it('renders muted marker dots for each snap point', () => {
const { getByTestId } = render(
<BatchSellPercentageSlider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import { useTailwind } from '@metamask/design-system-twrnc-preset';
const HANDLE_SIZE = 24;
const MARKER_SIZE = 4;
const PERCENTAGE_STEP = 25;
export const SNAP_POINTS = [0, 25, 50, 75, 100];
export const SNAP_POINTS = [25, 50, 75, 100];
const MIN_PERCENTAGE = SNAP_POINTS[0];

export function snapToPercentageStep(value: number): number {
const snappedValue = Math.round(value / PERCENTAGE_STEP) * PERCENTAGE_STEP;
return Math.max(0, Math.min(100, snappedValue));
return Math.max(MIN_PERCENTAGE, Math.min(100, snappedValue));
}

interface BatchSellPercentageSliderProps {
Expand All @@ -34,14 +35,15 @@ export function BatchSellPercentageSlider({
testID,
}: BatchSellPercentageSliderProps) {
const tw = useTailwind();
const snappedValue = snapToPercentageStep(value);
const sliderWidth = useSharedValue(0);
const translateX = useSharedValue(0);
const widthRef = useRef(0);

const updatePosition = useCallback(
(nextValue: number, width = widthRef.current) => {
const snappedValue = snapToPercentageStep(nextValue);
translateX.value = (snappedValue / 100) * width;
const nextSnappedValue = snapToPercentageStep(nextValue);
translateX.value = (nextSnappedValue / 100) * width;
},
[translateX],
);
Expand All @@ -66,14 +68,14 @@ export function BatchSellPercentageSlider({
const { width } = event.nativeEvent.layout;
widthRef.current = width;
sliderWidth.value = width;
updatePosition(value, width);
updatePosition(snappedValue, width);
},
[sliderWidth, updatePosition, value],
[sliderWidth, snappedValue, updatePosition],
);

useEffect(() => {
updatePosition(value);
}, [updatePosition, value]);
updatePosition(snappedValue);
}, [snappedValue, updatePosition]);

const progressStyle = useAnimatedStyle(() => ({
width: translateX.value,
Expand Down Expand Up @@ -106,19 +108,24 @@ export function BatchSellPercentageSlider({
(event: AccessibilityActionEvent) => {
const nextValue =
event.nativeEvent.actionName === 'increment'
? snapToPercentageStep(value + PERCENTAGE_STEP)
: snapToPercentageStep(value - PERCENTAGE_STEP);
? snapToPercentageStep(snappedValue + PERCENTAGE_STEP)
: snapToPercentageStep(snappedValue - PERCENTAGE_STEP);

onValueChange(nextValue);
},
[onValueChange, value],
[onValueChange, snappedValue],
);

return (
<GestureHandlerRootView
testID={testID}
accessibilityRole="adjustable"
accessibilityValue={{ min: 0, max: 100, now: value, text: `${value}%` }}
accessibilityValue={{
min: MIN_PERCENTAGE,
max: 100,
now: snappedValue,
text: `${snappedValue}%`,
}}
accessibilityActions={[{ name: 'increment' }, { name: 'decrement' }]}
onAccessibilityAction={handleAccessibilityAction}
style={tw.style('h-6 w-full')}
Expand Down
Loading
Loading