From 0fe748a5ae34b5807b0adb70b9336395342fb838 Mon Sep 17 00:00:00 2001 From: Ryan Keairns Date: Thu, 3 Jan 2019 16:08:13 -0600 Subject: [PATCH] Add List Group component (#1377) * initial commit of list group component * adds link action and cleanup * dark mode fix, changelog * EuiListGroup accepts array of items * misc feedback edits * use html elements * allow action links on all list items * add size prop for text * make fav work in demo * docs use pin icons * fix focus and dark styles * fix svg height for ie11 * Simplify inner contents of `EuiListGroupItem` * keyboard focus styles, more padding * add label class for nav collapsed * increase margin between icon and label * docs help text edits * make focus background full width --- CHANGELOG.md | 2 + src-docs/src/routes.js | 4 + src-docs/src/views/list_group/list_group.js | 80 +++++++++ .../views/list_group/list_group_example.js | 88 ++++++++++ .../list_group/list_group_link_actions.js | 156 ++++++++++++++++++ .../src/views/list_group/list_group_links.js | 97 +++++++++++ src/components/index.js | 5 + src/components/index.scss | 1 + .../__snapshots__/list_group.test.js.snap | 9 + .../list_group_item.test.js.snap | 34 ++++ src/components/list_group/_index.scss | 4 + src/components/list_group/_list_group.scss | 25 +++ .../list_group/_list_group_item.scss | 97 +++++++++++ src/components/list_group/index.d.ts | 16 ++ src/components/list_group/index.js | 7 + src/components/list_group/list_group.js | 106 ++++++++++++ src/components/list_group/list_group.test.js | 16 ++ src/components/list_group/list_group_item.js | 155 +++++++++++++++++ .../list_group/list_group_item.test.js | 26 +++ 19 files changed, 928 insertions(+) create mode 100644 src-docs/src/views/list_group/list_group.js create mode 100644 src-docs/src/views/list_group/list_group_example.js create mode 100644 src-docs/src/views/list_group/list_group_link_actions.js create mode 100644 src-docs/src/views/list_group/list_group_links.js create mode 100644 src/components/list_group/__snapshots__/list_group.test.js.snap create mode 100644 src/components/list_group/__snapshots__/list_group_item.test.js.snap create mode 100644 src/components/list_group/_index.scss create mode 100644 src/components/list_group/_list_group.scss create mode 100644 src/components/list_group/_list_group_item.scss create mode 100644 src/components/list_group/index.d.ts create mode 100644 src/components/list_group/index.js create mode 100644 src/components/list_group/list_group.js create mode 100644 src/components/list_group/list_group.test.js create mode 100644 src/components/list_group/list_group_item.js create mode 100644 src/components/list_group/list_group_item.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e96e789a8..23ee3b919d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## [`master`](https://github.com/elastic/eui/tree/master) +- Added `EuiListGroup` and `EuiListGroupItem` components ([#1377](https://github.com/elastic/eui/pull/1377)) - Convert the other of the services to TypeScript ([#1392](https://github.com/elastic/eui/pull/1392)) - Changed single selection to select existing option in the list ([#1391](https://github.com/elastic/eui/pull/1391)) - Added `showUpdateButton` prop to `EuiSuperDatePicker` ([#1399](https://github.com/elastic/eui/pull/1399)) @@ -19,6 +20,7 @@ - Altered a few icons and added more: `crossInACircleFilled`, `editorRedo`, `editorUndo`, `grabHorizontal`, `minusInCircleFilled`, `plusInCircleFilled`, `sortable`, `starEmptySpace`, `starFilledSpace`, `starFilled`, `starMinusEmpty`, `starMinusFilled`, `starPlusEmpty`, `pinFilled` ([#1374](https://github.com/elastic/eui/pull/1374)) - Exclude `custom_typings` from `eui.d.ts` ([#1395](https://github.com/elastic/eui/pull/1395)) + **Bug fixes** - Only style anchor tags in `EuiText` that have no class attribute ([#1373](https://github.com/elastic/eui/pull/1373)) diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index 29a5a4f6ca9..2e97b630318 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -159,6 +159,9 @@ import { KeyPadMenuExample } import { LinkExample } from './views/link/link_example'; +import { ListGroupExample } + from './views/list_group/list_group_example'; + import { LoadingExample } from './views/loading/loading_example'; @@ -364,6 +367,7 @@ const navigation = [{ HealthExample, IconExample, ImageExample, + ListGroupExample, LoadingExample, ProgressExample, StatExample, diff --git a/src-docs/src/views/list_group/list_group.js b/src-docs/src/views/list_group/list_group.js new file mode 100644 index 00000000000..4112799a0d0 --- /dev/null +++ b/src-docs/src/views/list_group/list_group.js @@ -0,0 +1,80 @@ +import React, { Component, Fragment } from 'react'; + +import { + EuiListGroup, + EuiListGroupItem, + EuiSpacer, + EuiSwitch, + EuiCode, + EuiFlexGroup, + EuiFlexItem, +} from '../../../../src/components'; + +export default class extends Component { + constructor(props) { + super(props); + + this.state = { + flushWidth: false, + showBorder: false, + }; + } + + toggleFlushWidth = () => { + this.setState(prevState => ({ flushWidth: !prevState.flushWidth })); + }; + + toggleBorder = () => { + this.setState(prevState => ({ showBorder: !prevState.showBorder })); + }; + + render() { + const { + flushWidth, + showBorder, + } = this.state; + + return ( + + + + Show as flush} + checked={this.state.flushWidth} + onChange={this.toggleFlushWidth} + /> + + + Show as bordered} + checked={this.state.showBorder} + onChange={this.toggleBorder} + /> + + + + + + + + + + + + + + + + ); + } +} diff --git a/src-docs/src/views/list_group/list_group_example.js b/src-docs/src/views/list_group/list_group_example.js new file mode 100644 index 00000000000..ff94393646a --- /dev/null +++ b/src-docs/src/views/list_group/list_group_example.js @@ -0,0 +1,88 @@ +import React from 'react'; + +import { renderToHtml } from '../../services'; + +import { + GuideSectionTypes, +} from '../../components'; + +import { + EuiListGroup, + EuiListGroupItem, + EuiCode, +} from '../../../../src/components'; + +import ListGroup from './list_group'; +const listGroupSource = require('!!raw-loader!./list_group'); +const listGroupHtml = renderToHtml(ListGroup); + +import ListGroupLinks from './list_group_links'; +const listGroupLinksSource = require('!!raw-loader!./list_group_links'); +const listGroupLinksHtml = renderToHtml(ListGroupLinks); + +import ListGroupLinkActions from './list_group_link_actions'; +const listGroupLinkActionsSource = require('!!raw-loader!./list_group_link_actions'); +const listGroupLinkActionsHtml = renderToHtml(ListGroupLinkActions); + +export const ListGroupExample = { + title: 'List Group', + sections: [{ + source: [{ + type: GuideSectionTypes.JS, + code: listGroupSource, + }, { + type: GuideSectionTypes.HTML, + code: listGroupHtml, + }], + text: ( +

+ The ListGroup component is used to present +  ListGroupItems in a neatly formatted list. Use the +  flush and bordered properties + for full-width and bordered presentations, respectively. +

+ ), + props: { EuiListGroup, EuiListGroupItem }, + demo: , + }, { + title: 'List of links', + source: [{ + type: GuideSectionTypes.JS, + code: listGroupLinksSource, + }, { + type: GuideSectionTypes.HTML, + code: listGroupLinksHtml, + }], + text: ( +

+ Present ListGroupItems as links by providing an +  href value and change their appearance + with the size, isActive, and + isDisabled properties. As done in this example, the +  ListGroup component can also accept an array of + items via the listItems property. +

+ ), + demo: , + }, { + title: 'Secondary link actions', + source: [{ + type: GuideSectionTypes.JS, + code: listGroupLinkActionsSource, + }, { + type: GuideSectionTypes.HTML, + code: listGroupLinkActionsHtml, + }], + text: ( +

+ The extraAction property adds a secondary icon + button to any list item. It accepts several properites of its own, + including color, onClick, +  iconType and alwaysShow, + and can be used for actions such as pinning, favoriting, or deleting an + item. +

+ ), + demo: , + }], +}; diff --git a/src-docs/src/views/list_group/list_group_link_actions.js b/src-docs/src/views/list_group/list_group_link_actions.js new file mode 100644 index 00000000000..2ba0ad71618 --- /dev/null +++ b/src-docs/src/views/list_group/list_group_link_actions.js @@ -0,0 +1,156 @@ +import React, { Component, Fragment } from 'react'; + +import { + EuiListGroup, + EuiListGroupItem, + EuiSpacer, + EuiSwitch, + EuiCode, + EuiFlexGroup, + EuiFlexItem, +} from '../../../../src/components'; + +export default class extends Component { + constructor(props) { + super(props); + + this.state = { + flushWidth: false, + showBorder: false, + favorite1: undefined, + favorite2: undefined, + favorite3: undefined, + }; + } + + toggleFlushWidth = () => { + this.setState(prevState => ({ flushWidth: !prevState.flushWidth })); + }; + + toggleBorder = () => { + this.setState(prevState => ({ showBorder: !prevState.showBorder })); + }; + + link1Clicked = () => { + this.setState(prevState => { + return { + favorite1: prevState.favorite1 === 'link1' ? undefined : 'link1', + }; + }); + if (this.favorite1 === undefined) { document.activeElement.blur(); } + }; + + link2Clicked = () => { + this.setState(prevState => { + return { + favorite2: prevState.favorite2 === 'link2' ? undefined : 'link2', + }; + }); + if (this.favorite2 === undefined) { document.activeElement.blur(); } + }; + + link3Clicked = () => { + this.setState(prevState => { + return { + favorite3: prevState.favorite3 === 'link3' ? undefined : 'link3', + }; + }); + if (this.favorite3 === undefined) { document.activeElement.blur(); } + }; + + render() { + const { + flushWidth, + showBorder, + favorite1, + favorite2, + favorite3, + } = this.state; + + return ( + + + + Show as flush} + checked={this.state.flushWidth} + onChange={this.toggleFlushWidth} + /> + + + Show as bordered} + checked={this.state.showBorder} + onChange={this.toggleBorder} + /> + + + + + + + window.alert('Button clicked')} + isActive + extraAction={{ + color: 'subdued', + onClick: this.link1Clicked, + iconType: favorite1 === 'link1' ? 'pinFilled' : 'pin', + iconSize: 's', + 'aria-label': 'Favorite link1', + alwaysShow: favorite1 === 'link1', + }} + /> + + window.alert('Button clicked')} + label="EUI button link" + extraAction={{ + color: 'subdued', + onClick: this.link2Clicked, + iconType: favorite2 === 'link2' ? 'pinFilled' : 'pin', + iconSize: 's', + 'aria-label': 'Favorite link2', + alwaysShow: favorite2 === 'link2', + }} + /> + + window.alert('Button clicked')} + iconType="broom" + label="EUI button link" + extraAction={{ + color: 'subdued', + onClick: this.link3Clicked, + iconType: favorite3 === 'link3' ? 'pinFilled' : 'pin', + iconSize: 's', + 'aria-label': 'Favorite link3', + alwaysShow: favorite3 === 'link3', + isDisabled: true, + }} + /> + + window.alert('Action clicked'), + iconType: 'pin', + iconSize: 's', + 'aria-label': 'Favorite link4', + }} + /> + + + ); + } +} diff --git a/src-docs/src/views/list_group/list_group_links.js b/src-docs/src/views/list_group/list_group_links.js new file mode 100644 index 00000000000..19ce6c909c8 --- /dev/null +++ b/src-docs/src/views/list_group/list_group_links.js @@ -0,0 +1,97 @@ +import React, { Component, Fragment } from 'react'; + +import { + EuiListGroup, + EuiSpacer, + EuiSwitch, + EuiCode, + EuiFlexGroup, + EuiFlexItem, +} from '../../../../src/components'; + +const myContent = [ + { + label: 'First link', + href: '/#/display/list-group', + iconType: 'calendar', + size: 's', + }, + { + label: 'Second link is active', + href: '/#/display/list-group', + isActive: true, + iconType: 'clock', + size: 's', + }, + { + label: 'Third link is disabled', + href: '/#/display/list-group', + isDisabled: true, + iconType: 'compute', + size: 's', + }, + { + label: 'Fourth link', + href: '/#/display/list-group', + iconType: 'copyClipboard', + size: 's', + }, + { + label: 'Fifth link', + href: '/#/display/list-group', + iconType: 'crosshairs', + size: 's', + }, +]; + + +export default class extends Component { + constructor(props) { + super(props); + + this.state = { + flushWidth: false, + showBorder: false, + }; + } + + toggleFlushWidth = () => { + this.setState(prevState => ({ flushWidth: !prevState.flushWidth })); + }; + + toggleBorder = () => { + this.setState(prevState => ({ showBorder: !prevState.showBorder })); + }; + + render() { + const { + flushWidth, + showBorder, + } = this.state; + + return ( + + + + Show as flush} + checked={this.state.flushWidth} + onChange={this.toggleFlushWidth} + /> + + + Show as bordered} + checked={this.state.showBorder} + onChange={this.toggleBorder} + /> + + + + + + + + ); + } +} diff --git a/src/components/index.js b/src/components/index.js index e71d9a4f250..31bc138669d 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -197,6 +197,11 @@ export { EuiLink, } from './link'; +export { + EuiListGroup, + EuiListGroupItem, +} from './list_group'; + export { EUI_MODAL_CANCEL_BUTTON, EUI_MODAL_CONFIRM_BUTTON, diff --git a/src/components/index.scss b/src/components/index.scss index 1f059ce94af..333447b1ca0 100644 --- a/src/components/index.scss +++ b/src/components/index.scss @@ -33,6 +33,7 @@ @import 'image/index'; @import 'key_pad_menu/index'; @import 'link/index'; +@import 'list_group/index'; @import 'loading/index'; @import 'modal/index'; @import 'overlay_mask/index'; diff --git a/src/components/list_group/__snapshots__/list_group.test.js.snap b/src/components/list_group/__snapshots__/list_group.test.js.snap new file mode 100644 index 00000000000..fbd22fd489e --- /dev/null +++ b/src/components/list_group/__snapshots__/list_group.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiListGroup is rendered 1`] = ` +
    +`; diff --git a/src/components/list_group/__snapshots__/list_group_item.test.js.snap b/src/components/list_group/__snapshots__/list_group_item.test.js.snap new file mode 100644 index 00000000000..1b34c68b527 --- /dev/null +++ b/src/components/list_group/__snapshots__/list_group_item.test.js.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiListGroupItem is rendered 1`] = ` +
  • + + + Label + + +
  • +`; + +exports[`EuiListGroupItem renders href 1`] = ` +
  • + + + Label + + +
  • +`; diff --git a/src/components/list_group/_index.scss b/src/components/list_group/_index.scss new file mode 100644 index 00000000000..ab5c9e1dd4c --- /dev/null +++ b/src/components/list_group/_index.scss @@ -0,0 +1,4 @@ +// List group provides a way to neatly present a set of text-based items + +@import 'list_group'; +@import 'list_group_item'; diff --git a/src/components/list_group/_list_group.scss b/src/components/list_group/_list_group.scss new file mode 100644 index 00000000000..9e8fa0373e3 --- /dev/null +++ b/src/components/list_group/_list_group.scss @@ -0,0 +1,25 @@ +/** + * The List Group component provides neatly styled lists containing plain text + * or links. The outer container can be bordered, with padding, or borderless + * with links flush to the sides. + *( + * List items can be displayed with active and disabled states. + */ + +.euiListGroup { + padding: $euiSizeS; + + &.euiListGroup-flush { + padding: 0; + border: none; + } + + &.euiListGroup-bordered { + border-radius: $euiBorderRadius; + border: $euiBorderThin; + } +} + +.euiListGroup-maxWidthDefault { + max-width: $euiFormMaxWidth; +} diff --git a/src/components/list_group/_list_group_item.scss b/src/components/list_group/_list_group_item.scss new file mode 100644 index 00000000000..1177f140f59 --- /dev/null +++ b/src/components/list_group/_list_group_item.scss @@ -0,0 +1,97 @@ +.euiListGroupItem { + padding: 0; + margin-top: $euiSizeXS; + border-radius: $euiBorderRadius; + overflow: hidden; + display: flex; + align-items: center; + transition: background-color $euiAnimSpeedFast; + + &:first-child { + margin-top: 0; + } + + &.euiListGroupItem-isActive, + &.euiListGroupItem-isClickable:hover, + &.euiListGroupItem-isClickable:focus { + background-color: tintOrShade($euiColorLightestShade, 0%, 30%); + } + + &.euiListGroupItem-isClickable:hover, + &.euiListGroupItem-isClickable:focus { + text-decoration: underline; + } + + // Style all disabled list items whether or not they are links or buttons + &.euiListGroupItem-isDisabled, + &.euiListGroupItem-isDisabled:hover, + &.euiListGroupItem-isDisabled:focus, + &.euiListGroupItem-isDisabled .euiListGroupItem__button:hover, + &.euiListGroupItem-isDisabled .euiListGroupItem__button:focus { + color: $euiButtonColorDisabled; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + } +} + +.euiListGroupItem__text, +.euiListGroupItem__button { + padding: $euiSizeM $euiSizeS; + display: flex; + align-items: center; + flex-grow: 1; + text-align: left; +} + +.euiListGroupItem__button:focus { + text-decoration: underline; +} + +.euiListGroupItem__extraAction { + opacity: 0; + margin-right: $euiSizeS; + transition: opacity $euiAnimSpeedFast; + + .euiListGroupItem:not(.euiListGroupItem-isDisabled):focus &, + .euiListGroupItem:not(.euiListGroupItem-isDisabled):hover &, + &.euiListGroupItem__extraAction-alwaysShow, + &:focus { + opacity: 1; + } +} + +.euiListGroupItem__icon { + flex-grow: 0; + margin-right: $euiSizeM; +} + +.euiListGroupItem--xSmall { + font-size: $euiFontSizeXS; +} + +.euiListGroupItem--small { + font-size: $euiFontSizeS; +} + +.euiListGroupItem--large { + font-size: $euiFontSizeL; +} + +// Layout alts from euiListGroup + +.euiListGroup-flush .euiListGroupItem { + border-radius: 0; +} + +.euiListGroup-bordered .euiListGroupItem { + &:first-child { + border-top-left-radius: $euiBorderRadius; + border-top-right-radius: $euiBorderRadius; + } + + &:last-child { + border-bottom-left-radius: $euiBorderRadius; + border-bottom-right-radius: $euiBorderRadius; + } +} diff --git a/src/components/list_group/index.d.ts b/src/components/list_group/index.d.ts new file mode 100644 index 00000000000..f000d1953ed --- /dev/null +++ b/src/components/list_group/index.d.ts @@ -0,0 +1,16 @@ +import { CommonProps } from '../common'; +import { SFC } from 'react'; + +declare module '@elastic/eui' { + /** + * list group type defs + * + * @see './list_group.js' + */ + + type EuiListGroupProps = CommonProps & { + + }; + + export const EuiListGroup: SFC; +} diff --git a/src/components/list_group/index.js b/src/components/list_group/index.js new file mode 100644 index 00000000000..6a4e6a4ef5c --- /dev/null +++ b/src/components/list_group/index.js @@ -0,0 +1,7 @@ +export { + EuiListGroup, +} from './list_group'; + +export { + EuiListGroupItem, +} from './list_group_item'; diff --git a/src/components/list_group/list_group.js b/src/components/list_group/list_group.js new file mode 100644 index 00000000000..d35c62ff1f6 --- /dev/null +++ b/src/components/list_group/list_group.js @@ -0,0 +1,106 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import { + EuiListGroupItem, +} from './list_group_item'; + +export const EuiListGroup = ({ + children, + className, + flush, + bordered, + listItems, + maxWidth, + style, + ...rest, +}) => { + + let newStyle; + let widthClassName; + if (maxWidth !== true) { + const value = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth; + newStyle = { ...style, maxWidth: value }; + } else if (maxWidth === true) { + widthClassName = 'euiListGroup-maxWidthDefault'; + } + + const classes = classNames( + 'euiListGroup', + { + 'euiListGroup-flush': flush, + 'euiListGroup-bordered': bordered, + }, + widthClassName, + className + ); + + let childrenOrListItems = null; + if (listItems) { + childrenOrListItems = ( + listItems.map((item, index) => { + return [ + + ]; + }) + ); + } else { + childrenOrListItems = children; + } + + return ( +
      + {childrenOrListItems} +
    + ); +}; + +EuiListGroup.propTypes = { + listItems: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.node, + href: PropTypes.string, + extraAction: PropTypes.node, + iconType: PropTypes.string, + isActive: PropTypes.boolean, + isDisabled: PropTypes.boolean, + })), + children: PropTypes.node, + className: PropTypes.string, + + /** + * Remove container padding, stretching list items to the edges + */ + flush: PropTypes.bool, + + /** + * Add a border to the list container + */ + bordered: PropTypes.bool, + + /** + * Sets the max-width of the page, + * set to `true` to use the default size, + * set to `false` to not restrict the width, + * set to a number for a custom width in px, + * set to a string for a custom width in custom measurement. + */ + maxWidth: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.number, + PropTypes.string, + ]), +}; + +EuiListGroup.defaultProps = { + flush: false, + bordered: false, + maxWidth: true, +}; diff --git a/src/components/list_group/list_group.test.js b/src/components/list_group/list_group.test.js new file mode 100644 index 00000000000..4e0130b35c5 --- /dev/null +++ b/src/components/list_group/list_group.test.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../test/required_props'; + +import { EuiListGroup } from './list_group'; + +describe('EuiListGroup', () => { + test('is rendered', () => { + const component = render( + + ); + + expect(component) + .toMatchSnapshot(); + }); +}); diff --git a/src/components/list_group/list_group_item.js b/src/components/list_group/list_group_item.js new file mode 100644 index 00000000000..f3167d5d097 --- /dev/null +++ b/src/components/list_group/list_group_item.js @@ -0,0 +1,155 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import { EuiButtonIcon } from '../button'; +import { ICON_TYPES, EuiIcon } from '../icon'; + +const sizeToClassNameMap = { + xs: 'euiListGroupItem--xSmall', + s: 'euiListGroupItem--small', + m: 'euiListGroupItem--medium', + l: 'euiListGroupItem--large', +}; + +export const SIZES = Object.keys(sizeToClassNameMap); + +export const EuiListGroupItem = ({ + label, + isActive, + isDisabled, + href, + className, + iconType, + extraAction, + onClick, + size, + ...rest +}) => { + const classes = classNames( + 'euiListGroupItem', + sizeToClassNameMap[size], + { + 'euiListGroupItem-isActive': isActive, + 'euiListGroupItem-isDisabled': isDisabled, + 'euiListGroupItem-isClickable': href || onClick, + }, + className + ); + + let iconNode; + + if (iconType) { + iconNode = ( + + ); + } + + let extraActionNode; + + if (extraAction) { + const { + iconType, + alwaysShow, + ...rest + } = extraAction; + + const extraActionClasses = classNames( + 'euiListGroupItem__extraAction', + { 'euiListGroupItem__extraAction-alwaysShow': alwaysShow } + ); + + extraActionNode = ( + + ); + } + + // Handle the variety of interaction behavior + let itemContent; + + if (href && !isDisabled) { + itemContent = ( + + {iconNode} + {label} + + ); + } else if ((href && isDisabled) || onClick) { + itemContent = ( + + ); + } else { + itemContent = ( + + {iconNode} + {label} + + ); + } + + return ( +
  • + {itemContent} + {extraActionNode} +
  • + ); +}; + +EuiListGroupItem.propTypes = { + className: PropTypes.string, + + /** + * Set the size of the label text + */ + size: PropTypes.oneOf(SIZES), + + /** + * Content to be displyed in the list item + */ + label: PropTypes.node.isRequired, + + /** + * Apply styles indicating an item is active + */ + isActive: PropTypes.bool, + + /** + * Apply styles indicating an item is disabled + */ + isDisabled: PropTypes.bool, + + /** + * Make the list item label a link + */ + href: PropTypes.string, + + /** + * See `EuiIcon` + */ + iconType: PropTypes.oneOf(ICON_TYPES), + + /** + * Adds an `EuiButtonIcon` to the right side of the item; `iconType` is required; + * pass `alwaysShow` if you don't want the default behavior of only showing on hover + */ + extraAction: PropTypes.shape({ + iconType: PropTypes.oneOf(ICON_TYPES).isRequired, + alwaysShow: PropTypes.bool, + }), + + onClick: PropTypes.func, +}; + +EuiListGroupItem.defaultProps = { + isActive: false, + isDisabled: false, + size: 'm', +}; diff --git a/src/components/list_group/list_group_item.test.js b/src/components/list_group/list_group_item.test.js new file mode 100644 index 00000000000..5d9fbd47a21 --- /dev/null +++ b/src/components/list_group/list_group_item.test.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { render } from 'enzyme'; + +import { + EuiListGroupItem, +} from './list_group_item'; + +describe('EuiListGroupItem', () => { + test('is rendered', () => { + const component = render( + + ); + + expect(component) + .toMatchSnapshot(); + }); + + test('renders href', () => { + const component = render( + + ); + + expect(component) + .toMatchSnapshot(); + }); +});