diff --git a/CHANGELOG.md b/CHANGELOG.md index 276af7ccdd8..0d6227b5a69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## [`master`](https://github.com/elastic/eui/tree/master) +- Added `color` and `size` props and added support for click event to `EuiBetaBadge` ([#4798](https://github.com/elastic/eui/pull/4798)) + **Bug fixes** - Fixed `onBlur` and `data-test-subj` prop propagation in `EuiColorPicker` ([#4822](https://github.com/elastic/eui/pull/4822)) diff --git a/src-docs/src/views/badge/badge_example.js b/src-docs/src/views/badge/badge_example.js index 2013d9dace4..708b12563a8 100644 --- a/src-docs/src/views/badge/badge_example.js +++ b/src-docs/src/views/badge/badge_example.js @@ -308,6 +308,8 @@ export const BadgeExample = { If you pass in an iconType, only the icon will be used in the badge itself and the label will be applied as the title. Only use an icon when attaching the beta badge to small components. + Beta badges can also be made clickable by passing{' '} + href or onClick as needed.

They can also be used in conjunction with{' '} diff --git a/src-docs/src/views/badge/beta_badge.js b/src-docs/src/views/badge/beta_badge.js index b5a66e96d75..6d4de70c49d 100644 --- a/src-docs/src/views/badge/beta_badge.js +++ b/src-docs/src/views/badge/beta_badge.js @@ -2,23 +2,36 @@ import React from 'react'; import { EuiBetaBadge, EuiSpacer, EuiTitle } from '../../../../src/components'; +const colors = ['hollow', 'accent', 'subdued']; + export default () => (

- -   - -   - -   - - + {colors.map((item, index) => ( +
+ +   + +   + +   + +   + +   + + +
+ ))} +

Beta badges will also line up nicely with titles   @@ -28,5 +41,20 @@ export default () => ( />

+ +

Clickable beta badges

+
+ + alert('Goes to Lens')} + /> +   +
); diff --git a/src-docs/src/views/card/card_beta.js b/src-docs/src/views/card/card_beta.js index 2397e1ad2b3..610eb50d04b 100644 --- a/src-docs/src/views/card/card_beta.js +++ b/src-docs/src/views/card/card_beta.js @@ -7,8 +7,8 @@ import { EuiFlexItem, } from '../../../../src/components'; -const icons = ['dashboard', 'monitoring', 'watches']; -const badges = [null, 'Beta', 'Lab']; +const icons = ['dashboard', 'monitoring']; +const badges = [null, 'Beta']; const cardNodes = icons.map(function (item, index) { return ( @@ -29,4 +29,23 @@ const cardNodes = icons.map(function (item, index) { ); }); -export default () => {cardNodes} ; +export default () => ( + + {cardNodes} + + } + title="Lens" + isDisabled + description="Disabled cards can have active links using EuiBetaBadge." + betaBadgeProps={{ + href: 'http://www.elastic.co/subscriptions', + target: '_blank', + }} + betaBadgeLabel="Basic" + betaBadgeTooltipContent="This feature requires a Basic License" + onClick={() => {}} + /> + + +); diff --git a/src/components/badge/beta_badge/__snapshots__/beta_badge.test.tsx.snap b/src/components/badge/beta_badge/__snapshots__/beta_badge.test.tsx.snap index fbf03346008..6b3d4c7b8c2 100644 --- a/src/components/badge/beta_badge/__snapshots__/beta_badge.test.tsx.snap +++ b/src/components/badge/beta_badge/__snapshots__/beta_badge.test.tsx.snap @@ -3,10 +3,55 @@ exports[`EuiBetaBadge is rendered 1`] = ` Beta `; + +exports[`EuiBetaBadge props color accent is rendered 1`] = ` + + Beta + +`; + +exports[`EuiBetaBadge props color hollow is rendered 1`] = ` + + Beta + +`; + +exports[`EuiBetaBadge props color subdued is rendered 1`] = ` + + Beta + +`; + +exports[`EuiBetaBadge props size m is rendered 1`] = ` + + Beta + +`; + +exports[`EuiBetaBadge props size s is rendered 1`] = ` + + Beta + +`; diff --git a/src/components/badge/beta_badge/_beta_badge.scss b/src/components/badge/beta_badge/_beta_badge.scss index e38fd0349f9..6010065f358 100644 --- a/src/components/badge/beta_badge/_beta_badge.scss +++ b/src/components/badge/beta_badge/_beta_badge.scss @@ -17,6 +17,17 @@ &:focus { @include euiFocusRing; } + + &:not(.euiBetaBadge--hollow) { + box-shadow: none; + } + + &.euiBetaBadge--small { + @include fontSize($euiFontSize * .625); + line-height: $euiSize + $euiSizeXS; + padding: 0 $euiSizeM; + } + } // When it's just an icon, make it a circle @@ -28,4 +39,46 @@ position: relative; margin-top: -1px; } + + &.euiBetaBadge--small { + width: $euiSize + $euiSizeXS; + padding: 0; + } +} + +.euiBetaBadge--singleLetter { + padding: 0 0 0 1px; + width: $euiSizeL; + + &.euiBetaBadge--small { + width: $euiSize + $euiSizeXS; + padding: 0 0 0 1px; + } +} + +.euiBetaBadge--subdued { + $backgroundColor: tint($euiColorLightShade, 30%); + background: $backgroundColor; + color: chooseLightOrDarkText($backgroundColor, $euiColorGhost, $euiColorInk); + + &.euiBetaBadge-isClickable { + color: chooseLightOrDarkText($backgroundColor, $euiColorGhost, $euiColorInk); + } } + +.euiBetaBadge--hollow { + &.euiBetaBadge-isClickable { + $backgroundColor: tint($euiColorLightShade, 30%); + color: chooseLightOrDarkText($backgroundColor, $euiColorGhost, $euiColorInk); + } +} + +.euiBetaBadge--accent { + $backgroundColor: $euiColorAccentText; + background: $backgroundColor; + color: chooseLightOrDarkText($backgroundColor, $euiColorGhost, $euiColorInk); + + &.euiBetaBadge-isClickable { + color: chooseLightOrDarkText($backgroundColor, $euiColorGhost, $euiColorInk); + } +} \ No newline at end of file diff --git a/src/components/badge/beta_badge/beta_badge.test.tsx b/src/components/badge/beta_badge/beta_badge.test.tsx index 25a01d31b32..7bd45323947 100644 --- a/src/components/badge/beta_badge/beta_badge.test.tsx +++ b/src/components/badge/beta_badge/beta_badge.test.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../../test'; -import { EuiBetaBadge } from './beta_badge'; +import { EuiBetaBadge, COLORS, SIZES } from './beta_badge'; describe('EuiBetaBadge', () => { test('is rendered', () => { @@ -29,4 +29,26 @@ describe('EuiBetaBadge', () => { expect(component).toMatchSnapshot(); }); + + describe('props', () => { + describe('color', () => { + COLORS.forEach((color) => { + test(`${color} is rendered`, () => { + const component = render(); + + expect(component).toMatchSnapshot(); + }); + }); + }); + + describe('size', () => { + SIZES.forEach((size) => { + test(`${size} is rendered`, () => { + const component = render(); + + expect(component).toMatchSnapshot(); + }); + }); + }); + }); }); diff --git a/src/components/badge/beta_badge/beta_badge.tsx b/src/components/badge/beta_badge/beta_badge.tsx index 76026d9b9f3..58bcf2193af 100644 --- a/src/components/badge/beta_badge/beta_badge.tsx +++ b/src/components/badge/beta_badge/beta_badge.tsx @@ -17,25 +17,75 @@ * under the License. */ -import React, { FunctionComponent, HTMLAttributes, ReactNode } from 'react'; +import React, { + AriaAttributes, + Fragment, + FunctionComponent, + HTMLAttributes, + MouseEventHandler, + ReactNode, +} from 'react'; import classNames from 'classnames'; -import { CommonProps, ExclusiveUnion } from '../../common'; +import { CommonProps, ExclusiveUnion, keysOf } from '../../common'; + +import { getSecureRelForTarget } from '../../../services'; import { EuiToolTip, ToolTipPositions } from '../../tool_tip'; import { EuiIcon, IconType } from '../../icon'; +const colorToClassMap = { + accent: 'euiBetaBadge--accent', + subdued: 'euiBetaBadge--subdued', + hollow: 'euiBetaBadge--hollow', +}; + +export const COLORS: BetaBadgeColor[] = keysOf(colorToClassMap); +export type BetaBadgeColor = keyof typeof colorToClassMap; + +export type BetaBadgeSize = 's' | 'm'; + +export const sizeToClassMap: { [size in BetaBadgeSize]: string | null } = { + s: 'euiBetaBadge--small', + m: null, +}; + +export const SIZES = keysOf(sizeToClassMap); + +type WithButtonProps = { + /** + * Will apply an onclick to the badge itself + */ + onClick?: MouseEventHandler; + + /** + * Aria label applied to the onClick button + */ + onClickAriaLabel?: AriaAttributes['aria-label']; +} & Omit, 'onClick' | 'color'>; + +type WithAnchorProps = { + href: string; + target?: string; + rel?: string; +} & Omit, 'href' | 'color' | 'onClick'>; + +type WithSpanProps = Omit< + HTMLAttributes, + 'onClick' | 'color' | 'title' +>; + // `label` prop can be a `ReactNode` only if `title` or `tooltipContent` is provided -type LabelAsNode = ( - | { - title: string; - tooltipContent?: ReactNode; - } - | { - tooltipContent: ReactNode; - title?: string; - } -) & { +type LabelAsNode = ExclusiveUnion< + { + title: string; + tooltipContent?: ReactNode; + }, + { + tooltipContent: ReactNode; + title?: string; + } +> & { label: ReactNode; }; @@ -72,67 +122,133 @@ type BadgeProps = { * otherwise the label will be used */ title?: string; + /** + * Accepts accent, subdued and hollow. + */ + color?: BetaBadgeColor; + size?: BetaBadgeSize; } & ExclusiveUnion; export type EuiBetaBadgeProps = CommonProps & - Omit, 'title'> & + ExclusiveUnion< + ExclusiveUnion, + WithSpanProps + > & BadgeProps; export const EuiBetaBadge: FunctionComponent = ({ className, label, + color = 'hollow', tooltipContent, tooltipPosition = 'top', title, iconType, + onClick, + onClickAriaLabel, + href, + rel, + target, + size = 'm', ...rest }) => { + let singleLetter = false; + if (typeof label === 'string' && label.length === 1) { + singleLetter = true; + } + const classes = classNames( 'euiBetaBadge', { 'euiBetaBadge--iconOnly': iconType, + 'euiBetaBadge--singleLetter': singleLetter, + 'euiBetaBadge-isClickable': onClick || href, }, + colorToClassMap[color], + sizeToClassMap[size], className ); - let icon; + let icon: JSX.Element | undefined; if (iconType) { icon = (