Skip to content

Commit ec66109

Browse files
committed
Render editor page with basic nav actions
Added dashboard embeddable container to render the dashboard editor page. Signed-off-by: abbyhu2000 <[email protected]>
1 parent 468b172 commit ec66109

12 files changed

+945
-286
lines changed

src/plugins/dashboard/public/application/app.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ export const DashboardApp = ({ onAppLeave }: DashboardAppProps) => {
2929
exact
3030
path={[DashboardConstants.CREATE_NEW_DASHBOARD_URL, createDashboardEditUrl(':id')]}
3131
>
32-
<DashboardEditor />
32+
<div className="app-container dshAppContainer">
33+
<DashboardEditor />
34+
<div id="dashboardViewport" />
35+
</div>
3336
</Route>
3437
<DashboardNoMatch />
3538
</Switch>

src/plugins/dashboard/public/application/components/dashboard_editor.tsx

Lines changed: 40 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,22 @@
66
import React, { useEffect, useState } from 'react';
77
import { useParams } from 'react-router-dom';
88
import { EventEmitter } from 'events';
9-
import { EMPTY, Subscription, merge } from 'rxjs';
10-
import { catchError, distinctUntilChanged, map, mapTo, startWith, switchMap } from 'rxjs/operators';
11-
import deepEqual from 'fast-deep-equal';
129
import { DashboardTopNav } from '../components/dashboard_top_nav';
1310
import { useChromeVisibility } from '../utils/use/use_chrome_visibility';
1411
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
1512
import { useSavedDashboardInstance } from '../utils/use/use_saved_dashboard_instance';
16-
17-
import { DashboardServices, SavedDashboardPanel } from '../../types';
18-
import {
19-
DASHBOARD_CONTAINER_TYPE,
20-
DashboardContainer,
21-
DashboardContainerInput,
22-
DashboardPanelState,
23-
} from '../embeddable';
24-
import {
25-
ContainerOutput,
26-
ErrorEmbeddable,
27-
ViewMode,
28-
isErrorEmbeddable,
29-
} from '../../embeddable_plugin';
30-
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from '../dashboard_empty_screen';
31-
import { convertSavedDashboardPanelToPanelState } from '../lib/embeddable_saved_object_converters';
13+
import { DashboardServices } from '../../types';
3214
import { useDashboardAppState } from '../utils/use/use_dashboard_app_state';
15+
import { useDashboardContainer } from '../utils/use/use_dashboard_container';
16+
import { useEditorUpdates } from '../utils/use/use_editor_updates';
3317

3418
export const DashboardEditor = () => {
3519
const { id: dashboardIdFromUrl } = useParams<{ id: string }>();
3620
const { services } = useOpenSearchDashboards<DashboardServices>();
37-
const { embeddable, data, dashboardConfig, embeddableCapabilities, uiSettings, http } = services;
38-
const { query: queryService } = data;
39-
const { visualizeCapabilities, mapsCapabilities } = embeddableCapabilities;
40-
const timefilter = queryService.timefilter.timefilter;
4121
const isChromeVisible = useChromeVisibility(services.chrome);
4222
const [eventEmitter] = useState(new EventEmitter());
4323

44-
const { savedDashboardInstance } = useSavedDashboardInstance(
24+
const savedDashboardInstance = useSavedDashboardInstance(
4525
services,
4626
eventEmitter,
4727
isChromeVisible,
@@ -50,165 +30,49 @@ export const DashboardEditor = () => {
5030

5131
const { appState } = useDashboardAppState(services, eventEmitter, savedDashboardInstance);
5232

53-
const appStateData = appState?.get();
54-
if (!appStateData) {
55-
return null;
56-
}
57-
let dashboardContainer: DashboardContainer | undefined;
58-
let inputSubscription: Subscription | undefined;
59-
let outputSubscription: Subscription | undefined;
60-
61-
const dashboardDom = document.getElementById('dashboardViewport');
62-
const dashboardFactory = embeddable.getEmbeddableFactory<
63-
DashboardContainerInput,
64-
ContainerOutput,
65-
DashboardContainer
66-
>(DASHBOARD_CONTAINER_TYPE);
67-
68-
const getShouldShowEditHelp = () => {
69-
return (
70-
!appStateData.panels.length &&
71-
appStateData.viewMode === ViewMode.EDIT &&
72-
!dashboardConfig.getHideWriteControls()
73-
);
74-
};
75-
76-
const getShouldShowViewHelp = () => {
77-
return (
78-
!appStateData.panels.length &&
79-
appStateData.viewMode === ViewMode.VIEW &&
80-
!dashboardConfig.getHideWriteControls()
81-
);
82-
};
83-
84-
const shouldShowUnauthorizedEmptyState = () => {
85-
const readonlyMode =
86-
!appStateData.panels.length &&
87-
!getShouldShowEditHelp() &&
88-
!getShouldShowViewHelp() &&
89-
dashboardConfig.getHideWriteControls();
90-
const userHasNoPermissions =
91-
!appStateData.panels.length && !visualizeCapabilities.save && !mapsCapabilities.save;
92-
return readonlyMode || userHasNoPermissions;
93-
};
94-
95-
const getEmptyScreenProps = (
96-
shouldShowEditHelp: boolean,
97-
isEmptyInReadOnlyMode: boolean
98-
): DashboardEmptyScreenProps => {
99-
const emptyScreenProps: DashboardEmptyScreenProps = {
100-
onLinkClick: () => {}, // TODO
101-
showLinkToVisualize: shouldShowEditHelp,
102-
uiSettings,
103-
http,
104-
};
105-
if (shouldShowEditHelp) {
106-
emptyScreenProps.onVisualizeClick = () => {
107-
alert('click'); // TODO
108-
};
109-
}
110-
if (isEmptyInReadOnlyMode) {
111-
emptyScreenProps.isReadonlyMode = true;
112-
}
113-
return emptyScreenProps;
114-
};
33+
const { dashboardContainer } = useDashboardContainer(
34+
services,
35+
isChromeVisible,
36+
eventEmitter,
37+
savedDashboardInstance,
38+
appState
39+
);
11540

116-
const getDashboardInput = () => {
117-
const embeddablesMap: {
118-
[key: string]: DashboardPanelState;
119-
} = {};
120-
appStateData.panels.forEach((panel: SavedDashboardPanel) => {
121-
embeddablesMap[panel.panelIndex] = convertSavedDashboardPanelToPanelState(panel);
122-
});
41+
const { isEmbeddableRendered, currentAppState } = useEditorUpdates(
42+
services,
43+
eventEmitter,
44+
savedDashboardInstance,
45+
dashboardContainer,
46+
appState
47+
);
12348

124-
const lastReloadRequestTime = 0;
125-
return {
126-
id: savedDashboardInstance.id || '',
127-
filters: appStateData.filters,
128-
hidePanelTitles: appStateData?.options.hidePanelTitles,
129-
query: appStateData.query,
130-
timeRange: {
131-
..._.cloneDeep(timefilter.getTime()),
132-
},
133-
refreshConfig: timefilter.getRefreshInterval(),
134-
viewMode: appStateData.viewMode,
135-
panels: embeddablesMap,
136-
isFullScreenMode: appStateData?.fullScreenMode,
137-
isEmbeddedExternally: false, // TODO
138-
// isEmptyState: shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadonlyMode,
139-
isEmptyState: false, // TODO
140-
useMargins: appStateData.options.useMargins,
141-
lastReloadRequestTime, // TODO
142-
title: appStateData.title,
143-
description: appStateData.description,
144-
expandedPanelId: appStateData.expandedPanelId,
49+
useEffect(() => {
50+
// clean up all registered listeners if any is left
51+
return () => {
52+
eventEmitter.removeAllListeners();
14553
};
146-
};
147-
148-
if (dashboardFactory) {
149-
dashboardFactory
150-
.create(getDashboardInput())
151-
.then((container: DashboardContainer | ErrorEmbeddable | undefined) => {
152-
if (container && !isErrorEmbeddable(container)) {
153-
dashboardContainer = container;
154-
155-
dashboardContainer.renderEmpty = () => {
156-
const shouldShowEditHelp = getShouldShowEditHelp();
157-
const shouldShowViewHelp = getShouldShowViewHelp();
158-
const isEmptyInReadOnlyMode = shouldShowUnauthorizedEmptyState();
159-
const isEmptyState = shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadOnlyMode;
160-
return isEmptyState ? (
161-
<DashboardEmptyScreen
162-
{...getEmptyScreenProps(shouldShowEditHelp, isEmptyInReadOnlyMode)}
163-
/>
164-
) : null;
165-
};
166-
167-
outputSubscription = merge(
168-
// output of dashboard container itself
169-
dashboardContainer.getOutput$(),
170-
// plus output of dashboard container children,
171-
// children may change, so make sure we subscribe/unsubscribe with switchMap
172-
dashboardContainer.getOutput$().pipe(
173-
map(() => dashboardContainer!.getChildIds()),
174-
distinctUntilChanged(deepEqual),
175-
switchMap((newChildIds: string[]) =>
176-
merge(
177-
...newChildIds.map((childId) =>
178-
dashboardContainer!
179-
.getChild(childId)
180-
.getOutput$()
181-
.pipe(catchError(() => EMPTY))
182-
)
183-
)
184-
)
185-
)
186-
)
187-
.pipe(
188-
mapTo(dashboardContainer),
189-
startWith(dashboardContainer) // to trigger initial index pattern update
190-
// updateIndexPatternsOperator //TODO
191-
)
192-
.subscribe();
193-
194-
inputSubscription = dashboardContainer.getInput$().subscribe(() => {});
54+
}, [eventEmitter]);
19555

196-
if (dashboardDom && container) {
197-
container.render(dashboardDom);
198-
}
199-
}
200-
});
201-
}
56+
console.log('savedDashboardInstance', savedDashboardInstance);
57+
console.log('appState', appState);
58+
console.log('currentAppState', currentAppState);
59+
console.log('isEmbeddableRendered', isEmbeddableRendered);
60+
console.log('dashboardContainer', dashboardContainer);
20261

20362
return (
20463
<div>
205-
{savedDashboardInstance && appState && (
206-
<DashboardTopNav
207-
isChromeVisible={isChromeVisible}
208-
savedDashboardInstance={savedDashboardInstance}
209-
appState={appState}
210-
/>
211-
)}
64+
<div>
65+
{savedDashboardInstance && appState && dashboardContainer && currentAppState && (
66+
<DashboardTopNav
67+
isChromeVisible={isChromeVisible}
68+
savedDashboardInstance={savedDashboardInstance}
69+
stateContainer={appState}
70+
currentAppState={currentAppState}
71+
isEmbeddableRendered={isEmbeddableRendered}
72+
dashboardContainer={dashboardContainer}
73+
/>
74+
)}
75+
</div>
21276
</div>
21377
);
21478
};

src/plugins/dashboard/public/application/components/dashboard_top_nav.tsx

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@ import { Filter } from 'src/plugins/data/public';
88
import { useCallback } from 'react';
99
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
1010
import { getTopNavConfig } from '../top_nav/get_top_nav_config';
11-
import { DashboardAppState, DashboardServices, NavAction } from '../../types';
11+
import {
12+
DashboardAppStateContainer,
13+
DashboardAppState,
14+
DashboardServices,
15+
NavAction,
16+
} from '../../types';
17+
import { getNavActions } from '../utils/get_nav_actions';
18+
import { DashboardContainer } from '../embeddable';
1219

1320
interface DashboardTopNavProps {
1421
isChromeVisible: boolean;
1522
savedDashboardInstance: any;
16-
appState: DashboardAppState;
23+
stateContainer: DashboardAppStateContainer;
24+
currentAppState: DashboardAppState;
25+
isEmbeddableRendered: boolean;
26+
dashboardContainer?: DashboardContainer;
1727
}
1828

1929
enum UrlParams {
@@ -24,7 +34,14 @@ enum UrlParams {
2434
HIDE_FILTER_BAR = 'hide-filter-bar',
2535
}
2636

27-
const TopNav = ({ isChromeVisible, savedDashboardInstance, appState }: DashboardTopNavProps) => {
37+
const TopNav = ({
38+
isChromeVisible,
39+
savedDashboardInstance,
40+
stateContainer,
41+
currentAppState,
42+
isEmbeddableRendered,
43+
dashboardContainer,
44+
}: DashboardTopNavProps) => {
2845
const [filters, setFilters] = useState<Filter[]>([]);
2946
const [topNavMenu, setTopNavMenu] = useState<any>();
3047
const [isFullScreenMode, setIsFullScreenMode] = useState<any>();
@@ -44,27 +61,44 @@ const TopNav = ({ isChromeVisible, savedDashboardInstance, appState }: Dashboard
4461
};
4562

4663
const shouldShowNavBarComponent = (forceShow: boolean): boolean =>
47-
(forceShow || isChromeVisible) && !appState?.fullScreenMode;
64+
(forceShow || isChromeVisible) && !currentAppState?.fullScreenMode;
4865

4966
useEffect(() => {
5067
setFilters(queryService.filterManager.getFilters());
5168
}, [services, queryService]);
5269

5370
useEffect(() => {
54-
const navActions: {
55-
[key: string]: NavAction;
56-
} = {}; // TODO: need to implement nav actions
57-
setTopNavMenu(
58-
getTopNavConfig(appState?.viewMode, navActions, dashboardConfig.getHideWriteControls())
59-
);
60-
}, [appState, services, dashboardConfig]);
71+
if (isEmbeddableRendered) {
72+
const navActions = getNavActions(
73+
stateContainer,
74+
savedDashboardInstance,
75+
services,
76+
dashboardContainer
77+
);
78+
setTopNavMenu(
79+
getTopNavConfig(
80+
currentAppState?.viewMode,
81+
navActions,
82+
dashboardConfig.getHideWriteControls()
83+
)
84+
);
85+
}
86+
}, [
87+
currentAppState,
88+
services,
89+
dashboardConfig,
90+
dashboardContainer,
91+
savedDashboardInstance,
92+
stateContainer,
93+
isEmbeddableRendered,
94+
]);
6195

6296
useEffect(() => {
63-
setIsFullScreenMode(appState?.fullScreenMode);
64-
}, [appState, services]);
97+
setIsFullScreenMode(currentAppState?.fullScreenMode);
98+
}, [currentAppState, services]);
6599

66100
const shouldShowFilterBar = (forceHide: boolean): boolean =>
67-
!forceHide && (filters!.length > 0 || !appState?.fullScreenMode);
101+
!forceHide && (filters!.length > 0 || !currentAppState?.fullScreenMode);
68102

69103
const forceShowTopNavMenu = shouldForceDisplay(UrlParams.SHOW_TOP_MENU);
70104
const forceShowQueryInput = shouldForceDisplay(UrlParams.SHOW_QUERY_INPUT);
@@ -93,10 +127,10 @@ const TopNav = ({ isChromeVisible, savedDashboardInstance, appState }: Dashboard
93127
return isChromeVisible ? (
94128
<TopNavMenu
95129
appName={'dashboard'}
96-
savedQueryId={appState?.savedQuery}
130+
savedQueryId={currentAppState?.savedQuery}
97131
config={showTopNavMenu ? topNavMenu : undefined}
98132
className={isFullScreenMode ? 'osdTopNavMenu-isFullScreen' : undefined}
99-
screenTitle={appState.title}
133+
screenTitle={currentAppState.title}
100134
// showTopNavMenu={showTopNavMenu}
101135
showSearchBar={showSearchBar}
102136
showQueryBar={showQueryBar}

0 commit comments

Comments
 (0)