diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input.png index 7d3fb2048af..28a36993d7a 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input_With_Popover.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input_With_Popover.png index e79e57f6576..1fabca62ddc 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input_With_Popover.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Input_With_Popover.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Levels.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Levels.png index 1c4dfa789a1..72062106e27 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Levels.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Levels.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Ticks.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Ticks.png index 81c85e8ee02..3f7e8762949 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Ticks.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiDualRange_Ticks.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input.png index 9a0e81649d4..cfb521f37e4 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input_With_Popover.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input_With_Popover.png new file mode 100644 index 00000000000..eaa486f2c0d Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Input_With_Popover.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Levels.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Levels.png index dd549126cb3..7ff07fcac2f 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Levels.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Levels.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Ticks.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Ticks.png index bfa99e190a6..4cf2664190a 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Ticks.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Ticks.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Value_Tooltip.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Value_Tooltip.png index 4864dbf371d..f2cf78b4da5 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Value_Tooltip.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRange_Value_Tooltip.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input.png index 8f73613b183..6ff82473eab 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input_With_Popover.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input_With_Popover.png index cb8b4eb2974..9a9607b0574 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input_With_Popover.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Input_With_Popover.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Levels.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Levels.png index b0223dfbd55..76ca99d9074 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Levels.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Levels.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Ticks.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Ticks.png index 57cd818c303..815f24ac8ba 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Ticks.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiDualRange_Ticks.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input.png index 7cd7f7e4681..e82e830a946 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input_With_Popover.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input_With_Popover.png new file mode 100644 index 00000000000..6b40ff087ac Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Input_With_Popover.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Levels.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Levels.png index 3f850c9d448..e1ff7d8bc41 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Levels.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Levels.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Ticks.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Ticks.png index 5f4115f53d8..c18a999ca4a 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Ticks.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Ticks.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Value_Tooltip.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Value_Tooltip.png index b6e46f3124b..d54fb313f73 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Value_Tooltip.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRange_Value_Tooltip.png differ diff --git a/packages/eui/src/components/basic_table/__snapshots__/collapsed_item_actions.test.tsx.snap b/packages/eui/src/components/basic_table/__snapshots__/collapsed_item_actions.test.tsx.snap index c4bcc9134dc..3fc78452fba 100644 --- a/packages/eui/src/components/basic_table/__snapshots__/collapsed_item_actions.test.tsx.snap +++ b/packages/eui/src/components/basic_table/__snapshots__/collapsed_item_actions.test.tsx.snap @@ -48,14 +48,18 @@ exports[`CollapsedItemActions custom actions 1`] = ` data-popover-open="true" data-popover-panel="true" role="dialog" - style="top: -22px; left: -16px; z-index: 2000;" + style="top: -18px; left: -16px; z-index: 2000;" tabindex="0" >
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="top: 9px; left: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="top: 9px; left: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="top: 9px; right: 100%;" + > +
+

Nav item diff --git a/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsed/__snapshots__/collapsed_nav_popover.test.tsx.snap b/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsed/__snapshots__/collapsed_nav_popover.test.tsx.snap index 6fc0081a4bd..8efdac1f7ad 100644 --- a/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsed/__snapshots__/collapsed_nav_popover.test.tsx.snap +++ b/packages/eui/src/components/collapsible_nav_beta/collapsible_nav_item/collapsed/__snapshots__/collapsed_nav_popover.test.tsx.snap @@ -47,14 +47,18 @@ exports[`EuiCollapsedNavPopover renders 1`] = ` data-popover-open="true" data-popover-panel="true" role="dialog" - style="top: -22px; left: 16px; z-index: 2000;" + style="top: -18px; left: 16px; z-index: 2000;" tabindex="0" >
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="top: 9px; right: 100%;" + > +
+
tooltip content diff --git a/packages/eui/src/components/datagrid/body/cell/data_grid_cell_popover.spec.tsx b/packages/eui/src/components/datagrid/body/cell/data_grid_cell_popover.spec.tsx index da2ae883a92..5c87de9db26 100644 --- a/packages/eui/src/components/datagrid/body/cell/data_grid_cell_popover.spec.tsx +++ b/packages/eui/src/components/datagrid/body/cell/data_grid_cell_popover.spec.tsx @@ -254,7 +254,7 @@ describe('EuiDataGridCellPopover', () => { openCellPopover('B'); cy.get('[data-test-subj="euiDataGridExpansionPopover"]') .should('have.css', 'width', '400px') - .should('have.css', 'height', '88px'); + .should('have.css', 'height', '90px'); }); it('matches the width of the column if the column width is larger than 400px', () => { @@ -270,7 +270,7 @@ describe('EuiDataGridCellPopover', () => { openCellPopover('B'); cy.get('[data-test-subj="euiDataGridExpansionPopover"]') .should('have.css', 'width', '500px') - .should('have.css', 'height', '64px'); + .should('have.css', 'height', '66px'); }); }); }); diff --git a/packages/eui/src/components/datagrid/body/header/__snapshots__/column_actions.test.tsx.snap b/packages/eui/src/components/datagrid/body/header/__snapshots__/column_actions.test.tsx.snap index 6c37e70bbda..5ed8e1084c2 100644 --- a/packages/eui/src/components/datagrid/body/header/__snapshots__/column_actions.test.tsx.snap +++ b/packages/eui/src/components/datagrid/body/header/__snapshots__/column_actions.test.tsx.snap @@ -42,14 +42,18 @@ exports[`ColumnActions renders 1`] = ` data-popover-open="true" data-popover-panel="true" role="dialog" - style="top: 23px; left: -22px; z-index: 2000;" + style="top: 23px; left: -18px; z-index: 2000;" tabindex="0" >
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+

8 @@ -454,7 +454,7 @@ exports[`EuiRange props slider should display in popover 1`] = ` data-popover-panel="true" data-test-subj="test" role="dialog" - style="top: 0px; left: -22px; will-change: transform, opacity; z-index: 2000; inline-size: 0px;" + style="top: 0px; left: -18px; will-change: transform, opacity; z-index: 2000; inline-size: 0px;" >

before diff --git a/packages/eui/src/components/form/range/dual_range.stories.tsx b/packages/eui/src/components/form/range/dual_range.stories.tsx index 4784b2925d4..2da2a05ffa5 100644 --- a/packages/eui/src/components/form/range/dual_range.stories.tsx +++ b/packages/eui/src/components/form/range/dual_range.stories.tsx @@ -9,6 +9,7 @@ import React, { useEffect, useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; +import { LOKI_SELECTORS } from '../../../../.storybook/loki'; import { enableFunctionToggleControls, moveStorybookControlsToCategory, @@ -43,7 +44,7 @@ const meta: Meta = { }, showInput: { control: 'radio', - options: [true, false, 'inputWithPopover'], + options: [false, true, 'inputWithPopover'], }, inputPopoverProps: { if: { arg: 'showInput', eq: 'inputWithPopover' }, @@ -70,7 +71,6 @@ const meta: Meta = { minInputProps: {}, maxInputProps: {}, inputPopoverProps: {}, - ticks: [], }, }; moveStorybookControlsToCategory( @@ -152,31 +152,6 @@ export const Input: Story = { render: (args) => , }; -export const InputWithPopover: Story = { - parameters: { - controls: { - include: [ - 'showInput', - 'append', - 'prepend', - 'inputPopoverProps', - 'isInvalid', - 'isLoading', - 'max', - 'min', - 'value', - 'minInputProps', - 'maxInputProps', - ], - }, - }, - args: { - value: [25, 50], - showInput: 'inputWithPopover', - }, - render: (args) => , -}; - export const Levels: Story = { parameters: { controls: { @@ -207,7 +182,7 @@ const StatefulPlayground = ({ } }, [value]); - const handelOnChange = ( + const handleOnChange = ( values: EuiDualRangeProps['value'], isValid: boolean, e?: _DualRangeChangeEvent @@ -216,5 +191,41 @@ const StatefulPlayground = ({ onChange?.(values, isValid, e); }; - return ; + return ; +}; + +/** + * VRT only + */ + +export const InputWithPopover: Story = { + tags: ['vrt-only'], + parameters: { + loki: { chromeSelector: LOKI_SELECTORS.portal }, + }, + args: { + showInput: 'inputWithPopover', + min: 0, + max: 100, + value: [10, 80], + // Should render ticks, levels, highlights, and tooltips in their correct positions + showLabels: true, + showTicks: true, + ticks: [ + { label: '20kb', value: 20 }, + { label: '100kb', value: 100 }, + ], + levels: [ + { min: 0, max: 20, color: 'danger' }, + { min: 20, max: 100, color: 'success' }, + ], + }, + // Force input popover open via programmatic ref + render: function Render(args) { + const [ref, setRef] = useState(); + useEffect(() => { + ref?.onInputFocus(); + }, [ref]); + return ; + }, }; diff --git a/packages/eui/src/components/form/range/range.spec.tsx b/packages/eui/src/components/form/range/range.spec.tsx deleted file mode 100644 index 46489c8861f..00000000000 --- a/packages/eui/src/components/form/range/range.spec.tsx +++ /dev/null @@ -1,188 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/// -/// -/// - -import React from 'react'; - -import { EuiRange } from './range'; -import { EuiDualRange } from './dual_range'; - -const sharedProps = { - min: 0, - max: 100, - onChange: () => {}, - showTicks: true, - showLabels: true, - levels: [ - { - min: 0, - max: 20, - color: 'danger', - }, - { - min: 20, - max: 100, - color: 'success', - }, - ], -}; -const firstExpectedLevel = /^0px 256[.0-9]+px$/; -const secondExpectedLevel = /^72[.0-9]+px 0px$/; - -describe('EuiRange', () => { - const props = { - ...sharedProps, - value: 50, - showValue: true, - showRange: true, - tickInterval: 20, - }; - - // TODO: These should likely be visual snapshot regression tests instead - const assertRangePositions = () => { - // Ticks - cy.get('.euiRangeTick') - .first() - .should('have.css', 'inset-inline-start', '8px'); - cy.get('.euiRangeTick') - .eq(3) - .should('have.css', 'inset-inline-start') - .and('match', /^195[.0-9]+px$/); - cy.get('.euiRangeTick') - .last() - .should('have.css', 'inset-inline-start') - .and('match', /^320[.0-9]+px$/); - - // Levels - present in both EuiRangeLevels and EuiHighlight - cy.get('.euiRangeLevel') - .eq(0) - .should('have.css', 'inset-inline') - .and('match', firstExpectedLevel); - cy.get('.euiRangeLevel') - .eq(2) - .should('have.css', 'inset-inline') - .and('match', firstExpectedLevel); - - cy.get('.euiRangeLevel') - .eq(1) - .should('have.css', 'inset-inline') - .and('match', secondExpectedLevel); - cy.get('.euiRangeLevel') - .eq(3) - .should('have.css', 'inset-inline') - .and('match', secondExpectedLevel); - - // Highlight - cy.get('.euiRangeHighlight > div') - .should('have.css', 'margin-inline-start', '0px') - .should('have.css', 'inline-size') - .and('match', /^164[.0-9]+px$/); - - // Tooltip - cy.get('.euiRangeTooltip > output') - .should('have.css', 'inset-inline-start') - .and('match', /^156[.0-9]+px$/); - }; - - it('renders ticks, levels, highlights, and tooltips in their correct positions', () => { - cy.mount(); - assertRangePositions(); - }); - - it('inputWithPopover', () => { - cy.realMount( - - ); - cy.realPress('Tab'); - assertRangePositions(); - }); -}); - -describe('EuiDualRange', () => { - const props = { - ...sharedProps, - value: [10, 80] as [number, number], - ticks: [ - { label: '20kb', value: 20 }, - { label: '100kb', value: 100 }, - ], - }; - - // TODO: These should likely be visual snapshot regression tests instead - const assertRangePositions = () => { - // Ticks - cy.get('.euiRangeTick') - .first() - .should('have.css', 'inset-inline-start') - .and('match', /^69[.0-9]+px$/); - cy.get('.euiRangeTick') - .last() - .should('have.css', 'inset-inline-end', '0px'); - - // Levels - present in both EuiRangeLevels and EuiHighlight - cy.get('.euiRangeLevel') - .eq(0) - .should('have.css', 'inset-inline') - .and('match', firstExpectedLevel); - cy.get('.euiRangeLevel') - .eq(2) - .should('have.css', 'inset-inline') - .and('match', firstExpectedLevel); - - cy.get('.euiRangeLevel') - .eq(1) - .should('have.css', 'inset-inline') - .and('match', secondExpectedLevel); - cy.get('.euiRangeLevel') - .eq(3) - .should('have.css', 'inset-inline') - .and('match', secondExpectedLevel); - - // Highlight - cy.get('.euiRangeHighlight > div') - .should('have.css', 'margin-inline-start') - .and('match', /^32[.0-9]+px$/); - cy.get('.euiRangeHighlight > div') - .should('have.css', 'inline-size') - .and('match', /^229[.0-9]+px$/); - - // Thumbs - cy.get('.euiRangeThumb') - .first() - .should('have.css', 'inset-inline-start') - .and('match', /^31[.0-9]+px$/); - cy.get('.euiRangeThumb') - .last() - .should('have.css', 'inset-inline-start') - .and('match', /^249[.0-9]+px$/); - }; - - it('renders ticks, levels, highlights, and thumbs in their correct positions', () => { - cy.mount(); - assertRangePositions(); - }); - - it('inputWithPopover', () => { - cy.realMount( - - ); - cy.realPress('Tab'); - assertRangePositions(); - }); -}); diff --git a/packages/eui/src/components/form/range/range.stories.tsx b/packages/eui/src/components/form/range/range.stories.tsx index 1885e0afef0..1064ce772c2 100644 --- a/packages/eui/src/components/form/range/range.stories.tsx +++ b/packages/eui/src/components/form/range/range.stories.tsx @@ -9,6 +9,7 @@ import React, { useEffect, useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; +import { LOKI_SELECTORS } from '../../../../.storybook/loki'; import { enableFunctionToggleControls, moveStorybookControlsToCategory, @@ -66,6 +67,13 @@ const meta: Meta = { undefined: undefined, }, }, + showInput: { + control: 'radio', + options: [false, true, 'inputWithPopover'], + }, + inputPopoverProps: { + if: { arg: 'showInput', eq: 'inputWithPopover' }, + }, }, args: { min: 0, @@ -88,7 +96,6 @@ const meta: Meta = { // adding tickInterval value to prevent error about // too many ticks when enabling showTicks tickInterval: 10, - ticks: [], }, }; @@ -200,6 +207,7 @@ export const Levels: Story = { { min: 20, max: 100, color: 'success' }, ], showLabels: true, + showRange: true, }, render: (args) => , }; @@ -213,10 +221,47 @@ const StatefulPlayground = ({ value, onChange, ...rest }: EuiRangeProps) => { } }, [value]); - const handelOnChange = (e: _SingleRangeChangeEvent, isValid: boolean) => { + const handleOnChange = (e: _SingleRangeChangeEvent, isValid: boolean) => { setValue(e.currentTarget.value); onChange?.(e, isValid); }; - return ; + return ; +}; + +/** + * VRT only + */ + +export const InputWithPopover: Story = { + tags: ['vrt-only'], + parameters: { + loki: { chromeSelector: LOKI_SELECTORS.portal }, + }, + args: { + showInput: 'inputWithPopover', + min: 0, + max: 100, + value: 50, + // Should render ticks, levels, highlights, and tooltips in their correct positions + showLabels: true, + showValue: true, + showRange: true, + showTicks: true, + tickInterval: 20, + levels: [ + { min: 0, max: 20, color: 'danger' }, + { min: 20, max: 100, color: 'success' }, + ], + }, + // Force input popover open via programmatic ref + render: function Render(args) { + const [ref, setRef] = useState(); + useEffect(() => { + // For some reason Loki throws a width/render error if not wrapped in a timeout. + // This doesn't happen on production + if (ref) setTimeout(() => ref.onInputFocus(), 1); + }, [ref]); + return ; + }, }; diff --git a/packages/eui/src/components/form/range/range_tooltip.styles.ts b/packages/eui/src/components/form/range/range_tooltip.styles.ts index 871e9cfad61..1ef4e93e1e9 100644 --- a/packages/eui/src/components/form/range/range_tooltip.styles.ts +++ b/packages/eui/src/components/form/range/range_tooltip.styles.ts @@ -8,8 +8,9 @@ import { css } from '@emotion/react'; import { UseEuiTheme } from '../../../services'; -import { euiRangeVariables } from './range.styles'; import { euiFontSize, mathWithUnits } from '../../../global_styling'; +import { _popoverArrowStyles } from '../../../services/popover'; +import { euiRangeVariables } from './range.styles'; export const euiRangeTooltipStyles = (euiThemeContext: UseEuiTheme) => { const range = euiRangeVariables(euiThemeContext); @@ -32,13 +33,15 @@ export const euiRangeTooltipStyles = (euiThemeContext: UseEuiTheme) => { export const euiRangeTooltipValueStyles = (euiThemeContext: UseEuiTheme) => { const range = euiRangeVariables(euiThemeContext); - const { euiTheme } = euiThemeContext; - - const arrowSize = euiTheme.size.m; - const arrowSizeInt = parseInt(arrowSize, 10); - const arrowMinusSize = `${(arrowSizeInt / 2 - 1) * -1}px`; // Shift arrow 1px more than half its size to account for border radius + const { euiTheme, colorMode } = euiThemeContext; const toolTipBackgroundColor = euiTheme.components.tooltipBackground; + const borderColor = + colorMode === 'DARK' ? toolTipBackgroundColor : 'transparent'; + + const arrowSize = euiTheme.size.m; + const arrowOffset = euiTheme.size.l; + const arrowStyles = _popoverArrowStyles(euiThemeContext, arrowSize); return { euiRangeTooltip__value: css` @@ -47,42 +50,39 @@ export const euiRangeTooltipValueStyles = (euiThemeContext: UseEuiTheme) => { max-inline-size: ${mathWithUnits(euiTheme.size.base, (x) => x * 16)}; padding-block: ${euiTheme.size.xxs}; padding-inline: ${euiTheme.size.s}; - transform: translateX(0) translateY(-50%); + transform: translateY(-50%); ${euiFontSize(euiThemeContext, 's')} line-height: ${euiFontSize(euiThemeContext, 's').lineHeight}; color: ${euiTheme.colors.ghost}; background-color: ${toolTipBackgroundColor}; border: ${euiTheme.border.width.thin} solid ${toolTipBackgroundColor}; + border: ${euiTheme.border.width.thin} solid ${borderColor}; border-radius: ${euiTheme.border.radius.small}; &::before { content: ''; - position: absolute; - inset-block-end: 50%; - inline-size: ${arrowSize}; - block-size: ${arrowSize}; - transform-origin: center; - transform: translateY(50%) rotateZ(45deg); - background-color: ${toolTipBackgroundColor}; - border-radius: ${mathWithUnits( - euiTheme.border.radius.small, - (x) => x / 2 - )}; + ${arrowStyles._arrowStyles} + inset-block-start: 50%; + margin-block-start: ${mathWithUnits(arrowSize, (x) => x / -2)}; + background-color: inherit; + border: inherit; } `, left: css` - margin-inline-end: ${euiTheme.size.l}; + margin-inline-end: ${arrowOffset}; &::before { - inset-inline-end: ${arrowMinusSize}; + ${arrowStyles.positions.left} + inset-inline-start: 100%; } `, right: css` - margin-inline-start: ${euiTheme.size.l}; + margin-inline-start: ${arrowOffset}; &::before { - inset-inline-start: ${arrowMinusSize}; + ${arrowStyles.positions.right} + inset-inline-end: 100%; } `, hasTicks: css` diff --git a/packages/eui/src/components/form/super_select/__snapshots__/super_select.test.tsx.snap b/packages/eui/src/components/form/super_select/__snapshots__/super_select.test.tsx.snap index e068fa19900..10fdc402366 100644 --- a/packages/eui/src/components/form/super_select/__snapshots__/super_select.test.tsx.snap +++ b/packages/eui/src/components/form/super_select/__snapshots__/super_select.test.tsx.snap @@ -190,7 +190,7 @@ exports[`EuiSuperSelect renders 1`] = ` class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-light-isAttached-bottom" data-popover-panel="true" role="dialog" - style="top: 0px; left: -22px; will-change: transform, opacity; z-index: 2000; inline-size: 0px;" + style="top: 0px; left: -18px; will-change: transform, opacity; z-index: 2000; inline-size: 0px;" >

+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+

+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
@@ -431,14 +458,18 @@ exports[`EuiPopover props panelClassName is rendered 1`] = ` data-popover-open="true" data-popover-panel="true" role="dialog" - style="top: 16px; left: -22px; will-change: transform, opacity; z-index: 2000;" + style="top: 16px; left: -18px; will-change: transform, opacity; z-index: 2000;" tabindex="0" >
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; bottom: 100%;" + > +
+

{ ? 16 + offset : 8 + offset, arrowConfig: this.props.hasArrow - ? { arrowWidth: 24, arrowBuffer: 10 } + ? { arrowWidth: 16, arrowBuffer: 10 } : { arrowWidth: 0, arrowBuffer: 0 }, returnBoundingBox: this.props.attachToAnchor, allowCrossAxis: this.props.repositionToCrossAxis, diff --git a/packages/eui/src/components/popover/popover_arrow/__snapshots__/_popover_arrow.test.tsx.snap b/packages/eui/src/components/popover/popover_arrow/__snapshots__/_popover_arrow.test.tsx.snap index e1f6c22cb47..14d477c8c7c 100644 --- a/packages/eui/src/components/popover/popover_arrow/__snapshots__/_popover_arrow.test.tsx.snap +++ b/packages/eui/src/components/popover/popover_arrow/__snapshots__/_popover_arrow.test.tsx.snap @@ -3,35 +3,51 @@ exports[`EuiPopoverArrow position bottom is rendered 1`] = `

+> +
+
`; exports[`EuiPopoverArrow position left is rendered 1`] = `
+> +
+
`; exports[`EuiPopoverArrow position right is rendered 1`] = `
+> +
+
`; exports[`EuiPopoverArrow position top is rendered 1`] = `
+> +
+
`; diff --git a/packages/eui/src/components/popover/popover_arrow/_popover_arrow.styles.ts b/packages/eui/src/components/popover/popover_arrow/_popover_arrow.styles.ts index 308cf4d6f78..6f8233da917 100644 --- a/packages/eui/src/components/popover/popover_arrow/_popover_arrow.styles.ts +++ b/packages/eui/src/components/popover/popover_arrow/_popover_arrow.styles.ts @@ -7,63 +7,31 @@ */ import { css } from '@emotion/react'; -import { logicalSizeCSS, mathWithUnits } from '../../../global_styling'; +import { logicalSizeCSS } from '../../../global_styling'; +import { _popoverArrowStyles } from '../../../services/popover'; import { UseEuiTheme } from '../../../services'; -export const popoverArrowSize = 'base'; - export const euiPopoverArrowStyles = (euiThemeContext: UseEuiTheme) => { - const { euiTheme } = euiThemeContext; - - const arrowColor = 'var(--euiPopoverBackgroundColor)'; - const borderColor = euiTheme.colors.borderBaseFloating; - const arrowSizeBase = euiTheme.size[popoverArrowSize]; - const arrowSize = mathWithUnits(arrowSizeBase, (x) => x + 3); // calculation to ensure size parity - - const arrowPlusSize = mathWithUnits(arrowSize, (x) => (x / 2 + 1) * -1); - const arrowMinusSize = mathWithUnits(arrowSize, (x) => (x / 2 - 1) * -1); + const { euiTheme, colorMode } = euiThemeContext; + const hasBorder = colorMode === 'DARK'; - const adjustedPosition = 'calc(25% - 2px)'; + const arrowSize = euiTheme.size.base; + const arrowStyles = _popoverArrowStyles(euiThemeContext, arrowSize); return { - // Base - euiPopoverArrow: css` + // Wrapper + euiPopoverArrowWrapper: css` position: absolute; - ${logicalSizeCSS(arrowSize, arrowSize)} - transform-origin: center; - border-radius: ${mathWithUnits( - euiTheme.border.radius.small, - (x) => x / 2 - )}; - border: ${euiTheme.border.width.thin} solid transparent; - background-color: ${arrowColor}; - `, - - // POSITIONS - top: css` - border-block-end-color: ${borderColor}; - border-inline-end-color: ${borderColor}; - transform: translate(${adjustedPosition}, ${arrowPlusSize}) rotateZ(45deg); - `, - - bottom: css` - border-block-start-color: ${borderColor}; - border-inline-start-color: ${borderColor}; - transform: translate(${adjustedPosition}, ${arrowMinusSize}) - rotateZ(45deg); + ${logicalSizeCSS(arrowSize)} `, - left: css` - border-block-start-color: ${borderColor}; - border-inline-end-color: ${borderColor}; - transform: translate(${arrowPlusSize}, ${adjustedPosition}) rotateZ(45deg); + // Base + euiPopoverArrow: css` + ${arrowStyles._arrowStyles} + background-color: var(--euiPopoverBackgroundColor); + ${hasBorder ? `border: ${euiTheme.border.thin}` : ''} `, - right: css` - border-block-end-color: ${borderColor}; - border-inline-start-color: ${borderColor}; - transform: translate(${arrowMinusSize}, ${adjustedPosition}) - rotateZ(45deg); - `, + ...arrowStyles.positions, }; }; diff --git a/packages/eui/src/components/popover/popover_arrow/_popover_arrow.tsx b/packages/eui/src/components/popover/popover_arrow/_popover_arrow.tsx index 4238824ae23..eef2ad09c82 100644 --- a/packages/eui/src/components/popover/popover_arrow/_popover_arrow.tsx +++ b/packages/eui/src/components/popover/popover_arrow/_popover_arrow.tsx @@ -7,9 +7,9 @@ */ import React, { HTMLAttributes, FunctionComponent } from 'react'; +import { useEuiTheme } from '../../../services'; import { CommonProps } from '../../common'; import { euiPopoverArrowStyles } from './_popover_arrow.styles'; -import { useEuiTheme } from '../../../services'; export const POSITIONS = ['top', 'left', 'right', 'bottom'] as const; export type EuiPopoverArrowPositions = (typeof POSITIONS)[number]; @@ -22,6 +22,7 @@ export type EuiPopoverArrowProps = HTMLAttributes & export const EuiPopoverArrow: FunctionComponent = ({ children, position, + style, ...rest }) => { const euiTheme = useEuiTheme(); @@ -30,11 +31,16 @@ export const EuiPopoverArrow: FunctionComponent = ({ return (
+
{children}
); diff --git a/packages/eui/src/components/popover/popover_footer.stories.tsx b/packages/eui/src/components/popover/popover_footer.stories.tsx index 9ddb92a8a7e..717f07ccb3b 100644 --- a/packages/eui/src/components/popover/popover_footer.stories.tsx +++ b/packages/eui/src/components/popover/popover_footer.stories.tsx @@ -9,6 +9,7 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; +import { LOKI_SELECTORS } from '../../../.storybook/loki'; import { PADDING_SIZES } from '../../global_styling'; import { EuiButton } from '../button'; import { EuiPopover } from './popover'; @@ -17,6 +18,9 @@ import { EuiPopoverFooter, EuiPopoverFooterProps } from './popover_footer'; const meta: Meta = { title: 'Layout/EuiPopover/EuiPopoverFooter', component: EuiPopoverFooter, + parameters: { + loki: { chromeSelector: LOKI_SELECTORS.portal }, + }, decorators: [ (Story, { args }) => ( trigger}> diff --git a/packages/eui/src/components/popover/popover_panel/_popover_panel.styles.ts b/packages/eui/src/components/popover/popover_panel/_popover_panel.styles.ts index d6521156dbd..56aea213dcd 100644 --- a/packages/eui/src/components/popover/popover_panel/_popover_panel.styles.ts +++ b/packages/eui/src/components/popover/popover_panel/_popover_panel.styles.ts @@ -40,6 +40,8 @@ export const euiPopoverPanelStyles = (euiThemeContext: UseEuiTheme) => { euiTheme.animation.bounce } ${mathWithUnits(animationSpeed, (x) => x + 100)}`; + const hasVisibleBorder = colorMode === 'DARK'; + return { // Base euiPopover__panel: css` @@ -50,6 +52,8 @@ export const euiPopoverPanelStyles = (euiThemeContext: UseEuiTheme) => { pointer-events: none; opacity: 0; /* 2 */ background-color: var(--euiPopoverBackgroundColor); /* 4 */ + border: ${euiTheme.border.width.thin} solid + ${hasVisibleBorder ? euiTheme.border.color : 'transparent'}; ${euiCanAnimate} { /* 2 */ @@ -59,16 +63,6 @@ export const euiPopoverPanelStyles = (euiThemeContext: UseEuiTheme) => { &:focus { outline-offset: 0; } - - &::before { - content: ''; - position: absolute; - inset: 0; - border-radius: inherit; - border: ${euiTheme.border.width.thin} solid - ${euiTheme.colors.borderBaseFloating}; - pointer-events: none; - } `, isOpen: css` opacity: 1; diff --git a/packages/eui/src/components/selectable/selectable_list/__snapshots__/selectable_list_item.test.tsx.snap b/packages/eui/src/components/selectable/selectable_list/__snapshots__/selectable_list_item.test.tsx.snap index f870d340b47..498d2d9346b 100644 --- a/packages/eui/src/components/selectable/selectable_list/__snapshots__/selectable_list_item.test.tsx.snap +++ b/packages/eui/src/components/selectable/selectable_list/__snapshots__/selectable_list_item.test.tsx.snap @@ -557,7 +557,7 @@ exports[`EuiSelectableListItem props tooltip behavior on mouseover 1`] = `
I am a tooltip! @@ -637,7 +637,7 @@ exports[`EuiSelectableListItem props tooltip behavior when isFocused 1`] = `
I am a tooltip! diff --git a/packages/eui/src/components/table/table_pagination/__snapshots__/table_pagination.test.tsx.snap b/packages/eui/src/components/table/table_pagination/__snapshots__/table_pagination.test.tsx.snap index 5cf804dd3ed..cb9441e795d 100644 --- a/packages/eui/src/components/table/table_pagination/__snapshots__/table_pagination.test.tsx.snap +++ b/packages/eui/src/components/table/table_pagination/__snapshots__/table_pagination.test.tsx.snap @@ -209,14 +209,18 @@ exports[`EuiTablePagination renders 1`] = ` data-autofocus="true" data-popover-panel="true" role="dialog" - style="top: -16px; left: -22px; will-change: transform, opacity; z-index: 2000;" + style="top: -16px; left: -18px; will-change: transform, opacity; z-index: 2000;" tabindex="0" >
+ class="euiPopover__arrowWrapper emotion-euiPopoverArrowWrapper" + style="left: 9px; top: 100%;" + > +
+

content diff --git a/packages/eui/src/components/tool_tip/tool_tip.stories.tsx b/packages/eui/src/components/tool_tip/tool_tip.stories.tsx index 8c4deb06263..c28efa95907 100644 --- a/packages/eui/src/components/tool_tip/tool_tip.stories.tsx +++ b/packages/eui/src/components/tool_tip/tool_tip.stories.tsx @@ -10,7 +10,8 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { enableFunctionToggleControls } from '../../../.storybook/utils'; -import { LOKI_SELECTORS } from '../../../.storybook/loki'; +import { LOKI_SELECTORS, lokiPlayDecorator } from '../../../.storybook/loki'; +import { sleep } from '../../test'; import { EuiFlexGroup } from '../flex'; import { EuiButton } from '../button'; import { EuiToolTip, EuiToolTipProps } from './tool_tip'; @@ -59,16 +60,8 @@ export const Playground: Story = { children: Tooltip trigger, content: 'tooltip content', }, - // play: lokiPlayDecorator(async (context) => { - // const { bodyElement, step } = context; - - // const canvas = within(bodyElement); - - // await step('show tooltip on click', async () => { - // await userEvent.click(canvas.getByRole('button')); - // await waitFor(() => { - // expect(canvas.getByRole('tooltip')).toBeVisible(); - // }); - // }); - // }), + play: lokiPlayDecorator(async () => { + // Reduce VRT flakiness/screenshots before tooltip is fully visible + await sleep(300); + }), }; diff --git a/packages/eui/src/components/tool_tip/tool_tip.styles.ts b/packages/eui/src/components/tool_tip/tool_tip.styles.ts index b2054d7d603..56a8b2bc06f 100644 --- a/packages/eui/src/components/tool_tip/tool_tip.styles.ts +++ b/packages/eui/src/components/tool_tip/tool_tip.styles.ts @@ -7,14 +7,9 @@ */ import { css, keyframes } from '@emotion/react'; -import { - logicalCSS, - logicalSizeCSS, - euiFontSize, - euiCanAnimate, - mathWithUnits, -} from '../../global_styling'; +import { logicalCSS, euiFontSize, euiCanAnimate } from '../../global_styling'; import { UseEuiTheme } from '../../services'; +import { _popoverArrowStyles } from '../../services/popover'; import { euiShadow } from '../../themes/amsterdam'; export const euiToolTipBackgroundColor = (euiTheme: UseEuiTheme['euiTheme']) => @@ -48,20 +43,21 @@ const euiToolTipAnimationHorizontal = (size: string) => keyframes` `; export const euiToolTipStyles = (euiThemeContext: UseEuiTheme) => { - const { euiTheme } = euiThemeContext; + const { euiTheme, colorMode } = euiThemeContext; + + const hasVisibleBorder = colorMode === 'DARK'; const animationTiming = `${euiTheme.animation.slow} ease-out 0s forwards`; - // Shift arrow 1px more than half its size to account for border radius + const arrowSize = euiTheme.size.m; - const arrowPlusSize = mathWithUnits(arrowSize, (x) => (x / 2 + 1) * -1); - const arrowMinusSize = mathWithUnits(arrowSize, (x) => (x / 2 - 1) * -1); + const arrowStyles = _popoverArrowStyles(euiThemeContext, arrowSize); return { // Base euiToolTip: css` ${euiShadow(euiThemeContext)} - border-radius: ${euiTheme.border.radius.medium}; border: ${euiTheme.border.width.thin} solid - ${euiTheme.components.tooltipBorderFloating}; + ${hasVisibleBorder ? euiTheme.border.color : 'transparent'}; + border-radius: ${euiTheme.border.radius.medium}; background-color: ${euiToolTipBackgroundColor(euiTheme)}; color: ${euiTheme.colors.ghost}; z-index: ${euiTheme.levels.toast}; @@ -107,39 +103,11 @@ export const euiToolTipStyles = (euiThemeContext: UseEuiTheme) => { `, // Arrow euiToolTip__arrow: css` - content: ''; - position: absolute; - transform-origin: center; - border-radius: ${mathWithUnits( - euiTheme.border.radius.small, - (x) => x / 2 - )}; - border: ${euiTheme.border.width.thin} solid transparent; - background-color: ${euiToolTipBackgroundColor(euiTheme)}; - ${logicalSizeCSS(arrowSize, arrowSize)} + ${arrowStyles._arrowStyles} + background-color: inherit; + border: inherit; `, - arrowPositions: { - top: css` - border-block-end-color: ${euiTheme.colors.borderBaseFloating}; - border-inline-end-color: ${euiTheme.colors.borderBaseFloating}; - transform: translateY(${arrowPlusSize}) rotateZ(45deg); - `, - bottom: css` - border-block-start-color: ${euiTheme.colors.borderBaseFloating}; - border-inline-start-color: ${euiTheme.colors.borderBaseFloating}; - transform: translateY(${arrowMinusSize}) rotateZ(45deg); - `, - left: css` - border-block-start-color: ${euiTheme.colors.borderBaseFloating}; - border-inline-end-color: ${euiTheme.colors.borderBaseFloating}; - transform: translateX(${arrowPlusSize}) rotateZ(45deg); - `, - right: css` - border-block-end-color: ${euiTheme.colors.borderBaseFloating}; - border-inline-start-color: ${euiTheme.colors.borderBaseFloating}; - transform: translateX(${arrowMinusSize}) rotateZ(45deg); - `, - }, + arrowPositions: arrowStyles.positions, // Title euiToolTip__title: css` font-weight: ${euiTheme.font.weight.bold}; diff --git a/packages/eui/src/components/tool_tip/tool_tip.tsx b/packages/eui/src/components/tool_tip/tool_tip.tsx index 616ca2b5ab0..ff3112f8aa0 100644 --- a/packages/eui/src/components/tool_tip/tool_tip.tsx +++ b/packages/eui/src/components/tool_tip/tool_tip.tsx @@ -17,6 +17,7 @@ import classNames from 'classnames'; import { CommonProps } from '../common'; import { findPopoverPosition, htmlIdGenerator, keys } from '../../services'; +import { type EuiPopoverPosition } from '../../services/popover'; import { enqueueStateChange } from '../../services/react'; import { EuiResizeObserver } from '../observer/resize_observer'; import { EuiPortal } from '../portal'; @@ -117,7 +118,7 @@ interface State { hasFocus: boolean; calculatedPosition: ToolTipPositions; toolTipStyles: ToolTipStyles; - arrowStyles: undefined | { left: number; top: number }; + arrowStyles?: Record; id: string; } diff --git a/packages/eui/src/components/tour/__snapshots__/tour_step.test.tsx.snap b/packages/eui/src/components/tour/__snapshots__/tour_step.test.tsx.snap index 0aa353e4849..73848e8ae1b 100644 --- a/packages/eui/src/components/tour/__snapshots__/tour_step.test.tsx.snap +++ b/packages/eui/src/components/tour/__snapshots__/tour_step.test.tsx.snap @@ -27,13 +27,16 @@ exports[`EuiTourStep renders 1`] = ` data-popover-open="true" data-popover-panel="true" role="dialog" - style="top: -22px; left: -26px; will-change: transform, opacity; z-index: 2000;" + style="top: -18px; left: -26px; will-change: transform, opacity; z-index: 2000;" >
+
({ // Targets EuiPopoverPanel euiTour: css` - [data-popover-arrow='top']::before { - ${logicalCSS('border-top-color', _tourFooterBgColor(euiThemeContext))} + [data-popover-arrow='top'] { + background-color: ${_tourFooterBgColor(euiThemeContext)}; } `, }); export const euiTourBeaconStyles = ({ euiTheme }: UseEuiTheme) => { - const arrowSize = euiTheme.size[popoverArrowSize]; - const arrowHalfSize = mathWithUnits(arrowSize, (x) => x / 2); - const arrowOffset = mathWithUnits(arrowSize, (x) => x * -2); + const beaconSize = euiTheme.size.m; + const beaconOffset = mathWithUnits(beaconSize, (x) => x / -2); return { // Base @@ -45,20 +43,24 @@ export const euiTourBeaconStyles = ({ euiTheme }: UseEuiTheme) => { `, // Positions right: css` - ${logicalCSS('top', arrowHalfSize)} - ${logicalCSS('left', arrowOffset)} + ${logicalCSS('top', '50%')} + ${logicalCSS('left', '-50%')} + ${logicalCSS('margin-top', beaconOffset)} `, left: css` - ${logicalCSS('top', arrowHalfSize)} - ${logicalCSS('left', arrowSize)} + ${logicalCSS('top', '50%')} + ${logicalCSS('right', '-50%')} + ${logicalCSS('margin-top', beaconOffset)} `, top: css` - ${logicalCSS('top', arrowSize)} - ${logicalCSS('left', arrowHalfSize)} + ${logicalCSS('left', '50%')} + ${logicalCSS('bottom', '-50%')} + ${logicalCSS('margin-left', beaconOffset)} `, bottom: css` - ${logicalCSS('top', arrowOffset)} - ${logicalCSS('left', arrowHalfSize)} + ${logicalCSS('left', '50%')} + ${logicalCSS('top', '-50%')} + ${logicalCSS('margin-left', beaconOffset)} `, }; }; diff --git a/packages/eui/src/services/popover/index.ts b/packages/eui/src/services/popover/index.ts index b89b73f6184..9553f62ccf4 100644 --- a/packages/eui/src/services/popover/index.ts +++ b/packages/eui/src/services/popover/index.ts @@ -8,4 +8,7 @@ export { calculatePopoverPosition } from './calculate_popover_position'; export { findPopoverPosition, getElementZIndex } from './popover_positioning'; + +// Not exported as public APIs +export { _popoverArrowStyles } from './popover_arrow.styles'; export type { EuiPopoverPosition } from './types'; diff --git a/packages/eui/src/services/popover/popover_arrow.styles.ts b/packages/eui/src/services/popover/popover_arrow.styles.ts new file mode 100644 index 00000000000..860f88e1e05 --- /dev/null +++ b/packages/eui/src/services/popover/popover_arrow.styles.ts @@ -0,0 +1,65 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { css } from '@emotion/react'; +import { + logicalCSS, + logicalSizeCSS, + mathWithUnits, +} from '../../global_styling/functions'; +import { UseEuiTheme } from '../../services'; + +/** + * Arrow clipping/transform/positioning CSS shared between EuiPopover and EuiToolTip + */ +export const _popoverArrowStyles = ( + { euiTheme }: UseEuiTheme, + arrowSize: string +) => { + const arrowOffset = mathWithUnits(arrowSize, (x) => x / -2); + + const arrowBorderRadius = mathWithUnits( + euiTheme.border.radius.small, + (x) => x / 2 + ); + + return { + _arrowStyles: ` + position: absolute; + ${logicalSizeCSS(arrowSize)} + border-radius: ${arrowBorderRadius}; + /* Use clip-path to ensure that arrows don't overlap into popover content */ + clip-path: polygon(0 0, 100% 100%, 0 100%); + transform-origin: center; + `, + + positions: { + top: css` + ${logicalCSS('margin-top', arrowOffset)} + transform: rotate(-45deg); + `, + + bottom: css` + ${logicalCSS('bottom', 0)} + ${logicalCSS('margin-bottom', arrowOffset)} + transform: rotate(135deg); + `, + + left: css` + ${logicalCSS('margin-left', arrowOffset)} + transform: rotate(-135deg); + `, + + right: css` + ${logicalCSS('right', 0)} + ${logicalCSS('margin-right', arrowOffset)} + transform: rotate(45deg); + `, + }, + }; +}; diff --git a/packages/eui/src/services/popover/popover_positioning.test.ts b/packages/eui/src/services/popover/popover_positioning.test.ts index bae2c9d40ec..c31abd94f25 100644 --- a/packages/eui/src/services/popover/popover_positioning.test.ts +++ b/packages/eui/src/services/popover/popover_positioning.test.ts @@ -337,8 +337,8 @@ describe('popover_positioning', () => { top: 40, left: 25, arrow: { - top: 50, - left: 22.5, + top: '100%', + left: 21.5, }, }); }); @@ -359,8 +359,8 @@ describe('popover_positioning', () => { top: -15, left: 37.5, arrow: { - top: 50, - left: 10, + top: '100%', + left: 9, }, }); }); @@ -382,8 +382,8 @@ describe('popover_positioning', () => { top: -15, left: 37.5, arrow: { - top: 50, - left: 10, + top: '100%', + left: 9, }, }); }); @@ -406,8 +406,8 @@ describe('popover_positioning', () => { top: 50, left: 75, arrow: { - top: 50, - left: 22, + top: '100%', + left: 21, }, }); @@ -426,8 +426,8 @@ describe('popover_positioning', () => { top: 110, left: 25, arrow: { - top: 0, - left: 72, + bottom: '100%', + left: 71, }, }); }); @@ -448,8 +448,8 @@ describe('popover_positioning', () => { top: -82, left: 125, arrow: { - top: 184, - left: 0, + top: 183, + right: '100%', }, }); }); diff --git a/packages/eui/src/services/popover/popover_positioning.ts b/packages/eui/src/services/popover/popover_positioning.ts index 8cc1a131600..933b5478140 100644 --- a/packages/eui/src/services/popover/popover_positioning.ts +++ b/packages/eui/src/services/popover/popover_positioning.ts @@ -83,7 +83,7 @@ interface FindPopoverPositionResult { left: number; position: EuiPopoverPosition; fit: number; - arrow?: { left: number; top: number }; + arrow?: Record; anchorBoundingBox?: EuiClientRect; } @@ -265,7 +265,7 @@ interface GetPopoverScreenCoordinatesResult { top: number; left: number; fit: number; - arrow: { top: number; left: number } | undefined; + arrow?: Record; } /** @@ -360,14 +360,12 @@ export function getPopoverScreenCoordinates({ const primaryAxisPositionName = dimensionPositionAttribute[primaryAxisDimension]; // "height" -> "top" - const { primaryAxisPosition, primaryAxisArrowPosition } = - getPrimaryAxisPosition({ - position, - offset, - popoverBoundingBox, - anchorBoundingBox, - arrowConfig, - }); + const { primaryAxisPosition } = getPrimaryAxisPosition({ + position, + offset, + popoverBoundingBox, + anchorBoundingBox, + }); const popoverPlacement = { [crossAxisFirstSide]: crossAxisPosition, @@ -401,18 +399,18 @@ export function getPopoverScreenCoordinates({ ); const arrow = arrowConfig - ? { + ? ({ [crossAxisFirstSide]: - crossAxisArrowPosition! - popoverPlacement[crossAxisFirstSide], - [primaryAxisPositionName]: primaryAxisArrowPosition, - } + crossAxisArrowPosition! - popoverPlacement[crossAxisFirstSide] - 1, // Account for 1px border thin width + [position]: '100%', + } as Record) : undefined; return { fit, top: popoverPlacement.top, left: popoverPlacement.left, - arrow: arrow ? { left: arrow.left!, top: arrow.top! } : undefined, + arrow, }; } @@ -560,7 +558,6 @@ interface GetPrimaryAxisPositionArgs { offset: number; popoverBoundingBox: BoundingBox; anchorBoundingBox: BoundingBox; - arrowConfig?: { arrowWidth: number; arrowBuffer: number }; } function getPrimaryAxisPosition({ @@ -568,7 +565,6 @@ function getPrimaryAxisPosition({ offset, popoverBoundingBox, anchorBoundingBox, - arrowConfig, }: GetPrimaryAxisPositionArgs) { // if positioning to the top or left, the target position decreases // from the anchor's top or left, otherwise the position adds to the anchor's @@ -591,18 +587,7 @@ function getPrimaryAxisPosition({ (offset + primaryAxisOffset!) * (isOffsetDecreasing ? -1 : 1); const primaryAxisPosition = anchorEdgeOrigin + contentOffset; - let primaryAxisArrowPosition; - - if (arrowConfig) { - primaryAxisArrowPosition = isOffsetDecreasing - ? popoverSizeOnPrimaryAxis - : 0; - } - - return { - primaryAxisPosition, - primaryAxisArrowPosition, - }; + return { primaryAxisPosition }; } /**