diff --git a/src-docs/src/views/comment/comment.tsx b/src-docs/src/views/comment/comment.tsx index fb51890d2f4..7cf8fd35442 100644 --- a/src-docs/src/views/comment/comment.tsx +++ b/src-docs/src/views/comment/comment.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { EuiComment } from '../../../../src/components/comment'; -import { EuiIcon } from '../../../../src/components/icon'; import { EuiAvatar } from '../../../../src/components/avatar'; import { EuiBadge } from '../../../../src/components/badge'; import { EuiFlexGroup, EuiFlexItem } from '../../../../src/components/flex'; @@ -29,12 +28,6 @@ const longBody = ( ); -const bodyUpdate = ( - -

This type of comment can also have a body

-
-); - const copyAction = ( ( /> } /> + ( } - timestamp="Jan 4, 2020" - timelineIcon={ -
- -
- } - /> - } + timelineIcon="tag" /> ( timelineIcon={}> {longBody} - }> - {bodyUpdate} - ); diff --git a/src-docs/src/views/comment/comment_actions.tsx b/src-docs/src/views/comment/comment_actions.tsx new file mode 100644 index 00000000000..901d35d7cd7 --- /dev/null +++ b/src-docs/src/views/comment/comment_actions.tsx @@ -0,0 +1,108 @@ +import React, { Component, HTMLAttributes } from 'react'; +import { EuiComment } from '../../../../src/components/comment'; +import { EuiButtonIcon } from '../../../../src/components/button'; +import { EuiText } from '../../../../src/components/text'; +import { EuiPopover } from '../../../../src/components/popover'; +import { + EuiContextMenuPanel, + EuiContextMenuItem, +} from '../../../../src/components/context_menu'; +import { CommonProps } from '../../../../src/components/common'; + +const body = ( + +

+ This comment has custom actions available. See the upper right corner. +

+
+); + +export type CustomActionsProps = HTMLAttributes & + CommonProps & {}; + +interface CustomActionsState { + isPopoverOpen: boolean; +} + +export default class extends Component< + // export class CustomActions extends Component< + CustomActionsProps, + CustomActionsState +> { + state = { + isPopoverOpen: false, + }; + + togglePopover = () => { + this.setState(prevState => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + + closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + render() { + const { isPopoverOpen } = this.state; + const customActions = ( + this.togglePopover()} + /> + } + isOpen={isPopoverOpen} + closePopover={() => this.closePopover()} + panelPaddingSize="none" + anchorPosition="leftCenter"> + { + this.closePopover(); + }}> + Edit + , + { + this.closePopover(); + }}> + Share + , + { + this.closePopover(); + }}> + Copy + , + ]} + /> + + ); + return ( +
+ + {body} + +
+ ); + } +} diff --git a/src-docs/src/views/comment/comment_example.js b/src-docs/src/views/comment/comment_example.js index 45b26310fea..7b2fe9c028d 100644 --- a/src-docs/src/views/comment/comment_example.js +++ b/src-docs/src/views/comment/comment_example.js @@ -10,10 +10,56 @@ import Comment from './comment'; const commentSource = require('!!raw-loader!./comment'); const commentHtml = renderToHtml(Comment); +import CommentTypes from './comment_types'; +const commentTypesSource = require('!!raw-loader!./comment_types'); +const commentTypesHtml = renderToHtml(CommentTypes); + +import CommentTimelineIcons from './comment_timelineIcons'; +const commentTimelineIconsSource = require('!!raw-loader!./comment_timelineIcons'); +const commentTimelineIconsHtml = renderToHtml(CommentTimelineIcons); + +import CommentActions from './comment_actions'; +const commentActionsSource = require('!!raw-loader!./comment_actions'); +const commentActionsHtml = renderToHtml(CommentActions); + const commentSnippet = ` {body} `; +const commentTypesSnippet = [ + ` + {body} + +`, + ` +`, + ` + {body} + +`, +]; + +const commentTimelineIconsSnippet = [ + ` + {body} + +`, + ` +`, + ` + } username="janed"> + {body} + +`, +]; + +const commentActionsSnippet = ` + {body} +`; + export const CommentExample = { title: 'Comment', sections: [ @@ -31,29 +77,139 @@ export const CommentExample = { text: (

- Use EuiComment for displaying comment threads - with EuiCommentList. There are two different - types of comments regular and{' '} - update available through the{' '} - type prop. Use comments of type{' '} - update to display comments that generally do not - have a body and are logging actions that either the user or the - system has performed (e.g. “jsmith edited a case” or - “kibanamachine added the backport missing label”). + Use EuiComment for displaying comment threads with{' '} + EuiCommentList. Each EuiComment{' '} + has a header with the following parts: username,{' '} + event, timeStamp and{' '} + actions.

+
    +
  • + username can display a small avatar or icon + next to it. +
  • +
  • + timeStamp receives a date in the format of the + consumer's choice. +
  • +
  • + There are two different types of comments, + regular and update available + through the type prop.{' '} +
  • +
  • + timelineIcons has default icons and styling for + both types of comments. It can also be customized as needed. +
  • +
  • + Use children to pass the body of the comment. +
  • +
+
+ ), + props: { EuiComment }, + snippet: commentSnippet, + demo: , + }, + { + title: 'Comment types', + source: [ + { + type: GuideSectionTypes.JS, + code: commentTypesSource, + }, + { + type: GuideSectionTypes.HTML, + code: commentTypesHtml, + }, + ], + text: ( +

- The timelineIcon can be customized as needed. It - is recommended to use an element of dimensions 40x40. The default{' '} - timelineIcon is a user icon. + Use the default type of comment,{' '} + regular to display comments that a user has + written.

- Use children to pass the body of the comment. + Use comments of type update to display comments + that generally do not have a body and are logging actions that + either the user or the system has performed (e.g. “jsmith + edited a case” or “kibanamachine added the review + label”).

), props: { EuiComment }, - snippet: commentSnippet, - demo: , + snippet: commentTypesSnippet, + demo: , + }, + { + title: 'Custom timelineIcon', + source: [ + { + type: GuideSectionTypes.JS, + code: commentTimelineIconsSource, + }, + { + type: GuideSectionTypes.HTML, + code: commentTimelineIconsHtml, + }, + ], + text: ( +
+

+ There are three ways to use timelineIcon: +

+
    +
  1. + Use the defaults a) a user icon inside a 40x40 container for + comments of type + regular and b) a dot icon inside a 24x24 + container for comments of type update. +
  2. +
  3. + Pass a string with any of the icon types that{' '} + EuiIcon supports and it will receive the default + styling. +
  4. +
  5. + Pass any other element (e.g. EuiAvatar). It is + recommended not to use an element larger that 40x40. +
  6. +
+
+ ), + props: { EuiComment }, + snippet: commentTimelineIconsSnippet, + demo: , + }, + { + title: 'Actions', + source: [ + { + type: GuideSectionTypes.JS, + code: commentActionsSource, + }, + { + type: GuideSectionTypes.HTML, + code: commentActionsHtml, + }, + ], + text: ( +
+

+ To allow the user to perform actions from within a comment, use the{' '} + actionsprop. actions can be + any element. For example, for something simple you can use{' '} + EuiButtonIcon and for something more complex you + can combine that with EuiPopover and{' '} + EuiContextMenu. +

+
+ ), + props: { EuiComment }, + snippet: commentActionsSnippet, + demo: , }, ], }; diff --git a/src-docs/src/views/comment/comment_timelineIcons.tsx b/src-docs/src/views/comment/comment_timelineIcons.tsx new file mode 100644 index 00000000000..996f5214266 --- /dev/null +++ b/src-docs/src/views/comment/comment_timelineIcons.tsx @@ -0,0 +1,76 @@ +import React, { Fragment } from 'react'; +import { EuiComment } from '../../../../src/components/comment'; +import { EuiText } from '../../../../src/components/text'; +import { EuiAvatar } from '../../../../src/components/avatar'; +import { EuiCode } from '../../../../src/components/code'; +import { EuiSpacer } from '../../../../src/components/spacer'; + +const defaultBody = ( + +

+ These two comments are using the default timelineIcons. +

+
+); + +const iconStringBody = ( + +

+ This comment passed the string “tag” to the{' '} + timelineIcon prop. +

+
+); + +const customIconBody = ( + +

+ This comment has a custom element as its timelineIcon. +

+
+); + +export default () => ( + +
+ + {defaultBody} + + +
+ +
+ + {iconStringBody} + +
+ +
+ + }> + {customIconBody} + +
+
+); diff --git a/src-docs/src/views/comment/comment_types.tsx b/src-docs/src/views/comment/comment_types.tsx new file mode 100644 index 00000000000..3a27eb72bc6 --- /dev/null +++ b/src-docs/src/views/comment/comment_types.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { EuiComment } from '../../../../src/components/comment'; +import { EuiButtonIcon } from '../../../../src/components/button'; +import { EuiText } from '../../../../src/components/text'; +import { EuiCode } from '../../../../src/components/code'; + +const body = ( + +

+ This is the body of a comment of type regular +

+
+); + +const bodyUpdate = ( + +

+ Comments of type update can also have a body +

+
+); + +const copyAction = ( + +); + +export default () => ( +
+ + {body} + + + + {bodyUpdate} + +
+); diff --git a/src/components/comment/__snapshots__/comment.test.tsx.snap b/src/components/comment/__snapshots__/comment.test.tsx.snap index a73db9351f0..06fdaf1d2d3 100644 --- a/src/components/comment/__snapshots__/comment.test.tsx.snap +++ b/src/components/comment/__snapshots__/comment.test.tsx.snap @@ -13,7 +13,7 @@ exports[`EuiComment is rendered 1`] = ` class="euiCommentTimeline__content" >
-
-
`; @@ -65,7 +59,7 @@ exports[`EuiComment props event is rendered 1`] = ` class="euiCommentTimeline__content" >
commented
-
-
`; @@ -119,16 +107,20 @@ exports[`EuiComment props timelineIcon is rendered 1`] = ` class="euiCommentTimeline__content" >
- + +
@@ -150,17 +142,11 @@ exports[`EuiComment props timelineIcon is rendered 1`] = `
-
-
`; @@ -176,7 +162,7 @@ exports[`EuiComment props timestamp is rendered 1`] = ` class="euiCommentTimeline__content" >
- 21 days ago + on +
-
`; exports[`EuiComment props type is rendered 1`] = `
@@ -256,24 +242,18 @@ exports[`EuiComment props type is rendered 1`] = `
-
-
`; exports[`EuiComment renders a body 1`] = `
-
= ({ return (
- + = ({
{event}
{timestamp ? (
- {' '} + {' '}
) : ( @@ -65,7 +65,11 @@ export const EuiCommentEvent: FunctionComponent = ({
{actions}
-
{children}
+ {children ? ( +
{children}
+ ) : ( + undefined + )}
); }; diff --git a/src/components/comment/comment_timeline.tsx b/src/components/comment/comment_timeline.tsx index 31eb23f12b2..e98d446594e 100644 --- a/src/components/comment/comment_timeline.tsx +++ b/src/components/comment/comment_timeline.tsx @@ -1,5 +1,5 @@ import React, { FunctionComponent, ReactNode } from 'react'; -import { CommonProps } from '../common'; +import { CommonProps, keysOf } from '../common'; import classNames from 'classnames'; import { EuiIcon } from '../icon'; @@ -7,26 +7,53 @@ export type EuiCommentTimelineProps = CommonProps & { /** * Main icon that accompanies the comment. */ - timelineIcon?: ReactNode; + timelineIcon?: ReactNode | string; + type?: EuiCommentType; }; +const typeToClassNameMap = { + regular: 'euiCommentTimeline--regular', + update: 'euiCommentTimeline--update', +}; + +export const TYPES = keysOf(typeToClassNameMap); +export type EuiCommentType = keyof typeof typeToClassNameMap; + export const EuiCommentTimeline: FunctionComponent = ({ className, timelineIcon, + type = 'regular', ...rest }) => { const classes = classNames('euiCommentTimeline', className); + const iconClasses = classNames( + { + euiCommentTimeline__default: + !timelineIcon || typeof timelineIcon === 'string', + }, + typeToClassNameMap[type] + ); + + let iconRender; + if (typeof timelineIcon === 'string') { + iconRender = ( + + ); + } else if (timelineIcon) { + iconRender = timelineIcon; + } else { + iconRender = ( + + ); + } return (
- {timelineIcon ? ( - timelineIcon - ) : ( -
- -
- )} +
{iconRender}
);