Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserves originating path when returning from editor #115118

Merged
merged 7 commits into from
Nov 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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