Skip to content

Commit

Permalink
[RUM Dashboard] Visitors by region map (#77135)
Browse files Browse the repository at this point in the history
Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
shahzad31 and elasticmachine authored Sep 14, 2020
1 parent 1a49c4e commit dcd119c
Show file tree
Hide file tree
Showing 20 changed files with 1,033 additions and 17 deletions.
15 changes: 6 additions & 9 deletions x-pack/plugins/apm/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"apmOss",
"data",
"licensing",
"triggers_actions_ui"
"triggers_actions_ui",
"embeddable"
],
"optionalPlugins": [
"cloud",
Expand All @@ -22,17 +23,13 @@
],
"server": true,
"ui": true,
"configPath": [
"xpack",
"apm"
],
"extraPublicDirs": [
"public/style/variables"
],
"configPath": ["xpack", "apm"],
"extraPublicDirs": ["public/style/variables"],
"requiredBundles": [
"kibanaReact",
"kibanaUtils",
"observability",
"home"
"home",
"maps"
]
}
17 changes: 13 additions & 4 deletions x-pack/plugins/apm/public/application/csmApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext';
import { UrlParamsProvider } from '../context/UrlParamsContext';
import { useBreadcrumbs } from '../hooks/use_breadcrumbs';
import { ConfigSchema } from '../index';
import { ApmPluginSetupDeps } from '../plugin';
import { ApmPluginSetupDeps, ApmPluginStartDeps } from '../plugin';
import { createCallApmApi } from '../services/rest/createCallApmApi';
import { px, units } from '../style/variables';

Expand Down Expand Up @@ -70,11 +70,13 @@ export function CsmAppRoot({
deps,
history,
config,
corePlugins: { embeddable },
}: {
core: CoreStart;
deps: ApmPluginSetupDeps;
history: AppMountParameters['history'];
config: ConfigSchema;
corePlugins: ApmPluginStartDeps;
}) {
const i18nCore = core.i18n;
const plugins = deps;
Expand All @@ -86,7 +88,7 @@ export function CsmAppRoot({
return (
<RedirectAppLinks application={core.application}>
<ApmPluginContext.Provider value={apmPluginContextValue}>
<KibanaContextProvider services={{ ...core, ...plugins }}>
<KibanaContextProvider services={{ ...core, ...plugins, embeddable }}>
<i18nCore.Context>
<Router history={history}>
<UrlParamsProvider>
Expand All @@ -110,12 +112,19 @@ export const renderApp = (
core: CoreStart,
deps: ApmPluginSetupDeps,
{ element, history }: AppMountParameters,
config: ConfigSchema
config: ConfigSchema,
corePlugins: ApmPluginStartDeps
) => {
createCallApmApi(core.http);

ReactDOM.render(
<CsmAppRoot core={core} deps={deps} history={history} config={config} />,
<CsmAppRoot
core={core}
deps={deps}
history={history}
config={config}
corePlugins={corePlugins}
/>,
element
);
return () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ export function CoreVitalItem({
setInFocusInd(ind);
}}
/>
<EuiSpacer size="xl" />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { PageLoadDistribution } from './PageLoadDistribution';
import { I18LABELS } from './translations';
import { VisitorBreakdown } from './VisitorBreakdown';
import { CoreVitals } from './CoreVitals';
import { VisitorBreakdownMap } from './VisitorBreakdownMap';

export function RumDashboard() {
return (
Expand Down Expand Up @@ -67,6 +68,9 @@ export function RumDashboard() {
<EuiFlexItem grow={3}>
<VisitorBreakdown />
</EuiFlexItem>
<EuiFlexItem grow={3}>
<VisitorBreakdownMap />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* 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, { useEffect, useState, useRef } from 'react';
import uuid from 'uuid';
import styled from 'styled-components';

import {
MapEmbeddable,
MapEmbeddableInput,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../../maps/public/embeddable';
import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/common/constants';
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
import {
ErrorEmbeddable,
ViewMode,
isErrorEmbeddable,
} from '../../../../../../../../src/plugins/embeddable/public';
import { getLayerList } from './LayerList';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { RenderTooltipContentParams } from '../../../../../../maps/public';
import { MapToolTip } from './MapToolTip';
import { useMapFilters } from './useMapFilters';
import { EmbeddableStart } from '../../../../../../../../src/plugins/embeddable/public';

const EmbeddedPanel = styled.div`
z-index: auto;
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
position: relative;
.embPanel__content {
display: flex;
flex: 1 1 100%;
z-index: 1;
min-height: 0; // Absolute must for Firefox to scroll contents
}
&&& .mapboxgl-canvas {
animation: none !important;
}
`;

interface KibanaDeps {
embeddable: EmbeddableStart;
}
export function EmbeddedMapComponent() {
const { urlParams } = useUrlParams();

const { start, end, serviceName } = urlParams;

const mapFilters = useMapFilters();

const [embeddable, setEmbeddable] = useState<
MapEmbeddable | ErrorEmbeddable | undefined
>();

const embeddableRoot: React.RefObject<HTMLDivElement> = useRef<
HTMLDivElement
>(null);

const {
services: { embeddable: embeddablePlugin },
} = useKibana<KibanaDeps>();

if (!embeddablePlugin) {
throw new Error('Embeddable start plugin not found');
}
const factory: any = embeddablePlugin.getEmbeddableFactory(
MAP_SAVED_OBJECT_TYPE
);

const input: MapEmbeddableInput = {
id: uuid.v4(),
filters: mapFilters,
refreshConfig: {
value: 0,
pause: false,
},
viewMode: ViewMode.VIEW,
isLayerTOCOpen: false,
query: {
query: 'transaction.type : "page-load"',
language: 'kuery',
},
...(start && {
timeRange: {
from: new Date(start!).toISOString(),
to: new Date(end!).toISOString(),
},
}),
hideFilterActions: true,
};

function renderTooltipContent({
addFilters,
closeTooltip,
features,
isLocked,
getLayerName,
loadFeatureProperties,
loadFeatureGeometry,
}: RenderTooltipContentParams) {
const props = {
addFilters,
closeTooltip,
isLocked,
getLayerName,
loadFeatureProperties,
loadFeatureGeometry,
};

return <MapToolTip {...props} features={features} />;
}

useEffect(() => {
if (embeddable != null && serviceName) {
embeddable.updateInput({ filters: mapFilters });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mapFilters]);

// DateRange updated useEffect
useEffect(() => {
if (embeddable != null && start != null && end != null) {
const timeRange = {
from: new Date(start).toISOString(),
to: new Date(end).toISOString(),
};
embeddable.updateInput({ timeRange });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [start, end]);

useEffect(() => {
async function setupEmbeddable() {
if (!factory) {
throw new Error('Map embeddable not found.');
}
const embeddableObject: any = await factory.create({
...input,
title: 'Visitors by region',
});

if (embeddableObject && !isErrorEmbeddable(embeddableObject)) {
embeddableObject.setRenderTooltipContent(renderTooltipContent);
await embeddableObject.setLayerList(getLayerList());
}

setEmbeddable(embeddableObject);
}

setupEmbeddable();

// we want this effect to execute exactly once after the component mounts
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// We can only render after embeddable has already initialized
useEffect(() => {
if (embeddableRoot.current && embeddable) {
embeddable.render(embeddableRoot.current);
}
}, [embeddable, embeddableRoot]);

return (
<EmbeddedPanel>
<div
data-test-subj="xpack.apm.regionMap.embeddedPanel"
className="embPanel__content"
ref={embeddableRoot}
/>
</EmbeddedPanel>
);
}

EmbeddedMapComponent.displayName = 'EmbeddedMap';

export const EmbeddedMap = React.memo(EmbeddedMapComponent);
Loading

0 comments on commit dcd119c

Please sign in to comment.