Skip to content

Commit

Permalink
First version of new by-value editor
Browse files Browse the repository at this point in the history
Fixing broken behavior and applying relevant changes

Adding changes to dashboard

Removing unnecessary empty line

Removing unnecessary deepClone

Fixing some stuff in dashboard container

Extracting logic into common components

Fixing eslint

Fix breadcrumbs

Fixing error in search interceptor

Reintroduce mistakenly removed empty lines

Renaming function
  • Loading branch information
Maja Grubic committed Jul 17, 2020
1 parent 9296369 commit b4972da
Show file tree
Hide file tree
Showing 25 changed files with 550 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -436,12 +436,12 @@ export class DashboardAppController {
savedObjectId: incomingState.id,
});
} else if ('input' in incomingState) {
const input = incomingState.input;
const { input, type, embeddableId } = incomingState;
delete input.id;
const explicitInput = {
savedVis: input,
};
container.addOrUpdateEmbeddable<EmbeddableInput>(incomingState.type, explicitInput);
container.addOrUpdateEmbeddable<EmbeddableInput>(type, explicitInput, embeddableId);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
// TODO: In the current infrastructure, embeddables in a container do not react properly to
// changes. Removing the existing embeddable, and adding a new one is a temporary workaround
// until the container logic is fixed.

const finalPanels = { ...this.input.panels };
delete finalPanels[previousPanelState.explicitInput.id];
const newPanelId = newPanelState.explicitInput?.id ? newPanelState.explicitInput.id : uuid.v4();
Expand All @@ -196,9 +197,10 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
EEI extends EmbeddableInput = EmbeddableInput,
EEO extends EmbeddableOutput = EmbeddableOutput,
E extends IEmbeddable<EEI, EEO> = IEmbeddable<EEI, EEO>
>(type: string, explicitInput: Partial<EEI>) {
if (explicitInput.id && this.input.panels[explicitInput.id]) {
this.replacePanel(this.input.panels[explicitInput.id], {
>(type: string, explicitInput: Partial<EEI>, embeddableId?: string) {
const idToReplace = embeddableId || explicitInput.id;
if (idToReplace && this.input.panels[idToReplace]) {
this.replacePanel(this.input.panels[idToReplace], {
type,
explicitInput: {
...explicitInput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,18 @@ test('is compatible when edit url is available, in edit mode and editable', asyn
test('redirects to app using state transfer', async () => {
applicationMock.currentAppId$ = of('superCoolCurrentApp');
const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock);
const embeddable = new EditableEmbeddable({ id: '123', viewMode: ViewMode.EDIT }, true);
const input = { id: '123', viewMode: ViewMode.EDIT };
const embeddable = new EditableEmbeddable(input, true);
embeddable.getOutput = jest.fn(() => ({ editApp: 'ultraVisualize', editPath: '/123' }));
await action.execute({ embeddable });
expect(stateTransferMock.navigateToEditor).toHaveBeenCalledWith('ultraVisualize', {
path: '/123',
state: { originatingApp: 'superCoolCurrentApp' },
state: {
originatingApp: 'superCoolCurrentApp',
byValueMode: true,
embeddableId: '123',
valueInput: input,
},
});
});

Expand Down
20 changes: 17 additions & 3 deletions src/plugins/embeddable/public/lib/actions/edit_panel_action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ import { take } from 'rxjs/operators';
import { ViewMode } from '../types';
import { EmbeddableFactoryNotFoundError } from '../errors';
import { EmbeddableStart } from '../../plugin';
import { IEmbeddable, EmbeddableEditorState, EmbeddableStateTransfer } from '../..';
import {
IEmbeddable,
EmbeddableEditorState,
EmbeddableStateTransfer,
SavedObjectEmbeddableInput,
} from '../..';

export const ACTION_EDIT_PANEL = 'editPanel';

Expand Down Expand Up @@ -109,8 +114,17 @@ export class EditPanelAction implements Action<ActionContext> {
const app = embeddable ? embeddable.getOutput().editApp : undefined;
const path = embeddable ? embeddable.getOutput().editPath : undefined;
if (app && path) {
const state = this.currentAppId ? { originatingApp: this.currentAppId } : undefined;
return { app, path, state };
if (this.currentAppId) {
const byValueMode = !(embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId;
const state: EmbeddableEditorState = {
originatingApp: this.currentAppId,
byValueMode,
valueInput: byValueMode ? embeddable.getInput() : undefined,
embeddableId: embeddable.id,
};
return { app, path, state };
}
return { app, path };
}
}

Expand Down
1 change: 1 addition & 0 deletions src/plugins/embeddable/public/lib/state_transfer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface EmbeddableEditorState {
originatingApp: string;
byValueMode?: boolean;
valueInput?: EmbeddableInput;
embeddableId?: string;
}

export function isEmbeddableEditorState(state: unknown): state is EmbeddableEditorState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class DefaultEditorController {
]
: visType.editorConfig.optionTabs),
];

this.state = {
vis,
optionTabs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDe
try {
const visId = vis.id as string;

const editPath = visId ? savedVisualizations.urlFor(visId) : '';
const editPath = visId ? savedVisualizations.urlFor(visId) : '#/edit_by_value';

const editUrl = visId
? getHttp().basePath.prepend(`/app/visualize${savedVisualizations.urlFor(visId)}`)
: '';
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/visualize/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { schema, TypeOf } from '@kbn/config-schema';

export const configSchema = schema.object({
showNewVisualizeFlow: schema.boolean({ defaultValue: false }),
showNewVisualizeFlow: schema.boolean({ defaultValue: true }),
});

export type ConfigSchema = TypeOf<typeof configSchema>;
10 changes: 9 additions & 1 deletion src/plugins/visualize/public/application/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ import { Route, Switch, useLocation } from 'react-router-dom';
import { syncQueryStateWithUrl } from '../../../data/public';
import { useKibana } from '../../../kibana_react/public';
import { VisualizeServices } from './types';
import { VisualizeEditor, VisualizeListing, VisualizeNoMatch } from './components';
import {
VisualizeEditor,
VisualizeListing,
VisualizeNoMatch,
VisualizeByValueEditor,
} from './components';
import { VisualizeConstants } from './visualize_constants';

export const VisualizeApp = () => {
Expand All @@ -48,6 +53,9 @@ export const VisualizeApp = () => {

return (
<Switch>
<Route exact path={`${VisualizeConstants.EDIT_BY_VALUE_PATH}`}>
<VisualizeByValueEditor />
</Route>
<Route path={[VisualizeConstants.CREATE_PATH, `${VisualizeConstants.EDIT_PATH}/:id`]}>
<VisualizeEditor />
</Route>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
export { VisualizeListing } from './visualize_listing';
export { VisualizeEditor } from './visualize_editor';
export { VisualizeNoMatch } from './visualize_no_match';
export { VisualizeByValueEditor } from './visualize_byvalue_editor';
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import './visualize_editor.scss';
import React, { useEffect, useState } from 'react';
import { EventEmitter } from 'events';

import { VisualizeInput } from 'src/plugins/visualizations/public';
import { useKibana } from '../../../../kibana_react/public';
import {
useChromeVisibility,
useVisByValue,
useVisualizeAppState,
useEditorUpdates,
useLinkedSearchUpdates,
} from '../utils';
import { VisualizeServices } from '../types';
import { VisualizeEditorCommon } from './visualize_editor_common';

export const VisualizeByValueEditor = () => {
const [originatingApp, setOriginatingApp] = useState<string>();
const { services } = useKibana<VisualizeServices>();
const [eventEmitter] = useState(new EventEmitter());
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(true);
const [embeddableId, setEmbeddableId] = useState<string>();
const [valueInput, setValueInput] = useState<VisualizeInput>();

useEffect(() => {
const { originatingApp: value, embeddableId: embeddableIdValue, valueInput: valueInputValue } =
services.embeddable
.getStateTransfer(services.scopedHistory)
.getIncomingEditorState({ keysToRemoveAfterFetch: ['id', 'embeddableId', 'valueInput'] }) ||
{};
setOriginatingApp(value);
setValueInput(valueInputValue);
setEmbeddableId(embeddableIdValue);
}, [services]);

const isChromeVisible = useChromeVisibility(services.chrome);

const { savedVisInstance, visEditorRef, visEditorController } = useVisByValue(
services,
eventEmitter,
isChromeVisible,
valueInput
);
const { appState, hasUnappliedChanges } = useVisualizeAppState(
services,
eventEmitter,
true,
savedVisInstance
);
const { isEmbeddableRendered, currentAppState } = useEditorUpdates(
services,
eventEmitter,
setHasUnsavedChanges,
appState,
savedVisInstance,
visEditorController,
true
);
useLinkedSearchUpdates(services, eventEmitter, appState, savedVisInstance);

useEffect(() => {
// clean up all registered listeners if any is left
return () => {
eventEmitter.removeAllListeners();
};
}, [eventEmitter]);

return (
<VisualizeEditorCommon
savedVisInstance={savedVisInstance}
appState={appState}
currentAppState={currentAppState}
isChromeVisible={isChromeVisible}
hasUnsavedChanges={hasUnsavedChanges}
hasUnappliedChanges={hasUnappliedChanges}
isEmbeddableRendered={isEmbeddableRendered}
originatingApp={originatingApp}
setHasUnsavedChanges={setHasUnsavedChanges}
visEditorRef={visEditorRef}
embeddableId={embeddableId}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import './visualize_editor.scss';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { EventEmitter } from 'events';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiScreenReaderOnly } from '@elastic/eui';

import { useKibana } from '../../../../kibana_react/public';
import {
Expand All @@ -33,8 +31,7 @@ import {
useLinkedSearchUpdates,
} from '../utils';
import { VisualizeServices } from '../types';
import { ExperimentalVisInfo } from './experimental_vis_info';
import { VisualizeTopNav } from './visualize_top_nav';
import { VisualizeEditorCommon } from './visualize_editor_common';

export const VisualizeEditor = () => {
const { id: visualizationIdFromUrl } = useParams();
Expand All @@ -53,6 +50,7 @@ export const VisualizeEditor = () => {
const { appState, hasUnappliedChanges } = useVisualizeAppState(
services,
eventEmitter,
false,
savedVisInstance
);
const { isEmbeddableRendered, currentAppState } = useEditorUpdates(
Expand All @@ -67,7 +65,9 @@ export const VisualizeEditor = () => {

useEffect(() => {
const { originatingApp: value } =
services.embeddable.getStateTransfer(services.scopedHistory).getIncomingEditorState() || {};
services.embeddable
.getStateTransfer(services.scopedHistory)
.getIncomingEditorState({ keysToRemoveAfterFetch: ['id', 'input'] }) || {};
setOriginatingApp(value);
}, [services]);

Expand All @@ -79,37 +79,18 @@ export const VisualizeEditor = () => {
}, [eventEmitter]);

return (
<div className={`app-container visEditor visEditor--${savedVisInstance?.vis.type.name}`}>
{savedVisInstance && appState && currentAppState && (
<VisualizeTopNav
currentAppState={currentAppState}
hasUnsavedChanges={hasUnsavedChanges}
setHasUnsavedChanges={setHasUnsavedChanges}
isChromeVisible={isChromeVisible}
isEmbeddableRendered={isEmbeddableRendered}
hasUnappliedChanges={hasUnappliedChanges}
originatingApp={originatingApp}
savedVisInstance={savedVisInstance}
stateContainer={appState}
visualizationIdFromUrl={visualizationIdFromUrl}
/>
)}
{savedVisInstance?.vis?.type?.isExperimental && <ExperimentalVisInfo />}
{savedVisInstance && (
<EuiScreenReaderOnly>
<h1>
<FormattedMessage
id="visualize.pageHeading"
defaultMessage="{chartName} {chartType} visualization"
values={{
chartName: savedVisInstance.savedVis.title,
chartType: savedVisInstance.vis.type.title,
}}
/>
</h1>
</EuiScreenReaderOnly>
)}
<div className={isChromeVisible ? 'visEditor__content' : 'visualize'} ref={visEditorRef} />
</div>
<VisualizeEditorCommon
savedVisInstance={savedVisInstance}
appState={appState}
currentAppState={currentAppState}
isChromeVisible={isChromeVisible}
hasUnsavedChanges={hasUnsavedChanges}
hasUnappliedChanges={hasUnappliedChanges}
isEmbeddableRendered={isEmbeddableRendered}
originatingApp={originatingApp}
visualizationIdFromUrl={visualizationIdFromUrl}
setHasUnsavedChanges={setHasUnsavedChanges}
visEditorRef={visEditorRef}
/>
);
};
Loading

0 comments on commit b4972da

Please sign in to comment.