diff --git a/CHANGELOG.md b/CHANGELOG.md
index a804c3c1d24..30d46e0ce4a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
- Converted `EuiFacetButton` to TypeScript ([#2226](https://github.com/elastic/eui/pull/2226))
- Adds an optional `onClear` prop to the the `EuiDatePicker` component ([#2235](https://github.com/elastic/eui/pull/2235))
+- Added support for `onClick` and `href` props on `EuiListGroupItem` and converted to TypeScript ([#1933](https://github.com/elastic/eui/pull/1933))
**Bug fixes**
diff --git a/src/components/list_group/__snapshots__/list_group.test.js.snap b/src/components/list_group/__snapshots__/list_group.test.tsx.snap
similarity index 100%
rename from src/components/list_group/__snapshots__/list_group.test.js.snap
rename to src/components/list_group/__snapshots__/list_group.test.tsx.snap
diff --git a/src/components/list_group/__snapshots__/list_group_item.test.js.snap b/src/components/list_group/__snapshots__/list_group_item.test.tsx.snap
similarity index 98%
rename from src/components/list_group/__snapshots__/list_group_item.test.js.snap
rename to src/components/list_group/__snapshots__/list_group_item.test.tsx.snap
index 26fc39e957d..3881ae91b0d 100644
--- a/src/components/list_group/__snapshots__/list_group_item.test.js.snap
+++ b/src/components/list_group/__snapshots__/list_group_item.test.tsx.snap
@@ -49,6 +49,22 @@ exports[`EuiListGroupItem props extraAction is rendered 1`] = `
`;
+exports[`EuiListGroupItem props href and onClick is rendered 1`] = `
+
+
+
+
+
+`;
+
exports[`EuiListGroupItem props href is rendered 1`] = `
`;
-
-exports[`EuiListGroupItem throws an warning if both onClick and href are provided but still renders 1`] = `
-
-
-
-
-
-`;
diff --git a/src/components/list_group/index.d.ts b/src/components/list_group/index.d.ts
deleted file mode 100644
index fc5e3b751ee..00000000000
--- a/src/components/list_group/index.d.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import {
- EuiButtonIconProps,
- EuiButtonPropsForButtonOrLink,
-} from '@elastic/eui'; // eslint-disable-line import/no-unresolved
-import { IconType } from '../icon';
-import { CommonProps, ExclusiveUnion } from '../common';
-import {
- AnchorHTMLAttributes,
- ButtonHTMLAttributes,
- FunctionComponent,
- HTMLAttributes,
- MouseEventHandler,
- ReactElement,
- ReactNode,
-} from 'react';
-
-declare module '@elastic/eui' {
- /**
- * list group type defs
- *
- * @see './list_group.js'
- */
-
- type EuiListGroupProps = CommonProps &
- HTMLAttributes & {
- bordered?: boolean;
- flush?: boolean;
- listItems?: Array>;
- maxWidth?: boolean | number | string;
- showToolTips?: boolean;
- wrapText?: boolean;
- };
-
- export const EuiListGroup: FunctionComponent;
-
- /**
- * list group item type defs
- *
- * @see './list_group_item.js'
- */
-
- interface EuiListGroupItemPropsBasics {
- size?: 'xs' | 's' | 'm' | 'l';
- label: ReactNode;
- isActive?: boolean;
- isDisabled?: boolean;
- href?: string;
- iconType?: IconType;
- icon?: ReactElement;
- showToolTip?: boolean;
- extraAction?: EuiButtonPropsForButtonOrLink<
- CommonProps &
- EuiButtonIconProps & {
- iconType: IconType;
- alwaysShow?: boolean;
- }
- >;
- onClick?: MouseEventHandler;
- wrapText?: boolean;
- }
-
- type EuiListGroupItemProps = EuiListGroupItemPropsBasics &
- CommonProps &
- ExclusiveUnion<
- ExclusiveUnion<
- ButtonHTMLAttributes,
- AnchorHTMLAttributes
- >,
- HTMLAttributes
- >;
-
- export const EuiListGroupItem: FunctionComponent;
-}
diff --git a/src/components/list_group/index.js b/src/components/list_group/index.ts
similarity index 99%
rename from src/components/list_group/index.js
rename to src/components/list_group/index.ts
index 1fb18ca4a48..69dca4816b4 100644
--- a/src/components/list_group/index.js
+++ b/src/components/list_group/index.ts
@@ -1,3 +1,2 @@
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
deleted file mode 100644
index 19c4e0ad51e..00000000000
--- a/src/components/list_group/list_group.js
+++ /dev/null
@@ -1,114 +0,0 @@
-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,
- wrapText,
- listItems,
- maxWidth,
- style,
- showToolTips,
- ...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 {
- if (showToolTips) {
- childrenOrListItems = React.Children.map(children, child => {
- return React.cloneElement(child, {
- showToolTip: true,
- });
- });
- } else {
- childrenOrListItems = children;
- }
- }
-
- return (
-
- {childrenOrListItems}
-
- );
-};
-
-EuiListGroup.propTypes = {
- listItems: PropTypes.arrayOf(PropTypes.shape(EuiListGroupItem.propTypes)),
- 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,
-
- /**
- * Allow link text to wrap
- */
- wrapText: PropTypes.bool,
-
- /**
- * Display tooltips on all list items
- */
- showToolTips: 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,
- wrapText: false,
- maxWidth: true,
- showToolTips: false,
-};
diff --git a/src/components/list_group/list_group.test.js b/src/components/list_group/list_group.test.tsx
similarity index 100%
rename from src/components/list_group/list_group.test.js
rename to src/components/list_group/list_group.test.tsx
diff --git a/src/components/list_group/list_group.tsx b/src/components/list_group/list_group.tsx
new file mode 100644
index 00000000000..c87b74bf513
--- /dev/null
+++ b/src/components/list_group/list_group.tsx
@@ -0,0 +1,112 @@
+import React, { FunctionComponent, HTMLAttributes, CSSProperties } from 'react';
+import classNames from 'classnames';
+
+import { EuiListGroupItem, EuiListGroupItemProps } from './list_group_item';
+import { CommonProps } from '../common';
+
+type EuiListGroupProps = CommonProps &
+ HTMLAttributes & {
+ /**
+ * Add a border to the list container
+ */
+ bordered?: boolean;
+
+ /**
+ * Remove container padding, stretching list items to the edges
+ */
+ flush?: boolean;
+
+ /**
+ * Items to display in this group
+ */
+ listItems?: EuiListGroupItemProps[];
+
+ /**
+ * 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?: boolean | number | string;
+
+ /**
+ * Display tooltips on all list items
+ */
+ showToolTips?: boolean;
+
+ /**
+ * Allow link text to wrap
+ */
+ wrapText?: boolean;
+ };
+
+export const EuiListGroup: FunctionComponent = ({
+ children,
+ className,
+ listItems,
+ style,
+ flush = false,
+ bordered = false,
+ wrapText = false,
+ maxWidth = true,
+ showToolTips = false,
+ ...rest
+}) => {
+ let newStyle: CSSProperties | undefined;
+ let widthClassName;
+ if (maxWidth !== true) {
+ let value: CSSProperties['maxWidth'];
+ if (typeof maxWidth === 'number') {
+ value = `${maxWidth}px`;
+ } else {
+ value = typeof maxWidth === 'string' ? maxWidth : undefined;
+ }
+
+ 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 {
+ if (showToolTips) {
+ childrenOrListItems = React.Children.map(children, child => {
+ if (React.isValidElement(child)) {
+ return React.cloneElement>(child, {
+ showToolTip: true,
+ });
+ }
+ });
+ } else {
+ childrenOrListItems = children;
+ }
+ }
+
+ return (
+
+ {childrenOrListItems}
+
+ );
+};
diff --git a/src/components/list_group/list_group_item.test.js b/src/components/list_group/list_group_item.test.tsx
similarity index 91%
rename from src/components/list_group/list_group_item.test.js
rename to src/components/list_group/list_group_item.test.tsx
index 181a578196e..ef4e9264c32 100644
--- a/src/components/list_group/list_group_item.test.js
+++ b/src/components/list_group/list_group_item.test.tsx
@@ -112,6 +112,16 @@ describe('EuiListGroupItem', () => {
expect(component).toMatchSnapshot();
});
});
+
+ describe('href and onClick', () => {
+ test('is rendered', () => {
+ const component = render(
+ {}} href="#" />
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+ });
});
test('renders a disabled button even if provided an href', () => {
@@ -132,7 +142,7 @@ describe('EuiListGroupItem', () => {
describe('throws an warning', () => {
const oldConsoleError = console.warn;
- let consoleStub;
+ let consoleStub: jest.Mock;
beforeEach(() => {
// We don't use jest.spyOn() here, because EUI's tests apply a global
@@ -145,18 +155,6 @@ describe('EuiListGroupItem', () => {
console.warn = oldConsoleError;
});
- test('if both onClick and href are provided but still renders', () => {
- const component = render(
- {}} href="#" />
- );
-
- expect(consoleStub).toBeCalled();
- expect(consoleStub.mock.calls[0][0]).toMatch(
- '`href` and `onClick` were passed'
- );
- expect(component).toMatchSnapshot();
- });
-
test('if both iconType and icon are provided but still renders', () => {
const component = render(
} />
diff --git a/src/components/list_group/list_group_item.js b/src/components/list_group/list_group_item.tsx
similarity index 52%
rename from src/components/list_group/list_group_item.js
rename to src/components/list_group/list_group_item.tsx
index c24f1e74217..9600cfeea64 100644
--- a/src/components/list_group/list_group_item.js
+++ b/src/components/list_group/list_group_item.tsx
@@ -1,33 +1,126 @@
-import React, { Fragment } from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ Fragment,
+ HTMLAttributes,
+ AnchorHTMLAttributes,
+ ButtonHTMLAttributes,
+ ReactNode,
+ ReactElement,
+ MouseEventHandler,
+ FunctionComponent,
+} from 'react';
import classNames from 'classnames';
+import {
+ EuiButtonIconProps,
+ EuiButtonPropsForButtonOrLink,
+ EuiButtonIcon as EuiButtonIconType,
+} from '@elastic/eui'; // eslint-disable-line import/no-unresolved
+// @ts-ignore
import { EuiButtonIcon } from '../button';
-import { IconPropType, EuiIcon } from '../icon';
+const EuiButtonIconTyped: typeof EuiButtonIconType = EuiButtonIcon;
+
+import { EuiIcon, IconType } from '../icon';
import { EuiToolTip } from '../tool_tip';
import { useInnerText } from '../inner_text';
+import { ExclusiveUnion, CommonProps } from '../common';
+
+type ItemSize = 'xs' | 's' | 'm' | 'l';
-const sizeToClassNameMap = {
+const sizeToClassNameMap: { [size in ItemSize]: string } = {
xs: 'euiListGroupItem--xSmall',
s: 'euiListGroupItem--small',
m: 'euiListGroupItem--medium',
l: 'euiListGroupItem--large',
};
-export const SIZES = Object.keys(sizeToClassNameMap);
-
-export const EuiListGroupItem = ({
+export const SIZES = Object.keys(sizeToClassNameMap) as ItemSize[];
+
+export type EuiListGroupItemProps = CommonProps &
+ ExclusiveUnion<
+ ExclusiveUnion<
+ ButtonHTMLAttributes,
+ AnchorHTMLAttributes
+ >,
+ HTMLAttributes
+ > & {
+ /**
+ * Size of the label text
+ */
+ size?: ItemSize;
+
+ /**
+ * Content to be displayed in the list item
+ */
+ label: ReactNode;
+
+ /**
+ * Apply styles indicating an item is active
+ */
+ isActive?: boolean;
+
+ /**
+ * Apply styles indicating an item is disabled
+ */
+ isDisabled?: boolean;
+
+ /**
+ * Make the list item label a link.
+ * While permitted, `href` and `onClick` should not be used together in most cases and may create problems.
+ */
+ href?: string;
+
+ /**
+ * Adds `EuiIcon` of `EuiIcon.type`
+ */
+ iconType?: IconType;
+
+ /**
+ * Custom node to pass as the icon. Cannot be used in conjunction
+ * with `iconType`.
+ */
+ icon?: ReactElement;
+
+ /**
+ * Display tooltip on list item
+ */
+ showToolTip?: boolean;
+
+ /**
+ * 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?: EuiButtonPropsForButtonOrLink<
+ CommonProps &
+ EuiButtonIconProps & {
+ iconType: IconType;
+ alwaysShow?: boolean;
+ }
+ >;
+
+ /**
+ * Make the list item label a button.
+ * While permitted, `href` and `onClick` should not be used together in most cases and may create problems.
+ */
+ onClick?: MouseEventHandler;
+
+ /**
+ * Allow link text to wrap
+ */
+ wrapText?: boolean;
+ };
+
+export const EuiListGroupItem: FunctionComponent = ({
label,
- isActive,
- isDisabled,
+ isActive = false,
+ isDisabled = false,
href,
className,
iconType,
icon,
extraAction,
onClick,
- size,
- showToolTip,
+ size = 'm',
+ showToolTip = false,
wrapText,
...rest
}) => {
@@ -70,7 +163,7 @@ export const EuiListGroupItem = ({
});
extraActionNode = (
-
+ ['onClick']}
+ className="euiListGroupItem__button"
+ {...rest as AnchorHTMLAttributes}>
{iconNode}
{labelContent}
);
-
- if (onClick) {
- console.warn(
- 'Both `href` and `onClick` were passed to EuiListGroupItem but only one can exist. The `href` was used.'
- );
- }
} else if ((href && isDisabled) || onClick) {
itemContent = (
+ {...rest as ButtonHTMLAttributes}>
{iconNode}
{labelContent}
@@ -138,8 +229,7 @@ export const EuiListGroupItem = ({
anchorClassName="euiListGroupItem__tooltip"
content={label}
position="right"
- delay="long"
- size="s">
+ delay="long">
{itemContent}
@@ -155,71 +245,3 @@ export const EuiListGroupItem = ({
return {itemContent} ;
};
-
-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,
-
- /**
- * Adds `EuiIcon` of `EuiIcon.type`
- */
- iconType: IconPropType,
-
- /**
- * Custom node to pass as the icon. Cannot be used in conjunction
- * with `iconType`.
- */
- icon: PropTypes.element,
-
- /**
- * Display tooltip on list item
- */
- showToolTip: PropTypes.bool,
-
- /**
- * 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: IconPropType.isRequired,
- alwaysShow: PropTypes.bool,
- }),
-
- onClick: PropTypes.func,
-
- /**
- * Allow link text to wrap
- */
- wrapText: PropTypes.bool,
-};
-
-EuiListGroupItem.defaultProps = {
- isActive: false,
- isDisabled: false,
- size: 'm',
- showToolTip: false,
-};