From ea9f71af3e60d1416c0831cc5800921de36efe1d Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Fri, 12 Jan 2024 14:53:08 -0800 Subject: [PATCH 1/7] Enhance EuiComboBox playground - add missing defaults - add missing Picked props :| (storybook why?? need to figure this out) - add support for `onCreateOption` UX - make story `onChange` and `onCreateOption` show up in actions pane --- .../combo_box/combo_box.stories.tsx | 56 +++++++++++++++---- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/src/components/combo_box/combo_box.stories.tsx b/src/components/combo_box/combo_box.stories.tsx index 27fbf67140e..0af9433391a 100644 --- a/src/components/combo_box/combo_box.stories.tsx +++ b/src/components/combo_box/combo_box.stories.tsx @@ -6,8 +6,9 @@ * Side Public License, v 1. */ +import React, { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import React, { useCallback, useState } from 'react'; +import { action } from '@storybook/addon-actions'; import { EuiComboBox, EuiComboBoxProps } from './combo_box'; @@ -23,11 +24,6 @@ const meta: Meta> = { title: 'EuiComboBox', // @ts-ignore typescript shenanigans component: EuiComboBox, - args: { - options: options, - selectedOptions: [options[0]], - singleSelection: false, - }, argTypes: { singleSelection: { control: 'radio', @@ -35,6 +31,30 @@ const meta: Meta> = { }, append: { control: 'text' }, prepend: { control: 'text' }, + // Storybook is skipping the Pick<> props from EuiComboBoxList for some annoying reason + onCreateOption: { control: 'boolean' }, // Set to a true/false for ease of testing + customOptionText: { control: 'text' }, + renderOption: { control: 'function' }, + }, + args: { + // Pass options in by default for ease of testing + options: options, + selectedOptions: [options[0]], + // Component defaults + delimiter: ',', + sortMatchesBy: 'none', + singleSelection: false, + noSuggestions: false, + async: false, + isCaseSensitive: false, + isClearable: true, + isDisabled: false, + isInvalid: false, + isLoading: false, + autoFocus: false, + compressed: false, + fullWidth: false, + onCreateOption: undefined, // Override Storybook's default callback }, }; @@ -42,15 +62,26 @@ export default meta; type Story = StoryObj>; export const Playground: Story = { - // The render function is a component, eslint just doesn't know it - /* eslint-disable react-hooks/rules-of-hooks */ - render: ({ singleSelection, ...args }) => { + render: function Render({ singleSelection, onCreateOption, ...args }) { const [selectedOptions, setSelectedOptions] = useState( args.selectedOptions ); - const onChange = useCallback((newOptions: any[]) => { - setSelectedOptions(newOptions); - }, []); + const onChange: EuiComboBoxProps<{}>['onChange'] = (options, ...args) => { + setSelectedOptions(options); + action('onChange')(options, ...args); + }; + const _onCreateOption: EuiComboBoxProps<{}>['onCreateOption'] = ( + searchValue, + ...args + ) => { + const createdOption = { label: searchValue }; + setSelectedOptions((prevState) => + !prevState || singleSelection + ? [createdOption] + : [...prevState, createdOption] + ); + action('onCreateOption')(searchValue, ...args); + }; return ( ); }, From b89dbbfd3c61dd90481a655858e78a5ecdfc33b5 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Fri, 12 Jan 2024 15:08:10 -0800 Subject: [PATCH 2/7] Enhance EuiCollapsibleNav + fix types - use generic for nav or div DOM element type instead of casting `as` - add missing control defaults - register onClose action in storybook tab --- .../collapsible_nav.stories.tsx | 26 ++++++++++++++++--- .../collapsible_nav/collapsible_nav.tsx | 4 +-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/components/collapsible_nav/collapsible_nav.stories.tsx b/src/components/collapsible_nav/collapsible_nav.stories.tsx index efefaf47e94..777f63732d1 100644 --- a/src/components/collapsible_nav/collapsible_nav.stories.tsx +++ b/src/components/collapsible_nav/collapsible_nav.stories.tsx @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import type { Meta, StoryObj } from '@storybook/react'; import React, { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { disableStorybookControls } from '../../../.storybook/utils'; import { EuiButton } from '../button'; import { EuiCollapsibleNav, EuiCollapsibleNavProps } from './collapsible_nav'; @@ -15,12 +17,27 @@ import { EuiCollapsibleNav, EuiCollapsibleNavProps } from './collapsible_nav'; const meta: Meta = { title: 'EuiCollapsibleNav', component: EuiCollapsibleNav, + argTypes: { + ...disableStorybookControls(['button']), + as: { options: ['nav', 'div'], control: 'radio' }, + maxWidth: { control: 'number' }, // TODO: also accepts bool | string + }, args: { // Component defaults + as: 'nav', + side: 'left', + size: 320, + paddingSize: 'none', + pushAnimation: false, + pushMinBreakpoint: 'l', isDocked: false, dockedBreakpoint: 'l', showButtonIfDocked: false, - size: 320, + closeButtonPosition: 'outside', + hideCloseButton: false, + includeFixedHeadersInFocusTrap: true, + outsideClickCloses: true, + ownFocus: true, }, // TODO: Improve props inherited from EuiFlyout, ideally through // a DRY import from `flyout.stories.tsx` once that's created @@ -40,7 +57,10 @@ const StatefulCollapsibleNav = (props: Partial) => { Toggle nav } - onClose={() => setIsOpen(false)} + onClose={(...args) => { + setIsOpen(false); + action('onClose')(...args); + }} /> ); }; diff --git a/src/components/collapsible_nav/collapsible_nav.tsx b/src/components/collapsible_nav/collapsible_nav.tsx index a2704d7f195..82bb1f1a612 100644 --- a/src/components/collapsible_nav/collapsible_nav.tsx +++ b/src/components/collapsible_nav/collapsible_nav.tsx @@ -25,7 +25,7 @@ import { euiCollapsibleNavStyles } from './collapsible_nav.styles'; // Extend all the flyout props except `onClose` because we handle this internally export type EuiCollapsibleNavProps = Omit< - EuiFlyoutProps, + EuiFlyoutProps<'nav' | 'div'>, 'type' | 'pushBreakpoint' > & { /** @@ -64,7 +64,7 @@ export const EuiCollapsibleNav: FunctionComponent = ({ showButtonIfDocked = false, dockedBreakpoint = 'l', // Setting different EuiFlyout defaults - as = 'nav' as EuiCollapsibleNavProps['as'], + as = 'nav', size = 320, side = 'left', ownFocus = true, From a21206a03ab325e342323bfdf38f20f485fb5946 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Wed, 17 Jan 2024 09:17:23 -0800 Subject: [PATCH 3/7] Add/update comment stories --- .../comment_list/comment.stories.tsx | 78 ++++++++++++ .../comment_list/comment_event.stories.tsx | 35 +----- .../comment_list/comment_list.stories.tsx | 111 ++++++++++++++++++ 3 files changed, 192 insertions(+), 32 deletions(-) create mode 100644 src/components/comment_list/comment.stories.tsx create mode 100644 src/components/comment_list/comment_list.stories.tsx diff --git a/src/components/comment_list/comment.stories.tsx b/src/components/comment_list/comment.stories.tsx new file mode 100644 index 00000000000..b56423f8faa --- /dev/null +++ b/src/components/comment_list/comment.stories.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { EuiButtonIcon } from '../button'; + +import { EuiComment, EuiCommentProps } from './comment'; + +/** + * Shared comment story utils/arg types + */ + +export const _eventColorArgType = { + options: [ + undefined, + 'subdued', + 'transparent', + 'plain', + 'warning', + 'danger', + 'success', + 'primary', + 'accent', + ], + control: { type: 'radio' }, +}; +export const _actionsExampleArgType = { + control: 'radio', + options: ['Example action', 'No actions'], + mapping: { + 'Example action': ( + + ), + 'No actions': null, + }, + defaultValue: 'Example action', +}; + +/** + * Rendered stories + */ + +const meta: Meta = { + title: 'EuiComment', + component: EuiComment, + argTypes: { + eventColor: _eventColorArgType, + actions: _actionsExampleArgType, + }, + excludeStories: /^_/, // Do not render the shared argTypes as stories +}; + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + args: { + username: 'User', + timelineAvatar: 'user', + event: 'posted', + eventIcon: 'pencil', + timestamp: 'on Jan 1, 2020', + actions: 'Example action', + children: 'A user message or any custom component', + }, +}; diff --git a/src/components/comment_list/comment_event.stories.tsx b/src/components/comment_list/comment_event.stories.tsx index 9a4ee0c8cb5..29f12f71891 100644 --- a/src/components/comment_list/comment_event.stories.tsx +++ b/src/components/comment_list/comment_event.stories.tsx @@ -6,10 +6,9 @@ * Side Public License, v 1. */ -import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { EuiButtonIcon } from '../button'; +import { _eventColorArgType, _actionsExampleArgType } from './comment.stories'; import { EuiCommentEvent, EuiCommentEventProps } from './comment_event'; const meta: Meta = { @@ -19,36 +18,8 @@ const meta: Meta = { username: 'janed', }, argTypes: { - eventColor: { - options: [ - undefined, - 'subdued', - 'transparent', - 'plain', - 'warning', - 'danger', - 'success', - 'primary', - 'accent', - ], - control: { type: 'radio' }, - defaultValue: undefined, - }, - actions: { - control: 'radio', - options: ['Example action', 'No actions'], - mapping: { - 'Example action': ( - - ), - 'No actions': null, - }, - }, + eventColor: _eventColorArgType, + actions: _actionsExampleArgType, }, }; diff --git a/src/components/comment_list/comment_list.stories.tsx b/src/components/comment_list/comment_list.stories.tsx new file mode 100644 index 00000000000..8eab65bf76b --- /dev/null +++ b/src/components/comment_list/comment_list.stories.tsx @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { EuiText } from '../text'; +import { EuiButtonIcon } from '../button'; +import { EuiBadge } from '../badge'; +import { EuiFlexGroup, EuiFlexItem } from '../flex'; + +import { EuiCommentList, EuiCommentListProps } from './comment_list'; + +const meta: Meta = { + title: 'EuiCommentList', + component: EuiCommentList, + args: { + // Component defaults + gutterSize: 'l', + }, +}; + +export default meta; +type Story = StoryObj; + +const copyAction = ( + +); + +export const Playground: Story = { + args: { + comments: [ + { + username: 'janed', + timelineAvatarAriaLabel: 'Jane Doe', + event: 'added a comment', + timestamp: 'on Jan 1, 2020', + children: ( + +

+ Far out in the uncharted backwaters of the unfashionable end of + the western spiral arm of the Galaxy lies a small unregarded + yellow sun. +

+
+ ), + actions: copyAction, + }, + { + username: 'juanab', + timelineAvatarAriaLabel: 'Juana Barros', + actions: copyAction, + event: 'pushed incident X0Z235', + timestamp: 'on Jan 3, 2020', + }, + { + username: 'pancho1', + timelineAvatarAriaLabel: 'Pancho Pérez', + event: 'edited case', + timestamp: 'on Jan 9, 2020', + eventIcon: 'pencil', + eventIconAriaLabel: 'edit', + }, + { + username: 'pedror', + timelineAvatarAriaLabel: 'Pedro Rodriguez', + actions: copyAction, + event: ( + + added tags + + case + + + phising + + + security + + + ), + timestamp: 'on Jan 11, 2020', + eventIcon: 'tag', + eventIconAriaLabel: 'tag', + }, + { + username: 'Assistant', + timelineAvatarAriaLabel: 'Assistant', + timestamp: 'on Jan 14, 2020, 1:39:04 PM', + children:

An error ocurred sending your message.

, + actions: copyAction, + eventColor: 'danger', + }, + ], + }, +}; From 7f60c87678dbdb482ecbeff84b4fae9526b7a0fb Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Wed, 17 Jan 2024 09:17:54 -0800 Subject: [PATCH 4/7] Remove top-level `EuiCommentTimeline` subcomponent export - not used by any other Elastic products, and unlikely to be used standalone --- src/components/comment_list/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/comment_list/index.ts b/src/components/comment_list/index.ts index e7da8e68796..35f30d9d688 100644 --- a/src/components/comment_list/index.ts +++ b/src/components/comment_list/index.ts @@ -12,7 +12,5 @@ export { EuiComment } from './comment'; export type { EuiCommentEventProps } from './comment_event'; export { EuiCommentEvent } from './comment_event'; -export { EuiCommentTimeline } from './comment_timeline'; - export type { EuiCommentListProps } from './comment_list'; export { EuiCommentList } from './comment_list'; From ee5cbcb252a5bd2a8c812bd2e680d4266011af9f Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Wed, 17 Jan 2024 10:21:51 -0800 Subject: [PATCH 5/7] Add context menu stories + fix missing `children` in exported EuiContextMenuItemProps --- .../context_menu/context_menu.stories.tsx | 147 ++++++++++++++++++ .../context_menu_item.stories.tsx | 43 +++++ .../context_menu/context_menu_item.tsx | 5 +- .../context_menu_panel.stories.tsx | 48 ++++++ 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 src/components/context_menu/context_menu.stories.tsx create mode 100644 src/components/context_menu/context_menu_item.stories.tsx create mode 100644 src/components/context_menu/context_menu_panel.stories.tsx diff --git a/src/components/context_menu/context_menu.stories.tsx b/src/components/context_menu/context_menu.stories.tsx new file mode 100644 index 00000000000..146ef8e2599 --- /dev/null +++ b/src/components/context_menu/context_menu.stories.tsx @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { EuiPopover } from '../popover'; +import { EuiButton } from '../button'; +import { EuiIcon } from '../icon'; + +import { EuiContextMenu, EuiContextMenuProps } from './context_menu'; + +const meta: Meta = { + title: 'EuiContextMenu', + component: EuiContextMenu, + args: { + // Component defaults + size: 'm', + }, +}; + +export default meta; +type Story = StoryObj; + +const noop = () => {}; + +const panels: EuiContextMenuProps['panels'] = [ + { + id: 0, + title: 'This is a context menu', + items: [ + { + name: 'Handle an onClick', + icon: 'search', + onClick: noop, + }, + { + name: 'Go to a link', + icon: 'user', + href: 'http://elastic.co', + target: '_blank', + }, + { + name: 'Nest panels', + icon: 'wrench', + panel: 1, + }, + { + name: 'Add a tooltip', + icon: 'document', + toolTipTitle: 'Optional tooltip', + toolTipContent: 'Optional content for a tooltip', + toolTipPosition: 'right', + onClick: noop, + }, + { + name: 'Use an app icon', + icon: 'visualizeApp', + onClick: noop, + }, + { + name: 'Pass an icon as a component to customize it', + icon: , + onClick: noop, + }, + { + name: 'Disabled option', + icon: 'user', + toolTipContent: 'For reasons, this item is disabled', + toolTipPosition: 'right', + disabled: true, + onClick: noop, + }, + ], + }, + { + id: 1, + initialFocusedItemIndex: 1, + title: 'Nest panels', + items: [ + { + name: 'PDF reports', + icon: 'user', + onClick: noop, + }, + { + name: 'Embed code', + icon: 'user', + panel: 2, + }, + { + name: 'Permalinks', + icon: 'user', + onClick: noop, + }, + ], + }, + { + id: 2, + title: 'Embed code', + content: ( +
+ I'm custom content! +
+ ), + }, +]; + +export const Playground: Story = { + args: { + initialPanelId: 0, + panels, + }, +}; + +export const InPopover: Story = { + args: { + initialPanelId: 0, + panels, + }, + render: function Render(args) { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + return ( + setIsPopoverOpen(!isPopoverOpen)}> + Toggle context menu in popover + + } + isOpen={isPopoverOpen} + closePopover={() => { + setIsPopoverOpen(false); + }} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + ); + }, +}; diff --git a/src/components/context_menu/context_menu_item.stories.tsx b/src/components/context_menu/context_menu_item.stories.tsx new file mode 100644 index 00000000000..82fd1ed5c36 --- /dev/null +++ b/src/components/context_menu/context_menu_item.stories.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { disableStorybookControls } from '../../../.storybook/utils'; + +import { + EuiContextMenuItem, + EuiContextMenuItemProps, +} from './context_menu_item'; + +const meta: Meta = { + title: 'EuiContextMenuItem', + component: EuiContextMenuItem, + argTypes: { + ...disableStorybookControls(['buttonRef']), + icon: { control: 'text' }, + }, + args: { + // Component defaults + size: 'm', + layoutAlign: 'center', + hasPanel: false, + disabled: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + args: { + children: 'Context menu item', + href: '', + icon: 'link', + toolTipContent: '', + }, +}; diff --git a/src/components/context_menu/context_menu_item.tsx b/src/components/context_menu/context_menu_item.tsx index a531be2431e..390ae73f560 100644 --- a/src/components/context_menu/context_menu_item.tsx +++ b/src/components/context_menu/context_menu_item.tsx @@ -7,6 +7,7 @@ */ import React, { + PropsWithChildren, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, @@ -35,7 +36,9 @@ export type EuiContextMenuItemLayoutAlignment = 'center' | 'top' | 'bottom'; export const SIZES = ['s', 'm'] as const; -export interface EuiContextMenuItemProps extends CommonProps { +export interface EuiContextMenuItemProps + extends PropsWithChildren, + CommonProps { icon?: EuiContextMenuItemIcon; hasPanel?: boolean; disabled?: boolean; diff --git a/src/components/context_menu/context_menu_panel.stories.tsx b/src/components/context_menu/context_menu_panel.stories.tsx new file mode 100644 index 00000000000..e33c25fcdaf --- /dev/null +++ b/src/components/context_menu/context_menu_panel.stories.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; + +import { EuiContextMenuItem } from './context_menu_item'; +import { + EuiContextMenuPanel, + EuiContextMenuPanelProps, +} from './context_menu_panel'; + +const meta: Meta = { + title: 'EuiContextMenuPanel', + component: EuiContextMenuPanel, + args: { + // Component defaults + size: 'm', + }, +}; + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + args: { + title: 'Context menu panel title', + items: [ + {}}> + Context menu item + , + {}}> + Context menu item + , + {}}> + Next Panel + , + ], + showPreviousPanel: action('showPreviousPanel'), + showNextPanel: action('showNextPanel'), + }, +}; From 7e5c357ec2d781e66c93c43cf816f6275de2b25b Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Wed, 17 Jan 2024 10:43:37 -0800 Subject: [PATCH 6/7] Add EuiContext story - there's a lot of TODOs at the bottom here because this is honestly kind of a weird component that feels very legacy --- src/components/context/context.stories.tsx | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/components/context/context.stories.tsx diff --git a/src/components/context/context.stories.tsx b/src/components/context/context.stories.tsx new file mode 100644 index 00000000000..9a915ff4bae --- /dev/null +++ b/src/components/context/context.stories.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { useEuiI18n, EuiI18n, EuiI18nNumber } from '../i18n'; + +import { EuiContext, EuiContextProps, EuiI18nConsumer } from './context'; + +const meta: Meta = { + title: 'EuiContext', + component: EuiContext, +}; + +export default meta; +type Story = StoryObj; + +const mappings = { + fr: { + 'euiContext.stories.greeting': 'Salutations, {name}!', + 'euiContext.stories.name': 'Jean Dupont', + 'euiContext.stories.guestNo': 'Vous êtes invité #', + }, +}; + +/* eslint-disable local/i18n */ +const ContextConsumer = () => { + const name = useEuiI18n('euiContext.stories.name', 'John Doe'); + return ( + + {({ locale }) => ( +
+ {name}, + }} + /> +

+ + +

+
+ )} +
+ ); +}; + +export const Playground: Story = { + args: { + children: , + i18n: { + locale: 'fr', + mapping: mappings.fr, + formatNumber: (value) => new Intl.NumberFormat('fr').format(value), + }, + }, +}; + +// TODO: Story with example that swaps between default English and FR mappings +// TODO: `render` example, `formatDateTime` example + +// TODO: Should this component be renamed `EuiI18nContext`, +// in light of the new existing EuiProvider component? From f30bda518ba1c286803cedf5169f4ca9f66dc7fd Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Wed, 17 Jan 2024 10:52:04 -0800 Subject: [PATCH 7/7] changelog --- changelogs/upcoming/7467.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelogs/upcoming/7467.md diff --git a/changelogs/upcoming/7467.md b/changelogs/upcoming/7467.md new file mode 100644 index 00000000000..c93f6b5fed3 --- /dev/null +++ b/changelogs/upcoming/7467.md @@ -0,0 +1,3 @@ +**Deprecations** + +- Remove unused public `EuiCommentTimeline` subcomponent export. Use the parent `EuiComment` or `EuiCommentList` components instead.