[Uptime] Remove Location map from Uptime monitor details page (#96517)
* remove LocationMap from Uptime Monitor details Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
91e1acd98d
commit
06e01c20e7
|
@ -22898,7 +22898,6 @@
|
|||
"xpack.uptime.certs.status.ok.label": " {okRelativeDate}",
|
||||
"xpack.uptime.charts.mlAnnotation.header": "スコア:{score}",
|
||||
"xpack.uptime.charts.mlAnnotation.severity": "深刻度:{severity}",
|
||||
"xpack.uptime.components.embeddables.embeddedMap.embeddablePanelTitle": "オブザーバー位置情報マップを監視",
|
||||
"xpack.uptime.controls.selectSeverity.criticalLabel": "致命的",
|
||||
"xpack.uptime.controls.selectSeverity.majorLabel": "メジャー",
|
||||
"xpack.uptime.controls.selectSeverity.minorLabel": "マイナー",
|
||||
|
@ -22928,12 +22927,7 @@
|
|||
"xpack.uptime.filterPopout.searchMessage.ariaLabel": "{title} を検索",
|
||||
"xpack.uptime.filterPopover.filterItem.label": "{title} {item}でフィルタリングします。",
|
||||
"xpack.uptime.integrationLink.missingDataMessage": "この統合に必要なデータが見つかりませんでした。",
|
||||
"xpack.uptime.locationAvailabilityViewToggleLegend": "トグルを表示",
|
||||
"xpack.uptime.locationMap.locations.missing.message": "重要な位置情報構成がありません。{codeBlock}フィールドを使用して、アップタイムチェック用に一意の地域を作成できます。",
|
||||
"xpack.uptime.locationMap.locations.missing.message1": "詳細については、ドキュメンテーションを参照してください。",
|
||||
"xpack.uptime.locationMap.locations.missing.title": "地理情報の欠測",
|
||||
"xpack.uptime.locationName.helpLinkAnnotation": "場所を追加",
|
||||
"xpack.uptime.mapToolTip.AvailabilityStat.title": "{value} %",
|
||||
"xpack.uptime.ml.durationChart.exploreInMlApp": "ML アプリで探索",
|
||||
"xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "異常検知",
|
||||
"xpack.uptime.ml.enableAnomalyDetectionPanel.cancelLabel": "キャンセル",
|
||||
|
@ -23585,4 +23579,4 @@
|
|||
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。",
|
||||
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23257,7 +23257,6 @@
|
|||
"xpack.uptime.certs.status.ok.label": " 对于 {okRelativeDate}",
|
||||
"xpack.uptime.charts.mlAnnotation.header": "分数:{score}",
|
||||
"xpack.uptime.charts.mlAnnotation.severity": "严重性:{severity}",
|
||||
"xpack.uptime.components.embeddables.embeddedMap.embeddablePanelTitle": "监测观察者位置地图",
|
||||
"xpack.uptime.controls.selectSeverity.criticalLabel": "紧急",
|
||||
"xpack.uptime.controls.selectSeverity.majorLabel": "重大",
|
||||
"xpack.uptime.controls.selectSeverity.minorLabel": "轻微",
|
||||
|
@ -23287,12 +23286,7 @@
|
|||
"xpack.uptime.filterPopout.searchMessage.ariaLabel": "搜索 {title}",
|
||||
"xpack.uptime.filterPopover.filterItem.label": "按 {title} {item} 筛选。",
|
||||
"xpack.uptime.integrationLink.missingDataMessage": "未找到此集成的所需数据。",
|
||||
"xpack.uptime.locationAvailabilityViewToggleLegend": "视图切换",
|
||||
"xpack.uptime.locationMap.locations.missing.message": "重要的地理位置配置缺失。您可以使用 {codeBlock} 字段为您的运行时间检查创建独特的地理区域。",
|
||||
"xpack.uptime.locationMap.locations.missing.message1": "在我们的文档中获取更多的信息。",
|
||||
"xpack.uptime.locationMap.locations.missing.title": "地理信息缺失",
|
||||
"xpack.uptime.locationName.helpLinkAnnotation": "添加位置",
|
||||
"xpack.uptime.mapToolTip.AvailabilityStat.title": "{value} %",
|
||||
"xpack.uptime.ml.durationChart.exploreInMlApp": "在 ML 应用中浏览",
|
||||
"xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "异常检测",
|
||||
"xpack.uptime.ml.enableAnomalyDetectionPanel.cancelLabel": "取消",
|
||||
|
@ -23954,4 +23948,4 @@
|
|||
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。",
|
||||
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,16 @@
|
|||
{
|
||||
"configPath": ["xpack", "uptime"],
|
||||
"configPath": [
|
||||
"xpack",
|
||||
"uptime"
|
||||
],
|
||||
"id": "uptime",
|
||||
"kibanaVersion": "kibana",
|
||||
"optionalPlugins": ["data", "home", "observability", "ml"],
|
||||
"optionalPlugins": [
|
||||
"data",
|
||||
"home",
|
||||
"observability",
|
||||
"ml"
|
||||
],
|
||||
"requiredPlugins": [
|
||||
"alerting",
|
||||
"embeddable",
|
||||
|
@ -14,5 +22,12 @@
|
|||
"server": true,
|
||||
"ui": true,
|
||||
"version": "8.0.0",
|
||||
"requiredBundles": ["observability", "kibanaReact", "kibanaUtils", "home", "data", "ml", "maps"]
|
||||
}
|
||||
"requiredBundles": [
|
||||
"observability",
|
||||
"kibanaReact",
|
||||
"kibanaUtils",
|
||||
"home",
|
||||
"data",
|
||||
"ml"
|
||||
]
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
export * from './ml';
|
||||
export * from './ping_list';
|
||||
export * from './status_details/location_map';
|
||||
export * from './status_details';
|
||||
export * from './ping_histogram';
|
||||
export * from './monitor_charts';
|
||||
|
|
|
@ -1,234 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LocationAvailability component doesnt shows warning if geo is provided 1`] = `
|
||||
<EuiErrorBoundary>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiFlexItem />
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<ToggleViewBtn
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
justifyContent="flexEnd"
|
||||
wrap={true}
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<LocationMap
|
||||
downPoints={Array []}
|
||||
upPoints={
|
||||
Array [
|
||||
Object {
|
||||
"location": Object {
|
||||
"lat": "40.730610",
|
||||
"lon": " -73.935242",
|
||||
},
|
||||
"name": "New York",
|
||||
},
|
||||
Object {
|
||||
"location": Object {
|
||||
"lat": "52.487448",
|
||||
"lon": " 13.394798",
|
||||
},
|
||||
"name": "Tokyo",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiErrorBoundary>
|
||||
`;
|
||||
|
||||
exports[`LocationAvailability component renders correctly against snapshot 1`] = `
|
||||
<EuiErrorBoundary>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
>
|
||||
<h3>
|
||||
Monitoring from
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<ToggleViewBtn
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
justifyContent="flexEnd"
|
||||
wrap={true}
|
||||
>
|
||||
<Styled(EuiFlexItem)
|
||||
grow={true}
|
||||
>
|
||||
<LocationStatusTags
|
||||
locations={
|
||||
Array [
|
||||
Object {
|
||||
"down_history": 0,
|
||||
"geo": Object {
|
||||
"location": Object {
|
||||
"lat": "40.730610",
|
||||
"lon": " -73.935242",
|
||||
},
|
||||
"name": "New York",
|
||||
},
|
||||
"summary": Object {
|
||||
"down": 0,
|
||||
"up": 4,
|
||||
},
|
||||
"timestamp": "2020-01-13T22:50:06.536Z",
|
||||
"up_history": 4,
|
||||
},
|
||||
Object {
|
||||
"down_history": 0,
|
||||
"geo": Object {
|
||||
"location": Object {
|
||||
"lat": "52.487448",
|
||||
"lon": " 13.394798",
|
||||
},
|
||||
"name": "Tokyo",
|
||||
},
|
||||
"summary": Object {
|
||||
"down": 0,
|
||||
"up": 4,
|
||||
},
|
||||
"timestamp": "2020-01-13T22:50:04.354Z",
|
||||
"up_history": 4,
|
||||
},
|
||||
Object {
|
||||
"down_history": 0,
|
||||
"geo": Object {
|
||||
"name": "Unnamed-location",
|
||||
},
|
||||
"summary": Object {
|
||||
"down": 0,
|
||||
"up": 4,
|
||||
},
|
||||
"timestamp": "2020-01-13T22:50:02.753Z",
|
||||
"up_history": 4,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</Styled(EuiFlexItem)>
|
||||
</EuiFlexGroup>
|
||||
</EuiErrorBoundary>
|
||||
`;
|
||||
|
||||
exports[`LocationAvailability component renders named locations that have missing geo data 1`] = `
|
||||
<EuiErrorBoundary>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<LocationMissingWarning />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<ToggleViewBtn
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
justifyContent="flexEnd"
|
||||
wrap={true}
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<LocationMap
|
||||
downPoints={Array []}
|
||||
upPoints={Array []}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiErrorBoundary>
|
||||
`;
|
||||
|
||||
exports[`LocationAvailability component shows warning if geo information is missing 1`] = `
|
||||
<EuiErrorBoundary>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<LocationMissingWarning />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<ToggleViewBtn
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
justifyContent="flexEnd"
|
||||
wrap={true}
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<LocationMap
|
||||
downPoints={Array []}
|
||||
upPoints={
|
||||
Array [
|
||||
Object {
|
||||
"location": Object {
|
||||
"lat": "52.487448",
|
||||
"lon": " 13.394798",
|
||||
},
|
||||
"name": "Tokyo",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiErrorBoundary>
|
||||
`;
|
|
@ -6,28 +6,16 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallowWithIntl } from '@kbn/test/jest';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { render } from '../../../../lib/helper/rtl_helpers';
|
||||
import { LocationAvailability } from './location_availability';
|
||||
import { MonitorLocations } from '../../../../../common/runtime_types';
|
||||
import { LocationMissingWarning } from '../location_map/location_missing';
|
||||
|
||||
// Note For shallow test, we need absolute time strings
|
||||
describe('LocationAvailability component', () => {
|
||||
let monitorLocations: MonitorLocations;
|
||||
let localStorageMock: any;
|
||||
|
||||
let selectedView = 'list';
|
||||
|
||||
beforeEach(() => {
|
||||
localStorageMock = {
|
||||
getItem: jest.fn().mockImplementation(() => selectedView),
|
||||
setItem: jest.fn(),
|
||||
};
|
||||
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
value: localStorageMock,
|
||||
});
|
||||
|
||||
monitorLocations = {
|
||||
monitorId: 'wapo',
|
||||
up_history: 12,
|
||||
|
@ -41,104 +29,34 @@ describe('LocationAvailability component', () => {
|
|||
down_history: 0,
|
||||
},
|
||||
{
|
||||
summary: { up: 4, down: 0 },
|
||||
summary: { up: 2, down: 2 },
|
||||
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||
timestamp: '2020-01-13T22:50:04.354Z',
|
||||
up_history: 4,
|
||||
down_history: 0,
|
||||
up_history: 2,
|
||||
down_history: 2,
|
||||
},
|
||||
{
|
||||
summary: { up: 4, down: 0 },
|
||||
summary: { up: 0, down: 4 },
|
||||
geo: { name: 'Unnamed-location' },
|
||||
timestamp: '2020-01-13T22:50:02.753Z',
|
||||
up_history: 4,
|
||||
down_history: 0,
|
||||
up_history: 0,
|
||||
down_history: 4,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
it('renders correctly against snapshot', () => {
|
||||
const component = shallowWithIntl(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('shows warning if geo information is missing', () => {
|
||||
selectedView = 'map';
|
||||
monitorLocations = {
|
||||
monitorId: 'wapo',
|
||||
up_history: 8,
|
||||
down_history: 0,
|
||||
locations: [
|
||||
{
|
||||
summary: { up: 4, down: 0 },
|
||||
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||
timestamp: '2020-01-13T22:50:04.354Z',
|
||||
up_history: 4,
|
||||
down_history: 0,
|
||||
},
|
||||
{
|
||||
summary: { up: 4, down: 0 },
|
||||
geo: { name: 'Unnamed-location' },
|
||||
timestamp: '2020-01-13T22:50:02.753Z',
|
||||
up_history: 4,
|
||||
down_history: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
const component = shallowWithIntl(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
||||
const warningComponent = component.find(LocationMissingWarning);
|
||||
expect(warningComponent).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('doesnt shows warning if geo is provided', () => {
|
||||
monitorLocations = {
|
||||
monitorId: 'wapo',
|
||||
up_history: 8,
|
||||
down_history: 0,
|
||||
locations: [
|
||||
{
|
||||
summary: { up: 4, down: 0 },
|
||||
geo: { name: 'New York', location: { lat: '40.730610', lon: ' -73.935242' } },
|
||||
timestamp: '2020-01-13T22:50:06.536Z',
|
||||
up_history: 4,
|
||||
down_history: 0,
|
||||
},
|
||||
{
|
||||
summary: { up: 4, down: 0 },
|
||||
geo: { name: 'Tokyo', location: { lat: '52.487448', lon: ' 13.394798' } },
|
||||
timestamp: '2020-01-13T22:50:04.354Z',
|
||||
up_history: 4,
|
||||
down_history: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
const component = shallowWithIntl(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
||||
const warningComponent = component.find(LocationMissingWarning);
|
||||
expect(warningComponent).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('renders named locations that have missing geo data', () => {
|
||||
monitorLocations = {
|
||||
monitorId: 'wapo',
|
||||
up_history: 4,
|
||||
down_history: 0,
|
||||
locations: [
|
||||
{
|
||||
summary: { up: 4, down: 0 },
|
||||
geo: { name: 'New York', location: undefined },
|
||||
timestamp: '2020-01-13T22:50:06.536Z',
|
||||
up_history: 4,
|
||||
down_history: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const component = shallowWithIntl(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
it('renders correctly', () => {
|
||||
render(<LocationAvailability monitorLocations={monitorLocations} />);
|
||||
expect(screen.getByRole('heading', { name: 'Monitoring from', level: 3 }));
|
||||
expect(screen.getByText('New York')).toBeInTheDocument();
|
||||
expect(screen.getByText('Tokyo')).toBeInTheDocument();
|
||||
expect(screen.getByText('Unnamed-location')).toBeInTheDocument();
|
||||
expect(screen.getByText('100.00 %')).toBeInTheDocument();
|
||||
expect(screen.getByText('50.00 %')).toBeInTheDocument();
|
||||
expect(screen.getByText('0.00 %')).toBeInTheDocument();
|
||||
expect(screen.getByText('Jan 13, 2020 5:50:06 PM')).toBeInTheDocument();
|
||||
expect(screen.getByText('Jan 13, 2020 5:50:04 PM')).toBeInTheDocument();
|
||||
expect(screen.getByText('Jan 13, 2020 5:50:02 PM')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,18 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiErrorBoundary, EuiTitle } from '@elastic/eui';
|
||||
import { LocationStatusTags } from '../availability_reporting';
|
||||
import { LocationPoint } from '../location_map/embeddables/embedded_map';
|
||||
import { MonitorLocations, MonitorLocation } from '../../../../../common/runtime_types';
|
||||
import { UNNAMED_LOCATION } from '../../../../../common/constants';
|
||||
import { LocationMissingWarning } from '../location_map/location_missing';
|
||||
import { useSelectedView } from './use_selected_view';
|
||||
import { LocationMap } from '../location_map';
|
||||
import { MonitorLocations } from '../../../../../common/runtime_types';
|
||||
import { MonitoringFrom } from '../translations';
|
||||
import { ToggleViewBtn } from './toggle_view_btn';
|
||||
|
||||
const EuiFlexItemTags = styled(EuiFlexItem)`
|
||||
width: 350px;
|
||||
|
@ -30,61 +24,20 @@ interface LocationMapProps {
|
|||
}
|
||||
|
||||
export const LocationAvailability = ({ monitorLocations }: LocationMapProps) => {
|
||||
const upPoints: LocationPoint[] = [];
|
||||
const downPoints: LocationPoint[] = [];
|
||||
|
||||
let isAnyGeoInfoMissing = false;
|
||||
|
||||
if (monitorLocations?.locations) {
|
||||
monitorLocations.locations.forEach(({ geo, summary }: MonitorLocation) => {
|
||||
if (geo?.name === UNNAMED_LOCATION || !geo?.location) {
|
||||
isAnyGeoInfoMissing = true;
|
||||
} else if (!!geo.location.lat && !!geo.location.lon) {
|
||||
if (summary?.down === 0) {
|
||||
upPoints.push(geo as LocationPoint);
|
||||
} else {
|
||||
downPoints.push(geo as LocationPoint);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const { selectedView: initialView } = useSelectedView();
|
||||
|
||||
const [selectedView, setSelectedView] = useState(initialView);
|
||||
|
||||
return (
|
||||
<EuiErrorBoundary>
|
||||
<EuiFlexGroup responsive={false} gutterSize={'none'} style={{ flexGrow: 0 }}>
|
||||
{selectedView === 'list' && (
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="s">
|
||||
<h3>{MonitoringFrom}</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{selectedView === 'map' && (
|
||||
<EuiFlexItem>{isAnyGeoInfoMissing && <LocationMissingWarning />}</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<ToggleViewBtn
|
||||
onChange={(val) => {
|
||||
setSelectedView(val);
|
||||
}}
|
||||
/>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="s">
|
||||
<h3>{MonitoringFrom}</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiFlexGroup wrap={true} gutterSize="none" justifyContent="flexEnd">
|
||||
{selectedView === 'list' && (
|
||||
<EuiFlexItemTags grow={true}>
|
||||
<LocationStatusTags locations={monitorLocations?.locations || []} />
|
||||
</EuiFlexItemTags>
|
||||
)}
|
||||
{selectedView === 'map' && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<LocationMap upPoints={upPoints} downPoints={downPoints} />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItemTags grow={true}>
|
||||
<LocationStatusTags locations={monitorLocations?.locations || []} />
|
||||
</EuiFlexItemTags>
|
||||
</EuiFlexGroup>
|
||||
</EuiErrorBoundary>
|
||||
);
|
||||
|
|
|
@ -1,66 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { EuiButtonGroup } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useSelectedView } from './use_selected_view';
|
||||
import { ChangeToListView, ChangeToMapView } from '../translations';
|
||||
|
||||
const ToggleViewButtons = styled.span`
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
onChange: (val: string) => void;
|
||||
}
|
||||
|
||||
export const ToggleViewBtn = ({ onChange }: Props) => {
|
||||
const toggleButtons = [
|
||||
{
|
||||
id: `listBtn`,
|
||||
label: ChangeToMapView,
|
||||
name: 'listView',
|
||||
iconType: 'list',
|
||||
'data-test-subj': 'uptimeMonitorToggleListBtn',
|
||||
'aria-label': ChangeToMapView,
|
||||
},
|
||||
{
|
||||
id: `mapBtn`,
|
||||
label: ChangeToListView,
|
||||
name: 'mapView',
|
||||
iconType: 'mapMarker',
|
||||
'data-test-subj': 'uptimeMonitorToggleMapBtn',
|
||||
'aria-label': ChangeToListView,
|
||||
},
|
||||
];
|
||||
|
||||
const { selectedView, setSelectedView } = useSelectedView();
|
||||
|
||||
const onChangeView = (optionId: string) => {
|
||||
const currView = optionId === 'listBtn' ? 'list' : 'map';
|
||||
setSelectedView(currView);
|
||||
onChange(currView);
|
||||
};
|
||||
|
||||
return (
|
||||
<ToggleViewButtons>
|
||||
<EuiButtonGroup
|
||||
options={toggleButtons}
|
||||
idToSelectedMap={{ listBtn: selectedView === 'list', mapBtn: selectedView === 'map' }}
|
||||
onChange={(id) => onChangeView(id)}
|
||||
type="multi"
|
||||
isIconOnly
|
||||
style={{ marginLeft: 'auto' }}
|
||||
legend={i18n.translate('xpack.uptime.locationAvailabilityViewToggleLegend', {
|
||||
defaultMessage: 'View toggle',
|
||||
})}
|
||||
/>
|
||||
</ToggleViewButtons>
|
||||
);
|
||||
};
|
|
@ -1,27 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const localKey = 'xpack.uptime.detailPage.selectedView';
|
||||
|
||||
interface Props {
|
||||
selectedView: string;
|
||||
setSelectedView: (val: string) => void;
|
||||
}
|
||||
|
||||
export const useSelectedView = (): Props => {
|
||||
const getSelectedView = localStorage.getItem(localKey) ?? 'list';
|
||||
|
||||
const [selectedView, setSelectedView] = useState(getSelectedView);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(localKey, selectedView);
|
||||
}, [selectedView]);
|
||||
|
||||
return { selectedView, setSelectedView };
|
||||
};
|
|
@ -1,27 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LocationMap component renders correctly against snapshot 1`] = `
|
||||
<styled.div>
|
||||
<EmbeddedMap
|
||||
downPoints={Array []}
|
||||
upPoints={
|
||||
Array [
|
||||
Object {
|
||||
"location": Object {
|
||||
"lat": "40.730610",
|
||||
"lon": " -73.935242",
|
||||
},
|
||||
"name": "New York",
|
||||
},
|
||||
Object {
|
||||
"location": Object {
|
||||
"lat": "52.487448",
|
||||
"lon": " 13.394798",
|
||||
},
|
||||
"name": "Tokyo",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</styled.div>
|
||||
`;
|
|
@ -1,123 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LocationMissingWarning component renders correctly against snapshot 1`] = `
|
||||
.c0 {
|
||||
margin-left: auto;
|
||||
margin-bottom: 3px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--directionRow"
|
||||
data-test-subj="xpack.uptime.locationMap.locationMissing"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero c0"
|
||||
>
|
||||
<div
|
||||
class="euiPopover euiPopover--anchorDownCenter"
|
||||
id="popover"
|
||||
>
|
||||
<div
|
||||
class="euiPopover__anchor"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--warning euiButton--small"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent__icon"
|
||||
data-euiicon-type="alert"
|
||||
/>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Geo Information Missing
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`LocationMissingWarning component shallow render correctly against snapshot 1`] = `
|
||||
<EuiFlexGroup
|
||||
data-test-subj="xpack.uptime.locationMap.locationMissing"
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
>
|
||||
<Styled(EuiFlexItem)
|
||||
grow={false}
|
||||
>
|
||||
<EuiPopover
|
||||
anchorPosition="downCenter"
|
||||
button={
|
||||
<EuiButton
|
||||
color="warning"
|
||||
iconType="alert"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Geo Information Missing"
|
||||
id="xpack.uptime.locationMap.locations.missing.title"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
}
|
||||
closePopover={[Function]}
|
||||
display="inlineBlock"
|
||||
hasArrow={true}
|
||||
id="popover"
|
||||
isOpen={false}
|
||||
ownFocus={true}
|
||||
panelPaddingSize="m"
|
||||
>
|
||||
<EuiText
|
||||
style={
|
||||
Object {
|
||||
"width": "350px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Important geo location configuration is missing. You can use the {codeBlock} field to create distinctive geographic regions for your uptime checks."
|
||||
id="xpack.uptime.locationMap.locations.missing.message"
|
||||
values={
|
||||
Object {
|
||||
"codeBlock": <EuiCode>
|
||||
observer.geo.??
|
||||
</EuiCode>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="xs"
|
||||
/>
|
||||
<EuiText
|
||||
style={
|
||||
Object {
|
||||
"width": "350px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Get more information in our documentation."
|
||||
id="xpack.uptime.locationMap.locations.missing.message1"
|
||||
values={Object {}}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="xs"
|
||||
/>
|
||||
<LocationLink />
|
||||
</EuiText>
|
||||
</EuiPopover>
|
||||
</Styled(EuiFlexItem)>
|
||||
</EuiFlexGroup>
|
||||
`;
|
|
@ -1,192 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import lowPolyLayerFeatures from '../low_poly_layer.json';
|
||||
|
||||
export const mockDownPointsLayer = {
|
||||
id: 'down_points',
|
||||
label: 'Down Locations',
|
||||
sourceDescriptor: {
|
||||
type: 'GEOJSON_FILE',
|
||||
__featureCollection: {
|
||||
features: [
|
||||
{
|
||||
id: 'Asia',
|
||||
type: 'feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [13.399262, 52.487239],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'APJ',
|
||||
type: 'feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [13.399262, 55.487239],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Canada',
|
||||
type: 'feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [14.399262, 54.487239],
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'FeatureCollection',
|
||||
},
|
||||
},
|
||||
visible: true,
|
||||
style: {
|
||||
type: 'VECTOR',
|
||||
properties: {
|
||||
fillColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#BC261E',
|
||||
},
|
||||
},
|
||||
lineColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
lineWidth: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 2,
|
||||
},
|
||||
},
|
||||
iconSize: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'VECTOR',
|
||||
};
|
||||
|
||||
export const mockUpPointsLayer = {
|
||||
id: 'up_points',
|
||||
label: 'Up Locations',
|
||||
sourceDescriptor: {
|
||||
type: 'GEOJSON_FILE',
|
||||
__featureCollection: {
|
||||
features: [
|
||||
{
|
||||
id: 'US-EAST',
|
||||
type: 'feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [13.399262, 52.487239],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'US-WEST',
|
||||
type: 'feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [13.399262, 55.487239],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Europe',
|
||||
type: 'feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [14.399262, 54.487239],
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'FeatureCollection',
|
||||
},
|
||||
},
|
||||
visible: true,
|
||||
style: {
|
||||
type: 'VECTOR',
|
||||
properties: {
|
||||
fillColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#98A2B2',
|
||||
},
|
||||
},
|
||||
lineColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
lineWidth: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 2,
|
||||
},
|
||||
},
|
||||
iconSize: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'VECTOR',
|
||||
};
|
||||
|
||||
export const mockLayerList = [
|
||||
{
|
||||
id: 'low_poly_layer',
|
||||
label: 'World countries',
|
||||
minZoom: 0,
|
||||
maxZoom: 24,
|
||||
alpha: 1,
|
||||
sourceDescriptor: {
|
||||
id: 'b7486535-171b-4d3b-bb2e-33c1a0a2854c',
|
||||
type: 'GEOJSON_FILE',
|
||||
__featureCollection: lowPolyLayerFeatures,
|
||||
},
|
||||
visible: true,
|
||||
style: {
|
||||
type: 'VECTOR',
|
||||
properties: {
|
||||
fillColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#cad3e4',
|
||||
},
|
||||
},
|
||||
lineColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
lineWidth: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 0,
|
||||
},
|
||||
},
|
||||
iconSize: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'VECTOR',
|
||||
},
|
||||
mockDownPointsLayer,
|
||||
mockUpPointsLayer,
|
||||
];
|
|
@ -1,174 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState, useContext, useRef } from 'react';
|
||||
import uuid from 'uuid';
|
||||
import styled from 'styled-components';
|
||||
import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal';
|
||||
import {
|
||||
MapEmbeddable,
|
||||
MapEmbeddableInput,
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
} from '../../../../../../../maps/public/embeddable';
|
||||
import * as i18n from './translations';
|
||||
import { GeoPoint } from '../../../../../../common/runtime_types';
|
||||
import { getLayerList } from './map_config';
|
||||
import { UptimeThemeContext, UptimeStartupPluginsContext } from '../../../../../contexts';
|
||||
import {
|
||||
isErrorEmbeddable,
|
||||
ViewMode,
|
||||
ErrorEmbeddable,
|
||||
} from '../../../../../../../../../src/plugins/embeddable/public';
|
||||
import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../maps/public';
|
||||
import { MapToolTipComponent } from './map_tool_tip';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import type { RenderTooltipContentParams } from '../../../../../../../maps/public/classes/tooltips/tooltip_property';
|
||||
|
||||
export interface EmbeddedMapProps {
|
||||
upPoints: LocationPoint[];
|
||||
downPoints: LocationPoint[];
|
||||
}
|
||||
|
||||
export type LocationPoint = Required<GeoPoint>;
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
|
||||
export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProps) => {
|
||||
const { colors } = useContext(UptimeThemeContext);
|
||||
const [embeddable, setEmbeddable] = useState<MapEmbeddable | ErrorEmbeddable | undefined>();
|
||||
const embeddableRoot: React.RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);
|
||||
const { embeddable: embeddablePlugin } = useContext(UptimeStartupPluginsContext);
|
||||
if (!embeddablePlugin) {
|
||||
throw new Error('Embeddable start plugin not found');
|
||||
}
|
||||
const factory: any = embeddablePlugin.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE);
|
||||
|
||||
const portalNode = React.useMemo(() => createPortalNode(), []);
|
||||
|
||||
const input: MapEmbeddableInput = {
|
||||
id: uuid.v4(),
|
||||
attributes: { title: '' },
|
||||
filters: [],
|
||||
hidePanelTitles: true,
|
||||
refreshConfig: {
|
||||
value: 0,
|
||||
pause: false,
|
||||
},
|
||||
viewMode: ViewMode.VIEW,
|
||||
isLayerTOCOpen: false,
|
||||
hideFilterActions: true,
|
||||
// Zoom Lat/Lon values are set to make sure map is in center in the panel
|
||||
// It wil also omit Greenland/Antarctica etc
|
||||
mapCenter: {
|
||||
lon: 11,
|
||||
lat: 20,
|
||||
zoom: 0,
|
||||
},
|
||||
mapSettings: {
|
||||
disableInteractive: true,
|
||||
hideToolbarOverlay: true,
|
||||
hideLayerControl: true,
|
||||
hideViewControl: true,
|
||||
},
|
||||
};
|
||||
|
||||
const renderTooltipContent = ({
|
||||
addFilters,
|
||||
closeTooltip,
|
||||
features,
|
||||
isLocked,
|
||||
getLayerName,
|
||||
loadFeatureProperties,
|
||||
loadFeatureGeometry,
|
||||
}: RenderTooltipContentParams) => {
|
||||
const props = {
|
||||
addFilters,
|
||||
closeTooltip,
|
||||
isLocked,
|
||||
getLayerName,
|
||||
loadFeatureProperties,
|
||||
loadFeatureGeometry,
|
||||
};
|
||||
const relevantFeatures = features.filter(
|
||||
(item: any) => item.layerId === 'up_points' || item.layerId === 'down_points'
|
||||
);
|
||||
if (relevantFeatures.length > 0) {
|
||||
return <OutPortal {...props} node={portalNode} features={relevantFeatures} />;
|
||||
}
|
||||
closeTooltip();
|
||||
return null;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function setupEmbeddable() {
|
||||
if (!factory) {
|
||||
throw new Error('Map embeddable not found.');
|
||||
}
|
||||
const embeddableObject: any = await factory.create({
|
||||
...input,
|
||||
title: i18n.MAP_TITLE,
|
||||
});
|
||||
|
||||
if (embeddableObject && !isErrorEmbeddable(embeddableObject)) {
|
||||
embeddableObject.setRenderTooltipContent(renderTooltipContent);
|
||||
embeddableObject.setLayerList(getLayerList(upPoints, downPoints, colors));
|
||||
}
|
||||
|
||||
setEmbeddable(embeddableObject);
|
||||
}
|
||||
|
||||
setupEmbeddable();
|
||||
|
||||
// we want this effect to execute exactly once after the component mounts
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// update map layers based on points
|
||||
useEffect(() => {
|
||||
if (embeddable && !isErrorEmbeddable(embeddable)) {
|
||||
embeddable.setLayerList(getLayerList(upPoints, downPoints, colors));
|
||||
}
|
||||
}, [upPoints, downPoints, embeddable, colors]);
|
||||
|
||||
// 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.uptime.locationMap.embeddedPanel"
|
||||
className="embPanel__content"
|
||||
ref={embeddableRoot}
|
||||
/>
|
||||
<InPortal node={portalNode}>
|
||||
<MapToolTipComponent />
|
||||
</InPortal>
|
||||
</EmbeddedPanel>
|
||||
);
|
||||
});
|
||||
|
||||
EmbeddedMap.displayName = 'EmbeddedMap';
|
File diff suppressed because it is too large
Load diff
|
@ -1,47 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getLayerList } from './map_config';
|
||||
import { mockLayerList } from './__mocks__/poly_layer_mock';
|
||||
import { LocationPoint } from './embedded_map';
|
||||
import { UptimeAppColors } from '../../../../../apps/uptime_app';
|
||||
|
||||
jest.mock('uuid', () => {
|
||||
return {
|
||||
v4: jest.fn(() => 'uuid.v4()'),
|
||||
};
|
||||
});
|
||||
|
||||
describe('map_config', () => {
|
||||
let upPoints: LocationPoint[];
|
||||
let downPoints: LocationPoint[];
|
||||
let colors: Pick<UptimeAppColors, 'gray' | 'danger'>;
|
||||
|
||||
beforeEach(() => {
|
||||
upPoints = [
|
||||
{ name: 'US-EAST', location: { lat: '52.487239', lon: '13.399262' } },
|
||||
{ location: { lat: '55.487239', lon: '13.399262' }, name: 'US-WEST' },
|
||||
{ location: { lat: '54.487239', lon: '14.399262' }, name: 'Europe' },
|
||||
];
|
||||
downPoints = [
|
||||
{ location: { lat: '52.487239', lon: '13.399262' }, name: 'Asia' },
|
||||
{ location: { lat: '55.487239', lon: '13.399262' }, name: 'APJ' },
|
||||
{ location: { lat: '54.487239', lon: '14.399262' }, name: 'Canada' },
|
||||
];
|
||||
colors = {
|
||||
danger: '#BC261E',
|
||||
gray: '#000',
|
||||
};
|
||||
});
|
||||
|
||||
describe('#getLayerList', () => {
|
||||
test('it returns the low poly layer', () => {
|
||||
const layerList = getLayerList(upPoints, downPoints, colors);
|
||||
expect(layerList).toStrictEqual(mockLayerList);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,175 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import lowPolyLayerFeatures from './low_poly_layer.json';
|
||||
import { LocationPoint } from './embedded_map';
|
||||
import { UptimeAppColors } from '../../../../../apps/uptime_app';
|
||||
|
||||
/**
|
||||
* Returns `Source/Destination Point-to-point` Map LayerList configuration, with a source,
|
||||
* destination, and line layer for each of the provided indexPatterns
|
||||
*
|
||||
*/
|
||||
export const getLayerList = (
|
||||
upPoints: LocationPoint[],
|
||||
downPoints: LocationPoint[],
|
||||
{ danger }: Pick<UptimeAppColors, 'danger'>
|
||||
) => {
|
||||
return [getLowPolyLayer(), getDownPointsLayer(downPoints, danger), getUpPointsLayer(upPoints)];
|
||||
};
|
||||
|
||||
export const getLowPolyLayer = () => {
|
||||
return {
|
||||
id: 'low_poly_layer',
|
||||
label: 'World countries',
|
||||
minZoom: 0,
|
||||
maxZoom: 24,
|
||||
alpha: 1,
|
||||
sourceDescriptor: {
|
||||
id: 'b7486535-171b-4d3b-bb2e-33c1a0a2854c',
|
||||
type: 'GEOJSON_FILE',
|
||||
__featureCollection: lowPolyLayerFeatures,
|
||||
},
|
||||
visible: true,
|
||||
style: {
|
||||
type: 'VECTOR',
|
||||
properties: {
|
||||
fillColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#cad3e4',
|
||||
},
|
||||
},
|
||||
lineColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
lineWidth: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 0,
|
||||
},
|
||||
},
|
||||
iconSize: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'VECTOR',
|
||||
};
|
||||
};
|
||||
|
||||
export const getDownPointsLayer = (downPoints: LocationPoint[], dangerColor: string) => {
|
||||
const features = downPoints?.map((point) => ({
|
||||
type: 'feature',
|
||||
id: point.name,
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [+point.location.lon, +point.location.lat],
|
||||
},
|
||||
}));
|
||||
return {
|
||||
id: 'down_points',
|
||||
label: 'Down Locations',
|
||||
sourceDescriptor: {
|
||||
type: 'GEOJSON_FILE',
|
||||
__featureCollection: {
|
||||
features,
|
||||
type: 'FeatureCollection',
|
||||
},
|
||||
},
|
||||
visible: true,
|
||||
style: {
|
||||
type: 'VECTOR',
|
||||
properties: {
|
||||
fillColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: dangerColor,
|
||||
},
|
||||
},
|
||||
lineColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
lineWidth: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 2,
|
||||
},
|
||||
},
|
||||
iconSize: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'VECTOR',
|
||||
};
|
||||
};
|
||||
|
||||
export const getUpPointsLayer = (upPoints: LocationPoint[]) => {
|
||||
const features = upPoints?.map((point) => ({
|
||||
type: 'feature',
|
||||
id: point.name,
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [+point.location.lon, +point.location.lat],
|
||||
},
|
||||
}));
|
||||
return {
|
||||
id: 'up_points',
|
||||
label: 'Up Locations',
|
||||
sourceDescriptor: {
|
||||
type: 'GEOJSON_FILE',
|
||||
__featureCollection: {
|
||||
features,
|
||||
type: 'FeatureCollection',
|
||||
},
|
||||
},
|
||||
visible: true,
|
||||
style: {
|
||||
type: 'VECTOR',
|
||||
properties: {
|
||||
fillColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#98A2B2',
|
||||
},
|
||||
},
|
||||
lineColor: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
lineWidth: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 2,
|
||||
},
|
||||
},
|
||||
iconSize: {
|
||||
type: 'STATIC',
|
||||
options: {
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'VECTOR',
|
||||
};
|
||||
};
|
|
@ -1,91 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useContext } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import {
|
||||
EuiDescriptionList,
|
||||
EuiDescriptionListDescription,
|
||||
EuiDescriptionListTitle,
|
||||
EuiOutsideClickDetector,
|
||||
EuiPopoverTitle,
|
||||
} from '@elastic/eui';
|
||||
import { TagLabel } from '../../availability_reporting';
|
||||
import { UptimeThemeContext } from '../../../../../contexts';
|
||||
import { AppState } from '../../../../../state';
|
||||
import { monitorLocationsSelector } from '../../../../../state/selectors';
|
||||
import { useMonitorId } from '../../../../../hooks';
|
||||
import { MonitorLocation } from '../../../../../../common/runtime_types/monitor';
|
||||
import type { RenderTooltipContentParams } from '../../../../../../../maps/public';
|
||||
import { formatAvailabilityValue } from '../../availability_reporting/availability_reporting';
|
||||
import { LastCheckLabel } from '../../translations';
|
||||
|
||||
type MapToolTipProps = Partial<RenderTooltipContentParams>;
|
||||
|
||||
export const MapToolTipComponent = ({ closeTooltip, features = [] }: MapToolTipProps) => {
|
||||
const { id: featureId, layerId } = features[0] ?? {};
|
||||
const locationName = featureId?.toString();
|
||||
const {
|
||||
colors: { gray, danger },
|
||||
} = useContext(UptimeThemeContext);
|
||||
|
||||
const monitorId = useMonitorId();
|
||||
|
||||
const monitorLocations = useSelector((state: AppState) =>
|
||||
monitorLocationsSelector(state, monitorId)
|
||||
);
|
||||
if (!locationName || !monitorLocations?.locations) {
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
timestamp,
|
||||
up_history: ups,
|
||||
down_history: downs,
|
||||
}: MonitorLocation = monitorLocations.locations!.find(
|
||||
({ geo }: MonitorLocation) => geo.name === locationName
|
||||
)!;
|
||||
|
||||
const availability = (ups / (ups + downs)) * 100;
|
||||
|
||||
return (
|
||||
<EuiOutsideClickDetector
|
||||
onOutsideClick={() => {
|
||||
if (closeTooltip != null) {
|
||||
closeTooltip();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<EuiPopoverTitle>
|
||||
{layerId === 'up_points' ? (
|
||||
<TagLabel label={locationName} color={gray} status="up" />
|
||||
) : (
|
||||
<TagLabel label={locationName} color={danger} status="down" />
|
||||
)}
|
||||
</EuiPopoverTitle>
|
||||
<EuiDescriptionList type="column" textStyle="reverse" compressed={true}>
|
||||
<EuiDescriptionListTitle>Availability</EuiDescriptionListTitle>
|
||||
<EuiDescriptionListDescription>
|
||||
{i18n.translate('xpack.uptime.mapToolTip.AvailabilityStat.title', {
|
||||
defaultMessage: '{value} %',
|
||||
values: { value: formatAvailabilityValue(availability) },
|
||||
description: 'A percentage value like 23.5%',
|
||||
})}
|
||||
</EuiDescriptionListDescription>
|
||||
<EuiDescriptionListTitle>{LastCheckLabel}</EuiDescriptionListTitle>
|
||||
<EuiDescriptionListDescription>
|
||||
{moment(timestamp).fromNow()}
|
||||
</EuiDescriptionListDescription>
|
||||
</EuiDescriptionList>
|
||||
</>
|
||||
</EuiOutsideClickDetector>
|
||||
);
|
||||
};
|
||||
|
||||
export const MapToolTip = React.memo(MapToolTipComponent);
|
|
@ -1,15 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const MAP_TITLE = i18n.translate(
|
||||
'xpack.uptime.components.embeddables.embeddedMap.embeddablePanelTitle',
|
||||
{
|
||||
defaultMessage: 'Monitor Observer Location Map',
|
||||
}
|
||||
);
|
|
@ -1,9 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './location_map';
|
||||
export * from '../availability_reporting/location_status_tags';
|
|
@ -1,34 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallowWithIntl } from '@kbn/test/jest';
|
||||
import { LocationMap } from './location_map';
|
||||
import { LocationPoint } from './embeddables/embedded_map';
|
||||
|
||||
// Note For shallow test, we need absolute time strings
|
||||
describe('LocationMap component', () => {
|
||||
let upPoints: LocationPoint[];
|
||||
|
||||
beforeEach(() => {
|
||||
upPoints = [
|
||||
{
|
||||
name: 'New York',
|
||||
location: { lat: '40.730610', lon: ' -73.935242' },
|
||||
},
|
||||
{
|
||||
name: 'Tokyo',
|
||||
location: { lat: '52.487448', lon: ' 13.394798' },
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
it('renders correctly against snapshot', () => {
|
||||
const component = shallowWithIntl(<LocationMap upPoints={upPoints} downPoints={[]} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -1,35 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { EmbeddedMap, LocationPoint } from './embeddables/embedded_map';
|
||||
|
||||
// These height/width values are used to make sure map is in center of panel
|
||||
// And to make sure, it doesn't take too much space
|
||||
const MapPanel = styled.div`
|
||||
height: 240px;
|
||||
width: 520px;
|
||||
margin-right: 65px;
|
||||
@media (max-width: 574px) {
|
||||
height: 250px;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
upPoints: LocationPoint[];
|
||||
downPoints: LocationPoint[];
|
||||
}
|
||||
|
||||
export const LocationMap = ({ upPoints, downPoints }: Props) => {
|
||||
return (
|
||||
<MapPanel>
|
||||
<EmbeddedMap upPoints={upPoints} downPoints={downPoints} />
|
||||
</MapPanel>
|
||||
);
|
||||
};
|
|
@ -1,22 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { renderWithIntl, shallowWithIntl } from '@kbn/test/jest';
|
||||
import { LocationMissingWarning } from './location_missing';
|
||||
|
||||
describe('LocationMissingWarning component', () => {
|
||||
it('shallow render correctly against snapshot', () => {
|
||||
const component = shallowWithIntl(<LocationMissingWarning />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders correctly against snapshot', () => {
|
||||
const component = renderWithIntl(<LocationMissingWarning />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -1,79 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPopover,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiCode,
|
||||
} from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { LocationLink } from '../../../common/location_link';
|
||||
|
||||
const EuiPopoverRight = styled(EuiFlexItem)`
|
||||
margin-left: auto;
|
||||
margin-bottom: 3px;
|
||||
margin-right: 5px;
|
||||
`;
|
||||
|
||||
export const LocationMissingWarning = () => {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
|
||||
const togglePopover = () => {
|
||||
setIsPopoverOpen(!isPopoverOpen);
|
||||
};
|
||||
|
||||
const button = (
|
||||
<EuiButton iconType="alert" size="s" color="warning" onClick={togglePopover}>
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.locationMap.locations.missing.title"
|
||||
defaultMessage="Geo Information Missing"
|
||||
/>
|
||||
</EuiButton>
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
data-test-subj="xpack.uptime.locationMap.locationMissing"
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
>
|
||||
<EuiPopoverRight grow={false}>
|
||||
<EuiPopover
|
||||
id="popover"
|
||||
button={button}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={togglePopover}
|
||||
>
|
||||
<EuiText style={{ width: '350px' }}>
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.locationMap.locations.missing.message"
|
||||
defaultMessage="Important geo location configuration is missing.
|
||||
You can use the {codeBlock} field to create distinctive geographic regions for
|
||||
your uptime checks."
|
||||
values={{ codeBlock: <EuiCode>observer.geo.??</EuiCode> }}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="xs" />
|
||||
<EuiText style={{ width: '350px' }}>
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.locationMap.locations.missing.message1"
|
||||
defaultMessage="Get more information in our documentation."
|
||||
/>
|
||||
<EuiSpacer size="xs" />
|
||||
<LocationLink />
|
||||
</EuiText>
|
||||
</EuiPopover>
|
||||
</EuiPopoverRight>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
Loading…
Reference in a new issue