diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index 15838c1ae02..ea4afe6b486 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -219,6 +219,8 @@ import { TextDiffExample } from './views/text_diff/text_diff_example'; import { TextExample } from './views/text/text_example'; +import { TimelineExample } from './views/timeline/timeline_example'; + import { TitleExample } from './views/title/title_example'; import { ToastExample } from './views/toast/toast_example'; @@ -507,6 +509,7 @@ const navigation = [ ProgressExample, StatExample, TextExample, + TimelineExample, TitleExample, ToastExample, ToolTipExample, diff --git a/src-docs/src/views/avatar/avatar_icon.js b/src-docs/src/views/avatar/avatar_icon.js index 5fefadc1fce..b1159470976 100644 --- a/src-docs/src/views/avatar/avatar_icon.js +++ b/src-docs/src/views/avatar/avatar_icon.js @@ -21,7 +21,12 @@ export default () => {     - + { + const docgenInfo = Array.isArray(EuiTimelineItem.__docgenInfo) + ? EuiTimelineItem.__docgenInfo[0] + : EuiTimelineItem.__docgenInfo; + const propsToUse = propUtilityForPlayground(docgenInfo.props); + + propsToUse.icon = { + ...propsToUse.icon, + type: PropTypes.ReactNode, + value: 'email', + }; + + propsToUse.verticalAlign = { + ...createOptionalEnum(propsToUse.verticalAlign), + value: 'center', + }; + + propsToUse.children = { + ...propsToUse.children, + type: PropTypes.ReactNode, + value: ` +

+ janet@elastic.co was invited to the project +

+
`, + hidden: false, + }; + + return { + config: { + componentName: 'EuiTimelineItem', + props: propsToUse, + scope: { + EuiTimelineItem, + EuiText, + EuiAvatar, + }, + imports: { + '@elastic/eui': { + named: ['EuiTimelineItem', 'EuiText', 'EuiAvatar'], + }, + }, + }, + }; +}; diff --git a/src-docs/src/views/timeline/timeline.tsx b/src-docs/src/views/timeline/timeline.tsx new file mode 100644 index 00000000000..8a9f46ed7b5 --- /dev/null +++ b/src-docs/src/views/timeline/timeline.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { + EuiTimeline, + EuiTimelineProps, + EuiText, +} from '../../../../src/components'; + +const items: EuiTimelineProps['items'] = [ + { + icon: 'email', + iconAriaLabel: 'Invitation', + children: ( + +

+ janet@elastic.co was invited to the project. +

+
+ ), + }, + { + icon: 'pencil', + iconAriaLabel: 'Edited', + children: ( + +

+ The project was renamed to Revenue Dashboard. +

+
+ ), + }, + { + icon: 'folderClosed', + iconAriaLabel: 'Project closed', + children: ( + +

The project was archived.

+
+ ), + }, +]; + +export default () => ; diff --git a/src-docs/src/views/timeline/timeline_example.js b/src-docs/src/views/timeline/timeline_example.js new file mode 100644 index 00000000000..ca86fa87de9 --- /dev/null +++ b/src-docs/src/views/timeline/timeline_example.js @@ -0,0 +1,187 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { GuideSectionTypes } from '../../components'; + +import { + EuiCode, + EuiTimeline, + EuiTimelineItem, +} from '../../../../src/components'; +import timelineItemConfig from './playground'; + +import Timeline from './timeline'; +const timelineSource = require('!!raw-loader!./timeline'); +const timelineSnippet = ``; + +import TimelineItem from './timeline_item'; +const timelineItemSource = require('!!raw-loader!./timeline_item'); +const timelineItemSnippet = ` + {content} +`; + +import TimelineItemEvent from './timeline_item_event'; +const timelineItemEventSource = require('!!raw-loader!./timeline_item_event'); +const timelineItemEventSnippet = ` + + + + + + + + + + +`; + +export const TimelineExample = { + title: 'Timeline', + isBeta: true, + isNew: true, + sections: [ + { + source: [ + { + type: GuideSectionTypes.TSX, + code: timelineSource, + }, + ], + text: ( + <> +

+ The EuiTimeline component standardizes the way a + timeline thread is displayed. Pass an array of{' '} + EuiTimelineItem objects and{' '} + EuiTimeline will generate a timeline thread. +

+

+ This component allows you to create any type of timeline, but for + more specific use cases you should consider: +

+
    +
  • + + EuiSteps + + : a timeline that allows you to present instructional content that + must be conducted in a particular order and might contain progress + indications. +
  • +
  • + + EuiCommentList + + : a timeline that allows you to display comments or logging + actions that either a user or a system has performed. +
  • +
+ + ), + props: { EuiTimeline, EuiTimelineItem }, + snippet: timelineSnippet, + demo: , + }, + { + title: 'Timeline item', + source: [ + { + type: GuideSectionTypes.TSX, + code: timelineItemSource, + }, + ], + text: ( + <> +

+ Each EuiTimelineItem has two parts: an icon on the + left side and an event on the right side. To create an item you need + the following props: +

+
    +
  • + icon: main icon that appears on the left side. + Can be passed as any IconType string or custom + node (e.g.{' '} + + EuiAvatar + + ). It is recommended not to use an element larger than 40x40. +
  • +
  • + children: any node as the event content. +
  • +
+ + ), + props: { EuiTimelineItem }, + snippet: timelineItemSnippet, + demo: , + playground: timelineItemConfig, + }, + { + title: 'A timeline thread with multiple items', + source: [ + { + type: GuideSectionTypes.TSX, + code: timelineItemEventSource, + }, + ], + text: ( + <> +

+ You can create a timeline thread by rendering multiple{' '} + EuiTimelineItem components. Wrapping these with an{' '} + EuiTimeline is not required, but we recommend + having all the EuiTimelineItems nested in the same + container. This way, we ensure that timeline styles are applied + correctly. +

+

+ When passing an icon and{' '} + children prop to each item, there are some design + considerations to take into account: +

+
    +
  • + icon: use icons of the same size to create a + better visual consistency. +
  • +
  • + children: when your content is just one line of + text, the recommendation is to use a a{' '} + + EuiText + {' '} + as a wrapper. For multi-line content consider using a{' '} + + EuiPanel + {' '} + or a{' '} + + EuiSplitPanel + {' '} + instead. For other types of components like editors, the + recommendation is to pass the children without any wrapper. +
  • +
+

+ The following example shows how to display multiple{' '} + EuiTimelineItems and how you can use a{' '} + EuiSplitPanel as the content wrapper. +

+ + ), + props: { + EuiTimelineItem, + }, + snippet: timelineItemEventSnippet, + demo: , + }, + ], +}; diff --git a/src-docs/src/views/timeline/timeline_item.tsx b/src-docs/src/views/timeline/timeline_item.tsx new file mode 100644 index 00000000000..e4c9162dcfe --- /dev/null +++ b/src-docs/src/views/timeline/timeline_item.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { EuiTimelineItem, EuiText, EuiCode } from '../../../../src/components'; + +export default () => ( + + +

+ I'm the children and you can find the{' '} + icon on the left side. +

+
+
+); diff --git a/src-docs/src/views/timeline/timeline_item_event.tsx b/src-docs/src/views/timeline/timeline_item_event.tsx new file mode 100644 index 00000000000..e5274e98216 --- /dev/null +++ b/src-docs/src/views/timeline/timeline_item_event.tsx @@ -0,0 +1,149 @@ +import React, { useState } from 'react'; +import { + EuiTimelineItem, + EuiAvatar, + EuiText, + EuiSwitch, + EuiSwitchEvent, + EuiAccordion, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiBadge, + EuiTitle, + EuiSplitPanel, + EuiPanel, + EuiHorizontalRule, +} from '../../../../src/components'; + +import { + useGeneratedHtmlId, + euiPaletteColorBlindBehindText, +} from '../../../../src/services'; + +export default () => { + const [checked1, setChecked1] = useState(false); + const [checked2, setChecked2] = useState(false); + const [checked3, setChecked3] = useState(false); + const buttonElementAccordionId = useGeneratedHtmlId({ + prefix: 'buttonElementAccordion', + }); + + // We could use the `euiPaletteColorBlind` for coloring the avatars + // But because we're placing an icon on top of these colors + // The `euiPaletteColorBlindBehindText` is a better choice to ensure better contrast + const colorBlindBehindText = euiPaletteColorBlindBehindText({ + sortBy: 'natural', + }); + + const onChange1 = (e: EuiSwitchEvent) => setChecked1(e.target.checked); + + const onChange2 = (e: EuiSwitchEvent) => setChecked2(e.target.checked); + + const onChange3 = (e: EuiSwitchEvent) => setChecked3(e.target.checked); + + const phase = ( + title: string, + checked: boolean, + onChange: (e: EuiSwitchEvent) => void, + avatarColor: string + ) => ( + + ) : ( + + ) + } + > + + + + + + + + +

{title}

+
+
+
+
+ + + +

+ Move data to the cold tier when you are searching it less often + and don't need to update it. The cold tier is optimized for + cost savings over search performance. +

+
+ + {checked && ( + <> + + + + Any content inside of EuiAccordion will + appear here. + + + + )} +
+
+
+ ); + + return ( +
+ + } + > + + + +

+ Hot phase Required +

+
+
+ + + +

+ Store your most recent, most frequently-searched data in the hot + tier. The hot tier provides the best indexing and search + performance by using the most powerful, expensive hardware. +

+
+
+
+
+ + {phase('Warm phase', checked1, onChange1, colorBlindBehindText[1])} + + {phase('Cold phase', checked2, onChange2, colorBlindBehindText[2])} + + {phase('Frozen phase', checked3, onChange3, colorBlindBehindText[3])} +
+ ); +}; diff --git a/src/components/avatar/__snapshots__/avatar.test.tsx.snap b/src/components/avatar/__snapshots__/avatar.test.tsx.snap index 912a8c4378a..ea4fd305758 100644 --- a/src/components/avatar/__snapshots__/avatar.test.tsx.snap +++ b/src/components/avatar/__snapshots__/avatar.test.tsx.snap @@ -3,7 +3,7 @@ exports[`EuiAvatar allows a name composed entirely of whitespace 1`] = `