Skip to content

Commit 15c1c7d

Browse files
committed
[canvas] fix embeddables not refreshing on manual refresh or auto-refresh (elastic#221326)
Fixes elastic#221321 ### test instructions 1) install sample web logs 2) import canvas saved object https://github.com/nreese/notes/blob/master/empty-canvas-workpad-saved-object-export.ndjson 3) refresh kibana 4) open canvas and add map embeddable 5) open browser network tab 6) click "Refresh data" button. Verify map requests new data 7) open "View" menu. Click "Refresh data". Verify map requests new data 8) set auto internal to "5s". Verify map requests new data on each interval --------- Co-authored-by: Elastic Machine <[email protected]> (cherry picked from commit e937e91) # Conflicts: # x-pack/platform/plugins/private/canvas/public/components/hooks/use_canvas_api.tsx # x-pack/plugins/canvas/types/embeddables.ts
1 parent a56b543 commit 15c1c7d

File tree

7 files changed

+116
-10
lines changed

7 files changed

+116
-10
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { useCallback, useMemo } from 'react';
9+
import { useDispatch, useSelector } from 'react-redux';
10+
import { BehaviorSubject, Subject, of } from 'rxjs';
11+
12+
import { SerializedPanelState, ViewMode } from '@kbn/presentation-publishing';
13+
14+
import { embeddableInputToExpression } from '../../../canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression';
15+
import { CanvasContainerApi } from '../../../types';
16+
import { METRIC_TYPE, trackCanvasUiMetric } from '../../lib/ui_metric';
17+
// @ts-expect-error unconverted file
18+
import { addElement } from '../../state/actions/elements';
19+
import { getSelectedPage } from '../../state/selectors/workpad';
20+
import { CANVAS_APP } from '../../../common/lib';
21+
import { coreServices } from '../../services/kibana_services';
22+
23+
const reload$ = new Subject<void>();
24+
25+
export function forceReload() {
26+
reload$.next();
27+
}
28+
29+
export const useCanvasApi: () => CanvasContainerApi = () => {
30+
const selectedPageId = useSelector(getSelectedPage);
31+
const dispatch = useDispatch();
32+
33+
const createNewEmbeddable = useCallback(
34+
(type: string, embeddableInput: object) => {
35+
if (trackCanvasUiMetric) {
36+
trackCanvasUiMetric(METRIC_TYPE.CLICK, type);
37+
}
38+
if (embeddableInput) {
39+
const expression = embeddableInputToExpression(embeddableInput, type, undefined, true);
40+
dispatch(addElement(selectedPageId, { expression }));
41+
}
42+
},
43+
[selectedPageId, dispatch]
44+
);
45+
46+
const getCanvasApi = useCallback((): CanvasContainerApi => {
47+
const panelStateMap: Record<string, BehaviorSubject<SerializedPanelState<object>>> = {};
48+
49+
function getSerializedStateForChild(childId: string) {
50+
return panelStateMap[childId]?.value ?? { rawState: {} };
51+
}
52+
53+
return {
54+
getAppContext: () => ({
55+
getCurrentPath: () => {
56+
const urlToApp = coreServices.application.getUrlForApp(CANVAS_APP);
57+
const inAppPath = window.location.pathname.replace(urlToApp, '');
58+
return inAppPath + window.location.search + window.location.hash;
59+
},
60+
currentAppId: CANVAS_APP,
61+
}),
62+
reload$,
63+
viewMode$: new BehaviorSubject<ViewMode>('edit'), // always in edit mode
64+
addNewPanel: async ({
65+
panelType,
66+
serializedState,
67+
}: {
68+
panelType: string;
69+
serializedState: SerializedPanelState<object>;
70+
}) => {
71+
createNewEmbeddable(panelType, serializedState.rawState);
72+
},
73+
disableTriggers: true,
74+
// this is required to disable inline editing now enabled by default
75+
canEditInline: false,
76+
type: 'canvas',
77+
getSerializedStateForChild,
78+
lastSavedStateForChild$: (childId: string) => panelStateMap[childId] ?? of(undefined),
79+
// Canvas auto saves so lastSavedState is the same as currentState
80+
getLastSavedStateForChild: getSerializedStateForChild,
81+
setSerializedStateForChild: (
82+
childId: string,
83+
serializePanelState: SerializedPanelState<object>
84+
) => {
85+
if (!panelStateMap[childId]) {
86+
panelStateMap[childId] = new BehaviorSubject(serializePanelState);
87+
return;
88+
}
89+
panelStateMap[childId].next(serializePanelState);
90+
},
91+
} as CanvasContainerApi;
92+
}, [createNewEmbeddable]);
93+
94+
return useMemo(() => getCanvasApi(), [getCanvasApi]);
95+
};

x-pack/plugins/canvas/public/components/workpad/workpad_shortcuts.component.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import React from 'react';
99
import { Shortcuts } from 'react-shortcuts';
1010
import { isTextInput } from '../../lib/is_text_input';
1111
import { Props } from './workpad.component';
12+
import { forceReload } from '../hooks/use_canvas_api';
1213

1314
type ShortcutProps = Pick<
1415
Props,
@@ -69,7 +70,10 @@ export class WorkpadShortcuts extends React.Component<ShortcutProps> {
6970

7071
// handle keypress events for editor events
7172
_keyMap: Shortcuts = {
72-
REFRESH: this.props.fetchAllRenderables,
73+
REFRESH: () => {
74+
forceReload();
75+
this.props.fetchAllRenderables();
76+
},
7377
UNDO: this.props.undoHistory,
7478
REDO: this.props.redoHistory,
7579
GRID: () => this.props.setGrid(!this.props.grid),

x-pack/plugins/canvas/public/components/workpad_header/fullscreen_control/fullscreen_control.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
1010
// @ts-expect-error no @types definition
1111
import { Shortcuts } from 'react-shortcuts';
1212
import { isTextInput } from '../../../lib/is_text_input';
13+
import { forceReload } from '../../hooks/use_canvas_api';
1314

1415
interface ChildrenProps {
1516
isFullscreen: boolean;
@@ -64,7 +65,10 @@ export class FullscreenControl extends React.PureComponent<Props> {
6465

6566
// handle keypress events for presentation events
6667
_keyMap: { [key: string]: (...args: any[]) => void } = {
67-
REFRESH: this.props.fetchAllRenderables,
68+
REFRESH: () => {
69+
forceReload();
70+
this.props.fetchAllRenderables();
71+
},
6872
PREV: this.previousPage,
6973
NEXT: this.nextPage,
7074
FULLSCREEN: this._toggleFullscreen,

x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.component.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useDispatch, useSelector } from 'react-redux';
1515
import { fetchAllRenderables } from '../../../state/actions/elements';
1616
import { getInFlight } from '../../../state/selectors/resolved_args';
1717
import { ToolTipShortcut } from '../../tool_tip_shortcut';
18-
import { useCanvasApi } from '../../hooks/use_canvas_api';
18+
import { forceReload } from '../../hooks/use_canvas_api';
1919

2020
const strings = {
2121
getRefreshAriaLabel: () =>
@@ -31,11 +31,10 @@ const strings = {
3131
export const RefreshControl = () => {
3232
const dispatch = useDispatch();
3333
const inFlight = useSelector(getInFlight);
34-
const canvasApi = useCanvasApi();
3534
const doRefresh = useCallback(() => {
36-
canvasApi.reload();
35+
forceReload();
3736
dispatch(fetchAllRenderables());
38-
}, [canvasApi, dispatch]);
37+
}, [dispatch]);
3938

4039
return (
4140
<EuiToolTip

x-pack/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import { WorkpadRoutingContext } from '../../../routes/workpad';
2626
import { ViewMenu as Component, Props as ComponentProps } from './view_menu.component';
2727
import { getFitZoomScale } from './lib/get_fit_zoom_scale';
28+
import { forceReload } from '../../hooks/use_canvas_api';
2829

2930
interface StateProps {
3031
zoomScale: number;
@@ -61,7 +62,10 @@ const mapStateToProps = (state: State) => {
6162
const mapDispatchToProps = (dispatch: Dispatch) => ({
6263
setZoomScale: (scale: number) => dispatch(setZoomScale(scale)),
6364
setWriteable: (isWorkpadWriteable: boolean) => dispatch(setWriteable(isWorkpadWriteable)),
64-
doRefresh: () => dispatch(fetchAllRenderables()),
65+
doRefresh: () => {
66+
forceReload();
67+
dispatch(fetchAllRenderables());
68+
},
6569
});
6670

6771
const mergeProps = (

x-pack/plugins/canvas/public/routes/workpad/hooks/use_refresh_helper.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { WorkpadRoutingContext } from '../workpad_routing_context';
1111
import { getInFlight } from '../../../state/selectors/resolved_args';
1212
// @ts-expect-error untyped local
1313
import { fetchAllRenderables } from '../../../state/actions/elements';
14+
import { forceReload } from '../../../components/hooks/use_canvas_api';
1415

1516
export const useRefreshHelper = () => {
1617
const dispatch = useDispatch();
@@ -25,6 +26,7 @@ export const useRefreshHelper = () => {
2526

2627
if (refreshInterval > 0 && !inFlight) {
2728
timer.current = window.setTimeout(() => {
29+
forceReload();
2830
dispatch(fetchAllRenderables());
2931
}, refreshInterval);
3032
}

x-pack/plugins/canvas/types/embeddables.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,4 @@ export type CanvasContainerApi = PublishesViewMode &
3030
HasType &
3131
HasSerializedChildState &
3232
PublishesReload &
33-
Partial<HasAppContext & PublishesUnifiedSearch> & {
34-
reload: () => void;
35-
};
33+
Partial<HasAppContext & PublishesUnifiedSearch>;

0 commit comments

Comments
 (0)