diff --git a/CHANGELOG.md b/CHANGELOG.md index 60083beaa89..9ff0dc982b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## [`master`](https://github.com/elastic/eui/tree/master) +- Added `href` prop to `EuiBadge` ([#3009](https://github.com/elastic/eui/pull/3009)) - Added props descriptions for `EuiComboBox` ([#3007](https://github.com/elastic/eui/pull/3007)) - Exported `dateFormatAliases` as a part of the public API ([#3043](https://github.com/elastic/eui/pull/3043)) - Exported `EuiTextProps` type definition ([#3039](https://github.com/elastic/eui/pull/3039)) diff --git a/src-docs/src/views/badge/badge_button.js b/src-docs/src/views/badge/badge_button.js index 2d65989cbff..4ed142c57be 100644 --- a/src-docs/src/views/badge/badge_button.js +++ b/src-docs/src/views/badge/badge_button.js @@ -41,5 +41,17 @@ export default () => ( onClick on both text and icon within badge + + window.alert('Badge clicked')} + onClickAriaLabel="Example of disabled button badge" + iconOnClick={() => window.alert('Disabled badge clicked')} + iconOnClickAriaLabel="Example of disabled button badge" + data-test-sub="testExample4"> + disabled button badge + + ); diff --git a/src-docs/src/views/badge/badge_example.js b/src-docs/src/views/badge/badge_example.js index 39d9b972990..07adb3511fa 100644 --- a/src-docs/src/views/badge/badge_example.js +++ b/src-docs/src/views/badge/badge_example.js @@ -71,6 +71,11 @@ const badgeButtonSnippet = [ `, ]; +import BadgeHref from './badge_href'; +const badgeHrefSource = require('!!raw-loader!./badge_href'); +const badgeHrefHtml = renderToHtml(BadgeHref); +const badgeHrefSnippet = ['']; + import BadgeTruncate from './badge_truncate'; const badgeTruncateSource = require('!!raw-loader!./badge_truncate'); const badgeTruncateHtml = renderToHtml(BadgeTruncate); @@ -177,6 +182,29 @@ export const BadgeExample = { snippet: badgeButtonSnippet, demo: , }, + { + title: 'Badge with href', + source: [ + { + type: GuideSectionTypes.JS, + code: badgeHrefSource, + }, + { + type: GuideSectionTypes.HTML, + code: badgeHrefHtml, + }, + ], + text: ( +
+

+ Badges can also be made to render anchor tags by passing an{' '} + href. +

+
+ ), + snippet: badgeHrefSnippet, + demo: , + }, { title: 'Badge groups and truncation', source: [ diff --git a/src-docs/src/views/badge/badge_href.js b/src-docs/src/views/badge/badge_href.js new file mode 100644 index 00000000000..d06ee4c2964 --- /dev/null +++ b/src-docs/src/views/badge/badge_href.js @@ -0,0 +1,38 @@ +import React from 'react'; + +import { + EuiBadge, + EuiFlexGroup, + EuiFlexItem, +} from '../../../../src/components'; + +export default () => ( + + + + badge as an anchor + + + + + anchor with target specified + + + + window.alert('Icon inside badge clicked')} + iconOnClickAriaLabel="Example of onClick event for icon within the anchor"> + anchor with an icon and iconOnClick + + + + + disabled anchor badge + + + +); diff --git a/src/components/badge/__snapshots__/badge.test.tsx.snap b/src/components/badge/__snapshots__/badge.test.tsx.snap index 187c4527013..98dbc15297a 100644 --- a/src/components/badge/__snapshots__/badge.test.tsx.snap +++ b/src/components/badge/__snapshots__/badge.test.tsx.snap @@ -38,6 +38,46 @@ exports[`EuiBadge is rendered 1`] = ` `; +exports[`EuiBadge is rendered with href provided 1`] = ` + + + + Content + + + +`; + +exports[`EuiBadge is rendered with iconOnClick and href provided 1`] = ` + + + + Content + + + +`; + exports[`EuiBadge is rendered with iconOnClick and onClick provided 1`] = ` - Content - + `; diff --git a/src/components/badge/_badge.scss b/src/components/badge/_badge.scss index d998f6060e1..ecfd6c7e712 100644 --- a/src/components/badge/_badge.scss +++ b/src/components/badge/_badge.scss @@ -22,8 +22,8 @@ &.euiBadge-isDisabled { // sass-lint:disable-block no-important // Using !important to override inline styles + color: makeHighContrastColor($euiButtonColorDisabled, $euiButtonColorDisabled, 2) !important; background-color: $euiButtonColorDisabled !important; - color: $euiButtonColorDisabledText !important; } &:focus-within { @@ -46,6 +46,7 @@ text-align: inherit; font-weight: inherit; line-height: inherit; + color: inherit; &:disabled { cursor: not-allowed; diff --git a/src/components/badge/badge.test.tsx b/src/components/badge/badge.test.tsx index ecdfd943a77..ed610de5e0d 100644 --- a/src/components/badge/badge.test.tsx +++ b/src/components/badge/badge.test.tsx @@ -34,6 +34,16 @@ describe('EuiBadge', () => { expect(component).toMatchSnapshot(); }); + test('is rendered with href provided', () => { + const component = render( + + Content + + ); + + expect(component).toMatchSnapshot(); + }); + test('is rendered with iconOnClick provided', () => { const component = render( { expect(component).toMatchSnapshot(); }); + test('is rendered with iconOnClick and href provided', () => { + const component = render( + + Content + + ); + + expect(component).toMatchSnapshot(); + }); + describe('props', () => { describe('iconType', () => { it('is rendered', () => { diff --git a/src/components/badge/badge.tsx b/src/components/badge/badge.tsx index 295769f688d..9f9bd6d9ce3 100644 --- a/src/components/badge/badge.tsx +++ b/src/components/badge/badge.tsx @@ -4,6 +4,7 @@ import React, { HTMLAttributes, MouseEventHandler, ReactNode, + Ref, } from 'react'; import classNames from 'classnames'; import { CommonProps, ExclusiveUnion, keysOf, PropsOf } from '../common'; @@ -26,6 +27,11 @@ type WithButtonProps = { onClickAriaLabel: AriaAttributes['aria-label']; } & Omit, 'onClick' | 'color'>; +type WithAnchorProps = { + href: string; + target?: string; +} & Omit, 'href' | 'color'>; + type WithSpanProps = Omit, 'onClick' | 'color'>; interface WithIconOnClick { @@ -66,7 +72,10 @@ export type EuiBadgeProps = { closeButtonProps?: Partial>; } & CommonProps & ExclusiveUnion & - ExclusiveUnion; + ExclusiveUnion< + ExclusiveUnion, + WithSpanProps + >; // TODO - replace with variables once https://github.com/elastic/eui/issues/2731 is closed const colorInk = '#000'; @@ -108,6 +117,8 @@ export const EuiBadge: FunctionComponent = ({ onClickAriaLabel, iconOnClickAriaLabel, closeButtonProps, + href, + target, ...rest }) => { checkValidColor(color); @@ -160,7 +171,7 @@ export const EuiBadge: FunctionComponent = ({ const classes = classNames( 'euiBadge', { - 'euiBadge-isClickable': onClick && !iconOnClick, + 'euiBadge-isClickable': (onClick || href) && !iconOnClick, 'euiBadge-isDisabled': isDisabled, 'euiBadge--hollow': color === 'hollow', }, @@ -172,6 +183,21 @@ export const EuiBadge: FunctionComponent = ({ 'euiBadge__icon', closeButtonProps && closeButtonProps.className ); + const Element = href && !isDisabled ? 'a' : 'button'; + const relObj: { + href?: string; + target?: string; + onClick?: + | ((event: React.MouseEvent) => void) + | ((event: React.MouseEvent) => void); + } = {}; + + if (href && !isDisabled) { + relObj.href = href; + relObj.target = target; + } else if (onClick) { + relObj.onClick = onClick; + } let optionalIcon: ReactNode = null; if (iconType) { @@ -209,46 +235,46 @@ export const EuiBadge: FunctionComponent = ({ ); } - if (onClick && iconOnClick) { + if (iconOnClick) { return ( {(ref, innerText) => ( - + )} {optionalIcon} ); - } else if (onClick) { + } else if (onClick || href) { return ( {(ref, innerText) => ( - + )} );