Skip to content

Commit

Permalink
Preserves originating path when returning from editor (elastic#115118)
Browse files Browse the repository at this point in the history
* Add originatingPath to edit_panel_action

Support originatingPath in embeddable state transfer

Fix ts error

* Fixed jest tests

* Parse originatingPath without using hash

* provide static container context on embeddable panel

* Fixed ts error

Co-authored-by: Anton Dosov <anton.dosov@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
3 people committed Nov 25, 2021
1 parent 767b23a commit f0f40a0
Show file tree
Hide file tree
Showing 15 changed files with 103 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/plugins/embeddable/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type {
EmbeddableEditorState,
EmbeddablePackageState,
EmbeddableRendererProps,
EmbeddableContainerContext,
} from './lib';
export {
ACTION_ADD_PANEL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ test('is compatible when edit url is available, in edit mode and editable', asyn

test('redirects to app using state transfer with by value mode', async () => {
applicationMock.currentAppId$ = of('superCoolCurrentApp');
const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock);
const testPath = '/test-path';
const action = new EditPanelAction(
getFactory,
applicationMock,
stateTransferMock,
() => testPath
);
const embeddable = new EditableEmbeddable(
{
id: '123',
Expand All @@ -67,13 +73,20 @@ test('redirects to app using state transfer with by value mode', async () => {
coolInput1: 1,
coolInput2: 2,
},
originatingPath: testPath,
},
});
});

test('redirects to app using state transfer without by value mode', async () => {
applicationMock.currentAppId$ = of('superCoolCurrentApp');
const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock);
const testPath = '/test-path';
const action = new EditPanelAction(
getFactory,
applicationMock,
stateTransferMock,
() => testPath
);
const embeddable = new EditableEmbeddable(
{ id: '123', viewMode: ViewMode.EDIT, savedObjectId: '1234' } as SavedObjectEmbeddableInput,
true
Expand All @@ -86,6 +99,7 @@ test('redirects to app using state transfer without by value mode', async () =>
originatingApp: 'superCoolCurrentApp',
embeddableId: '123',
valueInput: undefined,
originatingPath: testPath,
},
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export class EditPanelAction implements Action<ActionContext> {
constructor(
private readonly getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'],
private readonly application: ApplicationStart,
private readonly stateTransfer?: EmbeddableStateTransfer
private readonly stateTransfer?: EmbeddableStateTransfer,
private readonly getOriginatingPath?: () => string
) {
if (this.application?.currentAppId$) {
this.application.currentAppId$
Expand Down Expand Up @@ -104,15 +105,21 @@ export class EditPanelAction implements Action<ActionContext> {
public getAppTarget({ embeddable }: ActionContext): NavigationContext | undefined {
const app = embeddable ? embeddable.getOutput().editApp : undefined;
const path = embeddable ? embeddable.getOutput().editPath : undefined;

if (app && path) {
if (this.currentAppId) {
const byValueMode = !(embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId;

const originatingPath = this.getOriginatingPath?.();

const state: EmbeddableEditorState = {
originatingApp: this.currentAppId,
valueInput: byValueMode ? this.getExplicitInput({ embeddable }) : undefined,
embeddableId: embeddable.id,
searchSessionId: embeddable.getInput().searchSessionId,
originatingPath,
};

return { app, path, state };
}
return { app, path };
Expand Down
16 changes: 15 additions & 1 deletion src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ const removeById =
({ id }: { id: string }) =>
disabledActions.indexOf(id) === -1;

/**
* Embeddable container may provide information about its environment,
* Use it for drilling down data that is static or doesn't have to be reactive,
* otherwise prefer passing data with input$
* */
export interface EmbeddableContainerContext {
/**
* Current app's path including query and hash starting from {appId}
*/
getCurrentPath?: () => string;
}

interface Props {
embeddable: IEmbeddable<EmbeddableInput, EmbeddableOutput>;
getActions: UiActionsService['getTriggerCompatibleActions'];
Expand All @@ -70,6 +82,7 @@ interface Props {
showShadow?: boolean;
showBadges?: boolean;
showNotifications?: boolean;
containerContext?: EmbeddableContainerContext;
}

interface State {
Expand Down Expand Up @@ -373,7 +386,8 @@ export class EmbeddablePanel extends React.Component<Props, State> {
editPanel: new EditPanelAction(
this.props.getEmbeddableFactory,
this.props.application,
this.props.stateTransfer
this.props.stateTransfer,
this.props.containerContext?.getCurrentPath
),
};
};
Expand Down
18 changes: 16 additions & 2 deletions src/plugins/embeddable/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
IEmbeddable,
EmbeddablePanel,
SavedObjectEmbeddableInput,
EmbeddableContainerContext,
} from './lib';
import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition';
import { EmbeddableStateTransfer } from './lib/state_transfer';
Expand Down Expand Up @@ -97,7 +98,11 @@ export interface EmbeddableStart extends PersistableStateService<EmbeddableState
) => AttributeService<A, V, R>;
}

export type EmbeddablePanelHOC = React.FC<{ embeddable: IEmbeddable; hideHeader?: boolean }>;
export type EmbeddablePanelHOC = React.FC<{
embeddable: IEmbeddable;
hideHeader?: boolean;
containerContext?: EmbeddableContainerContext;
}>;

export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, EmbeddableStart> {
private readonly embeddableFactoryDefinitions: Map<string, EmbeddableFactoryDefinition> =
Expand Down Expand Up @@ -155,7 +160,15 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl

const getEmbeddablePanelHoc =
() =>
({ embeddable, hideHeader }: { embeddable: IEmbeddable; hideHeader?: boolean }) =>
({
embeddable,
hideHeader,
containerContext,
}: {
embeddable: IEmbeddable;
hideHeader?: boolean;
containerContext?: EmbeddableContainerContext;
}) =>
(
<EmbeddablePanel
hideHeader={hideHeader}
Expand All @@ -169,6 +182,7 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
application={core.application}
inspector={inspector}
SavedObjectFinder={getSavedObjectFinder(core.savedObjects, core.uiSettings)}
containerContext={containerContext}
/>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { VisualizeConstants } from '../..';

export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => {
const [originatingApp, setOriginatingApp] = useState<string>();
const [originatingPath, setOriginatingPath] = useState<string>();
const { services } = useKibana<VisualizeServices>();
const [eventEmitter] = useState(new EventEmitter());
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
Expand All @@ -39,8 +40,10 @@ export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => {
embeddableId: embeddableIdValue,
valueInput: valueInputValue,
searchSessionId,
originatingPath: pathValue,
} = stateTransferService.getIncomingEditorState(VisualizeConstants.APP_ID) || {};

setOriginatingPath(pathValue);
setOriginatingApp(value);
setValueInput(valueInputValue);
setEmbeddableId(embeddableIdValue);
Expand All @@ -64,7 +67,8 @@ export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => {
eventEmitter,
isChromeVisible,
valueInput,
originatingApp
originatingApp,
originatingPath
);
const { appState, hasUnappliedChanges } = useVisualizeAppState(
services,
Expand Down Expand Up @@ -99,6 +103,7 @@ export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => {
isEmbeddableRendered={isEmbeddableRendered}
originatingApp={originatingApp}
setOriginatingApp={setOriginatingApp}
originatingPath={originatingPath}
setHasUnsavedChanges={setHasUnsavedChanges}
visEditorRef={visEditorRef}
embeddableId={embeddableId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { VisualizeConstants } from '../..';
export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => {
const { id: visualizationIdFromUrl } = useParams<{ id: string }>();
const [originatingApp, setOriginatingApp] = useState<string>();
const [originatingPath, setOriginatingPath] = useState<string>();
const [embeddableIdValue, setEmbeddableId] = useState<string>();
const { services } = useKibana<VisualizeServices>();
const [eventEmitter] = useState(new EventEmitter());
Expand Down Expand Up @@ -61,6 +62,7 @@ export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => {
originatingApp: value,
searchSessionId,
embeddableId,
originatingPath: pathValue,
} = stateTransferService.getIncomingEditorState(VisualizeConstants.APP_ID) || {};

if (searchSessionId) {
Expand All @@ -71,6 +73,7 @@ export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => {

setEmbeddableId(embeddableId);
setOriginatingApp(value);
setOriginatingPath(pathValue);
}, [services]);

useEffect(() => {
Expand All @@ -91,6 +94,7 @@ export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => {
isEmbeddableRendered={isEmbeddableRendered}
originatingApp={originatingApp}
setOriginatingApp={setOriginatingApp}
originatingPath={originatingPath}
visualizationIdFromUrl={visualizationIdFromUrl}
setHasUnsavedChanges={setHasUnsavedChanges}
visEditorRef={visEditorRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface VisualizeEditorCommonProps {
visEditorRef: RefObject<HTMLDivElement>;
originatingApp?: string;
setOriginatingApp?: (originatingApp: string | undefined) => void;
originatingPath?: string;
visualizationIdFromUrl?: string;
embeddableId?: string;
}
Expand All @@ -52,6 +53,7 @@ export const VisualizeEditorCommon = ({
isEmbeddableRendered,
onAppLeave,
originatingApp,
originatingPath,
setOriginatingApp,
visualizationIdFromUrl,
embeddableId,
Expand Down Expand Up @@ -117,6 +119,7 @@ export const VisualizeEditorCommon = ({
isEmbeddableRendered={isEmbeddableRendered}
hasUnappliedChanges={hasUnappliedChanges}
originatingApp={originatingApp}
originatingPath={originatingPath}
setOriginatingApp={setOriginatingApp}
visInstance={visInstance}
stateContainer={appState}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface VisualizeTopNavProps {
setHasUnsavedChanges: (value: boolean) => void;
hasUnappliedChanges: boolean;
originatingApp?: string;
originatingPath?: string;
visInstance: VisualizeEditorVisInstance;
setOriginatingApp?: (originatingApp: string | undefined) => void;
stateContainer: VisualizeAppStateContainer;
Expand All @@ -46,6 +47,7 @@ const TopNav = ({
hasUnappliedChanges,
originatingApp,
setOriginatingApp,
originatingPath,
visInstance,
stateContainer,
visualizationIdFromUrl,
Expand Down Expand Up @@ -88,6 +90,7 @@ const TopNav = ({
openInspector,
originatingApp,
setOriginatingApp,
originatingPath,
visInstance,
stateContainer,
visualizationIdFromUrl,
Expand All @@ -104,6 +107,7 @@ const TopNav = ({
hasUnappliedChanges,
openInspector,
originatingApp,
originatingPath,
visInstance,
setOriginatingApp,
stateContainer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export interface TopNavConfigParams {
setHasUnsavedChanges: (value: boolean) => void;
openInspector: () => void;
originatingApp?: string;
originatingPath?: string;
setOriginatingApp?: (originatingApp: string | undefined) => void;
hasUnappliedChanges: boolean;
visInstance: VisualizeEditorVisInstance;
Expand All @@ -79,6 +80,7 @@ export const getTopNavConfig = (
setHasUnsavedChanges,
openInspector,
originatingApp,
originatingPath,
setOriginatingApp,
hasUnappliedChanges,
visInstance,
Expand Down Expand Up @@ -168,6 +170,8 @@ export const getTopNavConfig = (
if (saveOptions.dashboardId) {
path =
saveOptions.dashboardId === 'new' ? '#/create' : `#/view/${saveOptions.dashboardId}`;
} else if (originatingPath) {
path = originatingPath;
}

if (stateTransfer) {
Expand Down Expand Up @@ -232,7 +236,8 @@ export const getTopNavConfig = (
type: VISUALIZE_EMBEDDABLE_TYPE,
searchSessionId: data.search.session.getSessionId(),
};
stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { state });

stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { state, path: originatingPath });
};

const navigateToOriginatingApp = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export const useVisByValue = (
eventEmitter: EventEmitter,
isChromeVisible: boolean | undefined,
valueInput?: VisualizeInput,
originatingApp?: string
originatingApp?: string,
originatingPath?: string
) => {
const [state, setState] = useState<{
byValueVisInstance?: ByValueVisInstance;
Expand Down Expand Up @@ -55,7 +56,9 @@ export const useVisByValue = (
const originatingAppName = originatingApp
? stateTransferService.getAppNameFromId(originatingApp)
: undefined;
const redirectToOrigin = originatingApp ? () => navigateToApp(originatingApp) : undefined;
const redirectToOrigin = originatingApp
? () => navigateToApp(originatingApp, { path: originatingPath })
: undefined;
chrome?.setBreadcrumbs(
getEditBreadcrumbs({ byValue: true, originatingAppName, redirectToOrigin })
);
Expand All @@ -76,6 +79,7 @@ export const useVisByValue = (
state.visEditorController,
valueInput,
originatingApp,
originatingPath,
]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { RendererStrings } from '../../../i18n';
import { embeddableInputToExpression } from './embeddable_input_to_expression';
import { RendererFactory, EmbeddableInput } from '../../../types';
import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib';
import type { EmbeddableContainerContext } from '../../../../../../src/plugins/embeddable/public/';

const { embeddable: strings } = RendererStrings;

Expand All @@ -31,14 +32,21 @@ const embeddablesRegistry: {
const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => {
const I18nContext = core.i18n.Context;

const embeddableContainerContext: EmbeddableContainerContext = {
getCurrentPath: () => window.location.hash,
};

return (embeddableObject: IEmbeddable) => {
return (
<div
className={CANVAS_EMBEDDABLE_CLASSNAME}
style={{ width: '100%', height: '100%', cursor: 'auto' }}
>
<I18nContext>
<plugins.embeddable.EmbeddablePanel embeddable={embeddableObject} />
<plugins.embeddable.EmbeddablePanel
embeddable={embeddableObject}
containerContext={embeddableContainerContext}
/>
</I18nContext>
</div>
);
Expand Down
Loading

0 comments on commit f0f40a0

Please sign in to comment.