Skip to content

Commit

Permalink
chore(utils): updated loop util to allow for a specific min value
Browse files Browse the repository at this point in the history
  • Loading branch information
mlaursen committed Feb 17, 2021
1 parent 0566e14 commit 51bcf92
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 38 deletions.
9 changes: 8 additions & 1 deletion packages/expansion-panel/src/usePanels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,14 @@ export function usePanels({
return;
}

attemptFocus(loop(currentIndex, panels.length - 1, increment), panels);
attemptFocus(
loop({
value: currentIndex,
max: panels.length - 1,
increment,
}),
panels
);
},
[panels]
);
Expand Down
47 changes: 31 additions & 16 deletions packages/utils/src/__tests__/loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,48 @@ import { loop } from "../loop";

describe("loop", () => {
it("should increase the number by 1 when the number is less than or equal to the max number", () => {
expect(loop(0, 3, true)).toBe(1);
expect(loop(0, 1, true)).toBe(1);
expect(loop(1, 100, true)).toBe(2);
expect(loop({ value: 0, max: 3, increment: true })).toBe(1);
expect(loop({ value: 0, max: 1, increment: true })).toBe(1);
expect(loop({ value: 1, max: 100, increment: true })).toBe(2);
});

it("should decrease the number by 1 when the number is greater than 0", () => {
expect(loop(1, 3, false)).toBe(0);
expect(loop(1, 1, false)).toBe(0);
expect(loop(100, 100, false)).toBe(99);
expect(loop({ value: 1, max: 3, increment: false })).toBe(0);
expect(loop({ value: 1, max: 1, increment: false })).toBe(0);
expect(loop({ value: 100, max: 100, increment: false })).toBe(99);
});

it("should correctly loop around to 0 when the number is the max value and incrementing", () => {
expect(loop(100, 100, true)).toBe(0);
expect(loop(1, 1, true)).toBe(0);
expect(loop(20, 20, true)).toBe(0);
expect(loop({ value: 100, max: 100, increment: true })).toBe(0);
expect(loop({ value: 1, max: 1, increment: true })).toBe(0);
expect(loop({ value: 20, max: 20, increment: true })).toBe(0);
});

it("should correctly loop around to the max value when the number is 0 and decrementing", () => {
expect(loop(0, 100, false)).toBe(100);
expect(loop(0, 1, false)).toBe(1);
expect(loop(0, 20, false)).toBe(20);
expect(loop({ value: 0, max: 100, increment: false })).toBe(100);
expect(loop({ value: 0, max: 1, increment: false })).toBe(1);
expect(loop({ value: 0, max: 20, increment: false })).toBe(20);
});

it("should only keep the number between 0 and the max value if the minmax arg is enabled", () => {
expect(loop(-1, 20, true, true)).toBe(0);
expect(loop(-1, 20, false, true)).toBe(0);
expect(loop(100, 20, true, true)).toBe(20);
expect(loop(100, 20, false, true)).toBe(20);
expect(loop({ value: -1, max: 20, increment: true, minmax: true })).toBe(0);
expect(loop({ value: -1, max: 20, increment: false, minmax: true })).toBe(
0
);
expect(loop({ value: 100, max: 20, increment: true, minmax: true })).toBe(
20
);
expect(loop({ value: 100, max: 20, increment: false, minmax: true })).toBe(
20
);
});

it("should allow for a custom min value", () => {
expect(loop({ value: 0, min: 1, max: 10, increment: false })).toBe(10);
expect(loop({ value: 0, min: -10, max: 10, increment: false })).toBe(-1);
expect(loop({ value: -10, min: -10, max: 10, increment: false })).toBe(10);

// defaults to 0
expect(loop({ value: 0, max: 10, increment: false })).toBe(10);
});
});
63 changes: 48 additions & 15 deletions packages/utils/src/loop.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,61 @@
/**
* @since 2.7.0
*/
export interface LoopOptions {
/**
* The current value that should be modified.
*/
value: number;

/**
* An optional `min` value that can be used before looping to the `max` value.
*
* @default 0
*/
min?: number;

/**
* The max number that can be used before looping to the `min` value.
*/
max: number;

/**
* Boolean if the `value` should be incremented or decremented by `1`.
*/
increment: boolean;

/**
* Boolean if the looping should be ignored and only the `min`/`max` options
* should be respected. In other words, the looping behavior will be disabled
* and the `value` must be: `min >= value <= max`
*/
minmax?: boolean;
}

/**
* A small util that is used to increment or decrement a number until it reaches
* the max value or -1. When that happens, it will loop around to 0 or the max
* value respectively. This does not work for different increment numbers or any
* values below 0 for now.
*
* @internal
* @param x The number to increment or decrement
* @param max The max number that can be set
* @param increment Boolean if it should be incremented or decremented
* @param minmax Boolean if the loop functionality should be replaced with
* min-max behavior instead of allowing looping
* @param options {@link LoopOptions}
* @since 2.7.0 The `min` option was added and the arguments changed to an
* object
*/
export function loop(
x: number,
max: number,
increment: boolean,
minmax = false
): number {
let next = x + (increment ? 1 : -1);
export function loop({
value,
min = 0,
max,
increment,
minmax = false,
}: LoopOptions): number {
let next = value + (increment ? 1 : -1);
if (minmax) {
next = Math.min(max, Math.max(0, next));
next = Math.min(max, Math.max(min, next));
} else if (next > max) {
next = 0;
} else if (next < 0) {
next = min;
} else if (next < min) {
next = max;
}

Expand Down
12 changes: 6 additions & 6 deletions packages/utils/src/wia-aria/movement/useKeyboardMovement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,12 @@ export function useKeyboardMovement<
index = lastIndex;
break;
default:
index = loop(
focusedIndex,
lastIndex,
type === "increment",
!loopable
);
index = loop({
value: focusedIndex,
max: lastIndex,
increment: type === "increment",
minmax: !loopable,
});
}

if (index === focusedIndex) {
Expand Down

0 comments on commit 51bcf92

Please sign in to comment.