Skip to content

Commit ad874f1

Browse files
authored
Fixed dashboard loaded event (#3432)
1 parent 678bc15 commit ad874f1

File tree

1 file changed

+68
-3
lines changed
  • src/ui/units/dash/containers/Body

1 file changed

+68
-3
lines changed

src/ui/units/dash/containers/Body/Body.tsx

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ import type {RouteComponentProps} from 'react-router-dom';
3333
import {withRouter} from 'react-router-dom';
3434
import {compose} from 'recompose';
3535
import type {DashTab, DashTabLayout} from 'shared';
36-
import {Feature, FixedHeaderQa, SCROLL_TITLE_DEBOUNCE_TIME} from 'shared';
36+
import {
37+
Feature,
38+
FixedHeaderQa,
39+
SCROLL_TITLE_DEBOUNCE_TIME,
40+
SCR_USER_AGENT_HEADER_VALUE,
41+
} from 'shared';
42+
import {getAllTabItems} from 'shared/utils/dash';
3743
import type {DatalensGlobalState} from 'ui';
3844
import {
3945
DEFAULT_DASH_MARGINS,
@@ -63,6 +69,8 @@ import {FixedContainerWrapperWithContext, FixedControlsWrapperWithContext} from
6369

6470
import './Body.scss';
6571

72+
const VIEWPORT_DASH_LOADED_EVENT_DEBOUNCE_TIME = 1000;
73+
6674
// Do not change class name, the snapter service uses
6775
const b = block('dash-body');
6876

@@ -110,6 +118,7 @@ type DashBodyState = {
110118
margins: [number, number];
111119
renderers: DashKitGroup[];
112120
};
121+
totalItemsCount: number;
113122
};
114123

115124
type BodyProps = StateProps & DispatchProps & RouteComponentProps & OwnProps;
@@ -145,6 +154,11 @@ class Body extends React.PureComponent<BodyProps, DashBodyState> {
145154
isTabUnmount = true;
146155
}
147156

157+
const newTotalItemsCount = getAllTabItems(props.tabData).length;
158+
if (newTotalItemsCount !== state.totalItemsCount) {
159+
updatedState.totalItemsCount = newTotalItemsCount;
160+
}
161+
148162
const currentHash = props.location.hash;
149163
const hasHashChanged = currentHash !== state.hash;
150164

@@ -183,6 +197,10 @@ class Body extends React.PureComponent<BodyProps, DashBodyState> {
183197
}
184198
}, SCROLL_TITLE_DEBOUNCE_TIME);
185199

200+
dispatchViewportDashLoadedEventDebounced = debounce(() => {
201+
return this.dispatchViewportDashLoadedEvent();
202+
}, VIEWPORT_DASH_LOADED_EVENT_DEBOUNCE_TIME);
203+
186204
_memoizedWidgetsMap: {
187205
layout: DashTabLayout[] | null;
188206
byGroup: Record<string, DashTabLayout[]>;
@@ -230,6 +248,7 @@ class Body extends React.PureComponent<BodyProps, DashBodyState> {
230248
},
231249
],
232250
},
251+
totalItemsCount: 0,
233252
};
234253
}
235254

@@ -256,6 +275,8 @@ class Body extends React.PureComponent<BodyProps, DashBodyState> {
256275
componentWillUnmount() {
257276
window.removeEventListener('wheel', this.interruptAutoScroll);
258277
window.removeEventListener('touchmove', this.interruptAutoScroll);
278+
this.scrollIntoViewWithDebounce.cancel();
279+
this.dispatchViewportDashLoadedEventDebounced.cancel();
259280
}
260281

261282
render() {
@@ -705,20 +726,61 @@ class Body extends React.PureComponent<BodyProps, DashBodyState> {
705726
if (isMounted) {
706727
this.state.loadedItemsMap.set(item.id, false);
707728

708-
if (this.state.loadedItemsMap.size === this.props.tabData?.items.length) {
729+
if (this.state.loadedItemsMap.size === this.state.totalItemsCount) {
709730
this.scrollIntoViewWithDebounce();
710731
}
711732
}
712733
};
713734

735+
private isElementOutsideViewport = (element: Element): boolean => {
736+
const rect = element.getBoundingClientRect();
737+
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
738+
const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
739+
740+
return (
741+
rect.bottom < 0 ||
742+
rect.top > viewportHeight ||
743+
rect.right < 0 ||
744+
rect.left > viewportWidth
745+
);
746+
};
747+
748+
private dispatchViewportDashLoadedEvent = () => {
749+
const {loadedItemsMap, dashEl} = this.state;
750+
751+
if (!dashEl) {
752+
return;
753+
}
754+
755+
const unloadedItemIds: string[] = [];
756+
loadedItemsMap.forEach((isLoaded, itemId) => {
757+
if (isLoaded !== true) {
758+
unloadedItemIds.push(itemId);
759+
}
760+
});
761+
762+
const allViewportItemsLoaded = unloadedItemIds.every((itemId) => {
763+
const itemElement = document.getElementById(itemId);
764+
if (!itemElement) {
765+
return false;
766+
}
767+
const result = this.isElementOutsideViewport(itemElement);
768+
return result;
769+
});
770+
771+
if (allViewportItemsLoaded) {
772+
dispatchDashLoadedEvent();
773+
}
774+
};
775+
714776
private handleItemRender = (item: ConfigItem) => {
715777
const {loadedItemsMap} = this.state;
716778

717779
if (loadedItemsMap.has(item.id) && loadedItemsMap.get(item.id) !== true) {
718780
loadedItemsMap.set(item.id, true);
719781

720782
const isLoaded =
721-
loadedItemsMap.size === this.props.tabData?.items.length &&
783+
loadedItemsMap.size === this.state.totalItemsCount &&
722784
Array.from(loadedItemsMap.values()).every(Boolean);
723785

724786
if (isLoaded && this.state.delayedScrollElement) {
@@ -731,7 +793,10 @@ class Body extends React.PureComponent<BodyProps, DashBodyState> {
731793
}
732794

733795
if (isLoaded) {
796+
this.dispatchViewportDashLoadedEventDebounced.cancel();
734797
dispatchDashLoadedEvent();
798+
} else if (navigator.userAgent === SCR_USER_AGENT_HEADER_VALUE) {
799+
this.dispatchViewportDashLoadedEventDebounced();
735800
}
736801

737802
this.setState({loaded: isLoaded});

0 commit comments

Comments
 (0)