diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04d9369662d..dbd023eacfb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
## [`master`](https://github.com/elastic/eui/tree/master)
+- Created `EuiBadgeGroup` component ([#2921](https://github.com/elastic/eui/pull/2921))
- Added SASS variables for text variants of the primary palette `$euiColorPrimaryText`, `$euiColorSecondaryText`, etc... Updated components to use these new variables. ([#2873](https://github.com/elastic/eui/pull/2873))
- Updated SASS mixin `makeHighContrastColor()` to default `$background: $euiPageBackgroundColor` and `$ratio: 4.5`. Created `makeGraphicContrastColor()` for graphic specific contrast levels of 3.0. ([#2873](https://github.com/elastic/eui/pull/2873))
diff --git a/src-docs/src/views/badge/badge_example.js b/src-docs/src/views/badge/badge_example.js
index 3568874491d..ecbe056b3c9 100644
--- a/src-docs/src/views/badge/badge_example.js
+++ b/src-docs/src/views/badge/badge_example.js
@@ -11,10 +11,12 @@ import {
EuiCode,
EuiBetaBadge,
EuiNotificationBadge,
+ EuiBadgeGroup,
EuiCallOut,
} from '../../../../src/components';
import Badge from './badge';
+
const badgeSource = require('!!raw-loader!./badge');
const badgeHtml = renderToHtml(Badge);
const badgeSnippet = [
@@ -91,6 +93,10 @@ const notificationBadgeHtml = renderToHtml(NotificationBadge);
const notificationBadgeSnippet = `3
`;
+import BadgeGroup from './badge_group';
+const badgeGroupSource = require('!!raw-loader!./badge_group');
+const badgeGroupHtml = renderToHtml(BadgeGroup);
+
export const BadgeExample = {
title: 'Badge',
sections: [
@@ -261,5 +267,26 @@ export const BadgeExample = {
snippet: notificationBadgeSnippet,
demo: ,
},
+ {
+ title: 'Badge Group',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: badgeGroupSource,
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: badgeGroupHtml,
+ },
+ ],
+ text: (
+
+ Grouping badges with EuiBadgeGroup, ensures they wrap and truncate
+ properly.
+
+ ),
+ props: { EuiBadgeGroup },
+ demo: ,
+ },
],
};
diff --git a/src-docs/src/views/badge/badge_group.js b/src-docs/src/views/badge/badge_group.js
new file mode 100644
index 00000000000..fcb71c53c2d
--- /dev/null
+++ b/src-docs/src/views/badge/badge_group.js
@@ -0,0 +1,31 @@
+import React, { Fragment } from 'react';
+
+import {
+ EuiBadge,
+ EuiFlexItem,
+ EuiBadgeGroup,
+} from '../../../../src/components';
+
+const badges = [
+ 'default',
+ 'hollow',
+ 'primary',
+ 'secondary',
+ 'accent',
+ 'warning',
+ 'danger',
+];
+
+export default () => {
+ return (
+
+
+ {badges.map(badge => (
+
+ {badge}
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/badge/__snapshots__/badge_group.test.tsx.snap b/src/components/badge/__snapshots__/badge_group.test.tsx.snap
new file mode 100644
index 00000000000..d9ac5dbdf26
--- /dev/null
+++ b/src/components/badge/__snapshots__/badge_group.test.tsx.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EuiBadgeGroup is rendered 1`] = `
+
+`;
diff --git a/src/components/badge/_badge_group.scss b/src/components/badge/_badge_group.scss
new file mode 100644
index 00000000000..1d52d0931f9
--- /dev/null
+++ b/src/components/badge/_badge_group.scss
@@ -0,0 +1,49 @@
+$gutterTypes: (
+ gutterExtraSmall: $euiSizeXS,
+ gutterSmall: $euiSizeS,
+ gutterMedium: $euiSize,
+ gutterLarge: $euiSizeL,
+ gutterExtraLarge: $euiSizeXXL,
+);
+
+.euiBadgeGroup {
+ display: flex;
+ align-items: center;
+ flex-flow: row;
+ flex-grow: 1;
+
+ .euiBadgeItem {
+ @include internetExplorerOnly {
+ min-width: 1px;
+ }
+
+ flex-grow: 1;
+ flex-basis: 0%;
+ }
+}
+
+// Gutter Sizes
+@each $gutterName, $gutterSize in $gutterTypes {
+ $halfGutterSize: $gutterSize * .5;
+
+ .euiBadgeGroup--#{$gutterName} {
+ margin: -$halfGutterSize;
+
+ & > .euiBadgeItem {
+ margin: $halfGutterSize;
+ }
+ }
+}
+
+// Wrap
+.euiBadgeGroup--wrap {
+ flex-wrap: wrap;
+}
+
+@include euiBreakpoint('xs', 's') {
+ .euiBadgeGroup--responsive {
+ flex-wrap: wrap;
+ margin-left: 0;
+ margin-right: 0;
+ }
+}
diff --git a/src/components/badge/_index.scss b/src/components/badge/_index.scss
index a567f7a42de..20bda8e5f8d 100644
--- a/src/components/badge/_index.scss
+++ b/src/components/badge/_index.scss
@@ -1,3 +1,4 @@
@import 'badge';
+@import 'badge_group';
@import 'beta_badge/index';
@import 'notification_badge/index';
diff --git a/src/components/badge/badge_group.test.tsx b/src/components/badge/badge_group.test.tsx
new file mode 100644
index 00000000000..bbf749bc599
--- /dev/null
+++ b/src/components/badge/badge_group.test.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../test/required_props';
+
+import { EuiBadge } from './badge';
+import { EuiBadgeGroup } from './badge_group';
+
+describe('EuiBadgeGroup', () => {
+ test('is rendered', () => {
+ const component = render(
+
+
+ Content
+
+
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+});
diff --git a/src/components/badge/badge_group.tsx b/src/components/badge/badge_group.tsx
new file mode 100644
index 00000000000..135f451d702
--- /dev/null
+++ b/src/components/badge/badge_group.tsx
@@ -0,0 +1,62 @@
+import React, { HTMLAttributes, Ref } from 'react';
+import classNames from 'classnames';
+import { CommonProps, keysOf } from '../common';
+
+type BadgeGroupGutterSize = keyof typeof gutterSizeToClassNameMap;
+
+export interface EuiBadgeGroupProps {
+ /**
+ * Space between badges
+ */
+ gutterSize?: BadgeGroupGutterSize;
+ /**
+ * Force each badges to be display block on smaller screens
+ */
+ responsive?: boolean;
+ /**
+ * Force each badge to wrap if necessary
+ */
+ wrap?: boolean;
+}
+
+const gutterSizeToClassNameMap = {
+ none: null,
+ xs: 'euiBadgeGroup--gutterExtraSmall',
+ s: 'euiBadgeGroup--gutterSmall',
+};
+
+export const GUTTER_SIZES = keysOf(gutterSizeToClassNameMap);
+
+export const EuiBadgeGroup = React.forwardRef<
+ HTMLDivElement,
+ CommonProps & HTMLAttributes & EuiBadgeGroupProps
+>(
+ (
+ {
+ children,
+ className,
+ gutterSize = 'xs',
+ responsive = true,
+ wrap = true,
+ ...rest
+ },
+ ref: Ref
+ ) => {
+ const classes = classNames(
+ 'euiBadgeGroup',
+ gutterSizeToClassNameMap[gutterSize as BadgeGroupGutterSize],
+ {
+ 'euiBadgeGroup--responsive': responsive,
+ 'euiBadgeGroup--wrap': wrap,
+ },
+ className
+ );
+
+ return (
+
+ {children}
+
+ );
+ }
+);
+EuiBadgeGroup.displayName = 'EuiBadgeGroup';
diff --git a/src/components/badge/index.ts b/src/components/badge/index.ts
index 6ab2cc152c3..fda8fa2acb4 100644
--- a/src/components/badge/index.ts
+++ b/src/components/badge/index.ts
@@ -3,3 +3,5 @@ export { EuiBadge, EuiBadgeProps } from './badge';
export { EuiBetaBadge } from './beta_badge';
export { EuiNotificationBadge } from './notification_badge';
+
+export { EuiBadgeGroup } from './badge_group';
diff --git a/src/components/index.js b/src/components/index.js
index f4cb7cf6652..1cb217b4f59 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -6,7 +6,12 @@ export { EuiAvatar } from './avatar';
export { EuiKeyboardAccessible, EuiScreenReaderOnly } from './accessibility';
-export { EuiBadge, EuiBetaBadge, EuiNotificationBadge } from './badge';
+export {
+ EuiBadge,
+ EuiBetaBadge,
+ EuiNotificationBadge,
+ EuiBadgeGroup,
+} from './badge';
export { EuiBottomBar } from './bottom_bar';