diff --git a/src/platform/packages/shared/kbn-chart-icons/src/assets/drop_illustration.tsx b/src/platform/packages/shared/kbn-chart-icons/src/assets/drop_illustration.tsx index ae19bf28f26ec..f66ab4f7d37fc 100644 --- a/src/platform/packages/shared/kbn-chart-icons/src/assets/drop_illustration.tsx +++ b/src/platform/packages/shared/kbn-chart-icons/src/assets/drop_illustration.tsx @@ -8,38 +8,93 @@ */ import React from 'react'; -import { EuiIconProps } from '@elastic/eui'; - -export const DropIllustration = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} - - - - - - - - - -); +import { EuiIconProps, useEuiTheme } from '@elastic/eui'; +import { css, keyframes } from '@emotion/react'; + +export const DropIllustration = ({ title, titleId, ...props }: Omit) => { + const { euiTheme } = useEuiTheme(); + return ( + + {title ? {title} : null} + + + + + + + + + + ); +}; + +const pulseArrow = keyframes` + 0% { + transform: translateY(0%); + } + + 65% { + transform: translateY(0%); + } + + 72% { + transform: translateY(10%); + } + + 79% { + transform: translateY(7%); + } + + 86% { + transform: translateY(10%); + } + + 95% { + transform: translateY(0); + } +`; + +const pulseContinuous = keyframes` + 0% { + transform: translateY(10%); + } + 25% { + transform: translateY(15%); + } + 50% { + transform: translateY(10%); + } + 75% { + transform: translateY(15%); + } + 100% { + transform: translateY(10%); + } +`; diff --git a/src/platform/packages/shared/kbn-chart-icons/tsconfig.json b/src/platform/packages/shared/kbn-chart-icons/tsconfig.json index cc843830192f8..eaf06a6cb0cee 100644 --- a/src/platform/packages/shared/kbn-chart-icons/tsconfig.json +++ b/src/platform/packages/shared/kbn-chart-icons/tsconfig.json @@ -2,16 +2,11 @@ "extends": "../../../../../tsconfig.base.json", "compilerOptions": { "outDir": "target/types", - "types": [ - "jest", - "node", - "react", - "@emotion/css/types" - ], }, "include": [ "**/*.ts", "**/*.tsx", + "../../../../../typings/**/*" ], "kbn_references": [ "@kbn/ui-theme" diff --git a/src/platform/packages/shared/kbn-visualization-ui-components/components/dimension_buttons/dimension_button.tsx b/src/platform/packages/shared/kbn-visualization-ui-components/components/dimension_buttons/dimension_button.tsx index 2f957b2e00ac3..3163f89e7dfd1 100644 --- a/src/platform/packages/shared/kbn-visualization-ui-components/components/dimension_buttons/dimension_button.tsx +++ b/src/platform/packages/shared/kbn-visualization-ui-components/components/dimension_buttons/dimension_button.tsx @@ -75,9 +75,12 @@ function DimensionButtonImpl({ -
+
css` + ${euiBreakpoint(euiThemeContext, ['m', 'l', 'xl'])} { + margin-right: ${euiThemeContext.euiTheme.size.m}; + position: relative; + &:after { + border-right: ${euiThemeContext.euiTheme.border.thin}; + bottom: 0; + content: ''; + display: block; + pointer-events: none; + position: absolute; + right: -${euiThemeContext.euiTheme.size.s}; + top: 0; + } + } +`; + function getLensTopNavConfig(options: { isByValueMode: boolean; actions: LensTopNavActions; @@ -123,7 +141,10 @@ function getLensTopNavConfig(options: { contextFromEmbeddable, isByValueMode, } = options; - const topNavMenu: TopNavMenuData[] = []; + + const topNavMenu: Array< + TopNavMenuData | ({ css: ({ euiTheme }: UseEuiTheme) => SerializedStyles } & TopNavMenuData) + > = []; const showSaveAndReturn = actions.saveAndReturn.visible; @@ -150,13 +171,13 @@ function getLensTopNavConfig(options: { values: { contextOriginatingApp }, }), run: actions.goBack.execute, - className: 'lnsNavItem__withDivider', testId: 'lnsApp_goBackToAppButton', description: i18n.translate('xpack.lens.app.goBackLabel', { defaultMessage: `Go back to {contextOriginatingApp}`, values: { contextOriginatingApp }, }), disableButton: !actions.goBack.enabled, + css: navItemWithDividerStyles, }); } @@ -169,12 +190,12 @@ function getLensTopNavConfig(options: { label: exploreDataInDiscoverLabel, run: actions.getUnderlyingDataUrl.execute, testId: 'lnsApp_openInDiscover', - className: 'lnsNavItem__withDivider', description: exploreDataInDiscoverLabel, disableButton: !actions.getUnderlyingDataUrl.enabled, tooltip: actions.getUnderlyingDataUrl.tooltip, target: '_blank', href: actions.getUnderlyingDataUrl.getLink?.(), + css: navItemWithDividerStyles, }); } @@ -210,11 +231,11 @@ function getLensTopNavConfig(options: { defaultMessage: 'Settings', }), run: actions.openSettings.execute, - className: 'lnsNavItem__withDivider', testId: 'lnsApp_settingsButton', description: i18n.translate('xpack.lens.app.settingsAriaLabel', { defaultMessage: 'Open the Lens settings menu', }), + css: navItemWithDividerStyles, }); if (actions.cancel.visible) { diff --git a/x-pack/platform/plugins/shared/lens/public/app_plugin/mounter.tsx b/x-pack/platform/plugins/shared/lens/public/app_plugin/mounter.tsx index bc7f3a99686ad..7042117098395 100644 --- a/x-pack/platform/plugins/shared/lens/public/app_plugin/mounter.tsx +++ b/x-pack/platform/plugins/shared/lens/public/app_plugin/mounter.tsx @@ -398,8 +398,6 @@ export async function mountApp( window.dispatchEvent(new HashChangeEvent('hashchange')); }); - params.element.classList.add('lnsAppWrapper'); - render( diff --git a/x-pack/platform/plugins/shared/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx b/x-pack/platform/plugins/shared/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx index 2e8ff8390673f..bc938991d63b7 100644 --- a/x-pack/platform/plugins/shared/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx +++ b/x-pack/platform/plugins/shared/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx @@ -116,6 +116,7 @@ export const FlyoutWrapper = ({ margin-left: -${euiThemeVars.euiFormMaxWidth}; pointer-events: none; .euiFlyoutBody__overflow { + transform: initial; -webkit-mask-image: none; padding-left: inherit; margin-left: inherit; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/common/datapanel.styles.ts b/x-pack/platform/plugins/shared/lens/public/datasources/common/datapanel.styles.ts new file mode 100644 index 0000000000000..29bac5630c265 --- /dev/null +++ b/x-pack/platform/plugins/shared/lens/public/datasources/common/datapanel.styles.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UseEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; + +export const dataPanelStyles = ({ euiTheme }: UseEuiTheme) => { + return css` + padding: ${euiTheme.size.base} ${euiTheme.size.base} 0; + .unifiedFieldListItemButton.kbnFieldButton { + background: none; + box-shadow: none; + margin-bottom: calc(${euiTheme.size.xs} / 2); + } + .unifiedFieldListItemButton__dragging { + background: ${euiTheme.colors.backgroundBasePlain}; + } + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/common/field_item.scss b/x-pack/platform/plugins/shared/lens/public/datasources/common/field_item.scss deleted file mode 100644 index b9927ae4e7bf8..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/datasources/common/field_item.scss +++ /dev/null @@ -1,4 +0,0 @@ -.lnsFieldItem__fieldPanel { - min-width: 260px; - max-width: 300px; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/common/field_item.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/common/field_item.tsx index 51b4a07536f29..d1ab26a5cb645 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/common/field_item.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/common/field_item.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './field_item.scss'; - import React, { useCallback, useState, useMemo } from 'react'; import { EuiText, EuiButton, EuiPopoverFooter } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -251,6 +249,10 @@ export function InnerFieldItem(props: FieldItemProps) { isOpen={infoIsOpen} closePopover={closePopover} panelClassName="lnsFieldItem__fieldPanel" + panelStyle={{ + minWidth: '260px', + maxWidth: '300px', + }} initialFocus=".lnsFieldItem__fieldPanel" data-test-subj="lnsFieldListPanelField" panelProps={{ diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.scss b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.scss deleted file mode 100644 index 52582d5b07a40..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.scss +++ /dev/null @@ -1,27 +0,0 @@ -.lnsInnerIndexPatternDataPanel { - width: 100%; - height: 100%; - padding: $euiSize $euiSize 0; - .unifiedFieldListItemButton.kbnFieldButton { - background: none; - box-shadow: none; - margin-bottom: calc($euiSizeXS / 2); - } - .unifiedFieldListItemButton__dragging { - background: $euiColorBackgroundBasePlain; - } -} - -.lnsInnerIndexPatternDataPanel__switcher { - min-width: 0; -} - -.lnsInnerIndexPatternDataPanel__header { - display: flex; - align-items: center; - margin-bottom: $euiSizeS; -} - -.lnsChangeIndexPatternPopover__trigger { - padding: 0 $euiSize; -} diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.test.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.test.tsx index 9208b0f4f7f25..adf7fb9adf570 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.test.tsx @@ -10,8 +10,7 @@ import { DataView } from '@kbn/data-views-plugin/public'; import { UI_SETTINGS } from '@kbn/data-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; -import { render, screen, within } from '@testing-library/react'; -import { I18nProvider } from '@kbn/i18n-react'; +import { screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { FormBasedDataPanel, FormBasedDataPanelProps } from './datapanel'; import * as UseExistingFieldsApi from '@kbn/unified-field-list/src/hooks/use_existing_fields'; @@ -28,6 +27,7 @@ import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { createIndexPatternServiceMock } from '../../mocks/data_views_service_mock'; import { createMockFramePublicAPI } from '../../mocks'; import { DataViewsState } from '../../state_management'; +import { renderWithProviders } from '../../test_utils/test_utils'; const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); @@ -241,11 +241,8 @@ const waitToLoad = async () => await act(async () => new Promise((resolve) => setTimeout(resolve, 0))); const renderFormBasedDataPanel = async (propsOverrides?: Partial) => { - const { rerender, ...rest } = render( - , - { - wrapper: ({ children }) => {children}, - } + const { rerender, ...rest } = renderWithProviders( + ); await waitToLoad(); return { diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.tsx index ff51014f548d3..ab63dab30776d 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/datapanel.tsx @@ -5,10 +5,9 @@ * 2.0. */ -import './datapanel.scss'; import { uniq } from 'lodash'; import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react'; -import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { CoreStart } from '@kbn/core/public'; @@ -41,6 +40,7 @@ import type { import type { FormBasedPrivateState } from './types'; import { IndexPatternServiceAPI } from '../../data_views_service/service'; import { FieldItem } from '../common/field_item'; +import { dataPanelStyles } from '../common/datapanel.styles'; export type FormBasedDataPanelProps = Omit< DatasourceDataPanelProps, @@ -102,6 +102,7 @@ export function FormBasedDataPanel({ const { indexPatterns, indexPatternRefs } = frame.dataViews; const { currentIndexPatternId } = state; + const euiThemeContext = useEuiTheme(); const activeIndexPatterns = useMemo(() => { return uniq( ( @@ -118,7 +119,7 @@ export function FormBasedDataPanel({ {Object.keys(indexPatterns).length === 0 && indexPatternRefs.length === 0 ? ( @@ -201,6 +202,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({ layerFields?: string[]; activeIndexPatterns: IndexPattern[]; }) { + const euiThemeContext = useEuiTheme(); const { indexPatterns } = frame.dataViews; const currentIndexPattern = indexPatterns[currentIndexPatternId]; @@ -400,7 +402,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({ return ( } > diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/advanced_options.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/advanced_options.tsx index c8cbfb947a9e3..2fb3822abc34d 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/advanced_options.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/advanced_options.tsx @@ -32,6 +32,7 @@ export function AdvancedOptions(props: { options: AdvancedOption[] }) { } css={css` padding: 0 ${euiTheme.size.base} ${euiTheme.size.base}; + color: ${euiTheme.colors.primary}; `} > {props.options.map(({ dataTestSubj, inlineElement }) => ( diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_editor.scss b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_editor.scss deleted file mode 100644 index b85686a80099d..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_editor.scss +++ /dev/null @@ -1,65 +0,0 @@ -.lnsIndexPatternDimensionEditor { - height: 100%; -} - -.lnsIndexPatternDimensionEditor__header { - position: sticky; - top: 0; - background: $euiColorEmptyShade; - z-index: $euiZLevel1; // Raise it above the elements that are after it in DOM order - padding: 0 $euiSize; -} - -.lnsIndexPatternDimensionEditor-isFullscreen { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; -} - -.lnsIndexPatternDimensionEditor--padded { - padding: $euiSize; -} - -.lnsIndexPatternDimensionEditor--collapseNext { - margin-bottom: -$euiSizeL; - border-top: $euiBorderThin; - margin-top: 0 !important; -} - -.lnsIndexPatternDimensionEditor__columns { - display: block; - column-count: 2; - column-gap: $euiSizeM; -} - -.lnsIndexPatternDimensionEditor__operation .euiListGroupItem__label { - width: 100%; -} - -.lnsIndexPatternDimensionEditor__operation > button { - padding-top: 0; - padding-bottom: 0; - min-block-size: $euiSizeL; -} - -.lnsIndexPatternDimensionEditor__warning { - margin-bottom: $euiSize; - margin-top: $euiSizeS; -} - -.lnsIndexPatternDimensionEditor__droppable { - padding: $euiSizeXS; - border-radius: $euiBorderRadius; -} - -.lnsIndexPatternDimensionEditor__droppableItem { - margin-right: $euiSizeS; -} - -.lnsIndexPatternDimensionEditor-advancedOptions button { - &:hover, &:focus { - text-decoration-color: $euiColorPrimary; - } -} diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_editor.tsx index 98086df2e5ba1..cf8302c889356 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_editor.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import './dimension_editor.scss'; import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; @@ -25,6 +24,7 @@ import { EuiPanel, EuiBasicTable, EuiButtonIcon, + type UseEuiTheme, } from '@elastic/eui'; import ReactDOM from 'react-dom'; import { NameInput } from '@kbn/visualization-ui-components'; @@ -75,6 +75,7 @@ import { ParamEditorProps } from '../operations/definitions'; import { WrappingHelpPopover } from '../help_popover'; import { isColumn } from '../operations/definitions/helpers'; import type { FieldChoiceWithOperationType } from './field_select'; +import { operationsButtonStyles } from './shared_styles'; import type { IndexPattern, IndexPatternField } from '../../../types'; import { documentField } from '../document_field'; @@ -141,7 +142,9 @@ export function DimensionEditor(props: DimensionEditorProps) { const temporaryQuickFunction = Boolean(temporaryState === quickFunctionsName); const temporaryStaticValue = Boolean(temporaryState === staticValueOperationName); - const { euiTheme } = useEuiTheme(); + + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; const updateLayer = useCallback( (newLayer: Partial) => @@ -534,7 +537,7 @@ export function DimensionEditor(props: DimensionEditorProps) { isActive, size: 's', isDisabled: !!disabledStatus, - className: 'lnsIndexPatternDimensionEditor__operation', + css: operationsButtonStyles(euiThemeContext), 'data-test-subj': `lns-indexPatternDimension-${operationType}${ compatibleWithCurrentField ? '' : ' incompatible' }`, @@ -811,7 +814,7 @@ export function DimensionEditor(props: DimensionEditorProps) { fullWidth > 3 ? 'lnsIndexPatternDimensionEditor__columns' : ''} + css={sideNavItems.length > 3 ? operationsTwoColumnsStyles(euiThemeContext) : undefined} gutterSize="none" color="primary" listItems={ @@ -1290,3 +1293,11 @@ export function DimensionEditor(props: DimensionEditorProps) {
); } + +const operationsTwoColumnsStyles = ({ euiTheme }: UseEuiTheme) => { + return css` + display: block; + column-count: 2; + column-gap: ${euiTheme.size.m}; + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_panel.test.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_panel.test.tsx index 3df7248ae3f3d..134e3e278a1eb 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/dimension_panel.test.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import { ReactWrapper, ShallowWrapper, ComponentType, mount } from 'enzyme'; +import { ReactWrapper, ShallowWrapper } from 'enzyme'; import React, { ChangeEvent } from 'react'; -import { screen, act, render, within } from '@testing-library/react'; +import { screen, act, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { findTestSubject } from '@elastic/eui/lib/test'; import { @@ -17,7 +17,6 @@ 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'; @@ -48,9 +47,7 @@ import { TimeShift } from './time_shift'; import { ReducedTimeRange } from './reduced_time_range'; import { DimensionEditor } from './dimension_editor'; import { AdvancedOptions } from './advanced_options'; -import { coreMock } from '@kbn/core/public/mocks'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { LensAppServices } from '../../../app_plugin/types'; +import { mountWithProviders, renderWithProviders } from '../../../test_utils/test_utils'; jest.mock('./reference_editor', () => ({ ReferenceEditor: () => null, @@ -167,25 +164,6 @@ const bytesColumn: GenericIndexPatternColumn = { params: { format: { id: 'bytes' } }, }; -const wrappingComponent: React.FC<{ - children: React.ReactNode; -}> = ({ children }) => { - return ( - - {children} - - ); -}; - -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: wrappingComponent as ComponentType<{}>, - }); -} - /** * The datasource exposes four main pieces of code which are tested at * an integration test level. The main reason for this fairly high level @@ -292,21 +270,8 @@ describe('FormBasedDimensionEditor', () => { }); const renderDimensionPanel = (propsOverrides = {}) => { - const Wrapper: React.FC<{ - children: React.ReactNode; - }> = ({ children }) => { - return ( - - {children} - - ); - }; - - const rtlRender = render( - , - { - wrapper: Wrapper, - } + const rtlRender = renderWithProviders( + ); const getVisibleFieldSelectOptions = () => { @@ -396,14 +361,14 @@ describe('FormBasedDimensionEditor', () => { }; }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); const options = getFieldSelectComboBox(wrapper).prop('options'); expect(options![1].options!.map(({ label }) => label)).toEqual(['timestampLabel', 'source']); }); it('should indicate fields which are incompatible for the operation of the current column', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should indicate operations which are incompatible for the field of the current column', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should indicate when a transition is invalid due to filterOperations', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should not display hidden operation types', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); const items: EuiListGroupItemProps[] = wrapper.find(EuiListGroup).prop('listItems') || []; @@ -482,7 +447,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should indicate that reference-based operations are not compatible when they are incomplete', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should indicate that reference-based operations are compatible sometimes', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { it('should keep the operation when switching to another field compatible with this operation', async () => { const initialState: FormBasedPrivateState = getStateWithColumns({ col1: bytesColumn }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -601,7 +566,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should switch operations when selecting a field that requires another operation', async () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); const comboBox = getFieldSelectComboBox(wrapper); const option = comboBox.prop('options')![1].options!.find(({ label }) => label === 'source')!; @@ -633,7 +598,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should keep the field when switching to another operation compatible for this field', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should not set the state if selecting the currently active operation', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper @@ -677,7 +642,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should update label and custom label flag on label input changes', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper @@ -705,7 +670,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should not keep the label as long as it is the default label', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should keep the label on operation change if it is custom', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should remove customLabel flag if label is set to default', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { describe('transient invalid state', () => { it('should set the state if selecting an operation incompatible with the current field', async () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); await act(async () => { await wrapper @@ -836,7 +801,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should show error message in invalid state', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper @@ -850,7 +815,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should leave error state if a compatible operation is selected', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper @@ -868,7 +833,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should leave error state if the original operation is re-selected', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper @@ -886,7 +851,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should leave error state when switching from incomplete state to fieldless operation', async () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); await act(async () => { await wrapper @@ -901,7 +866,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should leave error state when re-selecting the original fieldless function', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should indicate fields compatible with selected operation', async () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); await act(async () => { await wrapper @@ -954,7 +919,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should select compatible operation if field not compatible with selected operation', async () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -1022,7 +987,7 @@ describe('FormBasedDimensionEditor', () => { references: ['ref'], }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -1049,7 +1014,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should select the Records field when count is selected on non-existing column', async () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should indicate document and field compatibility with selected document operation', async () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should set datasource state if compatible field is selected for operation', async () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); await act(async () => { await wrapper .find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]') @@ -1165,7 +1130,7 @@ describe('FormBasedDimensionEditor', () => { } it('should default to None if time scaling is not set', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); }); @@ -1179,7 +1144,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should show current time scaling if set', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( ); act(() => { @@ -1195,7 +1160,7 @@ describe('FormBasedDimensionEditor', () => { it('should allow to set time scaling initially', () => { const props = getProps({}); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); }); @@ -1232,7 +1197,7 @@ describe('FormBasedDimensionEditor', () => { operationType: 'sum', label: 'Sum of bytes per hour', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper.find('button[data-test-subj="lns-indexPatternDimension-count"]').simulate('click'); }); @@ -1261,7 +1226,7 @@ describe('FormBasedDimensionEditor', () => { operationType: 'sum', label: 'Sum of bytes per hour', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper .find('button[data-test-subj="lns-indexPatternDimension-average"]') @@ -1287,7 +1252,7 @@ describe('FormBasedDimensionEditor', () => { it('should allow to change time scaling', () => { const props = getProps({ timeScale: 's', label: 'Count of records per second' }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); }); @@ -1320,7 +1285,7 @@ describe('FormBasedDimensionEditor', () => { it('should not adjust label if it is custom', () => { const props = getProps({ timeScale: 's', customLabel: true, label: 'My label' }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper .find('[data-test-subj="indexPattern-time-scaling-unit"] select') @@ -1390,7 +1355,7 @@ describe('FormBasedDimensionEditor', () => { }), columnId: 'col2', }; - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); }); @@ -1400,7 +1365,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should show current reduced time range if set', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( ); expect( @@ -1410,7 +1375,7 @@ describe('FormBasedDimensionEditor', () => { it('should allow to set reduced time range initially', () => { const props = getProps({}); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); }); @@ -1442,7 +1407,7 @@ describe('FormBasedDimensionEditor', () => { operationType: 'sum', label: 'Sum of bytes per hour', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper.find('button[data-test-subj="lns-indexPatternDimension-count"]').simulate('click'); }); @@ -1466,7 +1431,7 @@ describe('FormBasedDimensionEditor', () => { const props = getProps({ timeShift: '1d', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper.find(ReducedTimeRange).find(EuiComboBox).prop('onCreateOption')!('7m', []); }); @@ -1490,7 +1455,7 @@ describe('FormBasedDimensionEditor', () => { const props = getProps({ reducedTimeRange: '5 months', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); expect(wrapper.find(ReducedTimeRange).find(EuiComboBox).prop('isInvalid')).toBeTruthy(); @@ -1547,7 +1512,7 @@ describe('FormBasedDimensionEditor', () => { }), columnId: 'col2', }; - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should show custom options if time shift is available', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); expect( wrapper .find(DimensionEditor) @@ -1576,7 +1541,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should show current time shift if set', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( ); expect(wrapper.find(TimeShift).find(EuiComboBox).prop('selectedOptions')[0].value).toEqual( @@ -1586,7 +1551,7 @@ describe('FormBasedDimensionEditor', () => { it('should allow to set time shift initially', () => { const props = getProps({}); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); }); @@ -1616,7 +1581,7 @@ describe('FormBasedDimensionEditor', () => { operationType: 'sum', label: 'Sum of bytes per hour', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper.find('button[data-test-subj="lns-indexPatternDimension-count"]').simulate('click'); }); @@ -1640,7 +1605,7 @@ describe('FormBasedDimensionEditor', () => { const props = getProps({ timeShift: '1d', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper.find(TimeShift).find(EuiComboBox).prop('onCreateOption')!('1h', []); }); @@ -1664,7 +1629,7 @@ describe('FormBasedDimensionEditor', () => { const props = getProps({ timeShift: '5 months', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); expect(wrapper.find(TimeShift).find(EuiComboBox).prop('isInvalid')).toBeTruthy(); @@ -1681,7 +1646,7 @@ describe('FormBasedDimensionEditor', () => { const props = getProps({ timeShift: 'startAt(2022-11-02T00:00:00.000Z)', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); expect(wrapper.find(TimeShift).find(EuiComboBox).prop('isInvalid')).toBeTruthy(); @@ -1725,7 +1690,7 @@ describe('FormBasedDimensionEditor', () => { } it('should not show custom options if time scaling is not available', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should show custom options if filtering is available', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { findTestSubject(wrapper, 'indexPattern-advanced-accordion').simulate('click'); }); @@ -1754,7 +1719,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should show current filter if set', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( @@ -1775,7 +1740,7 @@ describe('FormBasedDimensionEditor', () => { operationType: 'sum', label: 'Sum of bytes per hour', }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { wrapper.find('button[data-test-subj="lns-indexPatternDimension-count"]').simulate('click'); }); @@ -1801,7 +1766,7 @@ describe('FormBasedDimensionEditor', () => { filter: { language: 'kuery', query: 'a: b' }, }); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { const { updateLayer, columnId, layer } = wrapper.find(Filtering).props(); @@ -1832,7 +1797,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should render invalid field if field reference is broken', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should support selecting the operation before the field', async () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( ); await act(async () => { @@ -1915,7 +1880,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should select operation directly if only one field is possible', async () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should select operation directly if only document is possible', async () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( ); await act(async () => { @@ -1991,7 +1956,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should indicate compatible fields when selecting the operation first', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2015,7 +1980,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should indicate document compatibility when document operation is selected', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( { }); it('should not update when selecting the current field again', async () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); const comboBox = getFieldSelectComboBox(wrapper); @@ -2053,7 +2018,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should show all operations that are not filtered out', () => { - wrapper = mountWithServices( + wrapper = mountWithProviders( !op.isBucketed && op.dataType === 'number'} @@ -2086,7 +2051,7 @@ describe('FormBasedDimensionEditor', () => { // Prevents field format from being loaded setState.mockImplementation(() => {}); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2129,7 +2094,7 @@ describe('FormBasedDimensionEditor', () => { const initialState: FormBasedPrivateState = getStateWithColumns({ col1: bytesColumn, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); act(() => { @@ -2149,7 +2114,7 @@ describe('FormBasedDimensionEditor', () => { }); it('should keep the latest valid dimension when removing the selection in field combobox', () => { - wrapper = mountWithServices(); + wrapper = mountWithProviders(); act(() => { getFieldSelectComboBox(wrapper as ReactWrapper).prop('onChange')!([]); }); @@ -2169,7 +2134,7 @@ describe('FormBasedDimensionEditor', () => { }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2213,7 +2178,7 @@ describe('FormBasedDimensionEditor', () => { }, }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2254,7 +2219,7 @@ describe('FormBasedDimensionEditor', () => { }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2286,7 +2251,7 @@ describe('FormBasedDimensionEditor', () => { it('should hide the top level field selector when switching from non-reference to reference', async () => { (generateId as jest.Mock).mockReturnValue(`second`); - wrapper = mountWithServices(); + wrapper = mountWithProviders(); expect(wrapper.find('ReferenceEditor')).toHaveLength(0); @@ -2311,7 +2276,7 @@ describe('FormBasedDimensionEditor', () => { }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2337,7 +2302,7 @@ describe('FormBasedDimensionEditor', () => { }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2362,7 +2327,7 @@ describe('FormBasedDimensionEditor', () => { }), }; - wrapper = mountWithServices( + wrapper = mountWithProviders( { }), }; - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2444,7 +2409,7 @@ describe('FormBasedDimensionEditor', () => { }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2465,7 +2430,7 @@ describe('FormBasedDimensionEditor', () => { }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2484,7 +2449,7 @@ describe('FormBasedDimensionEditor', () => { }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( { it('should select the quick function tab by default', () => { const stateWithNoColumn: FormBasedPrivateState = getStateWithColumns({}); - wrapper = mountWithServices( + wrapper = mountWithProviders( ); @@ -2515,7 +2480,7 @@ describe('FormBasedDimensionEditor', () => { it('should select the static value tab when supported by default', () => { const stateWithNoColumn: FormBasedPrivateState = getStateWithColumns({}); - wrapper = mountWithServices( + wrapper = mountWithProviders( { }, }); - wrapper = mountWithServices( + wrapper = mountWithProviders( { + const euiThemeContext = useEuiTheme(); if ( temporaryStateType === 'none' || (currentOperationType != null && isQuickFunction(currentOperationType)) @@ -154,7 +161,7 @@ export const CalloutWarning = ({ return ( <> ); }; + +const dimensionEditorWarningStyles = ({ euiTheme }: UseEuiTheme) => { + return css` + margin-bottom: ${euiTheme.size.base}; + margin-top: ${euiTheme.size.s}; + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/field_select.scss b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/field_select.scss deleted file mode 100644 index 993174f3e6223..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/field_select.scss +++ /dev/null @@ -1,7 +0,0 @@ -.lnFieldSelect__option--incompatible { - color: $euiColorLightShade; -} - -.lnFieldSelect__option--nonExistant { - background-color: $euiColorLightestShade; -} diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/field_select.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/field_select.tsx index bf22e6622e4ab..91d4348113d9b 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/field_select.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/field_select.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import './field_select.scss'; import { partition } from 'lodash'; import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/format_selector.test.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/format_selector.test.tsx index c8875bfece64e..46807ccb98843 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/format_selector.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/format_selector.test.tsx @@ -8,11 +8,9 @@ import React from 'react'; import { FormatSelector, FormatSelectorProps } from './format_selector'; import { GenericIndexPatternColumn } from '../../..'; -import { LensAppServices } from '../../../app_plugin/types'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { I18nProvider } from '@kbn/i18n-react'; -import { coreMock, docLinksServiceMock } from '@kbn/core/public/mocks'; -import { fireEvent, render, screen, within } from '@testing-library/react'; +import { renderWithProviders } from '../../../test_utils/test_utils'; +import { docLinksServiceMock } from '@kbn/core/public/mocks'; +import { fireEvent, screen, within } from '@testing-library/react'; import userEvent, { type UserEvent } from '@testing-library/user-event'; const props = { @@ -30,39 +28,10 @@ const props = { docLinks: docLinksServiceMock.createStartContract(), }; -function createMockServices(): LensAppServices { - const services = coreMock.createStart(); - services.uiSettings.get.mockImplementation(() => '0.0'); - return { - ...services, - docLinks: { - links: { - indexPatterns: { fieldFormattersNumber: '' }, - }, - }, - } as unknown as LensAppServices; -} - const renderFormatSelector = (propsOverrides?: Partial) => { - const WrappingComponent: React.FC<{ - children: React.ReactNode; - }> = ({ children }) => { - return ( - - {children} - - ); - }; - return render(, { - wrapper: WrappingComponent, - }); + return renderWithProviders(); }; -// Skipped for update of userEvent v14: https://github.com/elastic/kibana/pull/189949 -// It looks like the individual tests within each it block are not really pure, -// see for example the first two tests, they run the same code but expect -// different results. With the updated userEvent code the tests no longer work -// with this setup and should be refactored. describe('FormatSelector', () => { let user: UserEvent; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx index 66a01daae0581..eab581c815958 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import './dimension_editor.scss'; import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRowProps, EuiSpacer, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; @@ -32,6 +31,7 @@ import type { FormBasedLayer } from '../types'; import type { IndexPattern, IndexPatternField, ParamEditorCustomProps } from '../../../types'; import type { FormBasedDimensionEditorProps } from './dimension_panel'; import { FormRow } from '../operations/definitions/shared_components'; +import { operationsButtonStyles } from './shared_styles'; const operationDisplay = getOperationDisplay(); @@ -56,7 +56,7 @@ const getFunctionOptions = ( return { label, value: operationType, - className: 'lnsIndexPatternDimensionEditor__operation', + css: operationsButtonStyles, 'data-test-subj': `lns-indexPatternDimension-${operationType}${ isCompatible ? '' : ' incompatible' }`, @@ -204,7 +204,7 @@ export const ReferenceEditor = (props: ReferenceEditorProps) => { const brokenFunctionOption = { label: selectedOperationType && operationDisplay[selectedOperationType].displayName, value: selectedOperationType, - className: 'lnsIndexPatternDimensionEditor__operation', + css: operationsButtonStyles, 'data-test-subj': `lns-indexPatternDimension-${selectedOperationType} incompatible`, } as EuiComboBoxOptionOption; functionOptions?.push(brokenFunctionOption); diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/shared_styles.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/shared_styles.tsx new file mode 100644 index 0000000000000..345dfef84a821 --- /dev/null +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/dimension_panel/shared_styles.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UseEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; + +export const operationsButtonStyles = ({ euiTheme }: UseEuiTheme) => { + return css` + > button { + padding-top: 0; + padding-bottom: 0; + min-block-size: ${euiTheme.size.l}; + } + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/help_popover.scss b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/help_popover.scss deleted file mode 100644 index af1bdcb459093..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/help_popover.scss +++ /dev/null @@ -1,13 +0,0 @@ -.lnsHelpPopover__panel { - max-inline-size: $euiSize * 30 !important; -} - -.lnsHelpPopover__content { - max-height: 40vh; - padding: $euiSizeM; - @include euiYScrollWithShadows; -} - -.lnsHelpPopover__buttonIcon { - margin-right: $euiSizeXS; -} diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/help_popover.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/help_popover.tsx index 2eeb3c1c2ee2b..c2b4b18739412 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/help_popover.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/help_popover.tsx @@ -16,10 +16,12 @@ import { EuiWrappingPopoverProps, EuiPopoverTitle, EuiText, + type UseEuiTheme, + useEuiTheme, } from '@elastic/eui'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { css } from '@emotion/react'; import { StartServices } from '../../types'; -import './help_popover.scss'; export const HelpPopoverButton = ({ children, @@ -28,17 +30,39 @@ export const HelpPopoverButton = ({ children: string; onClick: EuiLinkButtonProps['onClick']; }) => { + const euiThemeContext = useEuiTheme(); return ( - - + {children} ); }; +const HelpPopoverContent = ({ title, children }: { title?: string; children: ReactNode }) => { + const euiThemeContext = useEuiTheme(); + return ( + <> + {title && {title}} + + {children} + + + ); +}; + +const helpPopoverStyles = { + button: ({ euiTheme }: UseEuiTheme) => css` + margin-right: ${euiTheme.size.xs}; + `, + content: ({ euiTheme }: UseEuiTheme) => css` + max-height: 40vh; + padding: ${euiTheme.size.m}; + `, +}; + export const HelpPopover = ({ anchorPosition, button, @@ -58,18 +82,13 @@ export const HelpPopover = ({ - {title && {title}} - - - {children} - + {children} ); }; @@ -96,18 +115,13 @@ export const WrappingHelpPopover = ({ - {title && {title}} - - - {children} - + {children} ); diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/layerpanel.test.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/layerpanel.test.tsx index 93902547dd603..0134124668613 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/layerpanel.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/layerpanel.test.tsx @@ -8,10 +8,11 @@ import React from 'react'; import { FormBasedPrivateState } from './types'; import { FormBasedLayerPanelProps, LayerPanel } from './layerpanel'; -import { fireEvent, render, screen, within } from '@testing-library/react'; +import { fireEvent, screen, within } from '@testing-library/react'; import { getFieldByNameFactory } from './pure_helpers'; import { TermsIndexPatternColumn } from './operations'; import userEvent from '@testing-library/user-event'; +import { renderWithProviders } from '../../test_utils/test_utils'; Object.defineProperty(HTMLElement.prototype, 'scrollWidth', { value: 400 }); Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { value: 200 }); @@ -225,7 +226,7 @@ describe('Layer Data Panel', () => { }; }); - const renderLayerPanel = () => render(); + const renderLayerPanel = () => renderWithProviders(); it('should list all index patterns', async () => { renderLayerPanel(); diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.scss b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.scss deleted file mode 100644 index 6838812e4b999..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lnsIndexPatternDimensionEditor__filtersEditor { - width: $euiSize * 60; -} diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.tsx index 1f3f5ba94e63b..0668c41bc8318 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './filter_popover.scss'; - import React, { useState } from 'react'; import { EuiPopover, EuiSpacer } from '@elastic/eui'; import type { Query } from '@kbn/es-query'; @@ -66,7 +64,9 @@ export const FilterPopover = ({ return ( { // Workaround for timeout via https://github.com/testing-library/user-event/issues/833#issuecomment-1171452841 const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); const updateLayerSpy = jest.fn(); - render( + renderWithProviders( { describe('Modify filters', () => { it('should correctly show existing filters ', () => { const updateLayerSpy = jest.fn(); - render( + renderWithProviders( { const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); jest.useFakeTimers(); const updateLayerSpy = jest.fn(); - render( + renderWithProviders( { + const euiThemeContext = useEuiTheme(); const [activeFilterId, setActiveFilterId] = useState(''); const [localFilters, setLocalFilters] = useState(() => filters.map((filter) => ({ ...filter, id: generateId() })) @@ -275,6 +276,7 @@ export const FilterList = ({ title={i18n.translate('xpack.lens.indexPattern.filters.clickToEdit', { defaultMessage: 'Click to edit', })} + css={draggablePopoverButtonStyles(euiThemeContext)} > {filter.label || (filter.input.query as string) || defaultLabel} @@ -285,9 +287,7 @@ export const FilterList = ({ })} { - onAddFilter(); - }} + onClick={onAddFilter} label={i18n.translate('xpack.lens.indexPattern.filters.addaFilter', { defaultMessage: 'Add a filter', })} diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/formula/editor/formula.scss b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/formula/editor/formula.scss deleted file mode 100644 index 89d8ed7357c8d..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/formula/editor/formula.scss +++ /dev/null @@ -1,100 +0,0 @@ -.lnsFormula { - display: flex; - flex-direction: column; - - .lnsIndexPatternDimensionEditor-isFullscreen & { - height: 100%; - } - - & > * { - flex: 1; - min-height: 0; - } - - & > * + * { - border-top: $euiBorderThin; - } -} - -.lnsFormula__editor { - .lnsIndexPatternDimensionEditor-isFullscreen & { - border-bottom: none; - display: flex; - flex-direction: column; - } - - & > * + * { - border-top: $euiBorderThin; - } -} - -.lnsFormula__editorHeader, -.lnsFormula__editorFooter { - padding: $euiSizeS; -} - -.lnsFormula__editorFooter { - // make sure docs are rendered in front of monaco - z-index: 1; - border-bottom-right-radius: $euiBorderRadius; - border-bottom-left-radius: $euiBorderRadius; -} - -.lnsFormula__editorHeaderGroup, -.lnsFormula__editorFooterGroup { - display: block; // Overrides EUI's styling of `display: flex` on `EuiFlexItem` components -} - -.lnsFormula__editorContent { - background-color: $euiColorBackgroundBasePlain; - min-height: 0; - position: relative; - - .lnsIndexPatternDimensionEditor:not(.lnsIndexPatternDimensionEditor-isFullscreen) & { - height: 200px; - } - - .lnsIndexPatternDimensionEditor-isFullscreen & { - flex: 1; - } -} - -.lnsFormula__editorPlaceholder { - position: absolute; - top: 0; - left: $euiSize; - right: 0; - color: $euiTextSubduedColor; - // Matches monaco editor - font-family: Menlo, Monaco, 'Courier New', monospace; - pointer-events: none; -} - -.lnsFormula__warningText + .lnsFormula__warningText { - margin-top: $euiSizeS; - border-top: $euiBorderThin; - padding-top: $euiSizeS; -} - -.lnsFormula__editorHelp--inline { - align-items: center; - display: flex; - padding: $euiSizeXS; - - & > * + * { - margin-left: $euiSizeXS; - } -} - -.lnsFormula__editorError { - white-space: nowrap; -} - -.lnsFormula__docs { - background: $euiColorEmptyShade; -} - -.lnsFormulaOverflow { - // Needs to be higher than the modal and all flyouts - z-index: $euiZLevel9 + 1; -} diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx index 7d0731902eee8..e548a45d3a1b5 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx @@ -25,10 +25,10 @@ import { EuiToolTip, EuiSpacer, useEuiTheme, + type UseEuiTheme, } from '@elastic/eui'; import useUnmount from 'react-use/lib/useUnmount'; import { monaco } from '@kbn/monaco'; -import classNames from 'classnames'; import { CodeEditor, CodeEditorProps } from '@kbn/code-editor'; import { UI_SETTINGS } from '@kbn/data-plugin/public'; import { useDebounceWithOptions } from '../../../../../../shared_components'; @@ -50,7 +50,6 @@ import { } from './math_completion'; import { LANGUAGE_ID } from './math_tokenization'; -import './formula.scss'; import { FormulaIndexPatternColumn } from '../formula'; import { insertOrReplaceFormulaColumn } from '../parse'; import { filterByVisibleOperation } from '../util'; @@ -126,7 +125,8 @@ export function FormulaEditor({ const disposables = React.useRef([]); const editor1 = React.useRef(); - const { euiTheme } = useEuiTheme(); + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; const visibleOperationsMap = useMemo( () => filterByVisibleOperation(operationDefinitionMap), @@ -685,10 +685,12 @@ export function FormulaEditor({ // in the behavior of Monaco when it's first loaded and then reloaded. return (
{!isFullscreen && (
-
+
- + - + { toggleFullscreen(); @@ -803,7 +814,7 @@ export function FormulaEditor({ /> {!text ? ( -
+
{i18n.translate('xpack.lens.formulaPlaceholderText', { defaultMessage: 'Type a formula by combining functions with math, like:', @@ -815,9 +826,9 @@ export function FormulaEditor({ ) : null}
-
+
- + {isFullscreen ? ( setIsHelpOpen(!isHelpOpen)} > @@ -866,7 +878,7 @@ export function FormulaEditor({ {errorCount || warningCount ? ( - + {warnings.map(({ message, severity }, index) => ( -
+
); } + +const sharedEditorStyles = { + self: ({ euiTheme }: UseEuiTheme) => { + return css` + .lnsFormula { + display: flex; + flex-direction: column; + + & > * { + flex: 1; + min-height: 0; + } + + & > * + * { + border-top: ${euiTheme.border.thin}; + } + } + .lnsFormulaOverflow { + // Needs to be higher than the modal and all flyouts + z-index: ${euiTheme.levels.toast} + 1; + } + .lnsFormula__editorContent { + background-color: ${euiTheme.colors.backgroundBasePlain}; + min-height: 0; + position: relative; + } + `; + }, + formulaDocs: ({ euiTheme }: UseEuiTheme) => css` + display: flex; + flex-direction: column; + // make sure docs are rendered in front of monaco + z-index: 1; + background: ${euiTheme.colors.backgroundBasePlain}; + `, + editorHeader: ({ euiTheme }: UseEuiTheme) => css` + padding: ${euiTheme.size.s}; + `, + editorFooter: ({ euiTheme }: UseEuiTheme) => css` + padding: ${euiTheme.size.s}; + // make sure docs are rendered in front of monaco + z-index: 1; + border-bottom-right-radius: ${euiTheme.border.radius.medium}; + border-bottom-left-radius: ${euiTheme.border.radius.medium}; + `, + editorPlaceholder: ({ euiTheme }: UseEuiTheme) => css` + position: absolute; + top: 0; + left: ${euiTheme.size.base}; + right: 0; + color: ${euiTheme.colors.textSubdued} + // Matches monaco editor + font-family: Menlo, Monaco, 'Courier New', monospace; + pointer-events: none; + `, + warningText: ({ euiTheme }: UseEuiTheme) => css` + margin-top: ${euiTheme.size.s}; + border-top: ${euiTheme.border.thin}; + padding-top: ${euiTheme.size.s}; + `, + editorHelpLink: ({ euiTheme }: UseEuiTheme) => css` + align-items: center; + display: flex; + padding: ${euiTheme.size.xs}; + & > * + * { + margin-left: ${euiTheme.size.xs}; + } + `, +}; + +const defaultEditorStyles = ({ euiTheme }: UseEuiTheme) => { + return css` + .lnsFormula__editorContent { + height: 200px; + } + `; +}; + +const fullscreenEditorStyles = ({ euiTheme }: UseEuiTheme) => { + return css` + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + .lnsFormula__editor { + border-bottom: none; + display: flex; + flex-direction: column; + & > * + * { + border-top: ${euiTheme.border.thin}; + } + } + .lnsFormula__editorContent { + flex: 1; + } + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/ranges/advanced_editor.scss b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/ranges/advanced_editor.scss deleted file mode 100644 index 4af490e7479da..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/ranges/advanced_editor.scss +++ /dev/null @@ -1,10 +0,0 @@ -.lnsRangesOperation__popoverButton { - @include euiTextBreakWord; - @include euiFontSizeS; - min-height: $euiSizeXL; - width: 100%; -} - -.lnsRangesOperation__popoverNumberField { - width: 14ch; // Roughly 10 characters plus extra for the padding -} diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/ranges/advanced_editor.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/ranges/advanced_editor.tsx index c196c3191bae3..837a7fc6f1f6f 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/ranges/advanced_editor.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './advanced_editor.scss'; - import React, { useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { @@ -21,6 +19,7 @@ import { EuiToolTip, htmlIdGenerator, keys, + useEuiTheme, } from '@elastic/eui'; import { IFieldFormat } from '@kbn/field-formats-plugin/common'; import { @@ -28,11 +27,13 @@ import { DraggableBucketContainer, NewBucketButton, } from '@kbn/visualization-ui-components'; +import { css } from '@emotion/react'; import { useDebounceWithOptions } from '../../../../../shared_components'; import { RangeTypeLens, isValidRange } from './ranges'; import { FROM_PLACEHOLDER, TO_PLACEHOLDER, TYPING_DEBOUNCE_TIME } from './constants'; import { LabelInput } from '../shared_components'; import { isValidNumber } from '../helpers'; +import { draggablePopoverButtonStyles } from '../styles'; const generateId = htmlIdGenerator(); @@ -101,7 +102,9 @@ export const RangePopover = ({ { const newRange = { @@ -132,7 +135,9 @@ export const RangePopover = ({ { if (toRef && node) { @@ -203,6 +208,7 @@ export const AdvancedRangeEditor = ({ onToggleEditor: () => void; formatter: IFieldFormat; }) => { + const euiThemeContext = useEuiTheme(); const [activeRangeId, setActiveRangeId] = useState(''); // use a local state to store ids with range objects const [localRanges, setLocalRanges] = useState(() => @@ -303,8 +309,8 @@ export const AdvancedRangeEditor = ({ changeActiveRange(range.id)} - className="lnsRangesOperation__popoverButton" - data-test-subj="indexPattern-ranges-popover-trigger" + data-test-subj="dataView-ranges-popover-trigger" + css={draggablePopoverButtonStyles(euiThemeContext)} > { it('should start update the state with the default maxBars value', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { it('should update state when changing Max bars number', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { it('should update the state using the plus or minus buttons by the step amount', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { it('should show one range interval to start with', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { it('should use the parentFormat to create the trigger label', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { ); expect( - instance.find('[data-test-subj="indexPattern-ranges-popover-trigger"]').first().text() + instance.find('[data-test-subj="dataView-ranges-popover-trigger"]').first().text() ).toBe('0 - 1000'); }); @@ -541,7 +541,7 @@ describe('ranges', () => { // we intercept the formatter without an id assigned an print "Error" const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { ); expect( - instance.find('[data-test-subj="indexPattern-ranges-popover-trigger"]').first().text() + instance.find('[data-test-subj="dataView-ranges-popover-trigger"]').first().text() ).not.toBe('Error'); }); it('should add a new range', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { it('should add a new range with custom label', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { it('should open a popover to edit an existing range', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { it('should not accept invalid ranges', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { label: '', }); - const instance = mount( + const instance = mountWithProviders( { label: '', }); - const instance = mount( + const instance = mountWithProviders( { it('should correctly handle the default formatter for the field', () => { const updateLayerSpy = jest.fn(); - const instance = mount( + const instance = mountWithProviders( { params: { decimals: 0 }, }; - const instance = mount( + const instance = mountWithProviders( { it('should not update the state on mount', () => { const updateLayerSpy = jest.fn(); - mount( + mountWithProviders( { params: { decimals: 3 }, }; - const instance = mount( + const instance = mountWithProviders( {React.cloneElement(children, { prepend: ( - + {label} ), diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/styles.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/styles.tsx new file mode 100644 index 0000000000000..0473e58ae5a7d --- /dev/null +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/styles.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { euiFontSize, euiTextBreakWord, UseEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; + +export const draggablePopoverButtonStyles = (euiThemeContext: UseEuiTheme) => { + return css` + ${euiTextBreakWord()}; + ${euiFontSize(euiThemeContext, 's')}; + min-height: ${euiThemeContext.euiTheme.size.xl}; + width: 100%; + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/terms/index.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/terms/index.tsx index 62c5b777bf37b..bda79377ad3d2 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/terms/index.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/terms/index.tsx @@ -25,6 +25,7 @@ import { import { uniq } from 'lodash'; import { AggFunctionsMapping } from '@kbn/data-plugin/public'; import { buildExpressionFunction } from '@kbn/expressions-plugin/public'; +import { css } from '@emotion/react'; import { DOCUMENT_FIELD_NAME } from '../../../../../../common/constants'; import { insertOrReplaceColumn, updateColumnParam, updateDefaultLabels } from '../../layer_helpers'; import type { DataType, OperationMetadata } from '../../../../../types'; @@ -1008,6 +1009,9 @@ The top values of a specified field ranked by the chosen metric. <> = (field) => field?.meta.type; @@ -129,7 +131,7 @@ export function TextBasedDataPanel({ }, [hasSuggestionForField, dropOntoWorkspace] ); - + const euiThemeContext = useEuiTheme(); return ( diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/buttons/draggable_dimension_button.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/buttons/draggable_dimension_button.tsx index 4218eb6426d20..b6fbe8c7c3b04 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/buttons/draggable_dimension_button.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/buttons/draggable_dimension_button.tsx @@ -15,6 +15,8 @@ import { Droppable, DroppableProps, } from '@kbn/dom-drag-drop'; +import { css } from '@emotion/react'; +import { useEuiTheme } from '@elastic/eui'; import { isDraggedField } from '../../../../utils'; import { Datasource, @@ -64,6 +66,7 @@ export function DraggableDimensionButton({ indexPatterns: IndexPatternMap; }) { const [{ dragging }] = useDragDropContext(); + const { euiTheme } = useEuiTheme(); let getDropProps; @@ -139,6 +142,12 @@ export function DraggableDimensionButton({ ref={registerNewButtonRefMemoized} className="lnsLayerPanel__dimensionContainer" data-test-subj={group.dataTestSubj} + css={css` + position: relative; + & + & { + margin-top: ${euiTheme.size.s}; + } + `} > - + @@ -88,7 +87,6 @@ const DataLossWarning = ({ content, id }: { content?: string; id: string }) => { color={euiTheme.colors.warning} content={content} iconProps={{ - className: 'lnsChartSwitch__chartIcon', 'data-test-subj': `lnsChartSwitchPopoverAlert_${id}`, }} /> diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.scss deleted file mode 100644 index 1a33ed18581f4..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.scss +++ /dev/null @@ -1,39 +0,0 @@ -.lnsChartSwitch__header { - > * { - display: flex; - align-items: center; - } -} - -.lnsChartSwitch__options { - width: 384px; -} - -.lnsChartSwitch__summaryIcon { - transform: translateY(-1px); - color: $euiTextSubduedColor; - - @include euiBreakpoint('xl') { - margin-right: $euiSizeS; - } -} - -.lnsChartSwitch__summaryText { - @include euiBreakpoint('xs', 's', 'm', 'l') { - @include euiScreenReaderOnly; - } -} - -.lnsChartSwitch__append { - display: inline-flex; -} - -// Targeting img as this won't target normal EuiIcon's only the custom svgs's -img.lnsChartSwitch__chartIcon { // stylelint-disable-line selector-no-qualifying-type - // The large icons aren't square so max out the width to fill the height - width: 100%; -} - -.lnsChartSwitch__search { - width: 10 * $euiSizeXXL; -} diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.tsx index dc033435a6440..c64be98be6e1e 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import './chart_switch.scss'; import React, { useState, useMemo, memo } from 'react'; import { i18n } from '@kbn/i18n'; import { ExperimentalBadge } from '../../../../shared_components'; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_popover.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_popover.tsx index 31ae3257b8301..0e8885a53642b 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_popover.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_popover.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import './chart_switch.scss'; import React, { useState, memo } from 'react'; import { EuiPopover } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ChartSwitchTrigger } from '@kbn/visualization-ui-components'; +import { css } from '@emotion/react'; import { useLensSelector, selectVisualization } from '../../../../state_management'; import { ChartSwitch, ChartSwitchProps } from './chart_switch'; @@ -31,28 +31,29 @@ export const ChartSwitchPopover = memo(function ChartSwitchPopover( }; return ( -
- setFlyoutOpen(!flyoutOpen)} - /> - } - isOpen={flyoutOpen} - closePopover={() => setFlyoutOpen(false)} - anchorPosition="downLeft" - > - {flyoutOpen ? setFlyoutOpen(false)} /> : null} - -
+ setFlyoutOpen(!flyoutOpen)} + /> + } + isOpen={flyoutOpen} + closePopover={() => setFlyoutOpen(false)} + anchorPosition="downLeft" + > + {flyoutOpen ? setFlyoutOpen(false)} /> : null} + ); }); diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_selectable.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_selectable.tsx index 8c1b8cb53c05a..a08bcb8a7c334 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_selectable.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_selectable.tsx @@ -44,6 +44,9 @@ export const ChartSwitchSelectable = ({ isPreFiltered data-test-subj="lnsChartSwitchList" className="lnsChartSwitch__options" + css={css` + width: 384px; + `} height={computeListHeight(props.options as SelectableEntry[])} searchProps={{ compressed: true, @@ -51,6 +54,9 @@ export const ChartSwitchSelectable = ({ inputRef: (ref) => { ref?.focus({ preventScroll: true }); }, + css: css` + width: 400px; + `, className: 'lnsChartSwitch__search', 'data-test-subj': 'lnsChartSwitchSearch', onChange: setSearchTerm, diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx index 679de93b8ae1b..1266d032e1d48 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx @@ -24,7 +24,7 @@ import { coreMock } from '@kbn/core/public/mocks'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { generateId } from '../../../id_generator'; -import { mountWithProvider } from '../../../mocks'; +import { mountWithReduxStore } from '../../../mocks'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { ReactWrapper } from 'enzyme'; import { createIndexPatternServiceMock } from '../../../mocks/data_views_service_mock'; @@ -81,7 +81,7 @@ describe('ConfigPanel', () => { query?: Query | AggregateQuery ) { (generateId as jest.Mock).mockReturnValue(`newId`); - return mountWithProvider( + return mountWithReduxStore( , { preloadedState: { diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 6b6facc06f339..67e77ddfdb8c0 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -6,7 +6,7 @@ */ import React, { useMemo, memo, useCallback } from 'react'; -import { EuiForm } from '@elastic/eui'; +import { EuiForm, euiBreakpoint, useEuiTheme } from '@elastic/eui'; import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import { isOfAggregateQueryType } from '@kbn/es-query'; import { @@ -15,6 +15,7 @@ import { } from '@kbn/unified-search-plugin/public'; import { DragDropIdentifier, DropType } from '@kbn/dom-drag-drop'; +import { css } from '@emotion/react'; import { changeIndexPattern, onDropToDimension, @@ -62,6 +63,9 @@ export function LayerPanels( (state) => state.lens ); + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; + const dispatchLens = useLensDispatch(); const layerIds = activeVisualization.getLayerIds(visualization.state); @@ -252,7 +256,20 @@ export function LayerPanels( const hideAddLayerButton = query && isOfAggregateQueryType(query); return ( - + {layerIds.map((layerId, layerIndex) => { const { hidden, groups } = activeVisualization.getConfiguration({ layerId, diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss deleted file mode 100644 index 5fab178a9d1f5..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss +++ /dev/null @@ -1,17 +0,0 @@ - -.lnsLayerAddButton { - &:last-child { - align-self: unset; - } - &:hover { - text-decoration: none; - - .lnsLayerAddButton__label { - text-decoration: underline; - } - - .lnsLayerAddButton__techBadge, - .lnsLayerAddButton__techBadge * { - cursor: pointer; - }} -} diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index 13694613d40fe..899125dac6a4e 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './dimension_container.scss'; - import React from 'react'; import { FlyoutContainer } from '../../../shared_components/flyout_container'; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss deleted file mode 100644 index 83cc6f95120b3..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ /dev/null @@ -1,92 +0,0 @@ -@import '../../../mixins'; - -.lnsLayerPanel { - margin-bottom: $euiSize; - - // disable focus ring for mouse clicks, leave it for keyboard users - &:focus:not(:focus-visible) { - animation: none !important; // sass-lint:disable-line no-important - } -} - -.lnsLayerPanel__layerHeader { - padding: $euiSize; - border-bottom: $euiBorderThin; -} - -// fixes truncation for too long chart switcher labels -.lnsLayerPanel__layerSettingsWrapper { - min-width: 0; -} - -.lnsLayerPanel__settingsStaticHeader { - padding-left: $euiSizeXS; -} - -.lnsLayerPanel__settingsStaticHeaderIcon { - margin-right: $euiSizeS; - vertical-align: inherit; -} - -.lnsLayerPanel__settingsStaticHeaderTitle { - display: inline; -} - -.lnsLayerPanel__row { - padding: $euiSize; - - &:last-child { - border-radius: 0 0 $euiBorderRadius $euiBorderRadius; - } - - // Add border to the top of the next same panel - &+& { - border-top: $euiBorderThin; - margin-top: 0; - } - - &>* { - margin-bottom: 0; - } - - // Targeting EUI class as we are unable to apply a class to this element in component - &, - .euiFormRow__fieldWrapper { - &>*+* { - margin-top: $euiSizeS; - } - } -} - -.lnsLayerPanel__group { - margin: (-$euiSizeXS) (-$euiSize); - padding: $euiSizeXS $euiSize; -} - -.lnsLayerPanel__styleEditor { - padding: $euiSize; -} - -// Start dimension style overrides - -.lnsLayerPanel__dimensionContainer { - position: relative; - &+& { - margin-top: $euiSizeS; - } -} - -.domDroppable--replacing { - .dimensionTrigger__textLabel { - text-decoration: line-through; - } -} - -// Added .lnsLayerPanel__dimension specificity required for animation style override -.lnsLayerPanel__dimension .lnsLayerPanel__dimensionLink { - &:focus { - background-color: transparent; - text-decoration-thickness: $euiBorderWidthThin !important; - @include passDownFocusRing('.dimensionTrigger__textLabel'); - } -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index 9cfb1597ffdd4..649a760cb0ac8 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -18,7 +18,7 @@ import { createMockVisualization, createMockFramePublicAPI, createMockDatasource, - mountWithProvider, + mountWithReduxStore, createMockedDragDropContext, renderWithReduxStore, } from '../../../mocks'; @@ -701,7 +701,7 @@ describe('LayerPanel', () => { target.columnId !== 'a' ? { dropTypes: ['field_replace'], nextLabel: '' } : undefined ); - const { instance } = await mountWithProvider( + const { instance } = mountWithReduxStore( @@ -760,7 +760,7 @@ describe('LayerPanel', () => { nextLabel: '', }); - const { instance } = await mountWithProvider( + const { instance } = mountWithReduxStore( @@ -820,7 +820,7 @@ describe('LayerPanel', () => { const holder = document.createElement('div'); document.body.appendChild(holder); - const { instance } = await mountWithProvider( + const { instance } = mountWithReduxStore( , @@ -858,7 +858,7 @@ describe('LayerPanel', () => { ], }); - const { instance } = await mountWithProvider( + const { instance } = mountWithReduxStore( @@ -895,7 +895,7 @@ describe('LayerPanel', () => { ], }); - const { instance } = await mountWithProvider( + const { instance } = mountWithReduxStore( @@ -936,7 +936,7 @@ describe('LayerPanel', () => { mockDatasource.onDrop.mockReturnValue(true); const updateVisualization = jest.fn(); - const { instance } = await mountWithProvider( + const { instance } = mountWithReduxStore( { mockDatasource.onDrop.mockReturnValue(false); const updateVisualization = jest.fn(); - const { instance } = await mountWithProvider( + const { instance } = mountWithReduxStore( { mockDatasource.onDrop.mockReturnValue(true); - const { instance } = await mountWithProvider( + const { instance } = mountWithReduxStore( diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index bbdb5956910b1..cdeb38da5e8d6 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './layer_panel.scss'; - import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'; import { EuiPanel, @@ -16,6 +14,7 @@ import { EuiFormRow, EuiText, EuiIconTip, + useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; @@ -49,6 +48,7 @@ export function LayerPanel(props: LayerPanelProps) { }>({}); const [isPanelSettingsOpen, setPanelSettingsOpen] = useState(false); + const { euiTheme } = useEuiTheme(); const { framePublicAPI, @@ -360,13 +360,30 @@ export function LayerPanel(props: LayerPanelProps) {
-
+
- + * { + margin-bottom: 0; + } + + // Targeting EUI class as we are unable to apply a class to this element in component + &, + .euiFormRow__fieldWrapper { + & > * + * { + margin-top: ${euiTheme.size.s}; + } + } + `} className="lnsLayerPanel__row" fullWidth label={ @@ -509,8 +551,11 @@ export function LayerPanel(props: LayerPanelProps) { <> {group.accessors.length ? ( {group.accessors.map((accessorConfig, accessorIndex) => { const { columnId } = accessorConfig; @@ -731,7 +776,7 @@ export function LayerPanel(props: LayerPanelProps) { isInlineEditing={isInlineEditing} handleClose={closeDimensionEditor} panel={ - <> +
{openColumnGroup && openColumnId && layerDatasource && @@ -777,7 +822,11 @@ export function LayerPanel(props: LayerPanelProps) { activeVisualization.DimensionEditorComponent && openColumnGroup?.enableDimensionEditor && ( <> -
+
)} - +
} /> diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.scss deleted file mode 100644 index 261d6672df93a..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.scss +++ /dev/null @@ -1,10 +0,0 @@ -.lnsDataPanelWrapper { - flex: 1 0 100%; - overflow: hidden; -} - -.lnsDataPanelWrapper__switchSource { - position: absolute; - right: $euiSize + $euiSizeXS; - top: $euiSize + $euiSizeXS; -} diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx index da78db7ed0bc6..4df8633e4e664 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './data_panel_wrapper.scss'; - import React, { useMemo, memo, useEffect, useCallback } from 'react'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; @@ -15,6 +13,7 @@ import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public' import { DragDropIdentifier } from '@kbn/dom-drag-drop'; import memoizeOne from 'memoize-one'; import { isEqual } from 'lodash'; +import { css } from '@emotion/react'; import { Easteregg } from './easteregg'; import { StateSetter, @@ -194,7 +193,14 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { <> {DataPanelComponent && ( -
+
{DataPanelComponent(datasourceProps)}
)} diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/frame_layout.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/frame_layout.scss deleted file mode 100644 index 143de90e44d71..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/frame_layout.scss +++ /dev/null @@ -1,123 +0,0 @@ -@import '../../variables'; - -.lnsFrameLayout { - padding: 0; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow: hidden; - flex-direction: column; - @include euiBreakpoint('xs', 's', 'm') { - position: static; - } -} - -.lnsFrameLayout__wrapper { - position: relative; -} - -.lnsFrameLayout__pageContent { - overflow: hidden; - flex-grow: 1; - flex-direction: row; - @include euiBreakpoint('xs', 's', 'm') { - flex-wrap: wrap; - overflow: auto; - > * { - flex-basis: 100%; - } - > .lnsFrameLayout__sidebar { - min-height: $euiSizeL * 15; - } - } -} - -.visEditor { - height: 100%; - @include flexParent(); - @include euiBreakpoint('xs', 's', 'm') { - .visualization { - // While we are on a small screen the visualization is below the - // editor. In this cases it needs a minimum height, since it would otherwise - // maybe end up with 0 height since it just gets the flexbox rest of the screen. - min-height: $euiSizeL * 15; - } - } - - /* 1. Without setting this to 0 you will run into a bug where the filter bar modal is hidden under -a tilemap in an iframe: https://github.com/elastic/kibana/issues/16457 */ - > .visualize { - height: 100%; - flex: 1 1 auto; - display: flex; - z-index: 0; /* 1 */ - } -} - -.lnsFrameLayout__pageBody { - min-width: $lnsPanelMinWidth + $euiSizeXL; - overflow: hidden auto; - display: flex; - flex-direction: column; - flex: 1 1 100%; - // Leave out bottom padding so the suggestions scrollbar stays flush to window edge - // Leave out left padding so the left sidebar's focus states are visible outside of content bounds - // This also means needing to add same amount of margin to page content and suggestion items - padding: $euiSize $euiSize 0; - position: relative; - z-index: $lnsZLevel1; - border-left: $euiBorderThin; - border-right: $euiBorderThin; - @include euiScrollBar; - &:first-child { - padding-left: $euiSize; - } - - &.lnsFrameLayout__pageBody-isFullscreen { - flex: 1; - padding: 0; - } -} - -.lnsFrameLayout__sidebar { - margin: 0; - flex: 1 0 18%; - min-width: $lnsPanelMinWidth + $euiSize; - display: flex; - flex-direction: column; - position: relative; -} - -.lnsFrameLayout-isFullscreen .lnsFrameLayout__sidebar--left { - // Hide the datapanel in fullscreen mode. Using display: none does trigger - // a rerender when the container becomes visible again, maybe pushing offscreen is better - display: none; -} - -.lnsFrameLayout__sidebar--right { - flex-basis: 25%; - min-width: $lnsPanelMinWidth + 70; - max-width: $euiFormMaxWidth + $euiSizeXXL; - max-height: 100%; - - @include euiBreakpoint('xs', 's', 'm') { - max-width: 100%; - } - - .lnsConfigPanel { - padding: $euiSize $euiSize $euiSizeXL ($euiFormMaxWidth + $euiSize); - margin-left: -$euiFormMaxWidth; - @include euiYScroll; - @include euiBreakpoint('xs', 's', 'm') { - padding-left: $euiSize; - margin-left: 0; - } - } -} - -.lnsFrameLayout__sidebar-isFullscreen { - flex: 1; - max-width: none; -} diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/frame_layout.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/frame_layout.tsx index 23fc53ababe9f..e5c023b818869 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/frame_layout.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/frame_layout.tsx @@ -5,12 +5,19 @@ * 2.0. */ -import './frame_layout.scss'; - import React from 'react'; -import { EuiScreenReaderOnly, EuiFlexGroup, EuiFlexItem, EuiPage, EuiPageBody } from '@elastic/eui'; +import { + EuiScreenReaderOnly, + EuiFlexGroup, + EuiFlexItem, + EuiPage, + EuiPageBody, + useEuiTheme, + euiBreakpoint, + type UseEuiTheme, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import classNames from 'classnames'; +import { css } from '@emotion/react'; import { useLensSelector, selectIsFullscreenDatasource } from '../../state_management'; export interface FrameLayoutProps { @@ -23,6 +30,8 @@ export interface FrameLayoutProps { export function FrameLayout(props: FrameLayoutProps) { const isFullscreen = useLensSelector(selectIsFullscreenDatasource); + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; return ( @@ -40,21 +49,57 @@ export function FrameLayout(props: FrameLayoutProps) { ) : null} - + * { + flex-basis: 100%; + } + } + `} >

@@ -66,9 +111,29 @@ export function FrameLayout(props: FrameLayoutProps) { {props.dataPanel}

@@ -108,3 +178,15 @@ export function FrameLayout(props: FrameLayoutProps) { ); } + +const sidebarStyles = (euiThemeContext: UseEuiTheme) => css` + margin: 0; + flex: 1 0 18%; + min-width: 304px; + display: flex; + flex-direction: column; + position: relative; + ${euiBreakpoint(euiThemeContext, ['xs', 's', 'm'])} { + min-height: 360px; + } +`; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss deleted file mode 100644 index a4af057dd33b0..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss +++ /dev/null @@ -1,106 +0,0 @@ -@import '../../mixins'; -@import '../../variables'; - -.lnsSuggestionPanel .euiAccordion__buttonContent { - width: 100%; -} - -.lnsSuggestionPanel__suggestions { - @include lnsOverflowShadowHorizontal; - padding-top: $euiSizeXS; - overflow-x: scroll; - overflow-y: hidden; - display: flex; - - // Padding / negative margins to make room for overflow shadow - padding-left: $euiSizeXS; - margin-left: -$euiSizeXS; - padding-right: $euiSizeXS; - margin-right: -$euiSizeXS; - @include euiScrollBar; -} - -.lnsSuggestionPanel__button { - position: relative; // Let the expression progress indicator position itself against the button - flex: 0 0 auto; - height: $lnsSuggestionHeight; - margin-right: $euiSizeS; - margin-left: calc($euiSizeXS / 2); - margin-bottom: calc($euiSizeXS / 2); - padding: 0 $euiSizeS; - box-shadow: none !important; // sass-lint:disable-line no-important - - &:focus { - transform: none !important; // sass-lint:disable-line no-important - @include euiFocusRing; - } - - .lnsSuggestionPanel__expressionRenderer { - position: static; // Let the progress indicator position itself against the button - } -} - -.lnsSuggestionPanel__button-isSelected { - background-color: $euiColorLightestShade !important; // sass-lint:disable-line no-important - border-color: $euiColorMediumShade !important; // sass-lint:disable-line no-important - - &:not(:focus) { - box-shadow: none !important; // sass-lint:disable-line no-important - } - - &:focus { - @include euiFocusRing; - } - - &:hover { - transform: none !important; // sass-lint:disable-line no-important - } -} - -.lnsSuggestionPanel__button-fixedWidth { - width: $lnsSuggestionWidth !important; // sass-lint:disable-line no-important -} - -.lnsSuggestionPanel__suggestionIcon { - color: $euiColorDarkShade; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - padding: $euiSizeS; - - &:not(:only-child) { - height: calc(100% - #{$euiSizeL}); - } -} - -.lnsSuggestionPanel__chartWrapper { - display: flex; - height: 100%; - width: 100%; - pointer-events: none; -} - -.lnsSuggestionPanel__chartWrapper--withLabel { - height: calc(100% - #{$euiSizeL}); -} - -.lnsSuggestionPanel__buttonLabel { - @include euiTextTruncate; - @include euiFontSizeXS; - display: block; - font-weight: $euiFontWeightBold; - text-align: center; - flex-grow: 0; -} - -.lnsSuggestionPanel__applyChangesPrompt { - height: $lnsSuggestionHeight; - background-color: $euiColorLightestShade !important; - - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx index 24fe5c971558c..eb422cc388bb9 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx @@ -13,14 +13,16 @@ import { createExpressionRendererMock, DatasourceMock, createMockFramePublicAPI, + renderWithReduxStore, } from '../../mocks'; +import { screen } from '@testing-library/react'; import { act } from 'react-dom/test-utils'; import { ReactExpressionRendererType } from '@kbn/expressions-plugin/public'; import { SuggestionPanel, SuggestionPanelProps, SuggestionPanelWrapper } from './suggestion_panel'; import { getSuggestions } from './suggestion_helpers'; import { EuiIcon, EuiPanel, EuiToolTip, EuiAccordion } from '@elastic/eui'; import { IconChartDatatable } from '@kbn/chart-icons'; -import { mountWithProvider } from '../../mocks'; +import { mountWithReduxStore } from '../../mocks'; import { coreMock } from '@kbn/core/public/mocks'; import { @@ -32,6 +34,7 @@ import { VisualizationState, } from '../../state_management'; import { setChangesApplied } from '../../state_management/lens_slice'; +import { userEvent } from '@testing-library/user-event'; const SELECTORS = { APPLY_CHANGES_BUTTON: 'button[data-test-subj="lnsApplyChanges__suggestions"]', @@ -113,7 +116,7 @@ describe('suggestion_panel', () => { }); it('should avoid completely to render SuggestionPanel when in fullscreen mode', async () => { - const { instance, lensStore } = await mountWithProvider( + const { instance, lensStore } = mountWithReduxStore( ); expect(instance.find(SuggestionPanel).exists()).toBe(true); @@ -128,7 +131,7 @@ describe('suggestion_panel', () => { }); it('should display apply-changes prompt when changes not applied', async () => { - const { instance, lensStore } = await mountWithProvider(, { + const { instance, lensStore } = mountWithReduxStore(, { preloadedState: { ...preloadedState, visualization: { @@ -160,7 +163,7 @@ describe('suggestion_panel', () => { }); it('should list passed in suggestions', async () => { - const { instance } = await mountWithProvider(, { + const { instance } = mountWithReduxStore(, { preloadedState, }); @@ -196,10 +199,9 @@ describe('suggestion_panel', () => { }); it('should not update suggestions if current state is moved to staged preview', async () => { - const { instance, lensStore } = await mountWithProvider( - , - { preloadedState } - ); + const { instance, lensStore } = mountWithReduxStore(, { + preloadedState, + }); getSuggestionsMock.mockClear(); lensStore.dispatch(setState({ stagedPreview })); instance.update(); @@ -207,10 +209,9 @@ describe('suggestion_panel', () => { }); it('should update suggestions if staged preview is removed', async () => { - const { instance, lensStore } = await mountWithProvider( - , - { preloadedState } - ); + const { instance, lensStore } = mountWithReduxStore(, { + preloadedState, + }); getSuggestionsMock.mockClear(); lensStore.dispatch(setState({ stagedPreview, ...suggestionState })); instance.update(); @@ -219,25 +220,19 @@ describe('suggestion_panel', () => { expect(getSuggestionsMock).toHaveBeenCalledTimes(1); }); - it('should highlight currently active suggestion', async () => { - const { instance } = await mountWithProvider(, { + it('should select currently active suggestion', async () => { + const getSuggestionByName = (name: string) => screen.getByRole('listitem', { name }); + + renderWithReduxStore(, undefined, { preloadedState, }); - act(() => { - instance.find(SELECTORS.SUGGESTION_TILE_BUTTON).at(2).simulate('click'); - }); - - instance.update(); - - expect(instance.find(SELECTORS.SUGGESTION_TILE_BUTTON).at(2).prop('className')).toContain( - 'lnsSuggestionPanel__button-isSelected' - ); + expect(getSuggestionByName('Current visualization')).toHaveAttribute('aria-current', 'true'); + await userEvent.click(getSuggestionByName('Suggestion1')); + expect(getSuggestionByName('Suggestion1')).toHaveAttribute('aria-current', 'true'); }); it('should rollback suggestion if current panel is clicked', async () => { - const { instance, lensStore } = await mountWithProvider( - - ); + const { instance, lensStore } = mountWithReduxStore(); act(() => { instance.find(SELECTORS.SUGGESTION_TILE_BUTTON).at(2).simulate('click'); @@ -262,7 +257,7 @@ describe('suggestion_panel', () => { }); it('should dispatch visualization switch action if suggestion is clicked', async () => { - const { instance, lensStore } = await mountWithProvider(, { + const { instance, lensStore } = mountWithReduxStore(, { preloadedState, }); @@ -316,7 +311,7 @@ describe('suggestion_panel', () => { mockDatasource.toExpression.mockReturnValue('datasource_expression'); - const { instance } = await mountWithProvider(, { + const { instance } = mountWithReduxStore(, { preloadedState, }); @@ -344,14 +339,14 @@ describe('suggestion_panel', () => { }, }; - const { instance } = await mountWithProvider(, { + const { instance } = mountWithReduxStore(, { preloadedState: newPreloadedState, }); expect(instance.html()).toEqual(null); }); it('should hide the selections when the accordion is hidden', async () => { - const { instance } = await mountWithProvider(); + const { instance } = mountWithReduxStore(); expect(instance.find(EuiAccordion)).toHaveLength(1); act(() => { instance.find(EuiAccordion).at(0).simulate('change'); @@ -386,7 +381,7 @@ describe('suggestion_panel', () => { .mockReturnValueOnce('test | expression'); mockDatasource.toExpression.mockReturnValue('datasource_expression'); - mountWithProvider(); + mountWithReduxStore(); expect(expressionRendererMock).toHaveBeenCalledTimes(1); const passedExpression = (expressionRendererMock as jest.Mock).mock.calls[0][0].expression; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index 989abb55e8ada..d07d1240f3488 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './suggestion_panel.scss'; - import { camelCase, pick } from 'lodash'; import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -22,12 +20,17 @@ import { EuiAccordion, EuiText, EuiNotificationBadge, + type UseEuiTheme, + useEuiTheme, + euiFocusRing, + useEuiFontSize, + euiTextTruncate, + transparentize, } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import { IconType } from '@elastic/eui/src/components/icon/icon'; import { Ast, fromExpression, toExpression } from '@kbn/interpreter'; import { i18n } from '@kbn/i18n'; -import classNames from 'classnames'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { ExecutionContextSearch } from '@kbn/es-query'; import { @@ -126,8 +129,10 @@ const PreviewRenderer = ({ hasError: boolean; onRender: () => void; }) => { + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; const onErrorMessage = ( -
+
{!expression || hasError ? ( onErrorMessage @@ -188,6 +197,9 @@ const SuggestionPreview = ({ onRender: () => void; wrapSuggestions?: boolean; }) => { + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; + const xsFontSize = useEuiFontSize('xs'); return ( ) : ( - + )} {showTitleAsLabel && ( - {preview.title} + + {preview.title} + )}
@@ -266,6 +327,8 @@ export function SuggestionPanel({ const existsStagedPreview = useLensSelector((state) => Boolean(state.lens.stagedPreview)); const currentVisualization = useLensSelector(selectCurrentVisualization); const currentDatasourceStates = useLensSelector(selectCurrentDatasourceStates); + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; const framePublicAPI = useLensSelector((state) => selectFramePublicAPI(state, datasourceMap)); const changesApplied = useLensSelector(selectChangesApplied); @@ -438,7 +501,19 @@ export function SuggestionPanel({ } const renderApplyChangesPrompt = () => ( - +

{changesApplied ? renderSuggestionsUI() : renderApplyChangesPrompt()} @@ -699,3 +787,18 @@ function preparePreviewExpression( return typeof expression === 'string' ? fromExpression(expression) : expression; } + +const suggestionStyles = { + icon: ({ euiTheme }: UseEuiTheme) => css` + color: ${euiTheme.colors.darkShade}; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: ${euiTheme.size.s}; + &:not(:only-child) { + height: calc(100% - ${euiTheme.size.l}); + } + `, +}; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/geo_field_workspace_panel.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/geo_field_workspace_panel.scss deleted file mode 100644 index ba2d455c97692..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/geo_field_workspace_panel.scss +++ /dev/null @@ -1,5 +0,0 @@ -.lnsVisualizeGeoFieldWorkspacePanel__dragDrop { - padding: $euiSizeXXL ($euiSizeXL * 2); - border: $euiBorderThin; - border-radius: $euiBorderRadius; -} diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/geo_field_workspace_panel.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/geo_field_workspace_panel.tsx index 781f28ac1f86c..6f65a987c8372 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/geo_field_workspace_panel.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/geo_field_workspace_panel.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { EuiText } from '@elastic/eui'; +import { EuiText, UseEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { UiActionsStart, VISUALIZE_GEO_FIELD_TRIGGER } from '@kbn/ui-actions-plugin/public'; @@ -15,7 +15,7 @@ import { Droppable } from '@kbn/dom-drag-drop'; import { IndexPattern } from '../../../types'; import { getVisualizeGeoFieldMessage } from '../../../utils'; import { APP_ID } from '../../../../common/constants'; -import './geo_field_workspace_panel.scss'; +import { pageContentBodyStyles, promptIllustrationStyle } from './workspace_panel'; interface Props { fieldType: string; @@ -45,15 +45,15 @@ export function GeoFieldWorkspacePanel(props: Props) { } return ( -
- +
+

{getVisualizeGeoFieldMessage(props.fieldType)}

- + ); } + +const droppableStyles = ({ euiTheme }: UseEuiTheme) => { + return ` + padding: ${euiTheme.size.xxl} ${euiTheme.size.xxxl}; + border: ${euiTheme.border.thin}; + border-radius: ${euiTheme.border.radius}; + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/message_list.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/message_list.scss deleted file mode 100644 index 6d91fc7d6465f..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/message_list.scss +++ /dev/null @@ -1,26 +0,0 @@ -.lnsWorkspaceWarning__buttonText { - @include euiBreakpoint('xs', 's', 'm', 'l') { - @include euiScreenReaderOnly; - } -} - -.lnsWorkspaceWarningList { - max-height: $euiSize * 20; - width: $euiSize * 16; - @include euiYScroll; -} - -.lnsWorkspaceWarningList__item { - & + & { - border-top: $euiBorderThin; - } -} - -.lnsWorkspaceWarningList__textItem { - padding: $euiSize; -} - -.lnsWorkspaceWarningList__description { - overflow-wrap: break-word; - min-width: 0; -} diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/message_list.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/message_list.tsx index 75bd18c0f8457..7c6158ef7c72e 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/message_list.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/message_list.tsx @@ -5,9 +5,6 @@ * 2.0. */ -import './workspace_panel_wrapper.scss'; -import './message_list.scss'; - import React, { useState } from 'react'; import { EuiPopover, @@ -17,6 +14,7 @@ import { EuiToolTip, EuiFlexGroup, EuiFlexItem, + type UseEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css, SerializedStyles } from '@emotion/react'; @@ -78,7 +76,6 @@ export const MessageList = ({ minWidth={0} color={errorCount ? 'danger' : 'warning'} onClick={onButtonClick} - className="lnsWorkspaceWarning__button" data-test-subj="lens-message-list-trigger" title={buttonLabel} css={customButtonStyles} @@ -106,11 +103,11 @@ export const MessageList = ({ isOpen={isPopoverOpen} closePopover={closePopover} > -
    +
      {messages.map(({ hidePopoverIcon = false, ...message }, index) => (
    • {typeof message.longMessage === 'function' ? ( @@ -119,7 +116,7 @@ export const MessageList = ({ {!hidePopoverIcon && ( @@ -130,7 +127,7 @@ export const MessageList = ({ )} )} - + {message.longMessage} @@ -141,3 +138,22 @@ export const MessageList = ({ ); }; + +const workspaceWarningListStyles = { + self: css` + max-height: 320px; + width: 256px; + `, + item: ({ euiTheme }: UseEuiTheme) => ` + & + & { + border-top: 1px solid ${euiTheme.colors.lightShade}; + } + `, + textItem: ({ euiTheme }: UseEuiTheme) => ` + padding: ${euiTheme.size.base} + `, + description: css` + overflow-wrap: break-word; + min-width: 0; + `, +}; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/title.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/title.tsx index b7d3d211eb777..eb1de4f07d0c2 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/title.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/title.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './workspace_panel_wrapper.scss'; - import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiScreenReaderOnly } from '@elastic/eui'; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx index b57d5dc022a53..194f20fd6bbe1 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx @@ -17,7 +17,7 @@ import { renderWithReduxStore, } from '../../../mocks'; -import { mockDataPlugin, mountWithProvider } from '../../../mocks'; +import { mockDataPlugin, mountWithReduxStore } from '../../../mocks'; import { WorkspacePanel } from './workspace_panel'; import { ReactWrapper } from 'enzyme'; @@ -367,7 +367,7 @@ describe('workspace_panel', () => { const visualizationShowing = () => instance.exists(expressionRendererMock); - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { mockDatasource.getLayers.mockReturnValue(['first']); const props = defaultProps; - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { mockDatasource.getLayers.mockReturnValue(['first']); const props = defaultProps; - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { mockDatasource.getLayers.mockReturnValue(['first']); const props = defaultProps; - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { mockDatasource.toExpression.mockReturnValue('datasource'); mockDatasource.getLayers.mockReturnValue(['table1']); - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { expressionRendererMock = jest.fn((_arg) => ); - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { .mockReturnValueOnce('datasource second'); expressionRendererMock = jest.fn((_arg) => ); - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { const getUserMessages = jest.fn(() => messages); - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { let userMessages = [] as UserMessage[]; const getUserMessageFn = jest.fn(() => userMessages); - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { const mockAddUserMessages = jest.fn(() => mockRemoveUserMessages); const mockGetUserMessages = jest.fn(() => []); - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { first: mockDatasource.publicAPIMock, }; - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( { framePublicAPI.datasourceLayers = { first: mockDatasource.publicAPIMock, }; - const mounted = await mountWithProvider( + const mounted = mountWithReduxStore( (NaN); const esTookTime = useRef(0); + const { euiTheme } = useEuiTheme(); + const onRender$ = useCallback(() => { if (renderDeps.current) { if (!initialVisualizationRenderComplete.current) { @@ -495,19 +508,18 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ } return ( - +

      @@ -521,15 +533,21 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({

      {!expressionExists && ( - <> - -

      - {i18n.translate('xpack.lens.editorFrame.emptyWorkspaceHeading', { - defaultMessage: 'Lens is the recommended editor for creating visualizations', - })} -

      +
      + + {i18n.translate('xpack.lens.editorFrame.emptyWorkspaceHeading', { + defaultMessage: 'Lens is the recommended editor for creating visualizations', + })} -

      + +

      - +
      )}
      @@ -557,18 +575,13 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ }); return ( - +
      {applyChangesString}

      @@ -577,7 +590,8 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ })}

      -

      + +

      -

      {renderWorkspaceContents()}
      +
      + {renderWorkspaceContents()} +
      ); }; @@ -730,6 +759,7 @@ export const VisualizationWrapper = ({ onComponentRendered(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const { euiTheme } = useEuiTheme(); const searchContext = useLensSelector(selectExecutionContextSearch); // Used for reporting @@ -765,7 +795,16 @@ export const VisualizationWrapper = ({ return (
      ); }; + +export const promptIllustrationStyle = ({ euiTheme }: UseEuiTheme) => { + return css` + overflow: visible; // Shows arrow animation when it gets out of bounds + margin-top: 0; + margin-bottom: -${euiTheme.size.base}; + + margin-right: auto; + margin-left: auto; + max-width: 176px; + max-height: 176px; + `; +}; + +export const pageContentBodyStyles = ({ euiTheme }: UseEuiTheme) => { + return css` + flex-grow: 1; + display: flex; + align-items: stretch; + justify-content: stretch; + border: ${euiTheme.border.thin}; + border-radius: ${euiTheme.border.radius.medium}; + background: ${euiTheme.colors.emptyShade}; + height: 100%; + overflow: hidden; + & > * { + flex: 1 1 100%; + display: flex; + align-items: center; + justify-content: center; + } + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss deleted file mode 100644 index edc698dbcbcf2..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss +++ /dev/null @@ -1,185 +0,0 @@ -@import '../../../mixins'; - -.lnsWorkspacePanelWrapper { - margin-bottom: $euiSize; - display: flex; - flex-direction: column; - position: relative; // For positioning the dnd overlay - min-height: $euiSizeXXL * 10; - overflow: visible; - height: 100%; - - .lnsWorkspacePanelWrapper__content { - width: 100%; - height: 100%; - position: absolute; - } - - .lnsWorkspacePanelWrapper__pageContentBody { - flex-grow: 1; - display: flex; - align-items: stretch; - justify-content: stretch; - border: $euiBorderThin; - border-radius: $euiBorderRadius; - background: $euiColorEmptyShade; - height: 100%; - @include euiScrollBar; - &>* { - flex: 1 1 100%; - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; - } - } - - &.lnsWorkspacePanelWrapper--fullscreen { - margin-bottom: 0; - - .lnsWorkspacePanelWrapper__pageContentBody { - box-shadow: none; - } - } - -} - -.lnsWorkspacePanel__dragDrop { - &.domDroppable--active { - p { - transition: filter $euiAnimSpeedFast ease-in-out; - filter: blur(5px); - } - - .lnsExpressionRenderer { - transition: filter $euiAnimSpeedNormal ease-in-out, opacity $euiAnimSpeedNormal ease-in-out; - filter: blur($euiSizeXS); - opacity: .25; - } - } - - &.domDroppable--hover { - .lnsDropIllustration__hand { - animation: lnsWorkspacePanel__illustrationPulseContinuous 1.5s ease-in-out 0s infinite normal forwards; - } - } - - &.lnsWorkspacePanel__dragDrop--fullscreen { - border: none; - } -} - -.lnsWorkspacePanel__emptyContent { - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; - display: flex; - justify-content: center; - align-items: center; - transition: background-color $euiAnimSpeedFast ease-in-out; - - .lnsWorkspacePanel__actions { - margin-top: $euiSizeL; - } -} - -.lnsWorkspacePanelWrapper__toolbar { - margin-bottom: $euiSizeXS; -} - -.lnsWorkspacePanelWrapper__toolbar--fullscreen { - background-color: $euiColorEmptyShade; - justify-content: flex-end; - margin-bottom: 0; - padding: $euiSizeS $euiSizeS 0; -} - -.lnsWorkspacePanelWrapper__applyButton .euiButton__text { - @include euiBreakpoint('xs', 's', 'm', 'l') { - @include euiScreenReaderOnly; - } -} - -.lnsWorkspacePanel__promptIllustration { - overflow: visible; // Shows arrow animation when it gets out of bounds - margin-top: 0; - margin-bottom: -$euiSize; - - margin-right: auto; - margin-left: auto; - max-width: 176px; - max-height: 176px; -} - -.lnsWorkspacePanel__dropIllustration { - // Drop shadow values is a dupe of @euiBottomShadowMedium but used as a filter - // Hard-coded px values OK (@cchaos) - // sass-lint:disable-block indentation - filter: - drop-shadow(0 6px 12px transparentize($euiShadowColor, .8)) drop-shadow(0 4px 4px transparentize($euiShadowColor, .8)) drop-shadow(0 2px 2px transparentize($euiShadowColor, .8)); -} - -.lnsDropIllustration__adjustFill { - fill: $euiColorFullShade; -} - -.lnsDropIllustration__hand { - animation: lnsWorkspacePanel__illustrationPulseArrow 5s ease-in-out 0s infinite normal forwards; -} - -@keyframes lnsWorkspacePanel__illustrationPulseArrow { - 0% { - transform: translateY(0%); - } - - 65% { - transform: translateY(0%); - } - - 72% { - transform: translateY(10%); - } - - 79% { - transform: translateY(7%); - } - - 86% { - transform: translateY(10%); - } - - 95% { - transform: translateY(0); - } -} - -@keyframes lnsWorkspacePanel__illustrationPulseContinuous { - 0% { - transform: translateY(10%); - } - - 25% { - transform: translateY(15%); - } - - 50% { - transform: translateY(10%); - } - - 75% { - transform: translateY(15%); - } - - 100% { - transform: translateY(10%); - } -} - -.lnsVisualizationToolbar--fixed { - position: fixed; - width: 100%; - z-index: 1; - background-color: $euiColorLightestShade; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index a4077b6919823..123760851091d 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -5,10 +5,8 @@ * 2.0. */ -import './workspace_panel_wrapper.scss'; - import React, { useCallback } from 'react'; -import { EuiPageTemplate, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui'; +import { EuiPageTemplate, EuiFlexGroup, EuiFlexItem, EuiButton, useEuiTheme } from '@elastic/eui'; import classNames from 'classnames'; import { FormattedMessage } from '@kbn/i18n-react'; import { ChartSizeSpec } from '@kbn/chart-expressions-common'; @@ -74,11 +72,10 @@ const getAspectRatioStyles = ({ x, y }: { x: number; y: number }) => { export function VisualizationToolbar(props: { activeVisualization: Visualization | null; framePublicAPI: FramePublicAPI; - isFixedPosition?: boolean; }) { const dispatchLens = useLensDispatch(); const visualization = useLensSelector(selectVisualizationState); - const { activeVisualization, isFixedPosition } = props; + const { activeVisualization } = props; const setVisualizationState = useCallback( (newState: unknown) => { if (!activeVisualization) { @@ -99,12 +96,7 @@ export function VisualizationToolbar(props: { return ( <> {ToolbarComponent && ( - + {ToolbarComponent({ frame: props.framePublicAPI, state: visualization.state, @@ -128,6 +120,9 @@ export function WorkspacePanelWrapper({ }: WorkspacePanelWrapperProps) { const dispatchLens = useLensDispatch(); + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; + const changesApplied = useLensSelector(selectChangesApplied); const autoApplyEnabled = useLensSelector(selectAutoApplyEnabled); @@ -180,9 +175,16 @@ export function WorkspacePanelWrapper({ alignItems="flexEnd" gutterSize="s" direction="row" - className={classNames('lnsWorkspacePanelWrapper__toolbar', { - 'lnsWorkspacePanelWrapper__toolbar--fullscreen': isFullscreen, - })} + css={css` + margin-bottom: ${euiTheme.size.xs}; + ${isFullscreen && + ` + background-color: ${euiTheme.colors.emptyShade}; + justify-content: flex-end; + margin-bottom: 0; + padding: ${euiTheme.size.s} ${euiTheme.size.s} 0; + `} + `} responsive={false} > {!isFullscreen && ( @@ -238,10 +240,30 @@ export function WorkspacePanelWrapper({ contentProps={{ className: 'lnsWorkspacePanelWrapper__content', }} - className={classNames('lnsWorkspacePanelWrapper stretch-for-sharing', { - 'lnsWorkspacePanelWrapper--fullscreen': isFullscreen, - })} - css={{ height: '100%' }} + className={classNames('lnsWorkspacePanelWrapper stretch-for-sharing')} + css={css` + height: 100%; + margin-bottom: ${euiTheme.size.base}; + display: flex; + flex-direction: column; + position: relative; // For positioning the dnd overlay + min-height: 400px; + overflow: visible; + height: 100%; + + .lnsWorkspacePanelWrapper__content { + width: 100%; + height: 100%; + position: absolute; + } + ${isFullscreen && + ` + margin-bottom: 0; + .lnsWorkspacePanelWrapper__content { + padding: ${euiTheme.size.s} + } + `} + `} color="transparent" > { return ( -
      +
      { + return css` + position: relative; + width: 100%; + height: 100%; + display: flex; + overflow: auto; + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/mocks/index.ts b/x-pack/platform/plugins/shared/lens/public/mocks/index.ts index 91da76b4acee6..46f166c201239 100644 --- a/x-pack/platform/plugins/shared/lens/public/mocks/index.ts +++ b/x-pack/platform/plugins/shared/lens/public/mocks/index.ts @@ -25,7 +25,7 @@ export { mockDatasourceStates, defaultState, makeLensStore, - mountWithProvider, + mountWithReduxStore, renderWithReduxStore, } from './store_mocks'; export { lensPluginMock } from './lens_plugin_mock'; diff --git a/x-pack/platform/plugins/shared/lens/public/mocks/store_mocks.tsx b/x-pack/platform/plugins/shared/lens/public/mocks/store_mocks.tsx index 87667c21fed20..3a4510b7ff1b9 100644 --- a/x-pack/platform/plugins/shared/lens/public/mocks/store_mocks.tsx +++ b/x-pack/platform/plugins/shared/lens/public/mocks/store_mocks.tsx @@ -6,12 +6,12 @@ */ import React, { PropsWithChildren, ReactElement } from 'react'; -import { ReactWrapper, mount } from 'enzyme'; +import { ReactWrapper } from 'enzyme'; import { Provider } from 'react-redux'; import { PreloadedState } from '@reduxjs/toolkit'; -import { RenderOptions, render } from '@testing-library/react'; -import { I18nProvider } from '@kbn/i18n-react'; +import { RenderOptions } from '@testing-library/react'; import { LensAppServices } from '../app_plugin/types'; +import { mountWithProviders, renderWithProviders } from '../test_utils/test_utils'; import { makeConfigureStore, LensAppState, LensState, LensStoreDeps } from '../state_management'; import { getResolvedDateRange } from '../utils'; import { DatasourceMap, VisualizationMap } from '../types'; @@ -19,21 +19,15 @@ import { mockVisualizationMap } from './visualization_mock'; import { mockDatasourceMap } from './datasource_mock'; import { makeDefaultServices } from './services_mock'; -export const mockStoreDeps = ( - { - lensServices = makeDefaultServices(), - datasourceMap = mockDatasourceMap(), - visualizationMap = mockVisualizationMap(), - }: { - lensServices?: LensAppServices; - datasourceMap?: DatasourceMap; - visualizationMap?: VisualizationMap; - } = { - lensServices: makeDefaultServices(), - datasourceMap: mockDatasourceMap(), - visualizationMap: mockVisualizationMap(), - } -) => ({ +export const mockStoreDeps = ({ + lensServices = makeDefaultServices(), + datasourceMap = mockDatasourceMap(), + visualizationMap = mockVisualizationMap(), +}: { + lensServices?: LensAppServices; + datasourceMap?: DatasourceMap; + visualizationMap?: VisualizationMap; +} = {}) => ({ datasourceMap, visualizationMap, lensServices, @@ -86,17 +80,13 @@ export const renderWithReduxStore = ( const CustomWrapper = wrapper as React.ComponentType>; - const Wrapper: React.FC> = ({ children }) => { - return ( - - - {wrapper ? {children} : children} - - - ); - }; + const Wrapper: React.FC> = ({ children }) => ( + + {wrapper ? {children} : children} + + ); - const rtlRender = render(ui, { wrapper: Wrapper, ...options }); + const rtlRender = renderWithProviders(ui, { wrapper: Wrapper, ...options }); return { store, @@ -106,13 +96,11 @@ export const renderWithReduxStore = ( export function makeLensStore({ preloadedState, - dispatch, storeDeps = mockStoreDeps(), }: { storeDeps?: LensStoreDeps; preloadedState?: Partial; - dispatch?: jest.Mock; -}) { +} = {}) { const data = storeDeps.lensServices.data; const store = makeConfigureStore(storeDeps, { lens: { @@ -124,18 +112,17 @@ export function makeLensStore({ }, } as unknown as PreloadedState); - const origDispatch = store.dispatch; - store.dispatch = jest.fn(dispatch || origDispatch); + store.dispatch = jest.spyOn(store, 'dispatch') as jest.Mock; return { store, deps: storeDeps }; } export interface MountStoreProps { storeDeps?: LensStoreDeps; preloadedState?: Partial; - dispatch?: jest.Mock; } -export const mountWithProvider = async ( +// legacy enzyme usage: remove when all tests are migrated to @testing-library/react +export const mountWithReduxStore = ( component: React.ReactElement, store?: MountStoreProps, options?: { @@ -144,52 +131,24 @@ export const mountWithProvider = async ( attachTo?: HTMLElement; } ) => { - const { mountArgs, lensStore, deps } = getMountWithProviderParams(component, store, options); - const instance = mount(mountArgs.component, mountArgs.options); - return { instance, lensStore, deps }; -}; - -const getMountWithProviderParams = ( - component: React.ReactElement, - store?: MountStoreProps, - options?: { - wrappingComponent?: React.FC>; - wrappingComponentProps?: Record; - attachTo?: HTMLElement; - } -) => { - const { store: lensStore, deps } = makeLensStore(store || {}); + const { store: lensStore, deps } = makeLensStore(store); let wrappingComponent: React.FC> = ({ children }) => ( - - {children} - + {children} ); - - let restOptions: { - attachTo?: HTMLElement | undefined; - } = {}; - if (options) { - const { wrappingComponent: _wrappingComponent, wrappingComponentProps, ...rest } = options; - restOptions = rest; - - if (_wrappingComponent) { - wrappingComponent = ({ children }) => { - return _wrappingComponent({ - ...wrappingComponentProps, - children: {children}, - }); - }; - } + if (options?.wrappingComponent) { + wrappingComponent = ({ children }) => { + return options?.wrappingComponent?.({ + ...options?.wrappingComponentProps, + children: wrappingComponent({ children }), + }); + }; } - const mountArgs = { - component, - options: { - wrappingComponent, - ...restOptions, - } as unknown as ReactWrapper, - }; + const instance = mountWithProviders(component, { + ...options, + wrappingComponent, + } as unknown as ReactWrapper); - return { mountArgs, lensStore, deps }; + return { instance, lensStore, deps }; }; diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/expression_wrapper.tsx b/x-pack/platform/plugins/shared/lens/public/react_embeddable/expression_wrapper.tsx index e0d21d9ba8356..f402a55c78aad 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/expression_wrapper.tsx +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/expression_wrapper.tsx @@ -18,6 +18,7 @@ import classNames from 'classnames'; import { getOriginalRequestErrorMessages } from '../editor_frame_service/error_helper'; import { LensInspector } from '../lens_inspector_service'; import { UserMessage } from '../types'; +import { lnsExpressionRendererStyle } from '../expression_renderer_styles'; export interface ExpressionWrapperProps { ExpressionRenderer: ReactExpressionRendererType; @@ -76,12 +77,12 @@ export function ExpressionWrapper({ if (!expression) return null; return (
      ` + clip-path: polygon(-100% 0, 100% 0, 100% 100%, -100% 100%); + max-inline-size: 640px; + min-inline-size: 256px; + background:${euiTheme.colors.backgroundBaseSubdued}; + @include euiBreakpoint('xs', 's', 'm') { + clip-path: none; + } + .kbnOverlayMountWrapper { + padding-left: 400px; + margin-left: -400px; + pointer-events: none; + .euiFlyoutFooter { + pointer-events: auto; + } + } +`; diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/user_messages/error_panel.tsx b/x-pack/platform/plugins/shared/lens/public/react_embeddable/user_messages/error_panel.tsx index ee050382914c8..01c054a1e705c 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/user_messages/error_panel.tsx +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/user_messages/error_panel.tsx @@ -8,6 +8,7 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; import { UserMessage } from '../../types'; import { getLongMessage } from '../../user_messages_utils'; @@ -24,7 +25,16 @@ export function VisualizationErrorPanel({ const showMore = errors.length > 1; const canFixInLens = canEdit && errors.some(({ fixableInEditor }) => fixableInEditor); return ( -
      +
      { @@ -65,7 +64,13 @@ export const EmbeddableFeatureBadge = ({ messages }: { messages: UserMessage[] } gap: ${euiTheme.size.xs}; } &:hover { - color: ${euiTheme.colors.text}; + color: ${euiTheme.colors.textParagraph}; + } + // Make the visualization modifiers icon appear only on panel hover + .embPanel__content:hover & { + background: ${euiTheme.colors.backgroundBasePlain}; + transition: color ${euiTheme.animation.slow}, background ${euiTheme.animation.slow}; + color: ${euiTheme.colors.textParagraph}; } `} iconType="wrench" @@ -101,7 +106,12 @@ export const EmbeddableFeatureBadge = ({ messages }: { messages: UserMessage[] }

      {shortMessage}

      -
        +
          {messageGroup.map((message, i) => ( {getLongMessage(message)} ))} diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/toolbar_button.scss b/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/toolbar_button.scss deleted file mode 100644 index abd4d45a9e955..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/toolbar_button.scss +++ /dev/null @@ -1,61 +0,0 @@ -.kbnToolbarButton { - line-height: $euiButtonHeight; // Keeps alignment of text and chart icon - - // todo: once issue https://github.com/elastic/eui/issues/4730 is merged, this code might be safe to remove - // Some toolbar buttons are just icons, but EuiButton comes with margin and min-width that need to be removed - min-width: 0; - border-width: $euiBorderWidthThin; - border-style: solid; - border-color: $euiBorderColor; // Lighten the border color for all states - - // Override background color for non-disabled buttons - &:not(:disabled) { - background-color: $euiColorEmptyShade; - } - - .kbnToolbarButton__text > svg { - margin-top: -1px; // Just some weird alignment issue when icon is the child not the `iconType` - } - - .kbnToolbarButton__text:empty { - margin: 0; - } - - // Toolbar buttons don't look good with centered text when fullWidth - &[class*='fullWidth'] { - text-align: left; - - .kbnToolbarButton__content { - justify-content: space-between; - } - } -} - -.kbnToolbarButton--groupLeft { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.kbnToolbarButton--groupCenter { - border-radius: 0; - border-left: none; -} - -.kbnToolbarButton--groupRight { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - border-left: none; -} - -.kbnToolbarButton--bold { - font-weight: $euiFontWeightBold; -} - -.kbnToolbarButton--normal { - font-weight: $euiFontWeightRegular; -} - -.kbnToolbarButton--s { - box-shadow: none !important; // sass-lint:disable-line no-important - font-size: $euiFontSizeS; -} diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/toolbar_button.tsx b/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/toolbar_button.tsx index 395864f1d1a51..36406441cc72d 100644 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/toolbar_button.tsx +++ b/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/toolbar_button.tsx @@ -5,10 +5,17 @@ * 2.0. */ -import './toolbar_button.scss'; import React from 'react'; import classNames from 'classnames'; -import { EuiButton, PropsOf, EuiButtonProps } from '@elastic/eui'; +import { + EuiButton, + PropsOf, + EuiButtonProps, + type UseEuiTheme, + euiFontSize, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/react'; const groupPositionToClassMap = { none: null, @@ -57,6 +64,7 @@ export const ToolbarButton: React.FunctionComponent = ({ textProps, ...rest }) => { + const euiThemeContext = useEuiTheme(); const classes = classNames( 'kbnToolbarButton', groupPositionToClassMap[groupPosition], @@ -69,6 +77,7 @@ export const ToolbarButton: React.FunctionComponent = ({ data-test-subj={dataTestSubj} className={classes} iconSide="right" + css={toolbarButtonStyles(euiThemeContext)} iconType={hasArrow ? 'arrowDown' : ''} color="text" contentProps={{ @@ -85,3 +94,70 @@ export const ToolbarButton: React.FunctionComponent = ({ ); }; + +const toolbarButtonStyles = (euiThemeContext: UseEuiTheme) => { + const { euiTheme } = euiThemeContext; + return css` + &.kbnToolbarButton { + line-height: ${euiTheme.size.xxl}; // Keeps alignment of text and chart icon + + // todo: once issue https://github.com/elastic/eui/issues/4730 is merged, this code might be safe to remove + // Some toolbar buttons are just icons, but EuiButton comes with margin and min-width that need to be removed + min-width: 0; + border-width: ${euiTheme.border.width.thin}; + border-style: solid; + border-color: ${euiTheme.border.color}; // Lighten the border color for all states + + // Override background color for non-disabled buttons + &:not(:disabled) { + background-color: ${euiTheme.colors.backgroundBasePlain}; + } + + &.kbnToolbarButton__text > svg { + margin-top: -1px; // Just some weird alignment issue when icon is the child not the iconType + } + + &.kbnToolbarButton__text:empty { + margin: 0; + } + + // Toolbar buttons don't look good with centered text when fullWidth + &[class*='fullWidth'] { + text-align: left; + + .kbnToolbarButton__content { + justify-content: space-between; + } + } + } + + &.kbnToolbarButton--groupLeft { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + &.kbnToolbarButton--groupCenter { + border-radius: 0; + border-left: none; + } + + &.kbnToolbarButton--groupRight { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: none; + } + + &.kbnToolbarButton--bold { + font-weight: ${euiTheme.font.weight.bold}; + } + + &.kbnToolbarButton--normal { + font-weight: ${euiTheme.font.weight.regular}; + } + + &.kbnToolbarButton--s { + box-shadow: none !important; // sass-lint:disable-line no-important + font-size: ${euiFontSize(euiThemeContext, 's').fontSize}; + } + `; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/trigger.test.tsx b/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/trigger.test.tsx index 7ce360628b853..a305d9374e67d 100644 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/trigger.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/shared_components/dataview_picker/trigger.test.tsx @@ -6,21 +6,23 @@ */ import React from 'react'; import { EuiIcon } from '@elastic/eui'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { TriggerButton } from './trigger'; +import { renderWithProviders } from '../../test_utils/test_utils'; +import * as ToolbarButtonFile from './toolbar_button'; describe('TriggerButton', () => { describe('base version (no icons)', () => { it('should render the basic button', () => { - render( + renderWithProviders( ); expect(screen.getByText('Trigger label')).toBeInTheDocument(); }); it('should render the title if provided', () => { - render( + renderWithProviders( { it('should call the toggle callback on click', async () => { const toggleFn = jest.fn(); - render( + renderWithProviders( { }); it('should render the main label as red if missing', () => { - render( + const ToolbarButtonSpy = jest.spyOn(ToolbarButtonFile, 'ToolbarButton'); + renderWithProviders( { isMissingCurrent /> ); - // EUI danger red: rgb(167, 22, 39) - expect(screen.getByTestId('test-id')).toHaveStyle({ color: 'rgb(167, 22, 39)' }); + expect(ToolbarButtonSpy).toHaveBeenCalledWith( + expect.objectContaining({ color: 'danger' }), + {} + ); }); }); describe('with icons', () => { it('should render one icon', () => { - render( + renderWithProviders( { it('should render multiple icons', () => { const indexes = [1, 2, 3]; - render( + renderWithProviders( { }); it('should render the value together with the provided component', () => { - render( + renderWithProviders( css` + border-left: ${euiThemeContext.euiTheme.border.thin}; + ${euiShadow(euiThemeContext, 'xl')}; + position: fixed; + top: 0; + bottom: 0; + right: 0; + height: 100%; + z-index: ${euiThemeContext.euiTheme.levels.flyout}; + background: ${euiThemeContext.euiTheme.colors.backgroundBasePlain}; + display: flex; + flex-direction: column; + align-items: stretch; + animation: ${flyoutOpenCloseAnimation} ${euiThemeContext.euiTheme.animation.normal} + ${euiThemeContext.euiTheme.animation.resistance}; + .lnsIndexPatternDimensionEditor--padded { + padding: ${euiThemeContext.euiTheme.size.base}; + } + .lnsIndexPatternDimensionEditor--collapseNext { + margin-bottom: -${euiThemeContext.euiTheme.size.l}; + border-top: ${euiThemeContext.euiTheme.border.thin}; + margin-top: 0 !important; + } +`; diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/flyout_container.scss b/x-pack/platform/plugins/shared/lens/public/shared_components/flyout_container.scss deleted file mode 100644 index 2d26d07d8a682..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/flyout_container.scss +++ /dev/null @@ -1,47 +0,0 @@ -@import '../mixins'; - -.lnsDimensionContainer { - // Use the EuiFlyout style - @include euiFlyout; - // But with custom positioning to keep it within the sidebar contents - animation: euiFlyoutAnimation $euiAnimSpeedNormal $euiAnimSlightResistance; - max-width: none !important; - left: 0; - z-index: $euiZContentMenu; - - @include euiBreakpoint('m', 'l', 'xl') { - height: 100% !important; - position: absolute; - top: 0 !important; - } - - .lnsFrameLayout__sidebar-isFullscreen & { - border-left: $euiBorderThin; // Force border regardless of theme in fullscreen - box-shadow: none; - } -} - -.lnsDimensionContainer__header { - padding: $euiSize; - - .lnsFrameLayout__sidebar-isFullscreen & { - display: none; - } -} - -.lnsDimensionContainer__content { - flex: 1; - @include euiYScroll; -} - -.lnsDimensionContainer__footer { - padding: $euiSize; - - .lnsFrameLayout__sidebar-isFullscreen & { - display: none; - } -} - -.lnsBody--overflowHidden { - overflow: hidden; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/flyout_container.tsx b/x-pack/platform/plugins/shared/lens/public/shared_components/flyout_container.tsx index 2b865daa31c08..f8973d650f04d 100644 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/flyout_container.tsx +++ b/x-pack/platform/plugins/shared/lens/public/shared_components/flyout_container.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './flyout_container.scss'; - import React, { useState, useEffect, useCallback } from 'react'; import { css } from '@emotion/react'; import { @@ -18,9 +16,13 @@ import { EuiFlexGroup, EuiFlexItem, EuiFocusTrap, + type UseEuiTheme, + euiBreakpoint, + useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../utils'; +import { flyoutContainerStyles } from './flyout.styles'; function fromExcludedClickTarget(event: Event) { for ( @@ -61,6 +63,7 @@ export function FlyoutContainer({ isInlineEditing?: boolean; }) { const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false); + const euiThemeContext = useEuiTheme(); const closeFlyout = useCallback(() => { setFocusTrapIsEnabled(false); @@ -69,12 +72,14 @@ export function FlyoutContainer({ useEffect(() => { if (!isInlineEditing) { - document.body.classList.toggle('lnsBody--overflowHidden', isOpen); + if (isOpen) { + document.body.style.overflow = isOpen ? 'hidden' : ''; + } return () => { if (isOpen) { setFocusTrapIsEnabled(false); } - document.body.classList.remove('lnsBody--overflowHidden'); + document.body.style.overflow = ''; }; } }, [isInlineEditing, isOpen]); @@ -100,10 +105,13 @@ export function FlyoutContainer({ ref={panelContainerRef} role="dialog" aria-labelledby="lnsDimensionContainerTitle" - className="lnsDimensionContainer" - css={css` - box-shadow: ${isInlineEditing ? 'none !important' : 'inherit'}; - `} + css={[ + css` + box-shadow: ${isInlineEditing || isFullscreen ? 'none !important' : 'inherit'}; + `, + flyoutContainerStyles(euiThemeContext), + dimensionContainerStyles.self(euiThemeContext), + ]} onAnimationEnd={() => { if (isOpen) { // EuiFocusTrap interferes with animating elements with absolute position: @@ -113,7 +121,7 @@ export function FlyoutContainer({ } }} > - + {isInlineEditing && ( @@ -131,12 +139,7 @@ export function FlyoutContainer({ )} -

          - {label} -

          +

          {label}

          @@ -157,10 +160,18 @@ export function FlyoutContainer({
          -
          {children}
          +
          + {children} +
          {customFooter || ( - + ); } + +const dimensionContainerStyles = { + self: (euiThemeContext: UseEuiTheme) => { + return css` + // But with custom positioning to keep it within the sidebar contents + max-width: none !important; + left: 0; + ${euiBreakpoint(euiThemeContext, ['m', 'l', 'xl'])} { + height: 100% !important; + position: absolute; + top: 0 !important; + } + `; + }, + header: ({ euiTheme }: UseEuiTheme) => css` + padding: ${euiTheme.size.base}; + `, + footer: ({ euiTheme }: UseEuiTheme) => css` + padding: ${euiTheme.size.base}; + `, +}; diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/setting_with_sibling_flyout.scss b/x-pack/platform/plugins/shared/lens/public/shared_components/setting_with_sibling_flyout.scss deleted file mode 100644 index 27dc29ed8af0c..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/setting_with_sibling_flyout.scss +++ /dev/null @@ -1,28 +0,0 @@ -@import '../mixins'; - -.lnsSettingWithSiblingFlyout { - // Use the EuiFlyout style - @include euiFlyout; - // But with custom positioning to keep it within the sidebar contents - position: absolute; - right: 0; - left: 0; - top: 0; - bottom: 0; - animation: euiFlyoutAnimation $euiAnimSpeedNormal $euiAnimSlightResistance; - // making just a bit higher than the dimension flyout to stack on top of it - z-index: $euiZLevel3 + 1 -} - -.lnsSettingWithSiblingFlyout__header { - padding: $euiSize; -} - -.lnsSettingWithSiblingFlyout__content { - flex: 1; - @include euiYScroll; -} - -.lnsSettingWithSiblingFlyout__footer { - padding: $euiSize; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/setting_with_sibling_flyout.tsx b/x-pack/platform/plugins/shared/lens/public/shared_components/setting_with_sibling_flyout.tsx index fed4702e697b9..68f070a426064 100644 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/setting_with_sibling_flyout.tsx +++ b/x-pack/platform/plugins/shared/lens/public/shared_components/setting_with_sibling_flyout.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import './setting_with_sibling_flyout.scss'; - import { i18n } from '@kbn/i18n'; import React, { useState, useEffect, MutableRefObject } from 'react'; import { @@ -20,7 +18,11 @@ import { EuiFocusTrap, EuiOutsideClickDetector, EuiPortal, + type UseEuiTheme, + useEuiTheme, } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { flyoutContainerStyles } from './flyout.styles'; const DEFAULT_TITLE = i18n.translate('xpack.lens.colorSiblingFlyoutTitle', { defaultMessage: 'Color', @@ -43,6 +45,7 @@ export function SettingWithSiblingFlyout({ }) { const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false); const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); + const euiThemeContext = useEuiTheme(); const toggleFlyout = () => { setIsFlyoutOpen(!isFlyoutOpen); @@ -74,9 +77,15 @@ export function SettingWithSiblingFlyout({ role="dialog" aria-labelledby="lnsSettingWithSiblingFlyoutTitle" data-test-subj={dataTestSubj} - className="lnsSettingWithSiblingFlyout" + css={[ + flyoutContainerStyles(euiThemeContext), + siblingflyoutContainerStyles.self(euiThemeContext), + ]} > - + -

          - {title} -

          +

          {title}

          - {children &&
          {children}
          } + {children && ( +
          + {children} +
          + )} - + {i18n.translate('xpack.lens.settingWithSiblingFlyout.back', { defaultMessage: 'Back', @@ -120,3 +133,21 @@ export function SettingWithSiblingFlyout({ ); } + +const siblingflyoutContainerStyles = { + self: ({ euiTheme }: UseEuiTheme) => css` + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; + // making just a bit higher than the dimension flyout to stack on top of it + z-index: ${euiTheme.levels.menu}; + `, + header: ({ euiTheme }: UseEuiTheme) => css` + padding: ${euiTheme.size.base}; + `, + footer: ({ euiTheme }: UseEuiTheme) => css` + padding: ${euiTheme.size.base}; + `, +}; diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/static_header.tsx b/x-pack/platform/plugins/shared/lens/public/shared_components/static_header.tsx index b4c5d8931065e..5989938ab007c 100644 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/static_header.tsx +++ b/x-pack/platform/plugins/shared/lens/public/shared_components/static_header.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiTitle, IconType } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiTitle, IconType, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; export const StaticHeader = ({ @@ -18,12 +18,15 @@ export const StaticHeader = ({ icon?: IconType; indicator?: React.ReactNode; }) => { + const { euiTheme } = useEuiTheme(); return ( {icon && ( diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/toolbar_popover.scss b/x-pack/platform/plugins/shared/lens/public/shared_components/toolbar_popover.scss deleted file mode 100644 index 823859866eb2a..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/toolbar_popover.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lnsVisToolbar__popover { - width: 410px; -} diff --git a/x-pack/platform/plugins/shared/lens/public/shared_components/toolbar_popover.tsx b/x-pack/platform/plugins/shared/lens/public/shared_components/toolbar_popover.tsx index 042853735fcd8..94325995b05a3 100644 --- a/x-pack/platform/plugins/shared/lens/public/shared_components/toolbar_popover.tsx +++ b/x-pack/platform/plugins/shared/lens/public/shared_components/toolbar_popover.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import './toolbar_popover.scss'; import React, { PropsWithChildren, useState } from 'react'; import { EuiFlexItem, EuiPopover, EuiPopoverProps, EuiPopoverTitle, IconType } from '@elastic/eui'; import { ToolbarButton, ToolbarButtonProps } from '@kbn/shared-ux-button-toolbar'; @@ -41,6 +40,8 @@ export type ToolbarPopoverProps = Partial & { handleClose?: () => void; }; +const defaultPanelStyles = { width: '410px' }; + export const ToolbarPopover: React.FC> = ({ children, title, @@ -49,7 +50,7 @@ export const ToolbarPopover: React.FC> = groupPosition, buttonDataTestSubj, handleClose, - panelClassName = 'lnsVisToolbar__popover', + panelStyle = defaultPanelStyles, ...euiPopoverProps }) => { const [isOpen, setIsOpen] = useState(false); @@ -59,7 +60,7 @@ export const ToolbarPopover: React.FC> = return ( { let store: EnhancedStore<{ lens: LensAppState }>; beforeEach(() => { - store = makeLensStore({}).store; + store = makeLensStore().store; jest.clearAllMocks(); }); const customQuery = { query: 'custom' } as Query; diff --git a/x-pack/platform/plugins/shared/lens/public/state_management/load_initial.test.tsx b/x-pack/platform/plugins/shared/lens/public/state_management/load_initial.test.tsx index 0a47af299d136..f23b33eca7e98 100644 --- a/x-pack/platform/plugins/shared/lens/public/state_management/load_initial.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/state_management/load_initial.test.tsx @@ -118,13 +118,13 @@ describe('Initializing the store', () => { }, }); - const { store, deps } = makeLensStore({ + const { store } = makeLensStore({ storeDeps, preloadedState, }); await loadInitialAppState(store, defaultProps); - const { datasourceMap } = deps; + const { datasourceMap } = storeDeps; expect(datasourceMap.testDatasource.initialize).toHaveBeenCalledWith( datasource1State, diff --git a/x-pack/platform/plugins/shared/lens/public/test_utils/test_utils.tsx b/x-pack/platform/plugins/shared/lens/public/test_utils/test_utils.tsx new file mode 100644 index 0000000000000..fbee7a3146d5e --- /dev/null +++ b/x-pack/platform/plugins/shared/lens/public/test_utils/test_utils.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiThemeProvider } from '@elastic/eui'; +import { coreMock } from '@kbn/core/public/mocks'; +import { I18nProvider } from '@kbn/i18n-react'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { RenderOptions, render } from '@testing-library/react'; +import { ComponentType, MountRendererProps, mount } from 'enzyme'; +import React from 'react'; +import { PropsWithChildren, ReactElement } from 'react'; +import { LensAppServices } from '../app_plugin/types'; + +export const renderWithProviders = ( + ui: ReactElement, + renderOptions?: RenderOptions + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any => { + const { wrapper, ...options } = renderOptions || {}; + + const CustomWrapper = wrapper as React.ComponentType>; + + const Wrapper: React.FC> = ({ children }) => { + return ( + + + + {wrapper ? {children} : children} + + + + ); + }; + + const rtlRender = render(ui, { wrapper: Wrapper, ...options }); + + return rtlRender; +}; + +// legacy enzyme usage: remove when all tests are migrated to @testing-library/react +export const mountWithProviders = (component: React.ReactElement, options?: MountRendererProps) => { + const { wrappingComponent, wrappingComponentProps } = options || {}; + + const WrappingComponent = wrappingComponent as React.ComponentType>; + + const wrapper: React.FC> = ({ children }) => ( + + + + {WrappingComponent ? ( + {children} + ) : ( + children + )} + + + + ); + + const instance = mount(component, { + ...options, + wrappingComponent: wrapper as ComponentType>, + }); + return instance; +}; diff --git a/x-pack/platform/plugins/shared/lens/public/trigger_actions/open_lens_config/helpers.scss b/x-pack/platform/plugins/shared/lens/public/trigger_actions/open_lens_config/helpers.scss deleted file mode 100644 index a36f7e759a9f1..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/trigger_actions/open_lens_config/helpers.scss +++ /dev/null @@ -1,24 +0,0 @@ -// styles needed to display extra drop targets that are outside of the config panel main area while also allowing to scroll vertically -.lnsConfigPanel__overlay { - clip-path: polygon(-100% 0, 100% 0, 100% 100%, -100% 100%); - max-inline-size: $euiSizeXXL * 20; - min-inline-size: $euiSizeXXL * 8; - background: $euiColorBackgroundBaseSubdued; - @include euiBreakpoint('xs', 's', 'm') { - clip-path: none; - } - .kbnOverlayMountWrapper { - padding-left: $euiFormMaxWidth; - margin-left: -$euiFormMaxWidth; - pointer-events: none; - .euiFlyoutFooter { - pointer-events: auto; - } - } -} - -.lnsEditFlyoutBody { - .euiFlyoutBody__overflow { - transform: initial; - } -} diff --git a/x-pack/platform/plugins/shared/lens/public/trigger_actions/open_lens_config/in_app_embeddable_edit/in_app_embeddable_edit_action_helpers.tsx b/x-pack/platform/plugins/shared/lens/public/trigger_actions/open_lens_config/in_app_embeddable_edit/in_app_embeddable_edit_action_helpers.tsx index 5eeaf01d2034f..b262cbb2b0c7f 100644 --- a/x-pack/platform/plugins/shared/lens/public/trigger_actions/open_lens_config/in_app_embeddable_edit/in_app_embeddable_edit_action_helpers.tsx +++ b/x-pack/platform/plugins/shared/lens/public/trigger_actions/open_lens_config/in_app_embeddable_edit/in_app_embeddable_edit_action_helpers.tsx @@ -9,7 +9,6 @@ import { isOfAggregateQueryType } from '@kbn/es-query'; import { ENABLE_ESQL } from '@kbn/esql-utils'; import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { BehaviorSubject } from 'rxjs'; -import '../helpers.scss'; import { PublishingSubject } from '@kbn/presentation-publishing'; import { generateId } from '../../../id_generator'; import { setupPanelManagement } from '../../../react_embeddable/inline_editing/panel_management'; diff --git a/x-pack/platform/plugins/shared/lens/public/visualization_container.scss b/x-pack/platform/plugins/shared/lens/public/visualization_container.scss deleted file mode 100644 index 488b138cb0693..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualization_container.scss +++ /dev/null @@ -1,34 +0,0 @@ -.lnsVisualizationContainer { - overflow: auto hidden; - user-select: text; - @include euiScrollBar; -} - -.lnsExpressionRenderer { - position: relative; - width: 100%; - height: 100%; - display: flex; - overflow: auto; - // important for visualizations with no padding - border-radius: $euiBorderRadius; - @include euiScrollBar; - .lnsExpressionRenderer__component { - position: static; // Let the progress indicator position itself against the outer parent - } -} - -.lnsEmbeddedError { - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - overflow: auto; -} - -// Make the visualization modifiers icon appear only on panel hover -.embPanel__content:hover .lnsPanelFeatureList_button { - color: $euiTextColor; - background: $euiColorEmptyShade; - transition: color $euiAnimSpeedSlow, background $euiAnimSpeedSlow; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/visualization_container.test.tsx b/x-pack/platform/plugins/shared/lens/public/visualization_container.test.tsx deleted file mode 100644 index 498a44c2760f7..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualization_container.test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { VisualizationContainer } from './visualization_container'; -import { render, screen } from '@testing-library/react'; - -describe('VisualizationContainer', () => { - const renderVisContainer = (props?: React.HTMLAttributes) => { - return render(Hello!); - }; - test('renders child content', () => { - renderVisContainer(); - expect(screen.getByText('Hello!')).toBeInTheDocument(); - }); - - test('renders style', () => { - renderVisContainer({ style: { color: 'blue' } }); - expect(screen.getByText('Hello!')).toHaveStyle({ color: 'blue' }); - }); - - test('combines class names with container class', () => { - renderVisContainer({ className: 'myClass' }); - expect(screen.getByText('Hello!')).toHaveClass('myClass lnsVisualizationContainer'); - }); -}); diff --git a/x-pack/platform/plugins/shared/lens/public/visualization_container.tsx b/x-pack/platform/plugins/shared/lens/public/visualization_container.tsx deleted file mode 100644 index a9020278db235..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualization_container.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import './visualization_container.scss'; - -import React from 'react'; -import classNames from 'classnames'; - -export function VisualizationContainer({ - children, - className, - ...rest -}: React.HTMLAttributes) { - return ( -
          - {children} -
          - ); -} diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.scss b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.scss deleted file mode 100644 index ef998626f6127..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lnsDynamicColoringRow { - align-items: center; -} diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.test.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.test.tsx index db58f8cc44236..a07ef8d82d14c 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { DEFAULT_COLOR_MAPPING_CONFIG } from '@kbn/coloring'; -import { act, render, screen } from '@testing-library/react'; +import { act, screen } from '@testing-library/react'; import userEvent, { type UserEvent } from '@testing-library/user-event'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; @@ -18,8 +18,8 @@ import { createMockDatasource, createMockFramePublicAPI } from '../../../mocks'; import { TableDimensionEditor, TableDimensionEditorProps } from './dimension_editor'; import { ColumnState } from '../../../../common/expressions'; import { capitalize } from 'lodash'; -import { I18nProvider } from '@kbn/i18n-react'; import { getKbnPalettes } from '@kbn/palettes'; +import { renderWithProviders } from '../../../test_utils/test_utils'; describe('data table dimension editor', () => { let user: UserEvent; @@ -110,12 +110,12 @@ describe('data table dimension editor', () => { }); const renderTableDimensionEditor = (overrideProps?: Partial) => { - return render(, { + return renderWithProviders(, { wrapper: ({ children }) => ( - + <>
          {children} - + ), }); }; diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx index 58af2a37b20ab..25f0fd80a8b04 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx @@ -28,8 +28,6 @@ import { findMinMaxByColumnId, getAccessorType, } from '../../../shared_components'; - -import './dimension_editor.scss'; import { CollapseSetting } from '../../../shared_components/collapse_setting'; import { ColorMappingByValues } from '../../../shared_components/coloring/color_mapping_by_values'; import { ColorMappingByTerms } from '../../../shared_components/coloring/color_mapping_by_terms'; diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor_addtional_section.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor_addtional_section.tsx index 93c14230f63d9..e51f866197a6b 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor_addtional_section.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor_addtional_section.tsx @@ -24,8 +24,6 @@ import { import { isNumericFieldForDatatable } from '../../../../common/expressions/datatable/utils'; import { DatatableInspectorTables } from '../../../../common/expressions/datatable/datatable_fn'; -import './dimension_editor.scss'; - type ColumnType = DatatableVisualizationState['columns'][number]; type SummaryRowType = Extract; diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.scss b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.scss deleted file mode 100644 index 0f0ed53a10021..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.scss +++ /dev/null @@ -1,19 +0,0 @@ -.lnsDataTableContainer { - height: 100%; -} - -.lnsTableCell--multiline { - white-space: pre-wrap; -} - -.lnsTableCell--left { - text-align: left; -} - -.lnsTableCell--right { - text-align: right; -} - -.lnsTableCell--center { - text-align: center; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx index 3bbacf124668f..27f4d68ad0cdc 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import './table_basic.scss'; import { ColorMappingInputData, PaletteOutput, getFallbackDataBounds } from '@kbn/coloring'; import React, { useLayoutEffect, @@ -35,11 +34,11 @@ import { getColorCategories } from '@kbn/chart-expressions-common'; import { getOriginalId } from '@kbn/transpose-utils'; import { CoreTheme } from '@kbn/core/public'; import { getKbnPalettes } from '@kbn/palettes'; +import { css } from '@emotion/react'; import type { LensTableRowContextMenuEvent } from '../../../types'; import type { FormatFactory } from '../../../../common/types'; import { RowHeightMode } from '../../../../common/types'; import { LensGridDirection } from '../../../../common/expressions'; -import { VisualizationContainer } from '../../../visualization_container'; import { findMinMaxByColumnId, shouldColorByTerms } from '../../../shared_components'; import type { DataContextType, @@ -507,9 +506,13 @@ export const DatatableComponent = (props: DatatableRenderProps) => { if (isEmpty) { return ( - +
          - +
          ); } @@ -520,7 +523,11 @@ export const DatatableComponent = (props: DatatableRenderProps) => { }); return ( - +
          { ref={dataGridRef} /> - +
          ); }; + +const datatableContainerStyles = css` + height: 100%; + overflow: auto hidden; + user-select: text; + + .lnsTableCell--multiline { + white-space: pre-wrap; + } + + .lnsTableCell--left { + text-align: left; + } + + .lnsTableCell--right { + text-align: right; + } + + .lnsTableCell--center { + text-align: center; + } +`; diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/dimension_editor.scss b/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/dimension_editor.scss deleted file mode 100644 index d7664b9d2da16..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/dimension_editor.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lnsDynamicColoringRow { - align-items: center; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/dimension_editor.tsx index 8c1d3bd96f2d3..c658e3089fe5d 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/dimension_editor.tsx @@ -17,6 +17,7 @@ import { import { GaugeTicksPositions, GaugeColorModes } from '@kbn/expression-gauge-plugin/common'; import { getMaxValue, getMinValue } from '@kbn/expression-gauge-plugin/public'; import { TooltipWrapper } from '@kbn/visualization-utils'; +import { css } from '@emotion/react'; import { isNumericFieldForDatatable } from '../../../common/expressions/datatable/utils'; import { PalettePanelContainer } from '../../shared_components'; import type { VisualizationDimensionEditorProps } from '../../types'; @@ -24,8 +25,6 @@ import type { GaugeVisualizationState } from './constants'; import { defaultPaletteParams } from './palette_config'; import { getAccessorsFromState } from './utils'; -import './dimension_editor.scss'; - export function GaugeDimensionEditor( props: VisualizationDimensionEditorProps & { paletteService: PaletteRegistry; @@ -74,7 +73,9 @@ export function GaugeDimensionEditor( label={i18n.translate('xpack.lens.gauge.dynamicColoring.label', { defaultMessage: 'Band colors', })} - className="lnsDynamicColoringRow" + css={css` + align-items: center; + `} > color)} diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/toolbar_component/gauge_config_panel.scss b/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/toolbar_component/gauge_config_panel.scss deleted file mode 100644 index 893ed71235881..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/toolbar_component/gauge_config_panel.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lnsGaugeToolbar__popover { - width: 500px; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/toolbar_component/index.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/toolbar_component/index.tsx index a91aabd812b19..3bea80673a661 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/toolbar_component/index.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/gauge/toolbar_component/index.tsx @@ -26,7 +26,6 @@ import { } from '@kbn/chart-icons'; import type { VisualizationToolbarProps } from '../../../types'; import { ToolbarPopover, VisLabel } from '../../../shared_components'; -import './gauge_config_panel.scss'; import { gaugeTitlesByType, type GaugeVisualizationState } from '../constants'; const PREFIX = `lns_gaugeOrientation_`; @@ -109,7 +108,9 @@ const AppearancePopover = (props: VisualizationToolbarProps color)} diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.scss b/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.scss deleted file mode 100644 index d7664b9d2da16..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lnsDynamicColoringRow { - align-items: center; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.test.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.test.tsx index b4ac97cd5edd7..e6091bec0b5de 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.test.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { EuiButtonGroup } from '@elastic/eui'; import { FramePublicAPI, VisualizationDimensionEditorProps } from '../../types'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; import { MetricDimensionEditor } from './dimension_editor'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { ColorMode } from '@kbn/charts-plugin/public'; @@ -25,6 +24,7 @@ import { PalettePanelContainer } from '../../shared_components'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import type { LegacyMetricState } from '../../../common/types'; import { DatasourcePublicAPI } from '../..'; +import { mountWithProviders } from '../../test_utils/test_utils'; function paletteParamsContaining(paramsToCheck: PaletteOutput['params']) { return expect.objectContaining({ @@ -90,7 +90,7 @@ describe('metric dimension editor', () => { }); it('should not show the dynamic coloring option for non numeric columns', () => { - const instance = mountWithIntl(); + const instance = mountWithProviders(); expect( instance.find('[data-test-subj="lnsLegacyMetric_dynamicColoring_groups"]').exists() ).toBe(false); @@ -99,7 +99,7 @@ describe('metric dimension editor', () => { it('should set the dynamic coloring default to "none"', () => { frame.activeData!.first.columns[0].meta.type = 'number'; - const instance = mountWithIntl(); + const instance = mountWithProviders(); expect( instance .find('[data-test-subj="lnsLegacyMetric_dynamicColoring_groups"]') @@ -113,7 +113,7 @@ describe('metric dimension editor', () => { it('should show the dynamic palette display ony when colorMode is different from "none"', () => { frame.activeData!.first.columns[0].meta.type = 'number'; state.colorMode = ColorMode.Labels; - const instance = mountWithIntl(); + const instance = mountWithProviders(); expect( instance .find('[data-test-subj="lnsLegacyMetric_dynamicColoring_groups"]') @@ -126,7 +126,7 @@ describe('metric dimension editor', () => { it('should prefill the palette stops with some colors when enabling coloring', () => { frame.activeData!.first.columns[0].meta.type = 'number'; - const instance = mountWithIntl(); + const instance = mountWithProviders(); act(() => { instance @@ -146,7 +146,7 @@ describe('metric dimension editor', () => { it('should open the palette panel when "Settings" link is clicked in the palette input', () => { frame.activeData!.first.columns[0].meta.type = 'number'; state.colorMode = ColorMode.Background; - const instance = mountWithIntl(); + const instance = mountWithProviders(); act(() => { instance.find('[data-test-subj="lns_colorEditing_trigger"]').first().simulate('click'); @@ -160,7 +160,7 @@ describe('metric dimension editor', () => { frame.activeData!.first.columns[0].meta.type = 'number'; frame.activeData!.first.rows[0].foo = 0; state.colorMode = ColorMode.Background; - const instance = mountWithIntl(); + const instance = mountWithProviders(); act(() => { instance.find('[data-test-subj="lns_colorEditing_trigger"]').first().simulate('click'); @@ -174,7 +174,7 @@ describe('metric dimension editor', () => { frame.activeData!.first.columns[0].meta.type = 'number'; frame.activeData!.first.rows[0].foo = -1; state.colorMode = ColorMode.Background; - const instance = mountWithIntl(); + const instance = mountWithProviders(); act(() => { instance.find('[data-test-subj="lns_colorEditing_trigger"]').first().simulate('click'); @@ -189,7 +189,7 @@ describe('metric dimension editor', () => { frame.activeData!.first.rows[0].foo = 5; state.colorMode = ColorMode.None; state.palette = undefined; - const instance = mountWithIntl(); + const instance = mountWithProviders(); act(() => { instance diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.tsx index 552b5f0a52a3f..e3f5c5cea35ce 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/legacy_metric/dimension_editor.tsx @@ -14,14 +14,13 @@ import { import { i18n } from '@kbn/i18n'; import React from 'react'; import { ColorMode } from '@kbn/charts-plugin/common'; +import { css } from '@emotion/react'; import type { LegacyMetricState } from '../../../common/types'; import { isNumericFieldForDatatable } from '../../../common/expressions/datatable/utils'; import { PalettePanelContainer } from '../../shared_components'; import type { VisualizationDimensionEditorProps } from '../../types'; import { defaultPaletteParams } from './palette_config'; -import './dimension_editor.scss'; - const idPrefix = htmlIdGenerator()(); export function MetricDimensionEditor( @@ -134,12 +133,14 @@ export function MetricDimensionEditor( {hasDynamicColoring && ( color)} diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx index 113c4f0d9e015..a0a94ecaf1af7 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import './toolbar.scss'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/toolbar.scss b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/toolbar.scss deleted file mode 100644 index 3cfbe6480c61b..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/toolbar.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lnsPieToolbar__popover { - width: $euiFormMaxWidth; -} diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/toolbar.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/toolbar.tsx index 8262231ceb5d1..a10f8344da3ff 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/toolbar.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/toolbar.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import './toolbar.scss'; import React, { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/add_layer.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/add_layer.tsx index 2a9f125bfa663..c032be5511558 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/add_layer.tsx @@ -15,12 +15,13 @@ import { EuiFlexGroup, IconType, transparentize, + type UseEuiTheme, + useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; import { AddLayerFunction, VisualizationLayerDescription } from '../../types'; import { LoadAnnotationLibraryFlyout } from './load_annotation_library_flyout'; import type { ExtraAppendLayerArg } from './visualization'; @@ -68,15 +69,12 @@ export function AddLayerButton({ disabled, name: ( - - {label} - + {label} ), - className: 'lnsLayerAddButton', icon: icon && , ['data-test-subj']: `lnsLayerAddButton-${type}`, }; @@ -93,8 +91,7 @@ export function AddLayerButton({ panel: AddLayerPanelType.compatibleVisualizationTypes, toolTipContent, disabled, - name: {label}, - className: 'lnsLayerAddButton', + name: label, icon: icon && , ['data-test-subj']: `lnsLayerAddButton-${type}`, }; @@ -157,8 +154,7 @@ export function AddLayerButton({ return { toolTipContent, disabled, - name: {label}, - className: 'lnsLayerAddButton', + name: label, icon: icon && , ['data-test-subj']: `lnsLayerAddButton-${type}`, onClick: () => { @@ -172,8 +168,7 @@ export function AddLayerButton({ return { toolTipContent, disabled, - name: {label}, - className: 'lnsLayerAddButton', + name: label, icon: icon && , ['data-test-subj']: `lnsLayerAddButton-${type}`, onClick: () => { @@ -281,27 +276,32 @@ const ChartOptionWrapper = ({ onClick: () => void; type: string; }) => { + const euiThemeContext = useEuiTheme(); return ( ); }; + +const chartOptionWrapperStyles = ({ euiTheme }: UseEuiTheme) => css` + padding: ${euiTheme.size.s}; + border-bottom: ${euiTheme.border.thin}; + border-bottom-color: ${euiTheme.colors.backgroundBaseSubdued}; + width: 100%; + &:hover, + &:focus { + color: ${euiTheme.colors.primary}; + background-color: ${transparentize(euiTheme.colors.primary, 0.1)}; + span, + .euiText { + text-decoration: underline; + color: ${euiTheme.colors.primary}; + } + } +`; diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.scss b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.scss deleted file mode 100644 index 360a76274416d..0000000000000 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lnsVisToolbarAxis__popover { - width: 500px; -} \ No newline at end of file diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx index 9b03ac7c72a31..8c5027ee47648 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/axis_settings_popover.tsx @@ -26,8 +26,6 @@ import { AxisTicksSettings, } from '../../../shared_components'; import { XYLayerConfig, AxesSettingsConfig } from '../types'; - -import './axis_settings_popover.scss'; import { validateExtent } from '../../../shared_components/axis/extent/helpers'; import { getBounds } from '../../../shared_components/axis/extent/axis_extent_settings'; @@ -270,7 +268,9 @@ export const AxisSettingsPopover: React.FunctionComponent { describe('annotation layer header', () => { @@ -61,20 +61,20 @@ describe('layer header', () => { }; expect( - mountWithIntl() + mountWithProviders() .text() .trim() ).toBe('Annotations'); expect( - mountWithIntl() + mountWithProviders() .text() .trim() ).toBe(byRefGroupTitle); const cachedMetadata = { title: 'A cached title', description: '', tags: [] }; expect( - mountWithIntl( + mountWithProviders( ) .text() diff --git a/x-pack/test/functional/apps/lens/group2/persistent_context.ts b/x-pack/test/functional/apps/lens/group2/persistent_context.ts index b1e78d0ccb34d..487dc7c2684fc 100644 --- a/x-pack/test/functional/apps/lens/group2/persistent_context.ts +++ b/x-pack/test/functional/apps/lens/group2/persistent_context.ts @@ -45,7 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await navigationalSearch.focus(); await navigationalSearch.searchFor('type:lens lnsTableVis'); await navigationalSearch.clickOnOption(0); - await lens.waitForWorkspaceWithVisualization(); + await lens.waitForDatatableVisualization(); }); it('filters, time and query reflect the visualization state', async () => { expect(await lens.getDatatableHeaderText(1)).to.equal('404 › Median of bytes'); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 7bc3e96d5efe1..8669d61cdc413 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -347,7 +347,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }); }, - async waitForWorkspaceWithVisualization() { + async waitForDatatableVisualization() { await retry.try(async () => { await testSubjects.existOrFail(`lnsVisualizationContainer`); });