Skip to content

Commit

Permalink
WRQ-6220: Fixed spotlight navigation from the focused element clipp…
Browse files Browse the repository at this point in the history
…ed by an overflow container (#3210)

* WRQ-6220: Fixed `spotlight` navigation from the focused element clipped by an overflow container

Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com)

* Add unit tests

Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com)

* Fixed to ignore the spotlight root container when finding target from an overflow container

Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com)

---------

Co-authored-by: Seungcheon Baek <5037955+SeungcheonBaek@users.noreply.github.com>
  • Loading branch information
0x64 and 0x64 committed Feb 28, 2024
1 parent ca88a82 commit b7b6f8f
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 2 deletions.
10 changes: 10 additions & 0 deletions packages/spotlight/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

The following is a curated list of changes in the Enact spotlight module, newest changes on the top.

## [unreleased]

### Fixed

- `spotlight` navigation from the focused element clipped by an overflow container

## [4.8.0] - 2024-02-08

No significant changes.

## [4.7.9] - 2023-12-08

No significant changes.
Expand Down
12 changes: 10 additions & 2 deletions packages/spotlight/src/target.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import {
getLastContainer,
getNavigableContainersForNode,
isContainer,
isNavigable
isNavigable,
rootContainerId
} from './container';
import navigate from './navigate';
import {
contains,
getContainerRect,
getIntersectionRect,
getPointRect,
getRect,
getRects,
Expand Down Expand Up @@ -364,7 +366,13 @@ function getTargetByDirectionFromElement (direction, element) {
return getTargetBySelector(extSelector);
}

const elementRect = getRect(element);
const elementContainerId = getContainersForNode(element).pop();
let elementRect = null;
if (elementContainerId !== rootContainerId && getContainerConfig(elementContainerId)?.overflow) {
elementRect = getIntersectionRect(getContainerNode(elementContainerId), element);
} else {
elementRect = getRect(element);
}

const next = getNavigableContainersForNode(element)
.reduceRight((result, containerId, index, elementContainerIds) => {
Expand Down
77 changes: 77 additions & 0 deletions packages/spotlight/src/tests/utils-specs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
getIntersectionRect,
getRect,
getPointRect,
getContainerRect,
isStandardFocusable
Expand Down Expand Up @@ -36,6 +38,81 @@ const scenarios = {
};

describe('utils', () => {
describe('#getIntersectionRect', () => {
test('should return the intersection rect between two given rects', () => {
const rect1 = {
getBoundingClientRect: () => ({
left: 0,
top: 0,
width: 100,
height: 100
})
};
const rect2 = {
getBoundingClientRect: () => ({
left: 50,
top: 50,
width: 100,
height: 100
})
};
const expected = {
left: 50,
top: 50,
width: 50,
height: 50,
right: 100,
bottom: 100,
center: {
x: 75,
y: 75,
left: 75,
right: 75,
top: 75,
bottom: 75
},
element: rect2
};
const actual = getIntersectionRect(rect1, rect2);

expect(actual).toEqual(expected);
});
});

describe('#getRect', () => {
test('should return the rect value calculated based on the given element', () => {
const element = {
getBoundingClientRect: () => ({
left: 10,
top: 20,
width: 100,
height: 200
})
};

const expected = {
left: 10,
top: 20,
width: 100,
height: 200,
right: 110,
bottom: 220,
center: {
x: 60,
y: 120,
left: 60,
right: 60,
top: 120,
bottom: 120
},
element
};
const actual = getRect(element);

expect(actual).toEqual(expected);
});
});

describe('#getPointRect', () => {
test('should return an rect value calculated based on given position', () => {
const expected = {
Expand Down
33 changes: 33 additions & 0 deletions packages/spotlight/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,38 @@ const contains = curry((containerRect, elementRect) => {
return testIntersection('contains', containerRect, elementRect);
});

function getIntersectionRect (container, element) {
const {
left: L,
top: T,
width: W,
height: H
} = container.getBoundingClientRect();
const {
left: l,
top: t,
width: w,
height: h
} = element.getBoundingClientRect();
const intersectionRect = {
element,
left: Math.max(l, L),
right: Math.min(l + w, L + W),
top: Math.max(t, T),
bottom: Math.min(t + h, T + H)
};
intersectionRect.width = intersectionRect.right - intersectionRect.left;
intersectionRect.height = intersectionRect.bottom - intersectionRect.top;
intersectionRect.center = {
x: intersectionRect.left + Math.floor(intersectionRect.width / 2),
y: intersectionRect.top + Math.floor(intersectionRect.height / 2)
};
intersectionRect.center.left = intersectionRect.center.right = intersectionRect.center.x;
intersectionRect.center.top = intersectionRect.center.bottom = intersectionRect.center.y;

return intersectionRect;
}

function getRect (elem) {
const cr = elem.getBoundingClientRect();
const rect = {
Expand Down Expand Up @@ -209,6 +241,7 @@ function isElementHidden (element) {
export {
contains,
getContainerRect,
getIntersectionRect,
getPointRect,
getRect,
getRects,
Expand Down

0 comments on commit b7b6f8f

Please sign in to comment.