Skip to content

Commit 07bd7f8

Browse files
Dosantwildemat
authored andcommitted
[Chrome Grid] Make dashboard grid support custom scroll container (elastic#241823)
## Summary Should address close elastic#241706 ``` feature_flags.overrides: core.chrome.layoutType: 'grid' ``` ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [ ] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [ ] [See some risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) - [ ] ...
1 parent e44800b commit 07bd7f8

File tree

12 files changed

+214
-20
lines changed

12 files changed

+214
-20
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# @kbn/grid-layout
22

3-
Contains a simple drag and drop layout engine for Kibana Dashboards.
3+
Contains a drag and drop layout engine for Kibana Dashboards.

src/platform/packages/private/kbn-grid-layout/grid/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
GridSectionData,
1717
MainSection,
1818
} from './grid_section';
19+
import type { ScrollContainer } from './utils/scroll_container';
1920

2021
/**
2122
* The settings for how the grid should be rendered
@@ -63,6 +64,7 @@ export interface GridLayoutStateManager {
6364
accessMode$: BehaviorSubject<GridAccessMode>;
6465
gridDimensions$: BehaviorSubject<ObservedSize>;
6566
runtimeSettings$: BehaviorSubject<RuntimeGridSettings>;
67+
scrollContainer$: BehaviorSubject<ScrollContainer>;
6668

6769
activePanelEvent$: BehaviorSubject<ActivePanelEvent | undefined>;
6870
activeSectionEvent$: BehaviorSubject<ActiveSectionEvent | undefined>;

src/platform/packages/private/kbn-grid-layout/grid/use_grid_layout_events/keyboard_utils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10+
import { getScrollTop, scrollToContainer, type ScrollContainer } from '../utils/scroll_container';
11+
1012
export const updateClientY = (
1113
currentY: number,
1214
stepY: number,
1315
isCloseToEdge: boolean,
14-
type = 'drag'
16+
type = 'drag',
17+
scrollContainer: ScrollContainer
1518
) => {
1619
if (isCloseToEdge) {
1720
switch (type) {
1821
case 'drag':
19-
window.scrollTo({ top: window.scrollY + stepY, behavior: 'smooth' });
22+
scrollToContainer(scrollContainer, getScrollTop(scrollContainer) + stepY, 'smooth');
2023
return currentY;
2124
case 'resize':
2225
setTimeout(() =>

src/platform/packages/private/kbn-grid-layout/grid/use_grid_layout_events/panel/events.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export const useGridLayoutPanelEvents = ({
107107
onStart,
108108
onMove,
109109
onEnd,
110+
scrollContainer: gridLayoutStateManager.scrollContainer$.getValue(),
110111
});
111112
} else if (isTouchEvent(e)) {
112113
startTouchInteraction({

src/platform/packages/private/kbn-grid-layout/grid/use_grid_layout_events/panel/utils.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import { euiThemeVars } from '@kbn/ui-theme';
1111

12+
import { getScrollDimensions, isAtBottomOfPage } from '../../utils/scroll_container';
1213
import type { ActivePanelEvent, GridPanelData } from '../../grid_panel';
1314
import type { GridLayoutStateManager, RuntimeGridSettings } from '../../types';
1415
import { updateClientY } from '../keyboard_utils';
@@ -134,6 +135,7 @@ export const getNextKeyboardPositionForPanel = (
134135
const stepX = columnPixelWidth + gutterSize;
135136
const stepY = rowHeight + gutterSize;
136137
const gridPosition = gridLayoutStateManager.layoutRef.current?.getBoundingClientRect();
138+
const scrollContainer = gridLayoutStateManager.scrollContainer$.getValue();
137139

138140
switch (ev.code) {
139141
case KeyboardCode.Right: {
@@ -159,26 +161,34 @@ export const getNextKeyboardPositionForPanel = (
159161

160162
case KeyboardCode.Down: {
161163
// check if we are at the end of the scroll of the page
162-
const bottomOfPageReached = window.innerHeight + window.scrollY >= document.body.scrollHeight;
164+
const bottomOfPageReached = isAtBottomOfPage(scrollContainer);
165+
163166
// check if next key will cross the bottom edge
164167
// if we're at the end of the scroll of the page, the dragged handle can go down even more so we can reorder with the last row
165168
const bottomMaxPosition = bottomOfPageReached
166169
? panelPosition.bottom + stepY - (panelPosition.bottom - panelPosition.top) * 0.5
167170
: panelPosition.bottom + stepY + KEYBOARD_DRAG_BOTTOM_LIMIT;
168171

169-
const isCloseToBottom = bottomMaxPosition > window.innerHeight;
172+
const { clientHeight } = getScrollDimensions(scrollContainer);
173+
const isCloseToBottom = bottomMaxPosition > clientHeight;
170174

171175
return {
172176
...handlePosition,
173-
clientY: updateClientY(handlePosition.clientY, stepY, isCloseToBottom, type),
177+
clientY: updateClientY(
178+
handlePosition.clientY,
179+
stepY,
180+
isCloseToBottom,
181+
type,
182+
scrollContainer
183+
),
174184
};
175185
}
176186
case KeyboardCode.Up: {
177187
// check if next key will cross the top edge
178188
const isCloseToTop = panelPosition.top - stepY - keyboardDragTopLimit < 0;
179189
return {
180190
...handlePosition,
181-
clientY: updateClientY(handlePosition.clientY, -stepY, isCloseToTop, type),
191+
clientY: updateClientY(handlePosition.clientY, -stepY, isCloseToTop, type, scrollContainer),
182192
};
183193
}
184194
default:

src/platform/packages/private/kbn-grid-layout/grid/use_grid_layout_events/section/events.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export const useGridLayoutSectionEvents = ({ sectionId }: { sectionId: string })
7676
onStart,
7777
onMove,
7878
onEnd,
79+
scrollContainer: gridLayoutStateManager.scrollContainer$.getValue(),
7980
});
8081
} else if (isTouchEvent(e)) {
8182
startTouchInteraction({

src/platform/packages/private/kbn-grid-layout/grid/use_grid_layout_events/section/utils.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10+
import { getScrollDimensions, isAtBottomOfPage } from '../../utils/scroll_container';
1011
import type { GridLayoutStateManager } from '../../types';
1112
import { updateClientY } from '../keyboard_utils';
1213
import type { UserKeyboardEvent } from '../sensors/keyboard/types';
@@ -28,21 +29,29 @@ export const getNextKeyboardPosition = (
2829
const headerRef = headerRefs[sectionId];
2930
const headerRefHeight = (headerRef?.getBoundingClientRect().height ?? 48) * 0.5;
3031
const stepY = headerRefHeight;
32+
const scrollContainer = gridLayoutStateManager.scrollContainer$.getValue();
3133

3234
switch (ev.code) {
3335
case KeyboardCode.Down: {
34-
const bottomOfPageReached = window.innerHeight + window.scrollY >= document.body.scrollHeight;
36+
const bottomOfPageReached = isAtBottomOfPage(scrollContainer);
3537

3638
// check if next key will cross the bottom edge
3739
// if we're at the bottom of the page, the handle can go down even more so we can reorder with the last row
3840
const bottomMaxPosition = bottomOfPageReached
3941
? handlePosition.clientY + stepY
4042
: handlePosition.clientY + 2 * stepY;
41-
const isCloseToBottom = bottomMaxPosition > window.innerHeight;
43+
const { clientHeight } = getScrollDimensions(scrollContainer);
44+
const isCloseToBottom = bottomMaxPosition > clientHeight;
4245

4346
return {
4447
...handlePosition,
45-
clientY: updateClientY(handlePosition.clientY, stepY, isCloseToBottom),
48+
clientY: updateClientY(
49+
handlePosition.clientY,
50+
stepY,
51+
isCloseToBottom,
52+
'drag',
53+
scrollContainer
54+
),
4655
};
4756
}
4857
case KeyboardCode.Up: {
@@ -51,7 +60,13 @@ export const getNextKeyboardPosition = (
5160

5261
return {
5362
...handlePosition,
54-
clientY: updateClientY(handlePosition.clientY, -stepY, isCloseToTop),
63+
clientY: updateClientY(
64+
handlePosition.clientY,
65+
-stepY,
66+
isCloseToTop,
67+
'drag',
68+
scrollContainer
69+
),
5570
};
5671
}
5772
default:

src/platform/packages/private/kbn-grid-layout/grid/use_grid_layout_events/sensors/autoscroll.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10+
import {
11+
getClientHeight,
12+
getScrollDimensions,
13+
scrollByContainer,
14+
type ScrollContainer,
15+
} from '../../utils/scroll_container';
1016
import type { UserMouseEvent } from './mouse';
1117

1218
const DEADZONE = 0.35; // percent of the distance from the center of the screen on either side of the middle is considered deadzone and will not scroll.
@@ -21,7 +27,7 @@ export const stopAutoScroll = () => {
2127
shouldAutoScroll = false;
2228
};
2329

24-
export const startAutoScroll = () => {
30+
export const startAutoScroll = (container: ScrollContainer) => {
2531
if (shouldAutoScroll) return;
2632
shouldAutoScroll = true;
2733

@@ -34,9 +40,10 @@ export const startAutoScroll = () => {
3440

3541
if (latestMouseEvent) {
3642
// Scroll faster depending on how far the user's drag is from the center of the screen.
37-
const distanceFromCenterOfScreen = window.innerHeight / 2 - latestMouseEvent.clientY;
43+
const clientHeight = getClientHeight(container);
44+
const distanceFromCenterOfScreen = clientHeight / 2 - latestMouseEvent.clientY;
3845
const scrollDirection = distanceFromCenterOfScreen > 0 ? 'up' : 'down';
39-
const distanceFromCenterOfScreenPercentage = distanceFromCenterOfScreen / window.innerHeight;
46+
const distanceFromCenterOfScreenPercentage = distanceFromCenterOfScreen / clientHeight;
4047

4148
const dragDistanceSpeedMultiplier = Math.min(
4249
1,
@@ -47,11 +54,10 @@ export const startAutoScroll = () => {
4754
);
4855

4956
// scroll slower as we approach the bottom or the top of the page.
50-
const distanceToTop = scrollDirection === 'up' ? window.scrollY : Number.MAX_VALUE;
57+
const { scrollTop, scrollHeight } = getScrollDimensions(container);
58+
const distanceToTop = scrollDirection === 'up' ? scrollTop : Number.MAX_VALUE;
5159
const distanceToBottom =
52-
scrollDirection === 'down'
53-
? document.body.scrollHeight - window.innerHeight - window.scrollY
54-
: Number.MAX_VALUE;
60+
scrollDirection === 'down' ? scrollHeight - clientHeight - scrollTop : Number.MAX_VALUE;
5561
const nearestScrollEdgeDistance = Math.min(distanceToTop, distanceToBottom);
5662
const edgeSlowdownMultiplier = Math.min(
5763
1,
@@ -63,7 +69,7 @@ export const startAutoScroll = () => {
6369
PIXELS_PER_SECOND * (dragDistanceSpeedMultiplier * edgeSlowdownMultiplier) * deltaTime;
6470

6571
if (pixelsToScroll > 0) {
66-
window.scrollBy({ top: scrollDirection === 'up' ? -pixelsToScroll : pixelsToScroll });
72+
scrollByContainer(container, scrollDirection === 'up' ? -pixelsToScroll : pixelsToScroll);
6773
}
6874
}
6975

src/platform/packages/private/kbn-grid-layout/grid/use_grid_layout_events/sensors/mouse.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10+
import type { ScrollContainer } from '../../utils/scroll_container';
1011
import type { UserInteractionEvent } from '../types';
1112
import { handleAutoscroll, startAutoScroll, stopAutoScroll } from './autoscroll';
1213

@@ -34,15 +35,17 @@ export const startMouseInteraction = ({
3435
onStart,
3536
onMove,
3637
onEnd,
38+
scrollContainer,
3739
}: {
3840
e: UserMouseEvent;
3941
onStart: (e: UserInteractionEvent) => void;
4042
onMove: (e: UserInteractionEvent) => void;
4143
onEnd: () => void;
44+
scrollContainer: ScrollContainer;
4245
}) => {
4346
if (e.button !== MOUSE_BUTTON_LEFT) return;
4447
e.stopPropagation();
45-
startAutoScroll();
48+
startAutoScroll(scrollContainer);
4649

4750
const handleMouseMove = (ev: UserMouseEvent) => {
4851
handleAutoscroll(ev);

src/platform/packages/private/kbn-grid-layout/grid/use_grid_layout_state.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import type {
3737
import { getGridLayout, getOrderedLayout } from './utils/conversions';
3838
import { isLayoutEqual } from './utils/equality_checks';
3939
import { shouldShowMobileView } from './utils/mobile_view';
40+
import { getScrollContainer } from './utils/scroll_container';
4041

4142
export const useGridLayoutState = ({
4243
layout,
@@ -99,6 +100,7 @@ export const useGridLayoutState = ({
99100
const orderedLayout = getOrderedLayout(layout);
100101
const gridLayout$ = new BehaviorSubject<OrderedLayout>(orderedLayout);
101102
const gridDimensions$ = new BehaviorSubject<ObservedSize>({ width: 0, height: 0 });
103+
const scrollContainer$ = new BehaviorSubject(getScrollContainer());
102104
const activePanelEvent$ = new BehaviorSubject<ActivePanelEvent | undefined>(undefined);
103105
const activeSectionEvent$ = new BehaviorSubject<ActiveSectionEvent | undefined>(undefined);
104106

@@ -125,6 +127,7 @@ export const useGridLayoutState = ({
125127
accessMode$,
126128
gridDimensions$,
127129
runtimeSettings$,
130+
scrollContainer$,
128131
expandedPanelId$,
129132
isMobileView$: new BehaviorSubject<boolean>(
130133
shouldShowMobileView(accessMode, euiTheme.breakpoint.m)

0 commit comments

Comments
 (0)