[APM] Add support for dark mode (#69362)

* Use theme colors for stacktrace

* [APM] Use theme for all components

* Switch HoC withTheme to useTheme and convert classes to hooks

* Change hardcoded white to euiColorEmptyShade for ServiceMap

* Snapshots and Legends fix

* Switch to context and add test helper

* Fix tests and update snaps

* New snaps + new anomaly detection

* Remove shallow from testHelpers

* Remove commented tests

* Fix prettier

* Pass correct theme to cytoscape

* Fix ServiceMap

* fixes some rendering issues in service maps

* removed the old anomaly detection logic from service map popover contents, since it's been moved to a new component.

* Fix eslint, tsc lint issues and unit tests

* Remove types for styled-components default theme

* Update x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js

Co-authored-by: Casper Hübertz <casper@formgeist.com>

* fix OuterTheme

* Ise function declaration instead of expression

Co-authored-by: Balthazar Gronon <git@balthazar.dev>
Co-authored-by: Balthazar Gronon <bgronon@gmail.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Oliver Gupte <olivergupte@gmail.com>
Co-authored-by: Casper Hübertz <casper@formgeist.com>
This commit is contained in:
Søren Louv-Jansen 2020-06-19 12:53:25 +02:00 committed by GitHub
parent a489e5f0b1
commit 84f8b43c38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 601 additions and 531 deletions

View file

@ -8,8 +8,9 @@ import { ApmRoute } from '@elastic/apm-rum-react';
import React from 'react';
import ReactDOM from 'react-dom';
import { Route, Router, Switch } from 'react-router-dom';
import styled from 'styled-components';
import { EuiThemeProvider } from '../../../observability/public';
import styled, { ThemeProvider, DefaultTheme } from 'styled-components';
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { CoreStart, AppMountParameters } from '../../../../../src/core/public';
import { ApmPluginSetupDeps } from '../plugin';
import { ApmPluginContext } from '../context/ApmPluginContext';
@ -41,7 +42,13 @@ const App = () => {
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
return (
<EuiThemeProvider darkMode={darkMode}>
<ThemeProvider
theme={(outerTheme?: DefaultTheme) => ({
...outerTheme,
eui: darkMode ? euiDarkVars : euiLightVars,
darkMode,
})}
>
<MainContainer data-test-subj="apmMainContainer" role="main">
<UpdateBreadcrumbs routes={routes} />
<Route component={ScrollToTopOnPathChange} />
@ -53,7 +60,7 @@ const App = () => {
</Switch>
</APMIndicesPermission>
</MainContainer>
</EuiThemeProvider>
</ThemeProvider>
);
};

View file

@ -13,7 +13,6 @@ import {
EuiText,
EuiTitle,
} from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import React, { Fragment } from 'react';
import styled from 'styled-components';
@ -37,7 +36,7 @@ const Titles = styled.div`
const Label = styled.div`
margin-bottom: ${px(units.quarter)};
font-size: ${fontSizes.small};
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
`;
const Message = styled.div`

View file

@ -5,15 +5,14 @@
*/
import { EuiBetaBadge } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import React from 'react';
import styled from 'styled-components';
const BetaBadgeContainer = styled.div`
right: ${theme.gutterTypes.gutterMedium};
right: ${({ theme }) => theme.eui.gutterTypes.gutterMedium};
position: absolute;
top: ${theme.gutterTypes.gutterSmall};
top: ${({ theme }) => theme.eui.gutterTypes.gutterSmall};
z-index: 1; /* The element containing the cytoscape canvas has z-index = 0. */
`;

View file

@ -4,41 +4,44 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiButtonIcon, EuiPanel, EuiToolTip } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import React, { useContext, useEffect, useState } from 'react';
import { EuiButtonIcon, EuiPanel, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import { CytoscapeContext } from './Cytoscape';
import { animationOptions, nodeHeight } from './cytoscapeOptions';
import { getAnimationOptions, getNodeHeight } from './cytoscapeOptions';
import { getAPMHref } from '../../shared/Links/apm/APMLink';
import { useUrlParams } from '../../../hooks/useUrlParams';
import { APMQueryParams } from '../../shared/Links/url_helpers';
import { useTheme } from '../../../hooks/useTheme';
const ControlsContainer = styled('div')`
left: ${theme.gutterTypes.gutterMedium};
left: ${({ theme }) => theme.eui.gutterTypes.gutterMedium};
position: absolute;
top: ${theme.gutterTypes.gutterSmall};
top: ${({ theme }) => theme.eui.gutterTypes.gutterSmall};
z-index: 1; /* The element containing the cytoscape canvas has z-index = 0. */
`;
const Button = styled(EuiButtonIcon)`
display: block;
margin: ${theme.paddingSizes.xs};
margin: ${({ theme }) => theme.eui.paddingSizes.xs};
`;
const ZoomInButton = styled(Button)`
margin-bottom: ${theme.paddingSizes.s};
margin-bottom: ${({ theme }) => theme.eui.paddingSizes.s};
`;
const Panel = styled(EuiPanel)`
margin-bottom: ${theme.paddingSizes.s};
margin-bottom: ${({ theme }) => theme.eui.paddingSizes.s};
`;
const duration = parseInt(theme.euiAnimSpeedFast, 10);
const steps = 5;
function doZoom(cy: cytoscape.Core | undefined, increment: number) {
function doZoom(
cy: cytoscape.Core | undefined,
increment: number,
duration: number
) {
if (cy) {
const level = cy.zoom() + increment;
// @ts-ignore `.position()` _does_ work on a NodeCollection. It returns the position of the first element in the collection.
@ -93,10 +96,12 @@ function useDebugDownloadUrl(cy?: cytoscape.Core) {
}
export function Controls() {
const theme = useTheme();
const cy = useContext(CytoscapeContext);
const { urlParams } = useUrlParams();
const currentSearch = urlParams.kuery ?? '';
const [zoom, setZoom] = useState((cy && cy.zoom()) || 1);
const duration = parseInt(theme.eui.euiAnimSpeedFast, 10);
const downloadUrl = useDebugDownloadUrl(cy);
// Handle zoom events
@ -120,19 +125,19 @@ export function Controls() {
if (cy) {
const eles = cy.nodes();
cy.animate({
...animationOptions,
...getAnimationOptions(theme),
center: { eles },
fit: { eles, padding: nodeHeight },
fit: { eles, padding: getNodeHeight(theme) },
});
}
}
function zoomIn() {
doZoom(cy, increment);
doZoom(cy, increment, duration);
}
function zoomOut() {
doZoom(cy, -increment);
doZoom(cy, -increment, duration);
}
if (!cy) {

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import cytoscape from 'cytoscape';
import React, {
createContext,
CSSProperties,
@ -13,11 +12,13 @@ import React, {
useRef,
useState,
} from 'react';
import cytoscape from 'cytoscape';
import { debounce } from 'lodash';
import { useTheme } from '../../../hooks/useTheme';
import {
animationOptions,
cytoscapeOptions,
nodeHeight,
getAnimationOptions,
getCytoscapeOptions,
getNodeHeight,
} from './cytoscapeOptions';
import { useUiTracker } from '../../../../../observability/public';
@ -73,7 +74,8 @@ function rotatePoint(
function getLayoutOptions(
selectedRoots: string[],
height: number,
width: number
width: number,
nodeHeight: number
): cytoscape.LayoutOptions {
return {
name: 'breadthfirst',
@ -111,11 +113,14 @@ export function Cytoscape({
serviceName,
style,
}: CytoscapeProps) {
const theme = useTheme();
const [ref, cy] = useCytoscape({
...cytoscapeOptions,
...getCytoscapeOptions(theme),
elements,
});
const nodeHeight = getNodeHeight(theme);
// Add the height to the div style. The height is a separate prop because it
// is required and can trigger rendering when changed.
const divStyle = { ...style, height };
@ -149,7 +154,7 @@ export function Cytoscape({
const selectedRoots = selectRoots(event.cy);
const layout = cy.layout(
getLayoutOptions(selectedRoots, height, width)
getLayoutOptions(selectedRoots, height, width, nodeHeight)
);
layout.run();
@ -162,7 +167,7 @@ export function Cytoscape({
layoutstopDelayTimeout = setTimeout(() => {
if (serviceName) {
event.cy.animate({
...animationOptions,
...getAnimationOptions(theme),
fit: {
eles: event.cy.elements(),
padding: nodeHeight,
@ -237,7 +242,16 @@ export function Cytoscape({
}
clearTimeout(layoutstopDelayTimeout);
};
}, [cy, elements, height, serviceName, trackApmEvent, width]);
}, [
cy,
elements,
height,
serviceName,
trackApmEvent,
width,
nodeHeight,
theme,
]);
return (
<CytoscapeContext.Provider value={cy}>

View file

@ -4,12 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { act, render, wait } from '@testing-library/react';
import cytoscape from 'cytoscape';
import React, { FunctionComponent } from 'react';
import { act, wait } from '@testing-library/react';
import cytoscape from 'cytoscape';
import { CytoscapeContext } from './Cytoscape';
import { EmptyBanner } from './EmptyBanner';
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
import { renderWithTheme } from '../../../utils/testHelpers';
const cy = cytoscape({});
@ -29,7 +30,7 @@ describe('EmptyBanner', () => {
</CytoscapeContext.Provider>
</MockApmPluginContextWrapper>
);
const component = render(<EmptyBanner />, {
const component = renderWithTheme(<EmptyBanner />, {
wrapper: noCytoscapeWrapper,
});
@ -39,7 +40,7 @@ describe('EmptyBanner', () => {
describe('with no nodes', () => {
it('renders null', () => {
const component = render(<EmptyBanner />, {
const component = renderWithTheme(<EmptyBanner />, {
wrapper,
});
@ -49,7 +50,7 @@ describe('EmptyBanner', () => {
describe('with one node', () => {
it('does not render null', async () => {
const component = render(<EmptyBanner />, { wrapper });
const component = renderWithTheme(<EmptyBanner />, { wrapper });
await act(async () => {
cy.add({ data: { id: 'test id' } });

View file

@ -4,26 +4,27 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiCallOut } from '@elastic/eui';
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import React, { useContext, useEffect, useState } from 'react';
import { EuiCallOut } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink';
import { CytoscapeContext } from './Cytoscape';
import { useTheme } from '../../../hooks/useTheme';
const EmptyBannerContainer = styled.div`
margin: ${lightTheme.gutterTypes.gutterSmall};
margin: ${({ theme }) => theme.eui.gutterTypes.gutterSmall};
/* Add some extra margin so it displays to the right of the controls. */
left: calc(
${lightTheme.gutterTypes.gutterExtraLarge} +
${lightTheme.gutterTypes.gutterSmall}
${({ theme }) => theme.eui.gutterTypes.gutterExtraLarge} +
${({ theme }) => theme.eui.gutterTypes.gutterSmall}
);
position: absolute;
z-index: 1;
`;
export function EmptyBanner() {
const theme = useTheme();
const cy = useContext(CytoscapeContext);
const [nodeCount, setNodeCount] = useState(0);
@ -51,8 +52,8 @@ export function EmptyBanner() {
// subtract the space for controls and margins.
const width =
cy.width() -
parseInt(lightTheme.gutterTypes.gutterExtraLarge, 10) -
parseInt(lightTheme.gutterTypes.gutterLarge, 10);
parseInt(theme.eui.gutterTypes.gutterExtraLarge, 10) -
parseInt(theme.eui.gutterTypes.gutterLarge, 10);
return (
<EmptyBannerContainer style={{ width }}>

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
import { EuiProgress, EuiText, EuiSpacer } from '@elastic/eui';
import styled from 'styled-components';
@ -22,7 +21,7 @@ const Overlay = styled.div`
flex-direction: column;
align-items: center;
width: 100%;
padding: ${theme.gutterTypes.gutterMedium};
padding: ${({ theme }) => theme.eui.gutterTypes.gutterMedium};
`;
const ProgressBarContainer = styled.div`

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import cytoscape from 'cytoscape';
import React from 'react';
@ -19,7 +18,7 @@ const ItemRow = styled.div`
`;
const ItemTitle = styled.dt`
color: ${lightTheme.textColors.subdued};
color: ${({ theme }) => theme.eui.textColors.subdued};
`;
const ItemDescription = styled.dd``;

View file

@ -5,7 +5,6 @@
*/
import { EuiFlexGroup, EuiLoadingSpinner } from '@elastic/eui';
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { isNumber } from 'lodash';
import React from 'react';
@ -30,7 +29,7 @@ export const ItemRow = styled('tr')`
`;
export const ItemTitle = styled('td')`
color: ${lightTheme.textColors.subdued};
color: ${({ theme }) => theme.eui.textColors.subdued};
padding-right: 1rem;
`;

View file

@ -7,7 +7,6 @@
import { i18n } from '@kbn/i18n';
import React from 'react';
import styled from 'styled-components';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import {
EuiFlexGroup,
EuiFlexItem,
@ -15,6 +14,7 @@ import {
EuiIconTip,
EuiHealth,
} from '@elastic/eui';
import { useTheme } from '../../../../hooks/useTheme';
import { fontSize, px } from '../../../../style/variables';
import { asInteger } from '../../../../utils/formatters';
import { MLJobLink } from '../../../shared/Links/MachineLearningLinks/MLJobLink';
@ -33,11 +33,11 @@ const VerticallyCentered = styled.div`
`;
const SubduedText = styled.span`
color: ${theme.euiTextSubduedColor};
color: ${({ theme }) => theme.eui.euiTextSubduedColor};
`;
const EnableText = styled.section`
color: ${theme.euiTextSubduedColor};
color: ${({ theme }) => theme.eui.euiTextSubduedColor};
line-height: 1.4;
font-size: ${fontSize};
width: ${px(popoverMinWidth)};
@ -52,6 +52,7 @@ interface AnomalyDetectionProps {
}
export function AnomalyDetection({ serviceNodeData }: AnomalyDetectionProps) {
const theme = useTheme();
const anomalySeverity = serviceNodeData.anomaly_severity;
const anomalyScore = serviceNodeData.anomaly_score;
const actualValue = serviceNodeData.actual_value;
@ -81,7 +82,7 @@ export function AnomalyDetection({ serviceNodeData }: AnomalyDetectionProps) {
<EuiFlexGroup>
<EuiFlexItem>
<VerticallyCentered>
<EuiHealth color={getSeverityColor(anomalySeverity)} />
<EuiHealth color={getSeverityColor(theme, anomalySeverity)} />
<SubduedText>{ANOMALY_DETECTION_SCORE_METRIC}</SubduedText>
</VerticallyCentered>
</EuiFlexItem>

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiPopover } from '@elastic/eui';
import cytoscape from 'cytoscape';
import React, {
CSSProperties,
MouseEvent,
@ -15,9 +13,12 @@ import React, {
useRef,
useState,
} from 'react';
import { EuiPopover } from '@elastic/eui';
import cytoscape from 'cytoscape';
import { useTheme } from '../../../../hooks/useTheme';
import { SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames';
import { CytoscapeContext } from '../Cytoscape';
import { animationOptions } from '../cytoscapeOptions';
import { getAnimationOptions } from '../cytoscapeOptions';
import { Contents } from './Contents';
interface PopoverProps {
@ -25,6 +26,7 @@ interface PopoverProps {
}
export function Popover({ focusedServiceName }: PopoverProps) {
const theme = useTheme();
const cy = useContext(CytoscapeContext);
const [selectedNode, setSelectedNode] = useState<
cytoscape.NodeSingular | undefined
@ -93,16 +95,20 @@ export function Popover({ focusedServiceName }: PopoverProps) {
event.preventDefault();
if (cy) {
cy.animate({
...animationOptions,
...getAnimationOptions(theme),
center: { eles: cy.getElementById(selectedNodeServiceName) },
});
}
},
[cy, selectedNodeServiceName]
[cy, selectedNodeServiceName, theme]
);
const isAlreadyFocused = focusedServiceName === selectedNodeServiceName;
const onFocusClick = isAlreadyFocused
? centerSelectedNode
: (_event: MouseEvent<HTMLAnchorElement>) => deselect();
return (
<EuiPopover
anchorPosition={'upCenter'}
@ -115,9 +121,7 @@ export function Popover({ focusedServiceName }: PopoverProps) {
<Contents
isService={isService}
label={label}
onFocusClick={
isAlreadyFocused ? centerSelectedNode : (_event) => deselect()
}
onFocusClick={onFocusClick}
selectedNodeData={selectedNodeData}
selectedNodeServiceName={selectedNodeServiceName}
/>

View file

@ -3,46 +3,49 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import cytoscape from 'cytoscape';
import { CSSProperties } from 'react';
import {
SERVICE_NAME,
SPAN_DESTINATION_SERVICE_RESOURCE,
} from '../../../../common/elasticsearch_fieldnames';
import { EuiTheme } from '../../../../../observability/public';
import { severity } from '../../../../common/ml_job_constants';
import { defaultIcon, iconForNode } from './icons';
export const popoverMinWidth = 280;
export const getSeverityColor = (nodeSeverity?: string) => {
export function getSeverityColor(theme: EuiTheme, nodeSeverity?: string) {
switch (nodeSeverity) {
case severity.warning:
return theme.euiColorVis0;
return theme.eui.euiColorVis0;
case severity.minor:
case severity.major:
return theme.euiColorVis5;
return theme.eui.euiColorVis5;
case severity.critical:
return theme.euiColorVis9;
return theme.eui.euiColorVis9;
default:
return;
}
};
}
const getBorderColor: cytoscape.Css.MapperFunction<
cytoscape.NodeSingular,
string
> = (el: cytoscape.NodeSingular) => {
const hasAnomalyDetectionJob = el.data('ml_job_id') !== undefined;
const nodeSeverity = el.data('anomaly_severity');
if (hasAnomalyDetectionJob) {
return getSeverityColor(nodeSeverity) || theme.euiColorMediumShade;
}
if (el.hasClass('primary') || el.selected()) {
return theme.euiColorPrimary;
}
return theme.euiColorMediumShade;
};
function getBorderColorFn(
theme: EuiTheme
): cytoscape.Css.MapperFunction<cytoscape.NodeSingular, string> {
return (el: cytoscape.NodeSingular) => {
const hasAnomalyDetectionJob = el.data('ml_job_id') !== undefined;
const nodeSeverity = el.data('anomaly_severity');
if (hasAnomalyDetectionJob) {
return (
getSeverityColor(theme, nodeSeverity) || theme.eui.euiColorMediumShade
);
}
if (el.hasClass('primary') || el.selected()) {
return theme.eui.euiColorPrimary;
}
return theme.eui.euiColorMediumShade;
};
}
const getBorderStyle: cytoscape.Css.MapperFunction<
cytoscape.NodeSingular,
@ -56,7 +59,7 @@ const getBorderStyle: cytoscape.Css.MapperFunction<
}
};
const getBorderWidth = (el: cytoscape.NodeSingular) => {
function getBorderWidth(el: cytoscape.NodeSingular) {
const nodeSeverity = el.data('anomaly_severity');
if (nodeSeverity === severity.minor || nodeSeverity === severity.major) {
@ -66,7 +69,7 @@ const getBorderWidth = (el: cytoscape.NodeSingular) => {
} else {
return 4;
}
};
}
// IE 11 does not properly load some SVGs or draw certain shapes. This causes
// a runtime error and the map fails work at all. We would prefer to do some
@ -79,172 +82,183 @@ const getBorderWidth = (el: cytoscape.NodeSingular) => {
// @ts-ignore `documentMode` is not recognized as a valid property of `document`.
const isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
export const animationOptions: cytoscape.AnimationOptions = {
duration: parseInt(theme.euiAnimSpeedNormal, 10),
export const getAnimationOptions = (
theme: EuiTheme
): cytoscape.AnimationOptions => ({
duration: parseInt(theme.eui.euiAnimSpeedNormal, 10),
// @ts-ignore The cubic-bezier options here are not recognized by the cytoscape types
easing: theme.euiAnimSlightBounce,
};
const lineColor = '#C5CCD7';
easing: theme.eui.euiAnimSlightBounce,
});
const zIndexNode = 200;
const zIndexEdge = 100;
const zIndexEdgeHighlight = 110;
const zIndexEdgeHover = 120;
export const nodeHeight = parseInt(theme.avatarSizing.l.size, 10);
export const getNodeHeight = (theme: EuiTheme): number =>
parseInt(theme.eui.avatarSizing.l.size, 10);
function isService(el: cytoscape.NodeSingular) {
return el.data(SERVICE_NAME) !== undefined;
}
const style: cytoscape.Stylesheet[] = [
{
selector: 'node',
style: {
'background-color': 'white',
// The DefinitelyTyped definitions don't specify that a function can be
// used here.
//
// @ts-ignore
'background-image': isIE11
? undefined
: (el: cytoscape.NodeSingular) => iconForNode(el) ?? defaultIcon,
'background-height': (el: cytoscape.NodeSingular) =>
isService(el) ? '60%' : '40%',
'background-width': (el: cytoscape.NodeSingular) =>
isService(el) ? '60%' : '40%',
'border-color': getBorderColor,
'border-style': getBorderStyle,
'border-width': getBorderWidth,
color: (el: cytoscape.NodeSingular) =>
el.hasClass('primary') || el.selected()
? theme.euiColorPrimaryText
: theme.textColors.text,
// theme.euiFontFamily doesn't work here for some reason, so we're just
// specifying a subset of the fonts for the label text.
'font-family': 'Inter UI, Segoe UI, Helvetica, Arial, sans-serif',
'font-size': theme.euiFontSizeS,
ghost: 'yes',
'ghost-offset-x': 0,
'ghost-offset-y': 2,
'ghost-opacity': 0.15,
height: nodeHeight,
label: (el: cytoscape.NodeSingular) =>
isService(el)
? el.data(SERVICE_NAME)
: el.data(SPAN_DESTINATION_SERVICE_RESOURCE),
'min-zoomed-font-size': parseInt(theme.euiSizeS, 10),
'overlay-opacity': 0,
shape: (el: cytoscape.NodeSingular) =>
isService(el) ? (isIE11 ? 'rectangle' : 'ellipse') : 'diamond',
'text-background-color': theme.euiColorPrimary,
'text-background-opacity': (el: cytoscape.NodeSingular) =>
el.hasClass('primary') || el.selected() ? 0.1 : 0,
'text-background-padding': theme.paddingSizes.xs,
'text-background-shape': 'roundrectangle',
'text-margin-y': parseInt(theme.paddingSizes.s, 10),
'text-max-width': '200px',
'text-valign': 'bottom',
'text-wrap': 'ellipsis',
width: theme.avatarSizing.l.size,
'z-index': zIndexNode,
const getStyle = (theme: EuiTheme): cytoscape.Stylesheet[] => {
const lineColor = theme.eui.euiColorMediumShade;
return [
{
selector: 'node',
style: {
'background-color': theme.eui.euiColorGhost,
// The DefinitelyTyped definitions don't specify that a function can be
// used here.
//
// @ts-ignore
'background-image': isIE11
? undefined
: (el: cytoscape.NodeSingular) => iconForNode(el) ?? defaultIcon,
'background-height': (el: cytoscape.NodeSingular) =>
isService(el) ? '60%' : '40%',
'background-width': (el: cytoscape.NodeSingular) =>
isService(el) ? '60%' : '40%',
'border-color': getBorderColorFn(theme),
'border-style': getBorderStyle,
'border-width': getBorderWidth,
color: (el: cytoscape.NodeSingular) =>
el.hasClass('primary') || el.selected()
? theme.eui.euiColorPrimaryText
: theme.eui.textColors.text,
// theme.euiFontFamily doesn't work here for some reason, so we're just
// specifying a subset of the fonts for the label text.
'font-family': 'Inter UI, Segoe UI, Helvetica, Arial, sans-serif',
'font-size': theme.eui.euiFontSizeS,
ghost: 'yes',
'ghost-offset-x': 0,
'ghost-offset-y': 2,
'ghost-opacity': 0.15,
height: getNodeHeight(theme),
label: (el: cytoscape.NodeSingular) =>
isService(el)
? el.data(SERVICE_NAME)
: el.data(SPAN_DESTINATION_SERVICE_RESOURCE),
'min-zoomed-font-size': parseInt(theme.eui.euiSizeS, 10),
'overlay-opacity': 0,
shape: (el: cytoscape.NodeSingular) =>
isService(el) ? (isIE11 ? 'rectangle' : 'ellipse') : 'diamond',
'text-background-color': theme.eui.euiColorPrimary,
'text-background-opacity': (el: cytoscape.NodeSingular) =>
el.hasClass('primary') || el.selected() ? 0.1 : 0,
'text-background-padding': theme.eui.paddingSizes.xs,
'text-background-shape': 'roundrectangle',
'text-margin-y': parseInt(theme.eui.paddingSizes.s, 10),
'text-max-width': '200px',
'text-valign': 'bottom',
'text-wrap': 'ellipsis',
width: theme.eui.avatarSizing.l.size,
'z-index': zIndexNode,
},
},
},
{
selector: 'edge',
style: {
'curve-style': 'taxi',
// @ts-ignore
'taxi-direction': 'auto',
'line-color': lineColor,
'overlay-opacity': 0,
'target-arrow-color': lineColor,
'target-arrow-shape': isIE11 ? 'none' : 'triangle',
// The DefinitelyTyped definitions don't specify this property since it's
// fairly new.
//
// @ts-ignore
'target-distance-from-node': isIE11 ? undefined : theme.paddingSizes.xs,
width: 1,
'source-arrow-shape': 'none',
'z-index': zIndexEdge,
{
selector: 'edge',
style: {
'curve-style': 'taxi',
// @ts-ignore
'taxi-direction': 'auto',
'line-color': lineColor,
'overlay-opacity': 0,
'target-arrow-color': lineColor,
'target-arrow-shape': isIE11 ? 'none' : 'triangle',
// The DefinitelyTyped definitions don't specify this property since it's
// fairly new.
//
// @ts-ignore
'target-distance-from-node': isIE11
? undefined
: theme.eui.paddingSizes.xs,
width: 1,
'source-arrow-shape': 'none',
'z-index': zIndexEdge,
},
},
},
{
selector: 'edge[bidirectional]',
style: {
'source-arrow-shape': isIE11 ? 'none' : 'triangle',
'source-arrow-color': lineColor,
'target-arrow-shape': isIE11 ? 'none' : 'triangle',
// @ts-ignore
'source-distance-from-node': isIE11
? undefined
: parseInt(theme.paddingSizes.xs, 10),
'target-distance-from-node': isIE11
? undefined
: parseInt(theme.paddingSizes.xs, 10),
{
selector: 'edge[bidirectional]',
style: {
'source-arrow-shape': isIE11 ? 'none' : 'triangle',
'source-arrow-color': lineColor,
'target-arrow-shape': isIE11 ? 'none' : 'triangle',
// @ts-ignore
'source-distance-from-node': isIE11
? undefined
: parseInt(theme.eui.paddingSizes.xs, 10),
'target-distance-from-node': isIE11
? undefined
: parseInt(theme.eui.paddingSizes.xs, 10),
},
},
},
{
selector: 'edge[isInverseEdge]',
// @ts-ignore DefinitelyTyped says visibility is "none" but it's
// actually "hidden"
style: { visibility: 'hidden' },
},
{
selector: 'edge.nodeHover',
style: {
width: 2,
// @ts-ignore
'z-index': zIndexEdgeHover,
'line-color': theme.euiColorDarkShade,
'source-arrow-color': theme.euiColorDarkShade,
'target-arrow-color': theme.euiColorDarkShade,
{
selector: 'edge[isInverseEdge]',
// @ts-ignore DefinitelyTyped says visibility is "none" but it's
// actually "hidden"
style: { visibility: 'hidden' },
},
},
{
selector: 'node.hover',
style: {
'border-width': getBorderWidth,
{
selector: 'edge.nodeHover',
style: {
width: 4,
// @ts-ignore
'z-index': zIndexEdgeHover,
'line-color': theme.eui.euiColorDarkShade,
'source-arrow-color': theme.eui.euiColorDarkShade,
'target-arrow-color': theme.eui.euiColorDarkShade,
},
},
},
{
selector: 'edge.highlight',
style: {
width: 2,
'line-color': theme.euiColorPrimary,
'source-arrow-color': theme.euiColorPrimary,
'target-arrow-color': theme.euiColorPrimary,
// @ts-ignore
'z-index': zIndexEdgeHighlight,
{
selector: 'node.hover',
style: {
'border-width': getBorderWidth,
},
},
},
];
{
selector: 'edge.highlight',
style: {
width: 4,
'line-color': theme.eui.euiColorPrimary,
'source-arrow-color': theme.eui.euiColorPrimary,
'target-arrow-color': theme.eui.euiColorPrimary,
// @ts-ignore
'z-index': zIndexEdgeHighlight,
},
},
];
};
// The CSS styles for the div containing the cytoscape element. Makes a
// background grid of dots.
export const cytoscapeDivStyle: CSSProperties = {
export const getCytoscapeDivStyle = (theme: EuiTheme): CSSProperties => ({
background: `linear-gradient(
90deg,
${theme.euiPageBackgroundColor}
calc(${theme.euiSizeL} - calc(${theme.euiSizeXS} / 2)),
${theme.eui.euiPageBackgroundColor}
calc(${theme.eui.euiSizeL} - calc(${theme.eui.euiSizeXS} / 2)),
transparent 1%
)
center,
linear-gradient(
${theme.euiPageBackgroundColor}
calc(${theme.euiSizeL} - calc(${theme.euiSizeXS} / 2)),
${theme.eui.euiPageBackgroundColor}
calc(${theme.eui.euiSizeL} - calc(${theme.eui.euiSizeXS} / 2)),
transparent 1%
)
center,
${theme.euiColorLightShade}`,
backgroundSize: `${theme.euiSizeL} ${theme.euiSizeL}`,
margin: `-${theme.gutterTypes.gutterLarge}`,
${theme.eui.euiColorLightShade}`,
backgroundSize: `${theme.eui.euiSizeL} ${theme.eui.euiSizeL}`,
margin: `-${theme.eui.gutterTypes.gutterLarge}`,
marginTop: 0,
};
});
export const cytoscapeOptions: cytoscape.CytoscapeOptions = {
export const getCytoscapeOptions = (
theme: EuiTheme
): cytoscape.CytoscapeOptions => ({
autoungrabify: true,
boxSelectionEnabled: false,
maxZoom: 3,
minZoom: 0.2,
style,
};
style: getStyle(theme),
});

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { useTheme } from '../../../hooks/useTheme';
import {
invalidLicenseMessage,
isValidPlatinumLicense,
@ -18,7 +18,7 @@ import { callApmApi } from '../../../services/rest/createCallApmApi';
import { LicensePrompt } from '../../shared/LicensePrompt';
import { Controls } from './Controls';
import { Cytoscape } from './Cytoscape';
import { cytoscapeDivStyle } from './cytoscapeOptions';
import { getCytoscapeDivStyle } from './cytoscapeOptions';
import { EmptyBanner } from './EmptyBanner';
import { Popover } from './Popover';
import { useRefDimensions } from './useRefDimensions';
@ -29,7 +29,8 @@ interface ServiceMapProps {
serviceName?: string;
}
export function ServiceMap({ serviceName }: ServiceMapProps) {
export const ServiceMap = ({ serviceName }: ServiceMapProps) => {
const theme = useTheme();
const license = useLicense();
const { urlParams } = useUrlParams();
@ -67,14 +68,16 @@ export function ServiceMap({ serviceName }: ServiceMapProps) {
return isValidPlatinumLicense(license) ? (
<div
style={{ height: height - parseInt(theme.gutterTypes.gutterLarge, 10) }}
style={{
height: height - parseInt(theme.eui.gutterTypes.gutterLarge, 10),
}}
ref={ref}
>
<Cytoscape
elements={data?.elements ?? []}
height={height}
serviceName={serviceName}
style={cytoscapeDivStyle}
style={getCytoscapeDivStyle(theme)}
width={width}
>
<Controls />
@ -98,4 +101,4 @@ export function ServiceMap({ serviceName }: ServiceMapProps) {
</EuiFlexItem>
</EuiFlexGroup>
);
}
};

View file

@ -15,7 +15,7 @@ import {
EuiButtonIcon,
} from '@elastic/eui';
import { isEmpty } from 'lodash';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { useTheme } from '../../../../../hooks/useTheme';
import { FETCH_STATUS } from '../../../../../hooks/useFetcher';
import { ITableColumn, ManagedTable } from '../../../../shared/ManagedTable';
import { LoadingStatePrompt } from '../../../../shared/LoadingStatePrompt';
@ -32,15 +32,14 @@ import { ConfirmDeleteModal } from './ConfirmDeleteModal';
type Config = AgentConfigurationListAPIResponse[0];
export function AgentConfigurationList({
status,
data,
refetch,
}: {
interface Props {
status: FETCH_STATUS;
data: Config[];
refetch: () => void;
}) {
}
export const AgentConfigurationList = ({ status, data, refetch }: Props) => {
const theme = useTheme();
const [configToBeDeleted, setConfigToBeDeleted] = useState<Config | null>(
null
);
@ -128,7 +127,9 @@ export function AgentConfigurationList({
)
}
>
<EuiHealth color={isApplied ? 'success' : theme.euiColorLightShade} />
<EuiHealth
color={isApplied ? 'success' : theme.eui.euiColorLightShade}
/>
</EuiToolTip>
),
},
@ -218,4 +219,4 @@ export function AgentConfigurationList({
/>
</>
);
}
};

View file

@ -5,7 +5,6 @@
*/
import { EuiSpacer, EuiTitle } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { tint } from 'polished';
import React, { Fragment } from 'react';
@ -33,9 +32,9 @@ registerLanguage('sql', sql);
const DatabaseStatement = styled.div`
padding: ${px(units.half)} ${px(unit)};
background: ${tint(0.1, theme.euiColorWarning)};
background: ${({ theme }) => tint(0.1, theme.eui.euiColorWarning)};
border-radius: ${borderRadius};
border: 1px solid ${theme.euiColorLightShade};
border: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
font-family: ${fontFamilyCode};
font-size: ${fontSize};
`;

View file

@ -8,7 +8,6 @@ import React, { Fragment } from 'react';
import styled from 'styled-components';
import { EuiSpacer, EuiTitle } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import {
borderRadius,
fontFamilyCode,
@ -21,9 +20,9 @@ import { Span } from '../../../../../../../../typings/es_schemas/ui/span';
const ContextUrl = styled.div`
padding: ${px(units.half)} ${px(unit)};
background: ${theme.euiColorLightestShade};
background: ${({ theme }) => theme.eui.euiColorLightestShade};
border-radius: ${borderRadius};
border: 1px solid ${theme.euiColorLightShade};
border: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
font-family: ${fontFamilyCode};
font-size: ${fontSize};
`;

View file

@ -8,7 +8,6 @@ import React from 'react';
import styled from 'styled-components';
import { EuiIcon, EuiText, EuiTitle, EuiToolTip } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { isRumAgentName } from '../../../../../../../common/agent_name';
import { px, unit, units } from '../../../../../../style/variables';
@ -41,13 +40,13 @@ const Container = styled.div<IContainerStyleProps>`
padding-bottom: ${px(units.plus)};
margin-right: ${(props) => px(props.timelineMargins.right)};
margin-left: ${(props) => px(props.timelineMargins.left)};
border-top: 1px solid ${theme.euiColorLightShade};
background-color: ${(props) =>
props.isSelected ? theme.euiColorLightestShade : 'initial'};
border-top: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
background-color: ${({ isSelected, theme }) =>
isSelected ? theme.eui.euiColorLightestShade : 'initial'};
cursor: pointer;
&:hover {
background-color: ${theme.euiColorLightestShade};
background-color: ${({ theme }) => theme.eui.euiColorLightestShade};
}
`;

View file

@ -6,7 +6,6 @@
import React from 'react';
import cls from 'classnames';
import styled from 'styled-components';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { px, unit } from '../../style/variables';
// TODO: replace this component with EUITab w/ a href prop
@ -28,8 +27,8 @@ const Wrapper = styled.div<{ isSelected: boolean }>`
a {
display: inline-block;
padding: ${px(unit * 0.75)} ${px(unit)};
${({ isSelected }) =>
!isSelected ? `color: ${theme.euiTextColor} !important;` : ''}
${({ isSelected, theme }) =>
!isSelected ? `color: ${theme.eui.euiTextColor} !important;` : ''}
}
`;

View file

@ -4,14 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { isBoolean, isNumber, isObject } from 'lodash';
import React from 'react';
import styled from 'styled-components';
import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n';
const EmptyValue = styled.span`
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
text-align: left;
`;

View file

@ -7,6 +7,7 @@
import React from 'react';
import { KeyValueTable } from '..';
import { render } from '@testing-library/react';
import { renderWithTheme } from '../../../../utils/testHelpers';
function getKeys(output: ReturnType<typeof render>) {
const keys = output.getAllByTestId('dot-key');
@ -31,7 +32,7 @@ describe('KeyValueTable', () => {
{ key: 'nested.b.c', value: 'ccc' },
{ key: 'nested.a', value: 'aaa' },
];
const output = render(<KeyValueTable keyValuePairs={data} />);
const output = renderWithTheme(<KeyValueTable keyValuePairs={data} />);
const rows = output.container.querySelectorAll('tr');
expect(rows.length).toEqual(9);

View file

@ -16,32 +16,31 @@ import {
unit,
} from '../../../../style/variables';
import { tint } from 'polished';
import theme from '@elastic/eui/dist/eui_theme_light.json';
function getIconColor(type) {
function getIconColor(type, theme) {
switch (type) {
case 'field':
return theme.euiColorVis7;
return theme.eui.euiColorVis7;
case 'value':
return theme.euiColorVis0;
return theme.eui.euiColorVis0;
case 'operator':
return theme.euiColorVis1;
return theme.eui.euiColorVis1;
case 'conjunction':
return theme.euiColorVis3;
return theme.eui.euiColorVis3;
case 'recentSearch':
return theme.euiColorMediumShade;
return theme.eui.euiColorMediumShade;
}
}
const Description = styled.div`
color: ${theme.euiColorDarkShade};
color: ${({ theme }) => theme.eui.euiColorDarkShade};
p {
display: inline;
span {
font-family: ${fontFamilyCode};
color: ${theme.euiColorFullShade};
color: ${({ theme }) => theme.eui.euiColorFullShade};
padding: 0 ${px(units.quarter)};
display: inline-block;
}
@ -53,25 +52,25 @@ const ListItem = styled.li`
height: ${px(units.double)};
align-items: center;
display: flex;
background: ${(props) =>
props.selected ? theme.euiColorLightestShade : 'initial'};
background: ${({ selected, theme }) =>
selected ? theme.eui.euiColorLightestShade : 'initial'};
cursor: pointer;
border-radius: ${px(units.quarter)};
${Description} {
p span {
background: ${(props) =>
props.selected
? theme.euiColorEmptyShade
: theme.euiColorLightestShade};
background: ${({ selected, theme }) =>
selected
? theme.eui.euiColorEmptyShade
: theme.eui.euiColorLightestShade};
}
}
`;
const Icon = styled.div`
flex: 0 0 ${px(units.double)};
background: ${(props) => tint(0.1, getIconColor(props.type))};
color: ${(props) => getIconColor(props.type)};
background: ${({ type, theme }) => tint(0.1, getIconColor(type, theme))};
color: ${({ type, theme }) => getIconColor(type, theme)};
width: 100%;
height: 100%;
text-align: center;
@ -80,7 +79,7 @@ const Icon = styled.div`
const TextValue = styled.div`
flex: 0 0 ${px(unit * 16)};
color: ${theme.euiColorDarkestShade};
color: ${({ theme }) => theme.eui.euiColorDarkestShade};
padding: 0 ${px(units.half)};
`;

View file

@ -11,16 +11,15 @@ import { isEmpty } from 'lodash';
import Suggestion from './Suggestion';
import { units, px, unit } from '../../../../style/variables';
import { tint } from 'polished';
import theme from '@elastic/eui/dist/eui_theme_light.json';
const List = styled.ul`
width: 100%;
border: 1px solid ${theme.euiColorLightShade};
border: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
border-radius: ${px(units.quarter)};
box-shadow: 0px ${px(units.quarter)} ${px(units.double)}
${tint(0.1, theme.euiColorFullShade)};
${({ theme }) => tint(0.1, theme.eui.euiColorFullShade)};
position: absolute;
background: #fff;
background: ${({ theme }) => theme.eui.euiColorEmptyShade};
z-index: 10;
left: 0;
max-height: ${px(unit * 20)};

View file

@ -18,7 +18,6 @@ import {
EuiFlexGroup,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import styled from 'styled-components';
import { FilterBadgeList } from './FilterBadgeList';
import { unit, px } from '../../../../style/variables';
@ -39,9 +38,9 @@ const SelectContainer = styled.div`
`;
const Counter = styled.div`
border-radius: ${theme.euiBorderRadius};
background: ${theme.euiColorLightShade};
padding: 0 ${theme.paddingSizes.xs};
border-radius: ${({ theme }) => theme.eui.euiBorderRadius};
background: ${({ theme }) => theme.eui.euiColorLightShade};
padding: 0 ${({ theme }) => theme.eui.paddingSizes.xs};
`;
const ApplyButton = styled(EuiButton)`

View file

@ -5,8 +5,9 @@
*/
import React from 'react';
import { mount, shallow } from 'enzyme';
import { shallow } from 'enzyme';
import { CauseStacktrace } from './CauseStacktrace';
import { mountWithTheme } from '../../../utils/testHelpers';
describe('CauseStacktrace', () => {
describe('render', () => {
@ -15,7 +16,7 @@ describe('CauseStacktrace', () => {
const props = { id: 'testId', message: 'testMessage' };
expect(
mount(<CauseStacktrace {...props} />).find('CausedBy')
mountWithTheme(<CauseStacktrace {...props} />).find('CausedBy')
).toHaveLength(1);
});
});
@ -28,7 +29,7 @@ describe('CauseStacktrace', () => {
};
expect(
mount(<CauseStacktrace {...props} />)
mountWithTheme(<CauseStacktrace {...props} />)
.find('EuiTitle span')
.text()
).toEqual('…');

View file

@ -6,7 +6,6 @@
import React from 'react';
import styled from 'styled-components';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { EuiAccordion, EuiTitle } from '@elastic/eui';
import { px, unit } from '../../../style/variables';
@ -15,18 +14,18 @@ import { IStackframe } from '../../../../typings/es_schemas/raw/fields/stackfram
// @ts-ignore Styled Components has trouble inferring the types of the default props here.
const Accordion = styled(EuiAccordion)`
border-top: ${theme.euiBorderThin};
border-top: ${({ theme }) => theme.eui.euiBorderThin};
`;
const CausedByContainer = styled('h5')`
padding: ${theme.spacerSizes.s} 0;
padding: ${({ theme }) => theme.eui.spacerSizes.s} 0;
`;
const CausedByHeading = styled('span')`
color: ${theme.textColors.subdued};
color: ${({ theme }) => theme.eui.textColors.subdued};
display: block;
font-size: ${theme.euiFontSizeXS};
font-weight: ${theme.euiFontWeightBold};
font-size: ${({ theme }) => theme.eui.euiFontSizeXS};
font-weight: ${({ theme }) => theme.eui.euiFontWeightBold};
text-transform: uppercase;
`;

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { size } from 'lodash';
import { tint } from 'polished';
import React from 'react';
@ -41,7 +40,7 @@ const LineHighlight = styled.div<{ lineNumber: number }>`
height: ${px(units.eighth * 9)};
top: ${(props) => px(props.lineNumber * LINE_HEIGHT)};
pointer-events: none;
background-color: ${tint(0.1, theme.euiColorWarning)};
background-color: ${({ theme }) => tint(0.1, theme.eui.euiColorWarning)};
`;
const LineNumberContainer = styled.div<{ isLibraryFrame: boolean }>`
@ -49,10 +48,10 @@ const LineNumberContainer = styled.div<{ isLibraryFrame: boolean }>`
top: 0;
left: 0;
border-radius: ${borderRadius};
background: ${(props) =>
props.isLibraryFrame
? theme.euiColorEmptyShade
: theme.euiColorLightestShade};
background: ${({ isLibraryFrame, theme }) =>
isLibraryFrame
? theme.eui.euiColorEmptyShade
: theme.eui.euiColorLightestShade};
`;
const LineNumber = styled.div<{ highlight: boolean }>`
@ -60,12 +59,12 @@ const LineNumber = styled.div<{ highlight: boolean }>`
min-width: ${px(units.eighth * 21)};
padding-left: ${px(units.half)};
padding-right: ${px(units.quarter)};
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
line-height: ${px(unit + units.eighth)};
text-align: right;
border-right: 1px solid ${theme.euiColorLightShade};
background-color: ${(props) =>
props.highlight ? tint(0.1, theme.euiColorWarning) : null};
border-right: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
background-color: ${({ highlight, theme }) =>
highlight ? tint(0.1, theme.eui.euiColorWarning) : null};
&:last-of-type {
border-radius: 0 0 0 ${borderRadius};
@ -76,7 +75,7 @@ const LineContainer = styled.div`
overflow: auto;
margin: 0 0 0 ${px(units.eighth * 21)};
padding: 0;
background-color: ${theme.euiColorEmptyShade};
background-color: ${({ theme }) => theme.eui.euiColorEmptyShade};
&:last-of-type {
border-radius: 0 0 ${borderRadius} 0;

View file

@ -4,25 +4,24 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React, { Fragment } from 'react';
import styled from 'styled-components';
import { IStackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
import { fontFamilyCode, fontSize, px, units } from '../../../style/variables';
const FileDetails = styled.div`
color: ${theme.euiColorDarkShade};
color: ${({ theme }) => theme.eui.euiColorDarkShade};
padding: ${px(units.half)} 0;
font-family: ${fontFamilyCode};
font-size: ${fontSize};
`;
const LibraryFrameFileDetail = styled.span`
color: ${theme.euiColorDarkShade};
color: ${({ theme }) => theme.eui.euiColorDarkShade};
`;
const AppFrameFileDetail = styled.span`
color: ${theme.euiColorFullShade};
color: ${({ theme }) => theme.eui.euiColorFullShade};
`;
interface Props {

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
import styled from 'styled-components';
import { EuiAccordion } from '@elastic/eui';
@ -25,12 +24,12 @@ const ContextContainer = styled.div<{ isLibraryFrame: boolean }>`
position: relative;
font-family: ${fontFamilyCode};
font-size: ${fontSize};
border: 1px solid ${theme.euiColorLightShade};
border: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
border-radius: ${borderRadius};
background: ${(props) =>
props.isLibraryFrame
? theme.euiColorEmptyShade
: theme.euiColorLightestShade};
background: ${({ isLibraryFrame, theme }) =>
isLibraryFrame
? theme.eui.euiColorEmptyShade
: theme.eui.euiColorLightestShade};
`;
interface Props {

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import styled from 'styled-components';
import { EuiAccordion } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@ -15,7 +14,7 @@ import { KeyValueTable } from '../KeyValueTable';
import { flattenObject } from '../../../utils/flattenObject';
const VariablesContainer = styled.div`
background: ${theme.euiColorEmptyShade};
background: ${({ theme }) => theme.eui.euiColorEmptyShade};
border-radius: 0 0 ${borderRadius} ${borderRadius};
padding: ${px(units.half)} ${px(unit)};
`;

View file

@ -4,9 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { mount, ReactWrapper, shallow } from 'enzyme';
import React from 'react';
import { ReactWrapper, shallow } from 'enzyme';
import { IStackframe } from '../../../../../typings/es_schemas/raw/fields/stackframe';
import { mountWithTheme } from '../../../../utils/testHelpers';
import { Stackframe } from '../Stackframe';
import stacktracesMock from './stacktraces.json';
@ -15,7 +16,9 @@ describe('Stackframe', () => {
let wrapper: ReactWrapper;
beforeEach(() => {
const stackframe = stacktracesMock[0];
wrapper = mount(<Stackframe id="test" stackframe={stackframe} />);
wrapper = mountWithTheme(
<Stackframe id="test" stackframe={stackframe} />
);
});
it('should render correctly', () => {
@ -37,7 +40,9 @@ describe('Stackframe', () => {
let wrapper: ReactWrapper;
beforeEach(() => {
const stackframe = { line: {} } as IStackframe;
wrapper = mount(<Stackframe id="test" stackframe={stackframe} />);
wrapper = mountWithTheme(
<Stackframe id="test" stackframe={stackframe} />
);
});
it('should render only FrameHeading', () => {

View file

@ -383,8 +383,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"baseHash": -2021127760,
"componentId": "sc-fzoLsD",
"isStatic": false,
"rules": Array [
"
@ -401,7 +401,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-fzozJi",
"styledComponentId": "sc-fzoLsD",
"target": "code",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -413,8 +413,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": -1923298833,
"componentId": "sc-AxmLO",
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"isStatic": false,
"rules": Array [
"
@ -439,7 +439,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-AxmLO",
"styledComponentId": "sc-fzozJi",
"target": "pre",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -608,8 +608,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"baseHash": -2021127760,
"componentId": "sc-fzoLsD",
"isStatic": false,
"rules": Array [
"
@ -626,7 +626,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-fzozJi",
"styledComponentId": "sc-fzoLsD",
"target": "code",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -638,8 +638,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": -1923298833,
"componentId": "sc-AxmLO",
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"isStatic": false,
"rules": Array [
"
@ -664,7 +664,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-AxmLO",
"styledComponentId": "sc-fzozJi",
"target": "pre",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -834,8 +834,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"baseHash": -2021127760,
"componentId": "sc-fzoLsD",
"isStatic": false,
"rules": Array [
"
@ -852,7 +852,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-fzozJi",
"styledComponentId": "sc-fzoLsD",
"target": "code",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -864,8 +864,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": -1923298833,
"componentId": "sc-AxmLO",
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"isStatic": false,
"rules": Array [
"
@ -890,7 +890,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-AxmLO",
"styledComponentId": "sc-fzozJi",
"target": "pre",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -1070,8 +1070,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"baseHash": -2021127760,
"componentId": "sc-fzoLsD",
"isStatic": false,
"rules": Array [
"
@ -1088,7 +1088,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-fzozJi",
"styledComponentId": "sc-fzoLsD",
"target": "code",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -1100,8 +1100,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": -1923298833,
"componentId": "sc-AxmLO",
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"isStatic": false,
"rules": Array [
"
@ -1126,7 +1126,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-AxmLO",
"styledComponentId": "sc-fzozJi",
"target": "pre",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -1323,8 +1323,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"baseHash": -2021127760,
"componentId": "sc-fzoLsD",
"isStatic": false,
"rules": Array [
"
@ -1341,7 +1341,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-fzozJi",
"styledComponentId": "sc-fzoLsD",
"target": "code",
"toString": [Function],
"warnTooManyClasses": [Function],
@ -1353,8 +1353,8 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"baseHash": -1923298833,
"componentId": "sc-AxmLO",
"baseHash": 1280172402,
"componentId": "sc-fzozJi",
"isStatic": false,
"rules": Array [
"
@ -1379,7 +1379,7 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
"foldedComponentIds": Array [],
"render": [Function],
"shouldForwardProp": undefined,
"styledComponentId": "sc-AxmLO",
"styledComponentId": "sc-fzozJi",
"target": "pre",
"toString": [Function],
"warnTooManyClasses": [Function],

View file

@ -6,7 +6,6 @@
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiToolTip } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
import styled from 'styled-components';
import {
@ -32,7 +31,7 @@ const TooltipFieldName = styled.span`
const PropertyLabel = styled.div`
margin-bottom: ${px(units.half)};
font-size: ${fontSizes.small};
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
span {
cursor: help;

View file

@ -7,7 +7,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import { EuiBadge } from '@elastic/eui';
import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json';
import { useTheme } from '../../../hooks/useTheme';
import { px } from '../../../../public/style/variables';
import { units } from '../../../style/variables';
@ -19,12 +19,16 @@ const Badge = (styled(EuiBadge)`
margin-top: ${px(units.eighth)};
` as unknown) as typeof EuiBadge;
export const ErrorCountSummaryItemBadge = ({ count }: Props) => (
<Badge color={euiThemeLight.euiColorDanger}>
{i18n.translate('xpack.apm.transactionDetails.errorCount', {
defaultMessage:
'{errorCount, number} {errorCount, plural, one {Error} other {Errors}}',
values: { errorCount: count },
})}
</Badge>
);
export const ErrorCountSummaryItemBadge = ({ count }: Props) => {
const theme = useTheme();
return (
<Badge color={theme.eui.euiColorDanger}>
{i18n.translate('xpack.apm.transactionDetails.errorCount', {
defaultMessage:
'{errorCount, number} {errorCount, plural, one {Error} other {Errors}}',
values: { errorCount: count },
})}
</Badge>
);
};

View file

@ -5,8 +5,8 @@
*/
import React from 'react';
import { shallow, render } from 'enzyme';
import { UserAgentSummaryItem } from './UserAgentSummaryItem';
import { mountWithTheme } from '../../../utils/testHelpers';
describe('UserAgentSummaryItem', () => {
describe('render', () => {
@ -14,14 +14,14 @@ describe('UserAgentSummaryItem', () => {
it('renders', () => {
expect(() =>
shallow(<UserAgentSummaryItem {...props} />)
mountWithTheme(<UserAgentSummaryItem {...props} />)
).not.toThrowError();
});
describe('with a version', () => {
it('shows the version', () => {
const p = { ...props, version: '1.0' };
const wrapper = render(<UserAgentSummaryItem {...p} />);
const wrapper = mountWithTheme(<UserAgentSummaryItem {...p} />);
expect(wrapper.text()).toContain('(1.0)');
});

View file

@ -6,7 +6,6 @@
import React from 'react';
import styled from 'styled-components';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { UserAgent } from '../../../../typings/es_schemas/raw/fields/user_agent';
@ -14,7 +13,7 @@ import { UserAgent } from '../../../../typings/es_schemas/raw/fields/user_agent'
type UserAgentSummaryItemProps = UserAgent;
const Version = styled('span')`
font-size: ${theme.euiFontSizeS};
font-size: ${({ theme }) => theme.eui.euiFontSizeS};
`;
export function UserAgentSummaryItem({

View file

@ -6,16 +6,19 @@
import React from 'react';
import { ErrorCountSummaryItemBadge } from '../ErrorCountSummaryItemBadge';
import { render } from '@testing-library/react';
import { expectTextsInDocument } from '../../../../utils/testHelpers';
import {
expectTextsInDocument,
renderWithTheme,
} from '../../../../utils/testHelpers';
describe('ErrorCountSummaryItemBadge', () => {
it('shows singular error message', () => {
const component = render(<ErrorCountSummaryItemBadge count={1} />);
const component = renderWithTheme(<ErrorCountSummaryItemBadge count={1} />);
expectTextsInDocument(component, ['1 Error']);
});
it('shows plural error message', () => {
const component = render(<ErrorCountSummaryItemBadge count={2} />);
const component = renderWithTheme(<ErrorCountSummaryItemBadge count={2} />);
expectTextsInDocument(component, ['2 Errors']);
});
});

View file

@ -6,7 +6,6 @@
import React from 'react';
import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
import styled from 'styled-components';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { px, units } from '../../../../public/style/variables';
import { Maybe } from '../../../../typings/common';
@ -14,12 +13,9 @@ interface Props {
items: Array<Maybe<React.ReactElement>>;
}
// TODO: Light/Dark theme (@see https://github.com/elastic/kibana/issues/44840)
const theme = euiLightVars;
const Item = styled(EuiFlexItem)`
flex-wrap: nowrap;
border-right: 1px solid ${theme.euiColorLightShade};
border-right: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
padding-right: ${px(units.half)};
flex-flow: row nowrap;
line-height: 1.5;

View file

@ -5,7 +5,6 @@
*/
import React from 'react';
import { VerticalGridLines } from 'react-vis';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import {
EuiIcon,
EuiToolTip,
@ -14,6 +13,7 @@ import {
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useTheme } from '../../../../hooks/useTheme';
import { Maybe } from '../../../../../typings/common';
import { Annotation } from '../../../../../common/annotations';
import { PlotValues, SharedPlot } from './plotUtils';
@ -26,16 +26,15 @@ interface Props {
overlay: Maybe<HTMLElement>;
}
const style = {
stroke: theme.euiColorSecondary,
strokeDasharray: 'none',
};
export function AnnotationsPlot(props: Props) {
const { plotValues, annotations } = props;
export const AnnotationsPlot = ({ plotValues, annotations }: Props) => {
const theme = useTheme();
const tickValues = annotations.map((annotation) => annotation['@timestamp']);
const style = {
stroke: theme.eui.euiColorSecondary,
strokeDasharray: 'none',
};
return (
<>
<SharedPlot plotValues={plotValues}>
@ -65,10 +64,10 @@ export function AnnotationsPlot(props: Props) {
</EuiFlexGroup>
}
>
<EuiIcon type="dot" color={theme.euiColorSecondary} />
<EuiIcon type="dot" color={theme.eui.euiColorSecondary} />
</EuiToolTip>
</div>
))}
</>
);
}
};

View file

@ -4,10 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import PropTypes from 'prop-types';
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { Legend } from '../Legend';
import { useTheme } from '../../../../hooks/useTheme';
import {
unit,
units,
@ -15,7 +16,6 @@ import {
px,
truncate,
} from '../../../../style/variables';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { EuiIcon } from '@elastic/eui';
@ -36,7 +36,7 @@ const Container = styled.div`
const LegendContent = styled.span`
white-space: nowrap;
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
display: flex;
`;
@ -47,13 +47,13 @@ const TruncatedLabel = styled.span`
const SeriesValue = styled.span`
margin-left: ${px(units.quarter)};
color: ${theme.euiColorFullShade};
color: ${({ theme }) => theme.eui.euiColorFullShade};
display: inline-block;
`;
const MoreSeriesContainer = styled.div`
font-size: ${fontSizes.small};
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
`;
function MoreSeries({ hiddenSeriesCount }) {
@ -80,6 +80,8 @@ export default function Legends({
showAnnotations,
onAnnotationsToggle,
}) {
const theme = useTheme();
if (noHits && !hasAnnotations) {
return null;
}
@ -90,6 +92,7 @@ export default function Legends({
if (serie.hideLegend) {
return null;
}
const text = (
<LegendContent>
{truncateLegends ? (
@ -131,11 +134,11 @@ export default function Legends({
}
indicator={() => (
<div style={{ marginRight: px(units.quarter) }}>
<EuiIcon type="annotation" color={theme.euiColorSecondary} />
<EuiIcon type="annotation" color={theme.eui.euiColorSecondary} />
</div>
)}
disabled={!showAnnotations}
color={theme.euiColorSecondary}
color={theme.eui.euiColorSecondary}
/>
)}
<MoreSeries hiddenSeriesCount={hiddenSeriesCount} />

View file

@ -4,10 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { mount } from 'enzyme';
import moment from 'moment';
import React from 'react';
import { toJson } from '../../../../../utils/testHelpers';
import { toJson, mountWithTheme } from '../../../../../utils/testHelpers';
import { InnerCustomPlot } from '../index';
import responseWithData from './responseWithData.json';
import VoronoiPlot from '../VoronoiPlot';
@ -30,7 +29,7 @@ describe('when response has data', () => {
onHover = jest.fn();
onMouseLeave = jest.fn();
onSelectionEnd = jest.fn();
wrapper = mount(
wrapper = mountWithTheme(
<InnerCustomPlot
series={series}
onHover={onHover}
@ -274,7 +273,7 @@ describe('when response has no data', () => {
beforeEach(() => {
const series = getEmptySeries(1451606400000, 1451610000000);
wrapper = mount(
wrapper = mountWithTheme(
<InnerCustomPlot
annotations={annotations}
series={series}

View file

@ -5,7 +5,6 @@
*/
import React from 'react';
import { mount } from 'enzyme';
import d3 from 'd3';
import { HistogramInner } from '../index';
@ -14,7 +13,7 @@ import {
getDurationFormatter,
asInteger,
} from '../../../../../utils/formatters';
import { toJson } from '../../../../../utils/testHelpers';
import { toJson, mountWithTheme } from '../../../../../utils/testHelpers';
import { getFormattedBuckets } from '../../../../app/TransactionDetails/Distribution/index';
describe('Histogram', () => {
@ -26,7 +25,7 @@ describe('Histogram', () => {
const xMax = d3.max(buckets, (d) => d.x);
const timeFormatter = getDurationFormatter(xMax);
wrapper = mount(
wrapper = mountWithTheme(
<HistogramInner
buckets={buckets}
bucketSize={response.bucketSize}
@ -46,10 +45,6 @@ describe('Histogram', () => {
});
describe('Initially', () => {
it('should have default state', () => {
expect(wrapper.state()).toEqual({ hoveredBucket: {} });
});
it('should have default markup', () => {
expect(toJson(wrapper)).toMatchSnapshot();
});
@ -86,23 +81,6 @@ describe('Histogram', () => {
expect(tooltips.prop('y')).toEqual(27.5);
});
it('should update state with "hoveredBucket"', () => {
expect(wrapper.state()).toEqual({
hoveredBucket: {
samples: [
{
transactionId: '99c50a5b-44b4-4289-a3d1-a2815d128192',
},
],
style: { cursor: 'pointer' },
xCenter: 869010,
x0: 811076,
x: 926944,
y: 49,
},
});
});
it('should have correct markup for tooltip', () => {
const tooltips = wrapper.find('Tooltip');
expect(toJson(tooltips)).toMatchSnapshot();

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
import styled from 'styled-components';
import { useTheme } from '../../../../hooks/useTheme';
import { fontSizes, px, units } from '../../../../style/variables';
export enum Shape {
@ -20,11 +20,12 @@ interface ContainerProps {
clickable: boolean;
disabled: boolean;
}
const Container = styled.div<ContainerProps>`
display: flex;
align-items: center;
font-size: ${(props) => props.fontSize};
color: ${theme.euiColorDarkShade};
color: ${({ theme }) => theme.eui.euiColorDarkShade};
cursor: ${(props) => (props.clickable ? 'pointer' : 'initial')};
opacity: ${(props) => (props.disabled ? 0.4 : 1)};
user-select: none;
@ -36,6 +37,7 @@ interface IndicatorProps {
shape: Shape;
withMargin: boolean;
}
export const Indicator = styled.span<IndicatorProps>`
width: ${(props) => px(props.radius)};
height: ${(props) => px(props.radius)};
@ -61,7 +63,7 @@ interface Props {
export const Legend: React.FC<Props> = ({
onClick,
text,
color = theme.euiColorVis1,
color,
fontSize = fontSizes.small,
radius = units.minus - 1,
disabled = false,
@ -70,6 +72,9 @@ export const Legend: React.FC<Props> = ({
indicator,
...rest
}) => {
const theme = useTheme();
const indicatorColor = color || theme.eui.euiColorVis1;
return (
<Container
onClick={onClick}
@ -82,7 +87,7 @@ export const Legend: React.FC<Props> = ({
indicator()
) : (
<Indicator
color={color}
color={indicatorColor}
radius={radius}
shape={shape}
withMargin={!!text}

View file

@ -8,6 +8,7 @@ import { shallow } from 'enzyme';
import React from 'react';
import { AgentMarker } from './AgentMarker';
import { AgentMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks';
import { EuiThemeProvider } from '../../../../../../../observability/public';
describe('AgentMarker', () => {
const mark = {
@ -16,8 +17,14 @@ describe('AgentMarker', () => {
type: 'agentMark',
verticalLine: true,
} as AgentMark;
it('renders', () => {
const component = shallow(<AgentMarker mark={mark} />);
const component = shallow(
<EuiThemeProvider>
<AgentMarker mark={mark} />
</EuiThemeProvider>
);
expect(component).toMatchSnapshot();
});
});

View file

@ -4,22 +4,22 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiToolTip } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
import { EuiToolTip } from '@elastic/eui';
import styled from 'styled-components';
import { useTheme } from '../../../../../hooks/useTheme';
import { px, units } from '../../../../../style/variables';
import { asDuration } from '../../../../../utils/formatters';
import { Legend } from '../../Legend';
import { AgentMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks';
const NameContainer = styled.div`
border-bottom: 1px solid ${theme.euiColorMediumShade};
border-bottom: 1px solid ${({ theme }) => theme.eui.euiColorMediumShade};
padding-bottom: ${px(units.half)};
`;
const TimeContainer = styled.div`
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
padding-top: ${px(units.half)};
`;
@ -28,6 +28,8 @@ interface Props {
}
export const AgentMarker: React.FC<Props> = ({ mark }) => {
const theme = useTheme();
return (
<>
<EuiToolTip
@ -40,7 +42,7 @@ export const AgentMarker: React.FC<Props> = ({ mark }) => {
</div>
}
>
<Legend clickable color={theme.euiColorMediumShade} />
<Legend clickable color={theme.eui.euiColorMediumShade} />
</EuiToolTip>
</>
);

View file

@ -7,9 +7,12 @@
import React from 'react';
import { ErrorMarker } from './ErrorMarker';
import { ErrorMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks';
import { render, fireEvent } from '@testing-library/react';
import { fireEvent } from '@testing-library/react';
import { act } from '@testing-library/react-hooks';
import { expectTextsInDocument } from '../../../../../utils/testHelpers';
import {
expectTextsInDocument,
renderWithTheme,
} from '../../../../../utils/testHelpers';
describe('ErrorMarker', () => {
const mark = ({
@ -33,7 +36,7 @@ describe('ErrorMarker', () => {
} as unknown) as ErrorMark;
function openPopover(errorMark: ErrorMark) {
const component = render(<ErrorMarker mark={errorMark} />);
const component = renderWithTheme(<ErrorMarker mark={errorMark} />);
act(() => {
fireEvent.click(component.getByTestId('popover'));
});

View file

@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiPopover, EuiText } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React, { useState } from 'react';
import { EuiPopover, EuiText } from '@elastic/eui';
import styled from 'styled-components';
import { useTheme } from '../../../../../hooks/useTheme';
import {
TRACE_ID,
TRANSACTION_ID,
@ -54,6 +54,7 @@ function truncateMessage(errorMessage?: string) {
}
export const ErrorMarker: React.FC<Props> = ({ mark }) => {
const theme = useTheme();
const { urlParams } = useUrlParams();
const [isPopoverOpen, showPopover] = useState(false);
@ -63,7 +64,7 @@ export const ErrorMarker: React.FC<Props> = ({ mark }) => {
<Button
data-test-subj="popover"
clickable
color={theme.euiColorDanger}
color={theme.eui.euiColorDanger}
shape={Shape.square}
onClick={togglePopover}
/>

View file

@ -1,26 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AgentMarker renders 1`] = `
<Fragment>
<EuiToolTip
content={
<div>
<ForwardRef(styled.div)>
agent
</ForwardRef(styled.div)>
<ForwardRef(styled.div)>
1,000 μs
</ForwardRef(styled.div)>
</div>
<ThemeProvider
theme={[Function]}
>
<AgentMarker
mark={
Object {
"id": "agent",
"offset": 1000,
"type": "agentMark",
"verticalLine": true,
}
}
delay="regular"
id="agent"
position="top"
>
<Legend
clickable={true}
color="#98a2b3"
/>
</EuiToolTip>
</Fragment>
/>
</ThemeProvider>
`;

View file

@ -4,10 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { mount } from 'enzyme';
import React from 'react';
import { StickyContainer } from 'react-sticky';
import { mockMoment, toJson } from '../../../../utils/testHelpers';
import {
mountWithTheme,
mockMoment,
toJson,
} from '../../../../utils/testHelpers';
import { Timeline } from '.';
describe('Timeline', () => {
@ -50,7 +53,7 @@ describe('Timeline', () => {
],
};
const wrapper = mount(
const wrapper = mountWithTheme(
<StickyContainer>
<Timeline {...props} />
</StickyContainer>
@ -74,7 +77,7 @@ describe('Timeline', () => {
};
const mountTimeline = () =>
mount(
mountWithTheme(
<StickyContainer>
<Timeline {...props} />
</StickyContainer>

View file

@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { inRange } from 'lodash';
import React, { ReactNode } from 'react';
import { inRange } from 'lodash';
import { Sticky } from 'react-sticky';
import { XAxis, XYPlot } from 'react-vis';
import { useTheme } from '../../../../hooks/useTheme';
import { px } from '../../../../style/variables';
import { getDurationFormatter } from '../../../../utils/formatters';
import { Mark } from './';
@ -42,11 +42,12 @@ interface TimelineAxisProps {
topTraceDuration: number;
}
export function TimelineAxis({
export const TimelineAxis = ({
plotValues,
marks = [],
topTraceDuration,
}: TimelineAxisProps) {
}: TimelineAxisProps) => {
const theme = useTheme();
const { margins, tickValues, width, xDomain, xMax, xScale } = plotValues;
const tickFormatter = getDurationFormatter(xMax);
const xAxisTickValues = getXAxisTickValues(tickValues, topTraceDuration);
@ -59,7 +60,7 @@ export function TimelineAxis({
<div
style={{
position: 'absolute',
borderBottom: `1px solid ${theme.euiColorMediumShade}`,
borderBottom: `1px solid ${theme.eui.euiColorMediumShade}`,
height: px(margins.top),
zIndex: 2,
width: '100%',
@ -85,7 +86,7 @@ export function TimelineAxis({
tickFormat={(time?: number) => tickFormatter(time).formatted}
tickPadding={20}
style={{
text: { fill: theme.euiColorDarkShade },
text: { fill: theme.eui.euiColorDarkShade },
}}
/>
@ -106,4 +107,4 @@ export function TimelineAxis({
}}
</Sticky>
);
}
};

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React, { PureComponent } from 'react';
import React from 'react';
import { VerticalGridLines, XYPlot } from 'react-vis';
import { useTheme } from '../../../../hooks/useTheme';
import { Mark } from '../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks';
import { PlotValues } from './plotUtils';
@ -16,54 +16,51 @@ interface VerticalLinesProps {
topTraceDuration: number;
}
export class VerticalLines extends PureComponent<VerticalLinesProps> {
render() {
const { topTraceDuration, marks = [] } = this.props;
const {
width,
height,
margins,
xDomain,
tickValues,
} = this.props.plotValues;
export const VerticalLines = ({
topTraceDuration,
plotValues,
marks = [],
}: VerticalLinesProps) => {
const { width, height, margins, xDomain, tickValues } = plotValues;
const markTimes = marks
.filter((mark) => mark.verticalLine)
.map(({ offset }) => offset);
const markTimes = marks
.filter((mark) => mark.verticalLine)
.map(({ offset }) => offset);
return (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
}}
const theme = useTheme();
return (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
}}
>
<XYPlot
dontCheckIfEmpty
width={width}
height={height + margins.top}
margin={margins}
xDomain={xDomain}
>
<XYPlot
dontCheckIfEmpty
width={width}
height={height + margins.top}
margin={margins}
xDomain={xDomain}
>
<VerticalGridLines
tickValues={tickValues}
style={{ stroke: theme.euiColorLightestShade }}
/>
<VerticalGridLines
tickValues={tickValues}
style={{ stroke: theme.eui.euiColorLightestShade }}
/>
<VerticalGridLines
tickValues={markTimes}
style={{ stroke: theme.euiColorMediumShade }}
/>
<VerticalGridLines
tickValues={markTimes}
style={{ stroke: theme.eui.euiColorMediumShade }}
/>
{topTraceDuration > 0 && (
<VerticalGridLines
tickValues={[topTraceDuration]}
style={{ stroke: theme.euiColorMediumShade }}
/>
)}
</XYPlot>
</div>
);
}
}
{topTraceDuration > 0 && (
<VerticalGridLines
tickValues={[topTraceDuration]}
style={{ stroke: theme.eui.euiColorMediumShade }}
/>
)}
</XYPlot>
</div>
);
};

View file

@ -18,25 +18,24 @@ import {
fontSizes,
} from '../../../../style/variables';
import { Legend } from '../Legend';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { asAbsoluteDateTime } from '../../../../utils/formatters';
const TooltipElm = styled.div`
margin: 0 ${px(unit)};
transform: translateY(-50%);
border: 1px solid ${theme.euiColorLightShade};
background: ${theme.euiColorEmptyShade};
border: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
background: ${({ theme }) => theme.eui.euiColorEmptyShade};
border-radius: ${borderRadius};
font-size: ${fontSize};
color: ${theme.euiColorFullShade};
color: ${({ theme }) => theme.eui.euiColorFullShade};
`;
const Header = styled.div`
background: ${theme.euiColorLightestShade};
border-bottom: 1px solid ${theme.euiColorLightShade};
background: ${({ theme }) => theme.eui.euiColorLightestShade};
border-bottom: 1px solid ${({ theme }) => theme.eui.euiColorLightShade};
border-radius: ${borderRadius} ${borderRadius} 0 0;
padding: ${px(units.half)};
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
`;
const Content = styled.div`
@ -46,7 +45,7 @@ const Content = styled.div`
`;
const Footer = styled.div`
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
margin: ${px(units.half)};
font-size: ${fontSizes.small};
`;
@ -59,13 +58,13 @@ const LegendContainer = styled.div`
`;
const LegendGray = styled(Legend)`
color: ${theme.euiColorMediumShade};
color: ${({ theme }) => theme.eui.euiColorMediumShade};
padding-bottom: 0;
padding-right: ${px(units.half)};
`;
const Value = styled.div`
color: ${theme.euiColorDarkShade};
color: ${({ theme }) => theme.eui.euiColorDarkShade};
font-size: ${fontSize};
`;

View file

@ -13,8 +13,9 @@ import React, {
} from 'react';
import { Map, NavigationControl, Popup } from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { shade, tint } from 'polished';
import { EuiTheme } from '../../../../../../../observability/public';
import { useTheme } from '../../../../../hooks/useTheme';
import { ChoroplethToolTip } from './ChoroplethToolTip';
interface ChoroplethItem {
@ -47,8 +48,8 @@ const MAPBOX_STYLE =
const GEOJSON_SOURCE =
'https://vector.maps.elastic.co/files/world_countries_v1.geo.json?elastic_tile_service_tos=agree&my_app_name=ems-landing&my_app_version=7.2.0';
export function getProgressionColor(scale: number) {
const baseColor = euiLightVars.euiColorPrimary;
export function getProgressionColor(scale: number, theme: EuiTheme) {
const baseColor = theme.eui.euiColorPrimary;
const adjustedScale = 0.75 * scale + 0.05; // prevents pure black & white as min/max colors.
if (adjustedScale < 0.5) {
return tint(adjustedScale * 2, baseColor);
@ -66,8 +67,8 @@ const getMax = (items: ChoroplethItem[]) =>
Math.max(...items.map((item) => item.value));
export const ChoroplethMap: React.FC<Props> = (props) => {
const theme = useTheme();
const { items } = props;
const containerRef = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<Map | null>(null);
const popupRef = useRef<Popup | null>(null);
@ -215,7 +216,7 @@ export const ChoroplethMap: React.FC<Props> = (props) => {
const stops = items.map(({ key, value }) => [
key,
getProgressionColor(getValueScale(value)),
getProgressionColor(getValueScale(value), theme),
]);
const fillColor: mapboxgl.FillPaint['fill-color'] = {
@ -238,7 +239,7 @@ export const ChoroplethMap: React.FC<Props> = (props) => {
},
symbolLayer ? symbolLayer.id : undefined
);
}, [map, items, getValueScale]);
}, [map, items, theme, getValueScale]);
// side effect to only render the Popup when hovering a region with a matching item
useEffect(() => {

View file

@ -0,0 +1,14 @@
/*
* 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 { useContext } from 'react';
import { ThemeContext } from 'styled-components';
import { EuiTheme } from '../../../observability/public';
export function useTheme(): EuiTheme {
const theme = useContext(ThemeContext);
return theme;
}

View file

@ -6,12 +6,12 @@
/* global jest */
import { ReactWrapper } from 'enzyme';
import React from 'react';
import { ReactWrapper, mount, MountRendererProps } from 'enzyme';
import enzymeToJson from 'enzyme-to-json';
import { Location } from 'history';
import moment from 'moment';
import { Moment } from 'moment-timezone';
import React from 'react';
import { render, waitForElement } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { MemoryRouter } from 'react-router-dom';
@ -19,6 +19,7 @@ import { MemoryRouter } from 'react-router-dom';
import { APMConfig } from '../../server';
import { LocationProvider } from '../context/LocationContext';
import { PromiseReturnType } from '../../typings/common';
import { EuiThemeProvider } from '../../../observability/public';
import {
ESFilter,
ESSearchResponse,
@ -181,3 +182,27 @@ export async function inspectSearchParams(
}
export type SearchParamsMock = PromiseReturnType<typeof inspectSearchParams>;
export function renderWithTheme(
component: React.ReactNode,
params?: any,
{ darkMode = false } = {}
) {
return render(
<EuiThemeProvider darkMode={darkMode}>{component}</EuiThemeProvider>,
params
);
}
export function mountWithTheme(
tree: React.ReactElement<any>,
{ darkMode = false } = {}
) {
const WrappingThemeProvider = (props: any) => (
<EuiThemeProvider darkMode={darkMode}>{props.children}</EuiThemeProvider>
);
return mount(tree, {
wrappingComponent: WrappingThemeProvider,
} as MountRendererProps);
}