diff --git a/src-docs/src/views/badge/notification_badge.tsx b/src-docs/src/views/badge/notification_badge.tsx
index 99c7f9e46cd..009accce63b 100644
--- a/src-docs/src/views/badge/notification_badge.tsx
+++ b/src-docs/src/views/badge/notification_badge.tsx
@@ -1,5 +1,11 @@
import React from 'react';
-import { EuiNotificationBadge } from '../../../../src/components/badge/notification_badge';
+import { EuiFlexGroup, EuiNotificationBadge } from '../../../../src/components';
-export default () => 3;
+export default () => (
+
+ 1
+ 2
+ 3
+
+);
diff --git a/src-docs/src/views/filter_group/filter_group_multi.js b/src-docs/src/views/filter_group/filter_group_multi.js
index 5ea1b757c4b..bf9be4d07c2 100644
--- a/src-docs/src/views/filter_group/filter_group_multi.js
+++ b/src-docs/src/views/filter_group/filter_group_multi.js
@@ -63,6 +63,7 @@ export default () => {
const button = (
item.checked !== 'off').length}
diff --git a/src/components/badge/__snapshots__/badge.test.tsx.snap b/src/components/badge/__snapshots__/badge.test.tsx.snap
index 25dccfe71b9..80cce86b192 100644
--- a/src/components/badge/__snapshots__/badge.test.tsx.snap
+++ b/src/components/badge/__snapshots__/badge.test.tsx.snap
@@ -3,9 +3,8 @@
exports[`EuiBadge is disabled 1`] = `
{
- const { euiTheme, colorMode } = euiThemeContext;
+ const { euiTheme } = euiThemeContext;
+ const badgeColors = euiBadgeColors(euiThemeContext);
return {
euiBadge: css`
@@ -72,24 +73,28 @@ export const euiBadgeStyles = (euiThemeContext: UseEuiTheme) => {
cursor: not-allowed;
}
`,
+
+ // Colors
+ default: css(badgeColors.default),
+ hollow: css`
+ color: ${badgeColors.hollow.color};
+ background-color: ${badgeColors.hollow.backgroundColor};
+ border-color: ${badgeColors.hollow.borderColor};
+ `,
+ primary: css(badgeColors.primary),
+ accent: css(badgeColors.accent),
+ warning: css(badgeColors.warning),
+ danger: css(badgeColors.danger),
+ success: css(badgeColors.success),
disabled: css`
/* stylelint-disable declaration-no-important */
/* Using !important to override inline styles */
- color: ${euiButtonColor(euiThemeContext, 'disabled').color} !important;
- background-color: ${euiButtonColor(euiThemeContext, 'disabled')
- .backgroundColor} !important;
+ color: ${badgeColors.disabled.color} !important;
+ background-color: ${badgeColors.disabled.backgroundColor} !important;
/* stylelint-enable declaration-no-important */
`,
- // Hollow has a border and is mostly used for autocompleters.
- hollow: css`
- background-color: ${euiTheme.colors.emptyShade};
- border-color: ${colorMode === 'DARK'
- ? tint(euiTheme.border.color, 0.15)
- : euiTheme.border.color};
- color: ${euiTheme.colors.text};
- `,
// Content wrapper
euiBadge__content: css`
diff --git a/src/components/badge/badge.tsx b/src/components/badge/badge.tsx
index 1b9fd2afda4..e9bb385aff0 100644
--- a/src/components/badge/badge.tsx
+++ b/src/components/badge/badge.tsx
@@ -16,22 +16,18 @@ import React, {
useMemo,
} from 'react';
import classNames from 'classnames';
-import chroma from 'chroma-js';
import { CommonProps, ExclusiveUnion, PropsOf } from '../common';
import {
useEuiTheme,
- UseEuiTheme,
getSecureRelForTarget,
- isColorDark,
wcagContrastMin,
} from '../../services';
import { EuiInnerText } from '../inner_text';
import { EuiIcon, IconType } from '../icon';
-import { chromaValid, parseColor } from '../color_picker/utils';
import { validateHref } from '../../services/security/href_validator';
+import { getTextColor, getColorContrast, getIsValidColor } from './color_utils';
import { euiBadgeStyles } from './badge.styles';
-import { euiButtonFillColor } from '../../themes/amsterdam/global_styling/mixins';
export const ICON_SIDES = ['left', 'right'] as const;
type IconSide = (typeof ICON_SIDES)[number];
@@ -132,83 +128,49 @@ export const EuiBadge: FunctionComponent = ({
const isHrefValid = !href || validateHref(href);
const isDisabled = _isDisabled || !isHrefValid;
+ const isNamedColor = COLORS.includes(color as BadgeColor);
- const optionalCustomStyles = useMemo(() => {
- let textColor = null;
- let contrastRatio = null;
- let colorHex = null;
+ const customColorStyles = useMemo(() => {
+ // Named colors set their styles via Emotion CSS and not inline styles
+ if (isNamedColor) return style;
+ // Do our best to ensure custom colors provide sufficient contrast
try {
- // Check if a valid color name was provided
- if (COLORS.includes(color as BadgeColor)) {
- // Get the hex equivalent for the provided color name
- switch (color) {
- case 'hollow':
- return style; // hollow uses its own Emotion class
- case 'default':
- colorHex = euiTheme.euiTheme.colors.lightShade;
- break;
- default:
- type RemainingColors =
- | 'primary'
- | 'success'
- | 'accent'
- | 'warning'
- | 'danger';
- colorHex = euiButtonFillColor(
- euiTheme,
- color as RemainingColors
- ).backgroundColor;
- break;
- }
-
- // Set dark or light text color based upon best contrast
- textColor = setTextColor(euiTheme, colorHex);
-
- return {
- backgroundColor: colorHex,
- color: textColor,
- ...style,
- };
- } else {
- // This is a custom color- let's do our best to ensure that it provides sufficient contrast
-
- // Set dark or light text color based upon best contrast
- textColor = setTextColor(euiTheme, color);
-
- // Check the contrast
- contrastRatio = getColorContrast(textColor, color);
-
- if (contrastRatio < wcagContrastMin) {
- // It's low contrast, so lets show a warning in the console
- console.warn(
- 'Warning: ',
- color,
- ' badge has low contrast of ',
- contrastRatio.toFixed(2),
- '. Should be above ',
- wcagContrastMin,
- '.'
- );
- }
+ // Set dark or light text color based upon best contrast
+ const textColor = getTextColor(euiTheme, color);
- return {
- backgroundColor: color,
- color: textColor,
- ...style,
- };
+ // Check the contrast ratio. If it's low contrast, emit a console awrning
+ const contrastRatio = getColorContrast(textColor, color);
+ if (contrastRatio < wcagContrastMin) {
+ console.warn(
+ `Warning: ${color} badge has a low contrast of ${contrastRatio.toFixed(
+ 2
+ )}. Should be above ${wcagContrastMin}.`
+ );
}
+
+ return {
+ backgroundColor: color,
+ color: textColor,
+ ...style,
+ };
} catch (err) {
- handleInvalidColor(color);
+ if (!getIsValidColor(color)) {
+ console.warn(
+ 'EuiBadge expects a valid color. This can either be a three or six ' +
+ `character hex value, rgb(a) value, hsv value, hollow, or one of the following: ${COLORS}. ` +
+ `Instead got ${color}.`
+ );
+ }
}
- }, [color, style, euiTheme]);
+ }, [color, isNamedColor, style, euiTheme]);
const styles = euiBadgeStyles(euiTheme);
const cssStyles = [
styles.euiBadge,
+ isNamedColor && styles[color as BadgeColor],
(onClick || href) && !iconOnClick && styles.clickable,
isDisabled && styles.disabled,
- color === 'hollow' && styles.hollow,
];
const textCssStyles = [
styles.text.euiBadge__text,
@@ -307,7 +269,7 @@ export const EuiBadge: FunctionComponent = ({
if (iconOnClick) {
return onClick || href ? (
-
+
{iconSide === 'left' && optionalIcon}
@@ -335,7 +297,7 @@ export const EuiBadge: FunctionComponent = ({
= ({
aria-label={onClickAriaLabel}
className={classes}
css={cssStyles}
- style={optionalCustomStyles}
+ style={customColorStyles}
ref={ref as Ref}
title={innerText}
{...(relObj as HTMLAttributes)}
@@ -372,7 +334,7 @@ export const EuiBadge: FunctionComponent = ({
= ({
);
}
};
-
-function getColorContrast(textColor: string, color: string) {
- const contrastValue = chroma.contrast(textColor, color);
- return contrastValue;
-}
-
-function setTextColor({ euiTheme }: UseEuiTheme, bgColor: string) {
- const textColor = isColorDark(...chroma(bgColor).rgb())
- ? euiTheme.colors.ghost
- : euiTheme.colors.ink;
-
- return textColor;
-}
-
-function handleInvalidColor(color: null | BadgeColor | string) {
- const isNamedColor = COLORS.includes(color as BadgeColor);
- const isValidColorString = color && chromaValid(parseColor(color) || '');
- if (!isNamedColor && !isValidColorString) {
- console.warn(
- 'EuiBadge expects a valid color. This can either be a three or six ' +
- `character hex value, rgb(a) value, hsv value, hollow, or one of the following: ${COLORS}. ` +
- `Instead got ${color}.`
- );
- }
-}
diff --git a/src/components/badge/badge_group/__snapshots__/badge_group.test.tsx.snap b/src/components/badge/badge_group/__snapshots__/badge_group.test.tsx.snap
index e77c89dd12f..f5c142d69ef 100644
--- a/src/components/badge/badge_group/__snapshots__/badge_group.test.tsx.snap
+++ b/src/components/badge/badge_group/__snapshots__/badge_group.test.tsx.snap
@@ -25,8 +25,7 @@ exports[`EuiBadgeGroup is rendered 1`] = `
data-test-subj="test subject string"
>
{
const { euiTheme, colorMode } = euiThemeContext;
+ const badgeColors = euiBadgeColors(euiThemeContext);
return {
euiBetaBadge: css`
@@ -39,16 +41,13 @@ export const euiBetaBadgeStyles = (euiThemeContext: UseEuiTheme) => {
}
`,
// Colors
- accent: css`
- ${getBadgeColors(euiTheme.colors.accentText, euiThemeContext)}
- `,
- subdued: css`
- ${getBadgeColors(tint(euiTheme.colors.lightShade, 0.3), euiThemeContext)}
- `,
+ accent: css(badgeColors.accentText),
+ subdued: css(badgeColors.subdued),
hollow: css`
- ${getBadgeColors(euiTheme.colors.emptyShade, euiThemeContext)}
+ color: ${badgeColors.hollow.color};
+ background-color: ${badgeColors.hollow.backgroundColor};
box-shadow: inset 0 0 0 ${euiTheme.border.width.thin}
- ${euiTheme.border.color};
+ ${badgeColors.hollow.borderColor};
`,
// Font sizes
m: css`
@@ -93,18 +92,3 @@ export const euiBetaBadgeStyles = (euiThemeContext: UseEuiTheme) => {
`,
};
};
-
-// Util for detecting text color based on badge bg color
-export const getBadgeColors = (
- backgroundColor: string,
- { euiTheme }: UseEuiTheme
-) => {
- const textColor = isColorDark(...hexToRgb(backgroundColor))
- ? euiTheme.colors.ghost
- : euiTheme.colors.ink;
-
- return `
- background-color: ${backgroundColor};
- color: ${textColor};
- `;
-};
diff --git a/src/components/badge/color_utils.ts b/src/components/badge/color_utils.ts
new file mode 100644
index 00000000000..351404bce19
--- /dev/null
+++ b/src/components/badge/color_utils.ts
@@ -0,0 +1,74 @@
+/*
+ * 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 chroma from 'chroma-js';
+
+import { UseEuiTheme, isColorDark, tint } from '../../services';
+import {
+ euiButtonColor,
+ euiButtonFillColor,
+} from '../../themes/amsterdam/global_styling/mixins';
+import { chromaValid, parseColor } from '../color_picker/utils';
+
+export const euiBadgeColors = (euiThemeContext: UseEuiTheme) => {
+ const { euiTheme, colorMode } = euiThemeContext;
+
+ return {
+ // Colors shared between buttons and badges
+ primary: euiButtonFillColor(euiThemeContext, 'primary'),
+ success: euiButtonFillColor(euiThemeContext, 'success'),
+ warning: euiButtonFillColor(euiThemeContext, 'warning'),
+ danger: euiButtonFillColor(euiThemeContext, 'danger'),
+ accent: euiButtonFillColor(euiThemeContext, 'accent'),
+ disabled: euiButtonColor(euiThemeContext, 'disabled'),
+ // Colors unique to badges
+ default: getBadgeColors(euiThemeContext, euiTheme.colors.lightShade),
+ // Hollow has a border and is used for autocompleters and beta badges
+ hollow: {
+ ...getBadgeColors(euiThemeContext, euiTheme.colors.emptyShade),
+ borderColor:
+ colorMode === 'DARK'
+ ? tint(euiTheme.border.color, 0.15)
+ : euiTheme.border.color,
+ },
+ // Colors used by beta and notification badges
+ subdued: getBadgeColors(
+ euiThemeContext,
+ tint(euiTheme.colors.lightShade, 0.3)
+ ),
+ accentText: getBadgeColors(euiThemeContext, euiTheme.colors.accentText),
+ };
+};
+
+export const getBadgeColors = (
+ euiThemeContext: UseEuiTheme,
+ backgroundColor: string
+) => {
+ const color = getTextColor(euiThemeContext, backgroundColor);
+
+ return {
+ backgroundColor,
+ color,
+ };
+};
+
+export const getTextColor = ({ euiTheme }: UseEuiTheme, bgColor: string) => {
+ const textColor = isColorDark(...chroma(bgColor).rgb())
+ ? euiTheme.colors.ghost
+ : euiTheme.colors.ink;
+
+ return textColor;
+};
+
+export const getColorContrast = (textColor: string, color: string) => {
+ return chroma.contrast(textColor, color);
+};
+
+export const getIsValidColor = (color?: string) => {
+ return chromaValid(parseColor(color || '') || '');
+};
diff --git a/src/components/badge/notification_badge/__snapshots__/badge_notification.test.tsx.snap b/src/components/badge/notification_badge/__snapshots__/badge_notification.test.tsx.snap
index 53953cf5180..532aebb1b50 100644
--- a/src/components/badge/notification_badge/__snapshots__/badge_notification.test.tsx.snap
+++ b/src/components/badge/notification_badge/__snapshots__/badge_notification.test.tsx.snap
@@ -26,6 +26,14 @@ exports[`EuiNotificationBadge props color subdued is rendered 1`] = `
`;
+exports[`EuiNotificationBadge props color success is rendered 1`] = `
+
+ 5
+
+`;
+
exports[`EuiNotificationBadge props size m is rendered 1`] = `
{
const { euiTheme } = euiThemeContext;
+ const badgeColors = euiBadgeColors(euiThemeContext);
return {
euiNotificationBadge: css`
@@ -54,11 +54,8 @@ export const euiNotificationBadgeStyles = (euiThemeContext: UseEuiTheme) => {
${logicalCSS('min-width', euiTheme.size.l)}
`,
// Colors
- accent: css`
- ${getBadgeColors(euiTheme.colors.accentText, euiThemeContext)}
- `,
- subdued: css`
- ${getBadgeColors(tint(euiTheme.colors.lightShade, 0.3), euiThemeContext)}
- `,
+ accent: css(badgeColors.accentText),
+ success: css(badgeColors.success),
+ subdued: css(badgeColors.subdued),
};
};
diff --git a/src/components/badge/notification_badge/badge_notification.tsx b/src/components/badge/notification_badge/badge_notification.tsx
index a4e9ad8d746..7a1ea13b684 100644
--- a/src/components/badge/notification_badge/badge_notification.tsx
+++ b/src/components/badge/notification_badge/badge_notification.tsx
@@ -13,7 +13,7 @@ import { useEuiTheme } from '../../../services';
import { euiNotificationBadgeStyles } from './badge_notification.styles';
-export const COLORS = ['accent', 'subdued'] as const;
+export const COLORS = ['accent', 'subdued', 'success'] as const;
export type BadgeNotificationColor = (typeof COLORS)[number];
export const SIZES = ['s', 'm'] as const;
diff --git a/src/components/filter_group/__snapshots__/filter_button.test.tsx.snap b/src/components/filter_group/__snapshots__/filter_button.test.tsx.snap
index 25d9224e42f..53ae7242be7 100644
--- a/src/components/filter_group/__snapshots__/filter_button.test.tsx.snap
+++ b/src/components/filter_group/__snapshots__/filter_button.test.tsx.snap
@@ -42,6 +42,25 @@ exports[`EuiFilterButton is rendered 1`] = `
`;
+exports[`EuiFilterButton props badgeColor is rendered 1`] = `
+
+`;
+
exports[`EuiFilterButton props grow can be turned off 1`] = `