UNPKG

monday-ui-react-core

Version:

Official monday.com UI resources for application development in React.js

335 lines (270 loc) • 13.2 kB
import { NavDirections } from "../../../hooks/useFullKeyboardListeners"; import { getOppositeDirection, getDirectionMaps, getOutmostElementInDirection, getNextElementToFocusInDirection } from "../helper"; describe("GridKeyboardNavigationContext.helper", () => { const ELEMENT1 = { current: "e1" }; const ELEMENT2 = { current: "e2" }; const ELEMENT3 = { current: "e3" }; const ELEMENT4 = { current: "e4" }; const ELEMENT5 = { current: "e5" }; const UNMOUNTED_ELEMENT_1 = { current: null }; const UNMOUNTED_ELEMENT_2 = { current: null }; const DISABLED_ELEMENT = { current: { disabled: true } }; const DISABLED_DATASET_ELEMENT = { current: { dataset: { disabled: "true" } } }; const DATASET_NOT_DISABLED_ELEMENT = { current: { dataset: { disabled: "false" } } }; describe("getDirectionMaps", () => { it("should return empty direction maps when no positions are supplied", () => { const input = []; const expected = { [NavDirections.RIGHT]: new Map(), [NavDirections.LEFT]: new Map(), [NavDirections.UP]: new Map(), [NavDirections.DOWN]: new Map() }; const result = getDirectionMaps(input); expect(result).toEqual(expected); }); it("should ignore positions in unexpected object schema", () => { const input = [ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, // this is the only valid input { unexpectedKey1: ELEMENT2, unexpectedKey2: ELEMENT3 }, { topElement: ELEMENT4 }, { bottomElement: ELEMENT3 }, {} ]; const expected = { [NavDirections.RIGHT]: new Map(), [NavDirections.LEFT]: new Map(), [NavDirections.UP]: new Map([[ELEMENT2, ELEMENT1]]), [NavDirections.DOWN]: new Map([[ELEMENT1, ELEMENT2]]) }; const result = getDirectionMaps(input); expect(result).toEqual(expected); }); it("should return directions map which represent the relation between all positions", () => { const input = [ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: ELEMENT3 }, { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT5 } ]; const expected = { [NavDirections.RIGHT]: new Map([ [ELEMENT2, ELEMENT4], // ELEMENT4 is to the right of ELEMENT2... [ELEMENT4, ELEMENT5] ]), [NavDirections.LEFT]: new Map([ [ELEMENT5, ELEMENT4], [ELEMENT4, ELEMENT2] ]), [NavDirections.UP]: new Map([ [ELEMENT2, ELEMENT1], [ELEMENT3, ELEMENT2] ]), [NavDirections.DOWN]: new Map([ [ELEMENT1, ELEMENT2], [ELEMENT2, ELEMENT3] ]) }; const result = getDirectionMaps(input); expect(result).toEqual(expected); }); it("should throw when a circular vertical positioning is added", () => { const input = [ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: ELEMENT1 }, // invalid { leftElement: ELEMENT2, rightElement: ELEMENT4 }, // valid { leftElement: ELEMENT4, rightElement: ELEMENT5 } // valid ]; expect(() => getDirectionMaps(input)).toThrowError( "Circular positioning detected: the BOTTOM element is already positioned to the TOP of the TOP element. This probably means the layout isn't ordered correctly." ); }); it("should throw when a circular horizontal positioning is added", () => { const input = [ { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT2 }, // invalid { topElement: ELEMENT1, bottomElement: ELEMENT2 }, // valid { topElement: ELEMENT2, bottomElement: ELEMENT5 } // valid ]; expect(() => getDirectionMaps(input)).toThrowError( "Circular positioning detected: the RIGHT element is already positioned to the LEFT of the LEFT element. This probably means the layout isn't ordered correctly." ); }); }); describe("getOppositeDirection", () => { [ { direction: NavDirections.LEFT, opposite: NavDirections.RIGHT }, { direction: NavDirections.RIGHT, opposite: NavDirections.LEFT }, { direction: NavDirections.UP, opposite: NavDirections.DOWN }, { direction: NavDirections.DOWN, opposite: NavDirections.UP } ].forEach(({ direction, opposite }) => { it(`should return "${opposite}" as the opposite of "${direction}"`, () => { expect(getOppositeDirection(direction)).toBe(opposite); }); }); it("should throw on unknown input", () => { const input = "UNKNOWN!"; expect(() => getOppositeDirection(input)).toThrowError(`Unexpected direction: ${input}`); }); }); describe("getOutmostElementToFocus", () => { it("should return the right-most element when there are multiple horizontal connections", () => { const directionMaps = getDirectionMaps([ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: ELEMENT3 }, { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT5 } ]); const direction = NavDirections.RIGHT; const expected = ELEMENT5; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should return the left-most element when there are multiple horizontal connections", () => { const directionMaps = getDirectionMaps([ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: ELEMENT3 }, { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT5 } ]); const direction = NavDirections.LEFT; const expected = ELEMENT2; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should return the top-most element when there are multiple horizontal connections", () => { const directionMaps = getDirectionMaps([ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: ELEMENT3 }, { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT5 } ]); const direction = NavDirections.UP; const expected = ELEMENT1; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should return the bottom-most element when there are multiple horizontal connections", () => { const directionMaps = getDirectionMaps([ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: ELEMENT3 }, { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT5 } ]); const direction = NavDirections.DOWN; const expected = ELEMENT3; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should fallback to the left-most element when asking for the BOTTOM element, and there are no vertical relations", () => { const directionMaps = getDirectionMaps([ { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT5 } ]); const direction = NavDirections.DOWN; const expected = ELEMENT2; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should fallback to the left-most element when asking for the TOP element, and there are no vertical relations", () => { const directionMaps = getDirectionMaps([ { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT5 } ]); const direction = NavDirections.UP; const expected = ELEMENT2; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should fallback to the top-most element when asking for the LEFT element, and there are no horizontal relations", () => { const directionMaps = getDirectionMaps([ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: ELEMENT3 } ]); const direction = NavDirections.LEFT; const expected = ELEMENT1; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should fallback to the top-most element when asking for the RIGHT element, and there are no horizontal relations", () => { const directionMaps = getDirectionMaps([ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: ELEMENT3 } ]); const direction = NavDirections.RIGHT; const expected = ELEMENT1; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should skip the bottom-most element when asking for the bottom element, and the bottom-most element is not mounted", () => { const directionMaps = getDirectionMaps([ { topElement: ELEMENT1, bottomElement: ELEMENT2 }, { topElement: ELEMENT2, bottomElement: UNMOUNTED_ELEMENT_1 } ]); const direction = NavDirections.DOWN; const expected = ELEMENT2; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); it("should skip the two right-most elements when asking for the right element, and the two right-most elements are not mounted", () => { const directionMaps = getDirectionMaps([ { leftElement: ELEMENT1, rightElement: UNMOUNTED_ELEMENT_2 }, { leftElement: UNMOUNTED_ELEMENT_2, rightElement: UNMOUNTED_ELEMENT_1 } ]); const direction = NavDirections.RIGHT; const expected = ELEMENT1; const result = getOutmostElementInDirection(directionMaps, direction); expect(result).toEqual(expected); }); }); describe("getNextElementToFocusInDirection", () => { it("should return null if the referenced element isn't positioned on that direction", () => { const directionMaps = getDirectionMaps([ { leftElement: ELEMENT2, rightElement: ELEMENT4 }, { leftElement: ELEMENT4, rightElement: ELEMENT5 } ]); const directionMap = directionMaps[NavDirections.RIGHT]; const result = getNextElementToFocusInDirection(directionMap, ELEMENT1); // ELEMENT1 isn't mapped expect(result).toBeNull(); }); it("return null if there's only one next ref, and it is currently null", () => { const directionMaps = getDirectionMaps([{ leftElement: ELEMENT1, rightElement: UNMOUNTED_ELEMENT_1 }]); const directionMap = directionMaps[NavDirections.RIGHT]; const result = getNextElementToFocusInDirection(directionMap, ELEMENT1); expect(result).toBeNull(); }); it("return null if there's only one next ref, and it is disabled", () => { const directionMaps = getDirectionMaps([{ leftElement: ELEMENT1, rightElement: DISABLED_ELEMENT }]); const directionMap = directionMaps[NavDirections.RIGHT]; const result = getNextElementToFocusInDirection(directionMap, ELEMENT1); expect(result).toBeNull(); }); it("return null if there's only one next ref, and it is disabled with data-disabled='true'", () => { const directionMaps = getDirectionMaps([{ leftElement: ELEMENT1, rightElement: DISABLED_DATASET_ELEMENT }]); const directionMap = directionMaps[NavDirections.RIGHT]; const result = getNextElementToFocusInDirection(directionMap, ELEMENT1); expect(result).toBeNull(); }); it("return the next element ref even if it has data-disabled='false'", () => { const directionMaps = getDirectionMaps([{ leftElement: ELEMENT1, rightElement: DATASET_NOT_DISABLED_ELEMENT }]); const directionMap = directionMaps[NavDirections.RIGHT]; const result = getNextElementToFocusInDirection(directionMap, ELEMENT1); expect(result).toBe(DATASET_NOT_DISABLED_ELEMENT); }); it("return the next element ref in the given direction which is focusable, while skipping disabled or unmounted elements", () => { const directionMaps = getDirectionMaps([ { leftElement: ELEMENT1, rightElement: UNMOUNTED_ELEMENT_1 }, { leftElement: UNMOUNTED_ELEMENT_1, rightElement: DISABLED_ELEMENT }, { leftElement: DISABLED_ELEMENT, rightElement: ELEMENT2 } ]); const directionMap = directionMaps[NavDirections.RIGHT]; const result = getNextElementToFocusInDirection(directionMap, ELEMENT1); expect(result).toBe(ELEMENT2); }); }); });