@@ -65,7 +65,7 @@ exports[`EuiButtonContent props isLoading replaces iconType with spinner 1`] = `
>
diff --git a/src/components/button/button_content.tsx b/src/components/button/_button_content_deprecated.tsx
similarity index 87%
rename from src/components/button/button_content.tsx
rename to src/components/button/_button_content_deprecated.tsx
index 622f8024b7a..fa9dd804b79 100644
--- a/src/components/button/button_content.tsx
+++ b/src/components/button/_button_content_deprecated.tsx
@@ -26,8 +26,10 @@ export const ICON_SIDES = keysOf(iconSideToClassNameMap);
export type EuiButtonContentType = HTMLAttributes;
/**
- * *INTERNAL ONLY*
+ * *INTERNAL ONLY / DEPRECATED*
* This component is simply a helper component for reuse within other button components
+ * This component has been deprecated in favor of the new EuiButtonDisplayContent
+ * that can be found in `src/components/button/button_display/_button_display_content.tsx`.
*/
export interface EuiButtonContentProps extends CommonProps {
/**
@@ -40,7 +42,8 @@ export interface EuiButtonContentProps extends CommonProps {
iconSide?: ButtonContentIconSide;
isLoading?: boolean;
/**
- * Object of props passed to the wrapping the content's text/children only (not icon)
+ * Object of props passed to the wrapping the content's text (only if the children is a `string`)
+ * It doesn't apply to the icon.
*/
textProps?: HTMLAttributes &
CommonProps & {
@@ -50,7 +53,7 @@ export interface EuiButtonContentProps extends CommonProps {
iconSize?: 's' | 'm';
}
-export const EuiButtonContent: FunctionComponent<
+export const EuiButtonContentDeprecated: FunctionComponent<
EuiButtonContentType & EuiButtonContentProps
> = ({
children,
diff --git a/src/components/button/button.test.tsx b/src/components/button/button.test.tsx
index ac22ca17796..de901391660 100644
--- a/src/components/button/button.test.tsx
+++ b/src/components/button/button.test.tsx
@@ -11,7 +11,8 @@ import { render, mount } from 'enzyme';
import { requiredProps } from '../../test/required_props';
import { EuiButton, COLORS, SIZES } from './button';
-import { ICON_SIDES } from './button_content';
+
+import { ICON_SIDES } from './_button_content_deprecated';
describe('EuiButton', () => {
test('is rendered', () => {
diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx
index 29b45c16315..c95d7ec980d 100644
--- a/src/components/button/button.tsx
+++ b/src/components/button/button.tsx
@@ -30,8 +30,8 @@ import { getSecureRelForTarget } from '../../services';
import {
EuiButtonContentProps,
EuiButtonContentType,
- EuiButtonContent,
-} from './button_content';
+ EuiButtonContentDeprecated as EuiButtonContent,
+} from './_button_content_deprecated';
import { validateHref } from '../../services/security/href_validator';
export type ButtonColor =
@@ -174,7 +174,8 @@ export const EuiButton: FunctionComponent = ({
}
return (
- (
+export const EuiButtonDisplayDeprecated = forwardRef<
+ HTMLElement,
+ EuiButtonDisplayProps
+>(
(
{
element = 'button',
@@ -295,4 +302,4 @@ export const EuiButtonDisplay = forwardRef(
);
}
);
-EuiButtonDisplay.displayName = 'EuiButtonDisplay';
+EuiButtonDisplayDeprecated.displayName = 'EuiButtonDisplay';
diff --git a/src/components/button/button_content.test.tsx b/src/components/button/button_content.test.tsx
index 145de430f84..21ea25c4aa3 100644
--- a/src/components/button/button_content.test.tsx
+++ b/src/components/button/button_content.test.tsx
@@ -10,7 +10,7 @@ import React from 'react';
import { render } from 'enzyme';
import { requiredProps } from '../../test/required_props';
-import { EuiButtonContent } from './button_content';
+import { EuiButtonContentDeprecated as EuiButtonContent } from './_button_content_deprecated';
describe('EuiButtonContent', () => {
test('is rendered', () => {
diff --git a/src/components/button/button_display/_button_display.styles.ts b/src/components/button/button_display/_button_display.styles.ts
new file mode 100644
index 00000000000..f723a835f64
--- /dev/null
+++ b/src/components/button/button_display/_button_display.styles.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 { UseEuiTheme } from '../../../services';
+import {
+ euiFontSize,
+ logicalCSS,
+ logicalTextAlignStyle,
+} from '../../../global_styling';
+
+// Provides a solid reset and base for handling sizing layout
+// Does not include any visual styles
+export const euiButtonBaseCSS = () => {
+ return `
+ display: inline-block;
+ appearance: none;
+ cursor: pointer;
+ ${logicalTextAlignStyle('center')};
+ white-space: nowrap;
+ ${logicalCSS('max-width', '100%')};
+ vertical-align: middle;
+ `;
+};
+
+const _buttonSize = (size: string) => {
+ return `
+ ${logicalCSS('height', size)};
+ // prevents descenders from getting cut off
+ line-height: ${size};
+ `;
+};
+
+export const euiButtonDisplayStyles = (
+ euiThemeContext: UseEuiTheme,
+ minWidth: string
+) => {
+ const { euiTheme } = euiThemeContext;
+
+ return {
+ // Base
+ euiButtonDisplay: css`
+ ${euiButtonBaseCSS()};
+ ${minWidth && logicalCSS('min-width', minWidth)};
+ `,
+ // States
+ isDisabled: css`
+ cursor: not-allowed;
+ `,
+ fullWidth: css`
+ display: block;
+ width: 100%;
+ `,
+ // Sizes
+ xs: css(_buttonSize(euiTheme.size.l), euiFontSize(euiThemeContext, 'xs')),
+ s: css(_buttonSize(euiTheme.size.xl), euiFontSize(euiThemeContext, 's')),
+ m: css(_buttonSize(euiTheme.size.xxl), euiFontSize(euiThemeContext, 's')),
+ };
+};
diff --git a/src/components/button/button_display/_button_display.tsx b/src/components/button/button_display/_button_display.tsx
new file mode 100644
index 00000000000..73a791ac80c
--- /dev/null
+++ b/src/components/button/button_display/_button_display.tsx
@@ -0,0 +1,160 @@
+/*
+ * 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, {
+ forwardRef,
+ CSSProperties,
+ HTMLAttributes,
+ ReactNode,
+ Ref,
+} from 'react';
+
+// @ts-ignore module doesn't export `createElement`
+import { createElement } from '@emotion/react';
+import { useEuiTheme } from '../../../services';
+
+import {
+ CommonProps,
+ ExclusiveUnion,
+ PropsForAnchor,
+ PropsForButton,
+} from '../../common';
+
+import { euiButtonDisplayStyles } from './_button_display.styles';
+import {
+ EuiButtonDisplayContent,
+ EuiButtonDisplayContentProps,
+ EuiButtonDisplayContentType,
+} from './_button_display_content';
+
+/**
+ * Extends EuiButtonDisplayContentProps which provides
+ * `iconType`, `iconSide`, and `textProps`
+ */
+export interface EuiButtonDisplayCommonProps
+ extends EuiButtonDisplayContentProps,
+ CommonProps {
+ children?: ReactNode;
+ size?: 'xs' | 's' | 'm';
+ /**
+ * Applies the boolean state as the `aria-pressed` property to create a toggle button.
+ * *Only use when the readable text does not change between states.*
+ */
+ isSelected?: boolean;
+ /**
+ * Extends the button to 100% width
+ */
+ fullWidth?: boolean;
+ /**
+ * Override the default minimum width
+ */
+ minWidth?: CSSProperties['minWidth'];
+ /**
+ * Force disables the button and changes the icon to a loading spinner
+ */
+ isLoading?: boolean;
+ /**
+ * Object of props passed to the wrapping the button's content
+ */
+ contentProps?: EuiButtonDisplayContentType;
+ style?: CSSProperties;
+}
+
+export type EuiButtonDisplayPropsForAnchor = PropsForAnchor<
+ EuiButtonDisplayCommonProps,
+ {
+ buttonRef?: Ref;
+ }
+>;
+
+export type EuiButtonDisplayPropsForButton = PropsForButton<
+ EuiButtonDisplayCommonProps,
+ {
+ buttonRef?: Ref;
+ }
+>;
+
+export type Props = ExclusiveUnion<
+ EuiButtonDisplayPropsForAnchor,
+ EuiButtonDisplayPropsForButton
+>;
+
+export type EuiButtonDisplayProps = EuiButtonDisplayCommonProps &
+ HTMLAttributes & {
+ /**
+ * Provide a valid element to render the element as
+ */
+ element?: 'a' | 'button' | 'span' | 'label';
+ };
+
+/**
+ * EuiButtonDisplay is an internal-only component used for displaying
+ * any element as a button.
+ */
+export const EuiButtonDisplay = forwardRef(
+ (
+ {
+ element = 'button',
+ children,
+ iconType,
+ iconSide,
+ size = 'm',
+ isDisabled = false,
+ isLoading,
+ isSelected,
+ fullWidth,
+ minWidth,
+ contentProps,
+ textProps,
+ ...rest
+ },
+ ref
+ ) => {
+ const buttonIsDisabled = isLoading || isDisabled;
+
+ const minWidthPx: string =
+ minWidth === 'number' ? `${minWidth}px` : (minWidth as string);
+
+ const theme = useEuiTheme();
+
+ const styles = euiButtonDisplayStyles(theme, minWidthPx);
+ const cssStyles = [
+ styles.euiButtonDisplay,
+ styles[size],
+ fullWidth && styles.fullWidth,
+ isDisabled && styles.isDisabled,
+ ];
+
+ const innerNode = (
+
+ {children}
+
+ );
+
+ return createElement(
+ element,
+ {
+ css: cssStyles,
+ disabled: element === 'button' && buttonIsDisabled,
+ 'aria-pressed': element === 'button' ? isSelected : undefined,
+ ref,
+ ...rest,
+ },
+ innerNode
+ );
+ }
+);
+
+EuiButtonDisplay.displayName = 'EuiButtonDisplay';
diff --git a/src/components/button/button_display/_button_display_content.styles.ts b/src/components/button/button_display/_button_display_content.styles.ts
new file mode 100644
index 00000000000..9838957b43f
--- /dev/null
+++ b/src/components/button/button_display/_button_display_content.styles.ts
@@ -0,0 +1,38 @@
+/*
+ * 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 { UseEuiTheme } from '../../../services';
+import { logicalCSS } from '../../../global_styling';
+
+export const euiButtonDisplayContentStyles = ({ euiTheme }: UseEuiTheme) => ({
+ // Base
+ euiButtonDisplayContent: css`
+ ${logicalCSS('height', '100%')};
+ ${logicalCSS('width', '100%')};
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ vertical-align: middle;
+ gap: ${euiTheme.size.s};
+ `,
+ // Icon side
+ left: css``,
+ right: css`
+ flex-direction: row-reverse;
+ `,
+ euiButtonDisplayContent__spinner: css`
+ flex-shrink: 0;
+ `,
+ euiButtonDisplayContent__icon: css`
+ flex-shrink: 0;
+ `,
+ isDisabled: css`
+ pointer-events: auto;
+ cursor: not-allowed;
+ `,
+});
diff --git a/src/components/button/button_display/_button_display_content.tsx b/src/components/button/button_display/_button_display_content.tsx
new file mode 100644
index 00000000000..4bb7b1d5e25
--- /dev/null
+++ b/src/components/button/button_display/_button_display_content.tsx
@@ -0,0 +1,110 @@
+/*
+ * 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, { HTMLAttributes, FunctionComponent, Ref } from 'react';
+import { useEuiTheme } from '../../../services';
+import { CommonProps } from '../../common';
+import { EuiLoadingSpinner, EuiLoadingSpinnerProps } from '../../loading';
+import { EuiIcon, IconType } from '../../icon';
+import { euiButtonDisplayContentStyles } from './_button_display_content.styles';
+
+export type ButtonContentIconSide = 'left' | 'right' | undefined;
+
+export type EuiButtonDisplayContentType = HTMLAttributes;
+
+/**
+ * *INTERNAL ONLY*
+ * This component is simply a helper component for reuse within other button components.
+ */
+export interface EuiButtonDisplayContentProps extends CommonProps {
+ /**
+ * Any `type` accepted by EuiIcon
+ */
+ iconType?: IconType;
+ /**
+ * Can only be one side `left` or `right`
+ */
+ iconSide?: ButtonContentIconSide;
+ isLoading?: boolean;
+ /**
+ * Object of props passed to the wrapping the content's text/children only (not icon)
+ */
+ textProps?: HTMLAttributes &
+ CommonProps & {
+ ref?: Ref;
+ 'data-text'?: string;
+ };
+ iconSize?: 's' | 'm';
+ isDisabled: boolean;
+}
+
+export const EuiButtonDisplayContent: FunctionComponent<
+ EuiButtonDisplayContentType & EuiButtonDisplayContentProps
+> = ({
+ children,
+ textProps,
+ isLoading = false,
+ isDisabled = false,
+ iconType,
+ iconSize = 'm',
+ iconSide,
+ ...contentProps
+}) => {
+ const theme = useEuiTheme();
+ const styles = euiButtonDisplayContentStyles(theme);
+
+ const cssStyles = [
+ styles.euiButtonDisplayContent,
+ iconSide && styles[iconSide],
+ isDisabled && styles.isDisabled,
+ ];
+ const cssSpinnerStyles = [styles.euiButtonDisplayContent__spinner];
+ const cssIconStyles = [styles.euiButtonDisplayContent__icon];
+
+ // Add an icon to the button if one exists.
+ let icon;
+
+ // When the button is disabled the text gets gray
+ // and in some buttons the background gets a light gray
+ // for better contrast we want to change the border of the spinner
+ // to have the same color of the text. This way we ensure the borders
+ // are always visible. The default spinner color could be very light.
+ const loadingSpinnerColor = isDisabled
+ ? ({
+ border: 'currentColor',
+ } as EuiLoadingSpinnerProps['color'])
+ : undefined;
+
+ if (isLoading) {
+ icon = (
+
+ );
+ } else if (iconType) {
+ icon = (
+
+ );
+ }
+
+ const isText = typeof children === 'string';
+
+ return (
+
+ {icon}
+ {isText ? {children} : children}
+
+ );
+};
diff --git a/src/components/button/button_empty/__snapshots__/button_empty.test.tsx.snap b/src/components/button/button_empty/__snapshots__/button_empty.test.tsx.snap
index d3773a14d2d..2bc21572809 100644
--- a/src/components/button/button_empty/__snapshots__/button_empty.test.tsx.snap
+++ b/src/components/button/button_empty/__snapshots__/button_empty.test.tsx.snap
@@ -313,7 +313,7 @@ exports[`EuiButtonEmpty props isLoading is rendered 1`] = `
>
{
test('is rendered', () => {
diff --git a/src/components/button/button_empty/button_empty.tsx b/src/components/button/button_empty/button_empty.tsx
index a754862f1f7..40748723016 100644
--- a/src/components/button/button_empty/button_empty.tsx
+++ b/src/components/button/button_empty/button_empty.tsx
@@ -17,11 +17,13 @@ import {
keysOf,
} from '../../common';
import { getSecureRelForTarget } from '../../../services';
+
import {
- EuiButtonContent,
+ EuiButtonContentDeprecated as EuiButtonContent,
EuiButtonContentProps,
EuiButtonContentType,
-} from '../button_content';
+} from '../_button_content_deprecated';
+
import { validateHref } from '../../../services/security/href_validator';
export type EuiButtonEmptyColor =
diff --git a/src/components/button/button_group/button_group.tsx b/src/components/button/button_group/button_group.tsx
index 132c2b378e0..6b10869705d 100644
--- a/src/components/button/button_group/button_group.tsx
+++ b/src/components/button/button_group/button_group.tsx
@@ -11,7 +11,7 @@ import React, { FunctionComponent, HTMLAttributes, ReactNode } from 'react';
import { EuiScreenReaderOnly } from '../../accessibility';
import { EuiButtonGroupButton } from './button_group_button';
import { colorToClassNameMap, ButtonColor } from '../button';
-import { EuiButtonContentProps } from '../button_content';
+import { EuiButtonContentProps } from '../_button_content_deprecated';
import { CommonProps } from '../../common';
import { useGeneratedHtmlId } from '../../../services';
diff --git a/src/components/button/button_group/button_group_button.tsx b/src/components/button/button_group/button_group_button.tsx
index 422074ddd80..da61bf5752a 100644
--- a/src/components/button/button_group/button_group_button.tsx
+++ b/src/components/button/button_group/button_group_button.tsx
@@ -8,7 +8,7 @@
import classNames from 'classnames';
import React, { FunctionComponent } from 'react';
-import { EuiButtonDisplay } from '../button';
+import { EuiButtonDisplayDeprecated as EuiButtonDisplay } from '../button';
import { EuiButtonGroupOptionProps, EuiButtonGroupProps } from './button_group';
import { useInnerText } from '../../inner_text';
import { useGeneratedHtmlId } from '../../../services';
diff --git a/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap b/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap
index 1e90bbc2294..c2ef2912792 100644
--- a/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap
+++ b/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap
@@ -255,7 +255,7 @@ exports[`EuiButtonIcon props isLoading is rendered 1`] = `
>
diff --git a/src/components/control_bar/__snapshots__/control_bar.test.tsx.snap b/src/components/control_bar/__snapshots__/control_bar.test.tsx.snap
index d41b45eb7ed..c05dbff3287 100644
--- a/src/components/control_bar/__snapshots__/control_bar.test.tsx.snap
+++ b/src/components/control_bar/__snapshots__/control_bar.test.tsx.snap
@@ -349,7 +349,7 @@ exports[`EuiControlBar props leftOffset is rendered 1`] = `
}
type="button"
>
-
-
+
@@ -691,7 +691,7 @@ exports[`EuiControlBar props maxHeight is rendered 1`] = `
}
type="button"
>
-
-
+
@@ -1032,7 +1032,7 @@ exports[`EuiControlBar props mobile is rendered 1`] = `
}
type="button"
>
-
-
+
@@ -1277,7 +1277,7 @@ exports[`EuiControlBar props position is rendered 1`] = `
}
type="button"
>
-
-
+
@@ -1603,7 +1603,7 @@ exports[`EuiControlBar props rightOffset is rendered 1`] = `
}
type="button"
>
-
-
+
@@ -1949,7 +1949,7 @@ exports[`EuiControlBar props showContent is rendered 1`] = `
}
type="button"
>
-
-
+
@@ -2295,7 +2295,7 @@ exports[`EuiControlBar props size is rendered 1`] = `
}
type="button"
>
-
-
+
diff --git a/src/components/facet/__snapshots__/facet_button.test.tsx.snap b/src/components/facet/__snapshots__/facet_button.test.tsx.snap
index 9d6e5a809db..1e66f3b1ea5 100644
--- a/src/components/facet/__snapshots__/facet_button.test.tsx.snap
+++ b/src/components/facet/__snapshots__/facet_button.test.tsx.snap
@@ -3,16 +3,15 @@
exports[`EuiFacetButton is rendered 1`] = `
diff --git a/src/components/form/form_control_layout/__snapshots__/form_control_layout.test.tsx.snap b/src/components/form/form_control_layout/__snapshots__/form_control_layout.test.tsx.snap
index 30c77301ed4..21c110b8466 100644
--- a/src/components/form/form_control_layout/__snapshots__/form_control_layout.test.tsx.snap
+++ b/src/components/form/form_control_layout/__snapshots__/form_control_layout.test.tsx.snap
@@ -75,7 +75,7 @@ exports[`EuiFormControlLayout props compressed renders small-sized icon, clear b