[Lens] Remove scss from annotations plugin, visualization-ui-components and gauge expression (#208891)

## Summary

part of https://github.com/elastic/kibana/issues/208908

Replaces scss to css-in-js. I've tested all the changes.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Marta Bondyra 2025-02-18 13:31:09 +01:00 committed by GitHub
parent 148d47ced1
commit 91a19c0969
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 234 additions and 251 deletions

View file

@ -7,7 +7,6 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import './index.scss';
import { isFieldLensCompatible } from '@kbn/visualization-ui-components';
import React, { useCallback, useEffect, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
@ -365,7 +364,6 @@ const AnnotationEditorControls = ({
>
<EuiFormRow
display="rowCompressed"
className="lnsRowCompressedMargin"
fullWidth
label={i18n.translate('eventAnnotationComponents.xyChart.annotation.tooltip', {
defaultMessage: 'Show additional fields',

View file

@ -1,15 +0,0 @@
.lnsRowCompressedMargin+.lnsRowCompressedMargin {
margin-top: $euiSizeS;
}
.lnsConfigPanelAnnotations__date .euiFormControlLayout__prepend {
min-width: $euiSize * 3.25; // makes both labels ("from" and "to") the same width
}
.lnsConfigPanelAnnotations__addButton {
margin-top: $euiSizeXS;
}
.lnsConfigPanelAnnotations__fieldPicker {
cursor: pointer;
}

View file

@ -19,7 +19,8 @@ import type {
import { QueryInputServices } from '@kbn/visualization-ui-components';
import moment from 'moment';
import { act } from 'react-dom/test-utils';
import { EuiButtonGroup } from '@elastic/eui';
import { EuiButtonGroup, EuiThemeProvider } from '@elastic/eui';
import { render, screen } from '@testing-library/react';
jest.mock('@kbn/unified-search-plugin/public', () => ({
QueryStringInput: () => {
@ -70,18 +71,27 @@ describe('AnnotationsPanel', () => {
data: {},
} as QueryInputServices;
describe('Dimension Editor', () => {
test('shows correct options for line annotations', () => {
const component = mount(
const mountWithThemeProvider = (
propsOverrides: Partial<typeof AnnotationEditorControls> = {}
) => {
return mount(
<EuiThemeProvider>
<AnnotationEditorControls
annotation={customLineStaticAnnotation}
onAnnotationChange={() => {}}
dataView={{} as DataView}
dataView={mockDataView}
getDefaultRangeEnd={() => ''}
queryInputServices={mockQueryInputServices}
appName="myApp"
{...propsOverrides}
/>
);
</EuiThemeProvider>
);
};
describe('Dimension Editor', () => {
test('shows correct options for line annotations', () => {
const component = mountWithThemeProvider();
expect(
component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').prop('selected')
@ -124,16 +134,7 @@ describe('AnnotationsPanel', () => {
lineWidth: 3,
};
const component = mount(
<AnnotationEditorControls
annotation={rangeAnnotation}
onAnnotationChange={() => {}}
dataView={{} as DataView}
getDefaultRangeEnd={() => ''}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
);
const component = mountWithThemeProvider({ annotation: rangeAnnotation });
expect(
component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').prop('selected')
@ -156,21 +157,26 @@ describe('AnnotationsPanel', () => {
expect(component.find('[data-test-subj="lns-xyAnnotation-fillStyle"]').exists()).toBeTruthy();
});
test('calculates correct endTimstamp and transparent color when switching for range annotation and back', async () => {
test('calculates correct endTimestamp and transparent color when switching for range annotation and back', async () => {
const onAnnotationChange = jest.fn();
const rangeEndTimestamp = new Date().toISOString();
const component = mount(
<AnnotationEditorControls
annotation={customLineStaticAnnotation}
onAnnotationChange={onAnnotationChange}
dataView={{} as DataView}
getDefaultRangeEnd={() => rangeEndTimestamp}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
const { rerender } = render(
<EuiThemeProvider>
<AnnotationEditorControls
annotation={customLineStaticAnnotation}
onAnnotationChange={onAnnotationChange}
dataView={mockDataView}
getDefaultRangeEnd={() => rangeEndTimestamp}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
</EuiThemeProvider>
);
component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click');
act(() => {
screen.getByTestId('lns-xyAnnotation-rangeSwitch').click();
});
const expectedRangeAnnotation: RangeEventAnnotationConfig = {
color: '#FF00001A',
@ -185,19 +191,30 @@ describe('AnnotationsPanel', () => {
},
};
expect(onAnnotationChange).toBeCalledWith<EventAnnotationConfig[]>(expectedRangeAnnotation);
expect(onAnnotationChange).toHaveBeenCalledWith(expectedRangeAnnotation);
rerender(
<EuiThemeProvider>
<AnnotationEditorControls
annotation={expectedRangeAnnotation}
onAnnotationChange={onAnnotationChange}
dataView={mockDataView}
getDefaultRangeEnd={() => rangeEndTimestamp}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
</EuiThemeProvider>
);
expect(screen.getByTestId('lns-xyAnnotation-rangeSwitch').getAttribute('aria-checked')).toBe(
'true'
);
act(() => {
component.setProps({ annotation: expectedRangeAnnotation });
screen.getByTestId('lns-xyAnnotation-rangeSwitch').click();
});
expect(
component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked')
).toEqual(true);
component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click');
expect(onAnnotationChange).toBeCalledWith<EventAnnotationConfig[]>({
expect(onAnnotationChange).toHaveBeenCalledWith({
color: '#FF0000',
id: 'ann1',
isHidden: undefined,
@ -227,16 +244,7 @@ describe('AnnotationsPanel', () => {
filter: { type: 'kibana_query', query: '', language: 'kuery' },
};
const component = mount(
<AnnotationEditorControls
annotation={annotation}
onAnnotationChange={() => {}}
dataView={mockDataView}
getDefaultRangeEnd={() => ''}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
);
const component = mountWithThemeProvider({ annotation });
expect(
component.find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]').exists()
@ -264,16 +272,10 @@ describe('AnnotationsPanel', () => {
test('should prefill timeField with the default time field when switching to query based annotations', () => {
const onAnnotationChange = jest.fn();
const component = mount(
<AnnotationEditorControls
annotation={customLineStaticAnnotation}
onAnnotationChange={onAnnotationChange}
dataView={mockDataView}
getDefaultRangeEnd={() => ''}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
);
const component = mountWithThemeProvider({
annotation: customLineStaticAnnotation,
onAnnotationChange,
});
act(() => {
component
@ -291,16 +293,10 @@ describe('AnnotationsPanel', () => {
test('should avoid to retain specific manual configurations when switching to query based annotations', () => {
const onAnnotationChange = jest.fn();
const component = mount(
<AnnotationEditorControls
annotation={customLineStaticAnnotation}
onAnnotationChange={onAnnotationChange}
dataView={mockDataView}
getDefaultRangeEnd={() => ''}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
);
const component = mountWithThemeProvider({
annotation: customLineStaticAnnotation,
onAnnotationChange,
});
act(() => {
component
@ -336,16 +332,7 @@ describe('AnnotationsPanel', () => {
const onAnnotationChange = jest.fn();
const component = mount(
<AnnotationEditorControls
annotation={annotation}
onAnnotationChange={onAnnotationChange}
dataView={mockDataView}
getDefaultRangeEnd={() => ''}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
);
const component = mountWithThemeProvider({ annotation, onAnnotationChange });
act(() => {
component
@ -379,16 +366,7 @@ describe('AnnotationsPanel', () => {
const onAnnotationChange = jest.fn();
const component = mount(
<AnnotationEditorControls
annotation={annotation}
onAnnotationChange={onAnnotationChange}
dataView={mockDataView}
getDefaultRangeEnd={() => ''}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
);
const component = mountWithThemeProvider({ annotation, onAnnotationChange });
act(() => {
component
@ -412,16 +390,11 @@ describe('AnnotationsPanel', () => {
test('should fallback to the first date field available in the dataView if not time-based', () => {
const onAnnotationChange = jest.fn();
const component = mount(
<AnnotationEditorControls
annotation={customLineStaticAnnotation}
onAnnotationChange={onAnnotationChange}
dataView={{ ...mockDataView, timeFieldName: '' } as DataView}
getDefaultRangeEnd={() => ''}
queryInputServices={mockQueryInputServices}
appName="myApp"
/>
);
const component = mountWithThemeProvider({
annotation: customLineStaticAnnotation,
onAnnotationChange,
dataView: { ...mockDataView, timeFieldName: '' } as DataView,
});
act(() => {
component

View file

@ -70,7 +70,6 @@ export const ConfigPanelQueryAnnotation = ({
<EuiFormRow
hasChildLabel
display="rowCompressed"
className="lnsRowCompressedMargin"
fullWidth
label={i18n.translate('eventAnnotationComponents.xyChart.annotation.queryInput', {
defaultMessage: 'Annotation query',

View file

@ -9,7 +9,7 @@
import { i18n } from '@kbn/i18n';
import React from 'react';
import { EuiFormRow, EuiSwitch, EuiText, EuiDatePicker } from '@elastic/eui';
import { EuiFormRow, EuiSwitch, EuiText, EuiDatePicker, UseEuiTheme } from '@elastic/eui';
import moment from 'moment';
import type {
PointInTimeEventAnnotationConfig,
@ -31,7 +31,7 @@ export const ConfigPanelApplyAsRangeSwitch = ({
}) => {
const isRange = isRangeAnnotationConfig(annotation);
return (
<EuiFormRow display="columnCompressed" className="lnsRowCompressedMargin">
<EuiFormRow display="columnCompressed">
<EuiSwitch
data-test-subj="lns-xyAnnotation-rangeSwitch"
label={
@ -84,6 +84,15 @@ export const ConfigPanelApplyAsRangeSwitch = ({
);
};
const RowCompressedMarginStyle = ({ euiTheme }: UseEuiTheme) => `
& + .lnsRowCompressedMargin {
margin-top: ${euiTheme.size.s};
}
.euiFormControlLayout__prepend {
min-width: 50px; // makes both labels ("from" and "to") the same width
}
`;
export const ConfigPanelRangeDatePicker = ({
value,
label,
@ -102,9 +111,10 @@ export const ConfigPanelRangeDatePicker = ({
return (
<EuiFormRow
display="rowCompressed"
fullWidth
label={label}
className="lnsConfigPanelAnnotations__date lnsRowCompressedMargin"
fullWidth
className="lnsRowCompressedMargin"
css={RowCompressedMarginStyle}
>
<EuiDatePicker
compressed

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { EuiFlexItem, EuiPanel, EuiText, htmlIdGenerator } from '@elastic/eui';
import { EuiFlexItem, EuiPanel, EuiText, UseEuiTheme, htmlIdGenerator } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useState } from 'react';
import fastIsEqual from 'fast-deep-equal';
@ -43,6 +43,10 @@ function removeNewEmptyField(v: string) {
const generateId = htmlIdGenerator();
const NewBucketButtonStyles = ({ euiTheme }: UseEuiTheme) => `
margin-top: ${euiTheme.size.xs};
`;
interface LocalFieldEntry {
name: string;
id: string;
@ -78,7 +82,7 @@ export function TooltipSection({ currentConfig, setConfig, dataView }: FieldInpu
const addFieldButton = (
<NewBucketButton
className="lnsConfigPanelAnnotations__addButton"
css={NewBucketButtonStyles}
data-test-subj={`lnsXY-annotation-tooltip-add_field`}
onClick={() => {
setFields([...currentFields, { name: '', id: generateId() }]);

View file

@ -3,7 +3,5 @@
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0",
"sideEffects": [
"*.scss"
]
"sideEffects": false
}

View file

@ -1,15 +1,11 @@
{
"extends": "../../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"@emotion/react/types/css-prop",
]
"outDir": "target/types"
},
"include": [
"**/*",
"../../../../../typings/**/*",
],
"exclude": [
"target/**/*"

View file

@ -10,7 +10,6 @@
import React from 'react';
import { EuiColorPaletteDisplay } from '@elastic/eui';
import { css } from '@emotion/react';
import { euiThemeVars } from '@kbn/ui-theme';
import type { AccessorConfig } from './types';
export function PaletteIndicator({ accessorConfig }: { accessorConfig: AccessorConfig }) {
@ -25,12 +24,7 @@ export function PaletteIndicator({ accessorConfig }: { accessorConfig: AccessorC
`}
>
<EuiColorPaletteDisplay
className="lnsLayerPanel__palette"
css={css`
height: ${euiThemeVars.euiSizeXS} / 2;
border-radius: 0 0 (${euiThemeVars.euiBorderRadius} - 1px)
(${euiThemeVars.euiBorderRadius} - 1px);
&::after {
border: none;
}

View file

@ -1,29 +0,0 @@
.lnsDimensionEditorSection {
padding-top: $euiSize;
padding-bottom: $euiSize;
}
.lnsDimensionEditorSection:first-child {
padding-top: 0;
}
.lnsDimensionEditorSection:first-child .lnsDimensionEditorSection__border {
display: none;
}
.lnsDimensionEditorSection__border {
position: relative;
&:before {
content: '';
position: absolute;
top: -$euiSize;
right: -$euiSize;
left: -$euiSize;
border-top: 1px solid $euiColorLightShade;
}
}
.lnsDimensionEditorSection__heading {
padding-bottom: 16px;
}

View file

@ -7,9 +7,8 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { EuiTitle } from '@elastic/eui';
import { EuiTitle, UseEuiTheme } from '@elastic/eui';
import React from 'react';
import './dimension_editor_section.scss';
export const DimensionEditorSection = ({
children,
@ -19,10 +18,14 @@ export const DimensionEditorSection = ({
children?: React.ReactNode | React.ReactNode[];
}) => {
return (
<div className="lnsDimensionEditorSection">
<div className="lnsDimensionEditorSection__border" />
<div css={DimensionEditorSectionStyles.self}>
<div css={DimensionEditorSectionStyles.divider} />
{title && (
<EuiTitle size="xxs" className="lnsDimensionEditorSection__heading">
<EuiTitle
size="xxs"
data-test-subj="lnsDimensionEditorSectionHeading"
css={DimensionEditorSectionStyles.heading}
>
<h3>{title}</h3>
</EuiTitle>
)}
@ -30,3 +33,27 @@ export const DimensionEditorSection = ({
</div>
);
};
const DimensionEditorSectionStyles = {
self: ({ euiTheme }: UseEuiTheme) => `
padding-bottom: ${euiTheme.size.base};
padding-top: ${euiTheme.size.base};
&:first-child {
padding-top: 0;
}
`,
divider: ({ euiTheme }: UseEuiTheme) => `
position: relative;
&:before {
content: '';
position: absolute;
top: -${euiTheme.size.base};
right: -${euiTheme.size.base};
left: -${euiTheme.size.base};
border-top: 1px solid ${euiTheme.colors.lightShade};
}
`,
heading: ({ euiTheme }: UseEuiTheme) => `
padding-bottom: ${euiTheme.size.base};
`,
};

View file

@ -14,7 +14,6 @@ interface NewBucketButtonProps {
label: string;
onClick: () => void;
isDisabled?: boolean;
className?: string;
'data-test-subj'?: string;
}
@ -22,7 +21,6 @@ export const NewBucketButton = ({
label,
onClick,
isDisabled,
className,
'data-test-subj': dataTestSubj = 'lns-newBucket-add',
}: NewBucketButtonProps) => (
<EuiButtonEmpty
@ -32,7 +30,6 @@ export const NewBucketButton = ({
onClick={onClick}
isDisabled={isDisabled}
flush="left"
className={className}
>
{label}
</EuiButtonEmpty>

View file

@ -1,7 +0,0 @@
.lnFieldPicker__option--incompatible {
color: $euiColorLightShade;
}
.lnFieldPicker__option--nonExistant {
background-color: $euiColorLightestShade;
}

View file

@ -7,12 +7,10 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import './field_picker.scss';
import React from 'react';
import { i18n } from '@kbn/i18n';
import classNames from 'classnames';
import { comboBoxFieldOptionMatcher } from '@kbn/field-utils';
import { EuiComboBox, EuiComboBoxOptionOption, EuiComboBoxProps } from '@elastic/eui';
import { EuiComboBox, EuiComboBoxOptionOption, EuiComboBoxProps, useEuiTheme } from '@elastic/eui';
import { FieldIcon } from '@kbn/field-utils/src/components/field_icon';
import { calculateWidthFromCharCount } from '@kbn/calculate-width-from-char-count';
import type { FieldOptionValue, FieldOption } from './types';
@ -43,6 +41,7 @@ export function FieldPicker<T extends FieldOptionValue = FieldOptionValue>(
...rest
} = props;
const { euiTheme } = useEuiTheme();
const [selectedOption, setSelectedOption] = React.useState(activeField);
let maxLabelLength = 0;
@ -63,10 +62,10 @@ export function FieldPicker<T extends FieldOptionValue = FieldOptionValue>(
className="eui-alignMiddle"
/>
) : null,
className: classNames({
'lnFieldPicker__option--incompatible': !fieldOption.compatible,
'lnFieldPicker__option--nonExistant': !fieldOptionExists,
}),
css: {
color: !fieldOption.compatible ? euiTheme.colors.lightShade : undefined,
backgroundColor: !fieldOptionExists ? euiTheme.colors.lightestShade : undefined,
},
};
}),
};
@ -77,10 +76,10 @@ export function FieldPicker<T extends FieldOptionValue = FieldOptionValue>(
prepend: otherAttr.value.dataType ? (
<FieldIcon type={otherAttr.value.dataType} fill="none" className="eui-alignMiddle" />
) : null,
className: classNames({
'lnFieldPicker__option--incompatible': !compatible,
'lnFieldPicker__option--nonExistant': !exists,
}),
css: {
color: !compatible ? euiTheme.colors.lightShade : undefined,
backgroundColor: !exists ? euiTheme.colors.lightestShade : undefined,
},
};
});

View file

@ -1,11 +0,0 @@
// TODO - use emotion instead
.filterQueryInput__popoverButton {
@include euiTextBreakWord;
@include euiFontSizeS;
min-height: $euiSizeXL;
width: 100%;
}
.filterQueryInput__popover {
width: $euiSize * 60;
}

View file

@ -18,11 +18,14 @@ import {
EuiFlexGroup,
EuiIconTip,
EuiPopoverProps,
euiTextBreakWord,
useEuiFontSize,
UseEuiTheme,
} from '@elastic/eui';
import type { DataViewBase, Query } from '@kbn/es-query';
import { css } from '@emotion/react';
import { QueryInput, validateQuery } from '.';
import type { QueryInputServices } from '.';
import './filter_query_input.scss';
const filterByLabel = i18n.translate('visualizationUiComponents.filterQueryInput.label', {
defaultMessage: 'Filter by',
@ -46,6 +49,13 @@ export interface FilterQueryInputProps {
appName: string;
}
const LinkStyles = ({ euiTheme }: UseEuiTheme) => `
${euiTextBreakWord()};
${useEuiFontSize('s')};
min-height: ${euiTheme.size.xl};
width: 100%;
`;
export function FilterQueryInput({
inputFilter,
onChange,
@ -100,6 +110,11 @@ export function FilterQueryInput({
isOpen={filterPopoverOpen}
closePopover={onClosePopup}
display="block"
panelProps={{
css: css`
width: 960px;
`,
}}
panelClassName="filterQueryInput__popover"
initialFocus={dataTestSubj ? `textarea[data-test-subj='${dataTestSubj}']` : undefined}
button={
@ -113,6 +128,7 @@ export function FilterQueryInput({
onClick={() => {
setFilterPopoverOpen(!filterPopoverOpen);
}}
css={LinkStyles}
color={isInputFilterValid ? 'text' : 'danger'}
title={i18n.translate(
'visualizationUiComponents.filterQueryInput.clickToEdit',

View file

@ -3,7 +3,5 @@
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0",
"sideEffects": [
"*.scss"
]
"sideEffects": false
}

View file

@ -11,6 +11,7 @@
},
"include": [
"**/*",
"../../../../../typings/**/*"
],
"exclude": [
"target/**/*"

View file

@ -10,14 +10,14 @@
import React, { ChangeEvent, FormEvent } from 'react';
import type { EventAnnotationGroupConfig } from '@kbn/event-annotation-common';
import { getDefaultManualAnnotation } from '@kbn/event-annotation-common';
import { ReactWrapper } from 'enzyme';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { ComponentType, ReactWrapper, mount } from 'enzyme';
import { GroupEditorControls } from './group_editor_controls';
import { EuiTextAreaProps, EuiTextProps } from '@elastic/eui';
import { EuiTextAreaProps, EuiTextProps, EuiThemeProvider } from '@elastic/eui';
import type { DataView } from '@kbn/data-views-plugin/common';
import { act } from 'react-dom/test-utils';
import type { QueryInputServices } from '@kbn/visualization-ui-components';
import { AnnotationEditorControls } from '@kbn/event-annotation-components';
import { I18nProvider } from '@kbn/i18n-react';
jest.mock('@elastic/eui', () => {
return {
@ -53,8 +53,17 @@ describe('event annotation group editor', () => {
beforeEach(async () => {
updateMock = jest.fn();
setSelectedAnnotationMock = jest.fn();
const wrappingComponent: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
return (
<EuiThemeProvider>
<I18nProvider>{children}</I18nProvider>
</EuiThemeProvider>
);
};
wrapper = mountWithIntl(
wrapper = mount(
<GroupEditorControls
group={group}
update={updateMock}
@ -72,7 +81,10 @@ describe('event annotation group editor', () => {
queryInputServices={{} as QueryInputServices}
showValidation={false}
isAdHocDataView={() => false}
/>
/>,
{
wrappingComponent: wrappingComponent as ComponentType<{}>,
}
);
await act(async () => {

View file

@ -33,7 +33,6 @@
"@kbn/event-annotation-common",
"@kbn/lens-plugin",
"@kbn/ui-theme",
"@kbn/test-jest-helpers",
"@kbn/expressions-plugin",
"@kbn/es-query",
"@kbn/core-ui-settings-browser",

View file

@ -1,12 +0,0 @@
.gauge__wrapper {
flex: 1 1 0;
display: flex;
flex-direction: column;
// it is used for rendering at `Canvas`.
height: 100%;
}
.gauge__label {
width: 100%;
text-align: center;
}

View file

@ -8,6 +8,7 @@
*/
import React, { FC, useCallback } from 'react';
import { css } from '@emotion/react';
import { Chart, Bullet, BulletProps, Settings } from '@elastic/charts';
import { useEuiTheme } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
@ -38,10 +39,7 @@ import {
computeMinMax,
} from './utils';
import { getGaugeIconByType } from './utils/icons';
import './index.scss';
import { GaugeCentralMajorMode, GaugeTicksPosition } from '../../common/types';
import './gauge.scss';
import { useGaugeSizeByType } from './utils/use_gauge_size_by_type';
declare global {
@ -366,7 +364,14 @@ export const GaugeComponent: FC<GaugeRenderProps> = ({
};
return (
<div className="gauge__wrapper">
<div
css={css`
flex: 1 1 0;
display: flex;
flex-direction: column;
height: 100%; /* it is used for rendering in Canvas.*/
`}
>
<Chart {...getOverridesFor(overrides, 'chart')}>
<Settings
noResults={<EmptyPlaceholder icon={icon} renderComplete={onRenderChange} />}
@ -413,7 +418,16 @@ export const GaugeComponent: FC<GaugeRenderProps> = ({
]}
/>
</Chart>
{commonLabel && <div className="gauge__label">{commonLabel}</div>}
{commonLabel && (
<div
css={css`
width: 100%;
text-align: center;
`}
>
{commonLabel}
</div>
)}
</div>
);
};

View file

@ -1,9 +0,0 @@
.gauge-container {
height: 100%;
width: 100%;
// the FocusTrap is adding extra divs which are making the visualization redraw twice
// with a visible glitch. This make the chart library resilient to this extra reflow
overflow: hidden;
user-select: text;
@include euiScrollBar;
}

View file

@ -9,6 +9,7 @@
import { i18n } from '@kbn/i18n';
import React from 'react';
import { css } from '@emotion/react';
import { render, unmountComponentAtNode } from 'react-dom';
import { PersistedState } from '@kbn/visualizations-plugin/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
@ -93,7 +94,18 @@ export const gaugeRenderer: (
const { GaugeComponent } = await import('../components/gauge_component');
render(
<KibanaRenderContextProvider {...core}>
<div className="gauge-container" data-test-subj="gaugeChart">
<div
className="eui-scrollBar"
data-test-subj="gaugeChart"
css={css`
height: 100%;
width: 100%;
// the FocusTrap is adding extra divs which are making the visualization redraw twice
// with a visible glitch. This make the chart library resilient to this extra reflow
overflow: hidden;
user-select: text;
`}
>
<GaugeComponent
{...config}
setChartSize={setChartSize}

View file

@ -62,6 +62,7 @@ import { DataLayers } from './data_layers';
import { SplitChart } from './split_chart';
import { LegendSize } from '@kbn/visualizations-plugin/common';
import type { LayerCellValueActions } from '../types';
import { EuiThemeProvider } from '@elastic/eui';
const onClickValue = jest.fn();
const onClickMultiValue = jest.fn();
@ -3192,21 +3193,31 @@ describe('XYChart component', () => {
},
]),
]);
const component = mount(<XYChart {...defaultProps} args={args} />);
const component = mount(
<EuiThemeProvider>
<XYChart {...defaultProps} args={args} />
</EuiThemeProvider>
);
const groupedAnnotation = component.find(LineAnnotation);
expect(groupedAnnotation.length).toEqual(1);
// styles are passed because they are shared, dataValues is rounded to the interval
expect(groupedAnnotation).toMatchSnapshot();
// renders numeric icon for grouped annotations
const marker = mount(<div>{groupedAnnotation.prop('marker') as React.ReactNode}</div>);
const marker = mount(
<EuiThemeProvider>
<div>{groupedAnnotation.prop('marker') as React.ReactNode}</div>
</EuiThemeProvider>
);
const numberIcon = marker.find('NumberIcon');
expect(numberIcon.length).toEqual(1);
expect(numberIcon.text()).toEqual('3');
// checking tooltip
const renderLinks = mount(
<div>{(groupedAnnotation.prop('customTooltip') as Function)!()}</div>
<EuiThemeProvider>
<div>{(groupedAnnotation.prop('customTooltip') as Function)!()}</div>
</EuiThemeProvider>
);
expect(renderLinks.text()).toEqual(
'Event 1Mar 18, 2022 @ 04:25:00.000Event 2Mar 18, 2022 @ 04:25:00.020Event 3Mar 18, 2022 @ 04:25:00.001'

View file

@ -11,7 +11,6 @@ import { FtrService } from '../ftr_provider_context';
export class AnnotationEditorPageObject extends FtrService {
private readonly testSubjects = this.ctx.getService('testSubjects');
private readonly find = this.ctx.getService('find');
private readonly retry = this.ctx.getService('retry');
/**
@ -58,9 +57,7 @@ export class AnnotationEditorPageObject extends FtrService {
const queryInput = await this.testSubjects.find('annotation-query-based-query-input');
await queryInput.type(config.query);
const titles = await this.find.allByCssSelector(
'.euiFlyout h3.lnsDimensionEditorSection__heading'
);
const titles = await this.testSubjects.findAll(`lnsDimensionEditorSectionHeading`);
const lastTitle = titles[titles.length - 1];
await lastTitle.click(); // close query input pop-up
await lastTitle.focus(); // scroll down to the bottom of the section

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { ReactWrapper, ShallowWrapper, ComponentType } from 'enzyme';
import { ReactWrapper, ShallowWrapper, ComponentType, mount } from 'enzyme';
import React, { ChangeEvent } from 'react';
import { screen, act, render, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
@ -17,6 +17,7 @@ import {
EuiRange,
EuiSelect,
EuiComboBoxProps,
EuiThemeProvider,
} from '@elastic/eui';
import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks';
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
@ -26,7 +27,6 @@ import {
FormBasedDimensionEditorComponent,
FormBasedDimensionEditorProps,
} from './dimension_panel';
import { mount } from 'enzyme';
import { IUiSettingsClient, HttpSetup, CoreStart, NotificationsStart } from '@kbn/core/public';
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
import { useExistingFieldsReader } from '@kbn/unified-field-list/src/hooks/use_existing_fields';
@ -167,15 +167,22 @@ const bytesColumn: GenericIndexPatternColumn = {
params: { format: { id: 'bytes' } },
};
const services = coreMock.createStart() as unknown as LensAppServices;
const wrappingComponent: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
return (
<KibanaContextProvider services={coreMock.createStart() as unknown as LensAppServices}>
<EuiThemeProvider>{children}</EuiThemeProvider>
</KibanaContextProvider>
);
};
function mountWithServices(component: React.ReactElement): ReactWrapper {
return mount(component, {
// This is an elegant way to wrap a component in Enzyme
// preserving the root at the component level rather than
// at the wrapper one
wrappingComponent: KibanaContextProvider as ComponentType<{}>,
wrappingComponentProps: { services },
wrappingComponent: wrappingComponent as ComponentType<{}>,
});
}
@ -288,7 +295,11 @@ describe('FormBasedDimensionEditor', () => {
const Wrapper: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
return <KibanaContextProvider services={services}>{children}</KibanaContextProvider>;
return (
<KibanaContextProvider services={coreMock.createStart() as unknown as LensAppServices}>
{children}
</KibanaContextProvider>
);
};
const rtlRender = render(