[Canvas] Remove Angular and unnecessary reporting config from Canvas (#54050)

* Remove Angular from Canvas

* Remove reporting config behavior from Canvas since it's no longer needed

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Poff Poffenberger 2020-01-27 15:01:26 -06:00 committed by GitHub
parent 35603c8832
commit be9d9c2ffe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 118 additions and 371 deletions

View file

@ -50,14 +50,6 @@ jest.mock('@elastic/eui/packages/react-datepicker', () => {
jest.mock('plugins/interpreter/registries', () => ({}));
// Disabling this test due to https://github.com/elastic/eui/issues/2242
jest.mock(
'../public/components/workpad_header/workpad_export/__examples__/disabled_panel.stories',
() => {
return 'Disabled Panel';
}
);
// Disabling this test due to https://github.com/elastic/eui/issues/2242
jest.mock(
'../public/components/workpad_header/workpad_export/flyout/__examples__/share_website_flyout.stories',

View file

@ -7,13 +7,13 @@
import { i18n } from '@kbn/i18n';
import { CANVAS as canvas } from './constants';
export const AngularStrings = {
CanvasRootController: {
getReadOnlyBadgeText: () =>
export const CapabilitiesStrings = {
ReadOnlyBadge: {
getText: () =>
i18n.translate('xpack.canvas.badge.readOnly.text', {
defaultMessage: 'Read only',
}),
getReadOnlyBadgeTooltip: () =>
getTooltip: () =>
i18n.translate('xpack.canvas.badge.readOnly.tooltip', {
defaultMessage: 'Unable to save {canvas} workpads',
values: {

View file

@ -6,7 +6,7 @@
import { i18n } from '@kbn/i18n';
export * from './angular';
export * from './capabilities';
export * from './components';
export * from './constants';
export * from './errors';

View file

@ -1,8 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './state_management'; // Requires 6.2.0+
export * from './location_provider';

View file

@ -1,21 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ILocationProvider } from 'angular';
import { CoreStart, CanvasStartDeps } from '../../plugin';
export function initLocationProvider(coreStart: CoreStart, plugins: CanvasStartDeps) {
// disable angular's location provider
const app = plugins.__LEGACY.uiModules.get('apps/canvas');
app.config(($locationProvider: ILocationProvider) => {
$locationProvider.html5Mode({
enabled: false,
requireBase: false,
rewriteLinks: false,
});
});
}

View file

@ -1,13 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export function initStateManagement(coreStart, plugins) {
// disable the kibana state management
const app = plugins.__LEGACY.uiModules.get('apps/canvas');
app.config(stateManagementConfigProvider => {
stateManagementConfigProvider.disable();
});
}

View file

@ -1,54 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import { CanvasStartDeps, CoreStart } from '../../plugin';
import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
// @ts-ignore Untyped local
import { App } from '../../components/app';
import { AngularStrings } from '../../../i18n';
const { CanvasRootController: strings } = AngularStrings;
export function CanvasRootControllerFactory(coreStart: CoreStart, plugins: CanvasStartDeps) {
return function CanvasRootController(
canvasStore: Store,
$scope: any, // Untyped in Kibana
$element: any // Untyped in Kibana
) {
const domNode = $element[0];
// set the read-only badge when appropriate
coreStart.chrome.setBadge(
coreStart.application.capabilities.canvas.save
? undefined
: {
text: strings.getReadOnlyBadgeText(),
tooltip: strings.getReadOnlyBadgeTooltip(),
iconType: 'glasses',
}
);
render(
<KibanaContextProvider services={{ ...coreStart, ...plugins }}>
<I18nProvider>
<Provider store={canvasStore}>
<App />
</Provider>
</I18nProvider>
</KibanaContextProvider>,
domNode
);
$scope.$on('$destroy', () => {
unmountComponentAtNode(domNode);
});
};
}

View file

@ -1,7 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { CanvasRootControllerFactory } from './canvas';

View file

@ -1,7 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './store';

View file

@ -1,31 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { createStore } from '../../state/store';
import { getInitialState } from '../../state/initial_state';
export function initStore(coreStart, plugins) {
const app = plugins.__LEGACY.uiModules.get('apps/canvas');
app.service('canvasStore', (kbnVersion, basePath, reportingBrowserType, serverFunctions) => {
const initialState = getInitialState();
// Set the defaults from Kibana plugin
initialState.app = {
kbnVersion,
basePath,
reportingBrowserType,
serverFunctions,
ready: false,
};
const store = createStore(initialState);
// TODO: debugging, remove this
window.canvasStore = store;
return store;
});
}

View file

@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { Store } from 'redux';
import ReactDOM from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { Provider } from 'react-redux';
import { AppMountParameters, CoreStart } from 'kibana/public';
// @ts-ignore Untyped local
import { App } from './components/app';
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
export const renderApp = (
coreStart: CoreStart,
plugins: object,
{ element }: AppMountParameters,
canvasStore: Store
) => {
ReactDOM.render(
<KibanaContextProvider services={{ ...coreStart, ...plugins }}>
<I18nProvider>
<Provider store={canvasStore}>
<App />
</Provider>
</I18nProvider>
</KibanaContextProvider>,
element
);
return () => ReactDOM.unmountComponentAtNode(element);
};

View file

@ -1,50 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Storyshots components/Export/WorkpadExport disabled 1`] = `
<div>
<div
className="euiPopover euiPopover--anchorDownCenter"
container={null}
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
>
<div
className="euiPopover__anchor"
>
<span
className="euiToolTipAnchor"
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<button
aria-label="Share this workpad"
className="euiButtonIcon euiButtonIcon--primary"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
type="button"
>
<svg
aria-hidden={true}
className="euiIcon euiIcon--medium euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height={16}
role="img"
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</button>
</span>
</div>
</div>
</div>
`;
exports[`Storyshots components/Export/WorkpadExport enabled 1`] = `
<div>
<div

View file

@ -1,31 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import React from 'react';
import { DisabledPanel } from '../disabled_panel';
storiesOf('components/Export/DisabledPanel', module)
.addParameters({
info: {
inline: true,
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '20px 30px',
width: '290px',
},
},
},
})
.add('default', () => (
<div className="euiPanel">
<DisabledPanel onCopy={action('onCopy')} />
</div>
));

View file

@ -8,26 +8,13 @@ import { action } from '@storybook/addon-actions';
import React from 'react';
import { WorkpadExport } from '../workpad_export';
storiesOf('components/Export/WorkpadExport', module)
.add('enabled', () => (
<WorkpadExport
enabled={true}
onCopy={action('onCopy')}
onExport={action('onExport')}
getExportUrl={(type: string) => {
action(`getExportUrl('${type}')`);
return type;
}}
/>
))
.add('disabled', () => (
<WorkpadExport
enabled={false}
onCopy={action('onCopy')}
onExport={action('onExport')}
getExportUrl={(type: string) => {
action(`getExportUrl('${type}')`);
return type;
}}
/>
));
storiesOf('components/Export/WorkpadExport', module).add('enabled', () => (
<WorkpadExport
onCopy={action('onCopy')}
onExport={action('onExport')}
getExportUrl={(type: string) => {
action(`getExportUrl('${type}')`);
return type;
}}
/>
));

View file

@ -1,50 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiText, EuiSpacer, EuiCodeBlock, EuiCode } from '@elastic/eui';
import { Clipboard } from '../../clipboard';
const REPORTING_CONFIG = `xpack.reporting:
enabled: true
capture.browser.type: chromium`;
interface Props {
/** Handler to invoke when the Kibana configuration is copied. */
onCopy: () => void;
}
/**
* A panel to display within the Export menu when reporting is disabled.
*/
export const DisabledPanel = ({ onCopy }: Props) => (
<div className="canvasWorkpadExport__panelContent">
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.canvas.workpadHeaderWorkpadExport.pdfPanelDisabledDescription"
defaultMessage="Export to PDF is disabled. You must configure reporting to use the Chromium browser. Add
this to your {fileName} file."
values={{
fileName: <EuiCode>kibana.yml</EuiCode>,
}}
/>
</p>
</EuiText>
<EuiSpacer />
<Clipboard content={REPORTING_CONFIG} onCopy={onCopy}>
<EuiCodeBlock
className="canvasWorkpadExport__reportingConfig"
paddingSize="s"
fontSize="s"
language="yml"
>
{REPORTING_CONFIG}
</EuiCodeBlock>
</Clipboard>
</div>
);

View file

@ -10,8 +10,6 @@ import { jobCompletionNotifications } from '../../../../../reporting/public/lib/
// @ts-ignore Untyped local
import { getWorkpad, getPages } from '../../../state/selectors/workpad';
// @ts-ignore Untyped local
import { getReportingBrowserType } from '../../../state/selectors/app';
// @ts-ignore Untyped local
import { notify } from '../../../lib/notify';
import { getWindow } from '../../../lib/get_window';
// @ts-ignore Untyped local
@ -34,7 +32,6 @@ const { WorkpadHeaderWorkpadExport: strings } = ComponentStrings;
const mapStateToProps = (state: State) => ({
workpad: getWorkpad(state),
pageCount: getPages(state).length,
enabled: getReportingBrowserType(state) === 'chromium',
});
const getAbsoluteUrl = (path: string) => {
@ -51,15 +48,13 @@ const getAbsoluteUrl = (path: string) => {
interface Props {
workpad: CanvasWorkpad;
pageCount: number;
enabled: boolean;
}
export const WorkpadExport = compose<ComponentProps, {}>(
connect(mapStateToProps),
withKibana,
withProps(
({ workpad, pageCount, enabled, kibana }: Props & WithKibanaProps): ComponentProps => ({
enabled,
({ workpad, pageCount, kibana }: Props & WithKibanaProps): ComponentProps => ({
getExportUrl: type => {
if (type === 'pdf') {
const pdfUrl = getPdfUrl(workpad, { pageCount }, kibana.services.http.basePath.prepend);

View file

@ -9,7 +9,6 @@ import PropTypes from 'prop-types';
import { EuiButtonIcon, EuiContextMenu, EuiIcon } from '@elastic/eui';
// @ts-ignore Untyped local
import { Popover } from '../../popover';
import { DisabledPanel } from './disabled_panel';
import { PDFPanel } from './pdf_panel';
import { ShareWebsiteFlyout } from './flyout';
@ -29,8 +28,6 @@ export type OnCloseFn = (type: CloseTypes) => void;
export type GetExportUrlFn = (type: ExportUrlTypes) => string;
export interface Props {
/** True if exporting is enabled, false otherwise. */
enabled: boolean;
/** Handler to invoke when an export URL is copied to the clipboard. */
onCopy: OnCopyFn;
/** Handler to invoke when an end product is exported. */
@ -42,12 +39,7 @@ export interface Props {
/**
* The Menu for Exporting a Workpad from Canvas.
*/
export const WorkpadExport: FunctionComponent<Props> = ({
enabled,
onCopy,
onExport,
getExportUrl,
}) => {
export const WorkpadExport: FunctionComponent<Props> = ({ onCopy, onExport, getExportUrl }) => {
const [showFlyout, setShowFlyout] = useState(false);
const onClose = () => {
@ -106,16 +98,7 @@ export const WorkpadExport: FunctionComponent<Props> = ({
panel: {
id: 1,
title: strings.getShareDownloadPDFTitle(),
content: enabled ? (
getPDFPanel(closePopover)
) : (
<DisabledPanel
onCopy={() => {
onCopy('reportingConfig');
closePopover();
}}
/>
),
content: getPDFPanel(closePopover),
},
},
{
@ -160,7 +143,6 @@ export const WorkpadExport: FunctionComponent<Props> = ({
};
WorkpadExport.propTypes = {
enabled: PropTypes.bool.isRequired,
onCopy: PropTypes.func.isRequired,
onExport: PropTypes.func.isRequired,
getExportUrl: PropTypes.func.isRequired,

View file

@ -9,8 +9,6 @@ import { CanvasStartDeps } from './plugin'; // eslint-disable-line import/order
// @ts-ignore Untyped Kibana Lib
import chrome, { loadingCount } from 'ui/chrome'; // eslint-disable-line import/order
// @ts-ignore Untyped Module
import { uiModules } from 'ui/modules'; // eslint-disable-line import/order
import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; // eslint-disable-line import/order
import { Storage } from '../../../../../src/plugins/kibana_utils/public'; // eslint-disable-line import/order
// @ts-ignore Untyped Kibana Lib
@ -25,6 +23,7 @@ const shimCoreStart = {
...npStart.core,
};
const shimSetupPlugins = {};
const shimStartPlugins: CanvasStartDeps = {
...npStart.plugins,
__LEGACY: {
@ -33,12 +32,9 @@ const shimStartPlugins: CanvasStartDeps = {
// ToDo: Copy directly into canvas
formatMsg,
QueryString,
// ToDo: Remove in favor of core.application.register
setRootController: chrome.setRootController,
storage: Storage,
// ToDo: Won't be a part of New Platform. Will need to handle internally
trackSubUrlForApp: chrome.trackSubUrlForApp,
uiModules,
},
};

View file

@ -7,15 +7,15 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Chrome } from 'ui/chrome';
import { IModule } from 'angular';
import { i18n } from '@kbn/i18n';
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
import { CoreSetup, CoreStart, Plugin } from '../../../../../src/core/public';
// @ts-ignore: Untyped Local
import { initStateManagement, initLocationProvider } from './angular/config';
import { CanvasRootControllerFactory } from './angular/controllers';
// @ts-ignore: Untypled Local
import { initStore } from './angular/services';
import { CapabilitiesStrings } from '../i18n';
const { ReadOnlyBadge: strings } = CapabilitiesStrings;
import { createStore } from './store';
// @ts-ignore: untyped local component
import { HelpMenu } from './components/help_menu/help_menu';
// @ts-ignore: untyped local
@ -40,12 +40,8 @@ export interface CanvasStartDeps {
absoluteToParsedUrl: (url: string, basePath: string) => any;
formatMsg: any;
QueryString: any;
setRootController: Chrome['setRootController'];
storage: typeof Storage;
trackSubUrlForApp: Chrome['trackSubUrlForApp'];
uiModules: {
get: (module: string) => IModule;
};
};
}
@ -67,6 +63,22 @@ export class CanvasPlugin
// Things like registering functions to the interpreter that need
// to be available everywhere, not just in Canvas
core.application.register({
id: 'canvas',
title: 'Canvas App',
async mount(context, params) {
// Load application bundle
const { renderApp } = await import('./application');
// Setup our store
const canvasStore = await createStore(core, plugins);
// Get start services
const [coreStart, depsStart] = await core.getStartServices();
return renderApp(coreStart, depsStart, params, canvasStore);
},
});
return {};
}
@ -74,14 +86,19 @@ export class CanvasPlugin
loadExpressionTypes();
loadTransitions();
initStateManagement(core, plugins);
initLocationProvider(core, plugins);
initStore(core, plugins);
initClipboard(plugins.__LEGACY.storage);
initLoadingIndicator(core.http.addLoadingCountSource);
const CanvasRootController = CanvasRootControllerFactory(core, plugins);
plugins.__LEGACY.setRootController('canvas', CanvasRootController);
core.chrome.setBadge(
core.application.capabilities.canvas && core.application.capabilities.canvas.save
? undefined
: {
text: strings.getText(),
tooltip: strings.getTooltip(),
iconType: 'glasses',
}
);
core.chrome.setHelpExtension({
appName: i18n.translate('xpack.canvas.helpMenu.appName', {
defaultMessage: 'Canvas',

View file

@ -32,10 +32,6 @@ export function getBasePath(state: State): State['app']['basePath'] {
return state.app.basePath;
}
export function getReportingBrowserType(state: State): State['app']['reportingBrowserType'] {
return state.app.reportingBrowserType;
}
// return true only when the required parameters are in the state
export function isAppReady(state: State): boolean {
const appReady = getAppReady(state);

View file

@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// @ts-ignore Untyped local
import { createStore as createReduxStore } from './state/store';
// @ts-ignore Untyped local
import { getInitialState } from './state/initial_state';
import { CoreSetup } from '../../../../../src/core/public';
import { CanvasSetupDeps } from './plugin';
export async function createStore(core: CoreSetup, plugins: CanvasSetupDeps) {
const initialState = getInitialState();
const basePath = core.http.basePath.get();
// Retrieve server functions
const serverFunctionsResponse = await core.http.get(`/api/interpreter/fns`);
const serverFunctions = Object.values(serverFunctionsResponse);
initialState.app = {
basePath,
serverFunctions,
ready: false,
};
return createReduxStore(initialState);
}

View file

@ -13,25 +13,11 @@ export class Plugin {
public setup(core: CoreSetup, plugins: PluginsSetup) {
routes(core);
const { serverFunctions } = plugins.interpreter.register({ serverFunctions: functions });
plugins.interpreter.register({ serverFunctions: functions });
core.injectUiAppVars('canvas', async () => {
const config = core.getServerConfig();
const basePath = config.get('server.basePath');
const reportingBrowserType = (() => {
const configKey = 'xpack.reporting.capture.browser.type';
if (!config.has(configKey)) {
return null;
}
return config.get(configKey);
})();
return {
...plugins.kibana.injectedUiAppVars,
kbnIndex: config.get('kibana.index'),
serverFunctions: serverFunctions.toArray(),
basePath,
reportingBrowserType,
};
});

View file

@ -32,9 +32,7 @@ export interface AppState {
}
interface StoreAppState {
kbnVersion: string;
basePath: string;
reportingBrowserType: string;
// TODO: These server functions are actually missing the fn because they are serialized from the server
serverFunctions: CanvasFunction[];
ready: boolean;

View file

@ -5271,7 +5271,6 @@
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyAriaLabel": "この {URL} を使用してスクリプトから、または Watcher で {PDF} を生成することもできます。{URL} をクリップボードにコピーするにはエンターキーを押してください。",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyButtonLabel": "{POST} {URL} をコピー",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyDescription": "{POST} {URL} をコピーして {KIBANA} 外または ウォッチャー から生成することもできます。",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelDisabledDescription": "PDF へのエクスポートは無効になっています。Chromium ブラウザを使用するにはレポートの構成が必要です。これを {fileName} ファイルに追加します。",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateButtonLabel": "{PDF} を生成",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateDescription": "ワークパッドのサイズによって、{PDF} の生成には数分かかる場合があります。",
"xpack.canvas.workpadHeaderWorkpadExport.shareDownloadJSONTitle": "{JSON} をダウンロード",

View file

@ -5270,7 +5270,6 @@
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyAriaLabel": "或者,也可以从脚本或使用 {URL} 通过 Watcher 生成 {PDF}。按 Enter 键可将 {URL} 复制到剪贴板。",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyButtonLabel": "复制 {POST} {URL}",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyDescription": "或者,复制此 {POST} {URL} 以从 {KIBANA} 外部或从 Watcher 调用生成。",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelDisabledDescription": "导出到 PDF 已禁用。必须配置报告,才能使用 Chromium 浏览器。将其添加到您的 {fileName} 文件中。",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateButtonLabel": "生成 {PDF}",
"xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateDescription": "{PDF} 可能会花费 1 或 2 分钟生成,取决于 Workpad 的大小。",
"xpack.canvas.workpadHeaderWorkpadExport.shareDownloadJSONTitle": "下载为 {JSON}",