Skip to content

Commit

Permalink
Slider: don't accept 0 for the step (#233)
Browse files Browse the repository at this point in the history
  • Loading branch information
fbasso authored Nov 27, 2023
1 parent 55b25bf commit a7ecea9
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 12 deletions.
42 changes: 41 additions & 1 deletion core/lib/services/writables.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,46 @@ describe(`Writables service`, () => {
expect(normalizeValueFn(2)).toBe(2);
expect(normalizeValueFn(3)).toBe(3);
expect(normalizeValueFn(4)).toBe(3);
expect(normalizeValueFn(+'a')).toBe(INVALID_VALUE);
expect(normalizeValueFn(NaN)).toBe(INVALID_VALUE);
expect(normalizeValueFn('a' as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn(true as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn({} as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn(null as any)).toBe(INVALID_VALUE);

const normalizeStrictFn = typeNumberInRangeFactory(1, 3, {strict: true}).normalizeValue!;
expect(normalizeStrictFn(0)).toBe(INVALID_VALUE);
expect(normalizeStrictFn(1)).toBe(INVALID_VALUE);
expect(normalizeStrictFn(2)).toBe(2);
expect(normalizeStrictFn(3)).toBe(INVALID_VALUE);
expect(normalizeStrictFn(4)).toBe(INVALID_VALUE);
expect(normalizeValueFn(NaN)).toBe(INVALID_VALUE);
expect(normalizeValueFn('a' as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn(true as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn({} as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn(null as any)).toBe(INVALID_VALUE);

const normalizeNoClampFn = typeNumberInRangeFactory(1, 3, {useClamp: false}).normalizeValue!;
expect(normalizeNoClampFn(0)).toBe(INVALID_VALUE);
expect(normalizeNoClampFn(1)).toBe(1);
expect(normalizeNoClampFn(2)).toBe(2);
expect(normalizeNoClampFn(3)).toBe(3);
expect(normalizeNoClampFn(4)).toBe(INVALID_VALUE);
expect(normalizeValueFn(NaN)).toBe(INVALID_VALUE);
expect(normalizeValueFn('a' as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn(true as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn({} as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn(null as any)).toBe(INVALID_VALUE);

const normalizeCombineFn = typeNumberInRangeFactory(1, 3, {strict: true, useClamp: false}).normalizeValue!;
expect(normalizeCombineFn(0)).toBe(INVALID_VALUE);
expect(normalizeCombineFn(1)).toBe(INVALID_VALUE);
expect(normalizeCombineFn(2)).toBe(2);
expect(normalizeCombineFn(3)).toBe(INVALID_VALUE);
expect(normalizeCombineFn(4)).toBe(INVALID_VALUE);
expect(normalizeValueFn(NaN)).toBe(INVALID_VALUE);
expect(normalizeValueFn('a' as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn(true as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn({} as any)).toBe(INVALID_VALUE);
expect(normalizeValueFn(null as any)).toBe(INVALID_VALUE);
});
});
35 changes: 28 additions & 7 deletions core/lib/services/writables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,39 @@ export const typeNumber: WritableWithDefaultOptions<number> = {
normalizeValue: numberNormalizeFn,
};

export interface TypeNumberInRangeOptions {
/** If `true`, the range checking will be strict, excluding the minimum and maximum values. Default is `false`. */
strict?: boolean;

/** If `true`, values outside the range will be clamped to the minimum or maximum. Default is `true`. */
useClamp?: boolean;
}

/**
* A factory function that creates a type guard function to check and rectify a value is within a specified range.
* Factory function for creating a type constraint for numbers within a specified range.
*
* @param min - The minimum value allowed.
* @param max - The maximum value allowed.
* @returns A type guard function that returns the clamp value if the value is a value number, and INVALID_VALUE otherwise.
* @param min - The minimum value.
* @param max - The maximum value.
* @param options - Additional options to customize the behavior.
*
* @returns A type guard function that returns the clamp value or INVALID_VALUE depending on the provided options.
*/
export function typeNumberInRangeFactory(min: number, max: number) {
export function typeNumberInRangeFactory(min: number, max: number, options: TypeNumberInRangeOptions = {}) {
const {strict = false, useClamp = true} = options;
return <WritableWithDefaultOptions<number>>{
normalizeValue: (value) => {
const normalizedNumber = numberNormalizeFn(value);
return normalizedNumber === INVALID_VALUE ? INVALID_VALUE : clamp(normalizedNumber, max, min);
let normalizedNumber = numberNormalizeFn(value);
if (normalizedNumber !== INVALID_VALUE) {
if (!strict && useClamp) {
normalizedNumber = clamp(normalizedNumber, max, min);
}
if (normalizedNumber >= min && normalizedNumber <= max) {
if (!strict || (normalizedNumber !== min && normalizedNumber !== max)) {
return normalizedNumber;
}
}
}
return INVALID_VALUE;
},
};
}
Expand Down
6 changes: 3 additions & 3 deletions core/lib/slider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,12 @@ describe(`Slider basic`, () => {
expect(state).toStrictEqual(defaultStateValues);
});

test(`should set the step as 0 if the provided value less than 0`, () => {
test(`shouldn't accept 0 as a valid value`, () => {
slider.patch({
stepSize: -1,
stepSize: 0,
});

expect(state).toStrictEqual({...defaultStateValues, stepSize: 0});
expect(state).toStrictEqual(defaultStateValues);
});

test(`should snap the value to the valid step`, () => {
Expand Down
2 changes: 1 addition & 1 deletion core/lib/slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export function getSliderDefaultConfig() {
const configValidator: ConfigValidator<SliderProps> = {
min: typeNumber,
max: typeNumber,
stepSize: typeNumberInRangeFactory(0, +Infinity),
stepSize: typeNumberInRangeFactory(0, +Infinity, {strict: true}),
readonly: typeBoolean,
disabled: typeBoolean,
vertical: typeBoolean,
Expand Down

0 comments on commit a7ecea9

Please sign in to comment.