diff --git a/packages/react-components/src/components/ActionBar/ActionBar.tsx b/packages/react-components/src/components/ActionBar/ActionBar.tsx index aaaaafd9f..f8cb01552 100644 --- a/packages/react-components/src/components/ActionBar/ActionBar.tsx +++ b/packages/react-components/src/components/ActionBar/ActionBar.tsx @@ -5,9 +5,10 @@ import cx from 'clsx'; import { IActionMenuOption } from 'components/ActionMenu/types'; -import { ActionMenu, ActionMenuItem } from '../ActionMenu'; +import { ActionMenu } from '../ActionMenu'; import { Button } from '../Button'; import { Icon } from '../Icon'; +import { ListItem } from '../ListItem'; import { ActionBarItem } from './ActionBarItem'; import { IActionBarOption, IActionBarProps } from './types'; @@ -84,9 +85,7 @@ export const ActionBar: React.FC = ({ if (!o.hideInMenu) { const item = { key: o.key, - element: ( - {o.label} - ), + element: {o.label}, withDivider: o.withDivider, onClick: o.onClick, }; diff --git a/packages/react-components/src/components/ActionMenu/ActionMenu.mdx b/packages/react-components/src/components/ActionMenu/ActionMenu.mdx index 35c99cca2..91666a334 100644 --- a/packages/react-components/src/components/ActionMenu/ActionMenu.mdx +++ b/packages/react-components/src/components/ActionMenu/ActionMenu.mdx @@ -26,12 +26,12 @@ used to group related actions together and save space on the screen. options={[ { key: 'one', - element: Option one, + element: Option one, onClick: () => {}, }, { key: 'two', - element: Option two, + element: Option two, onClick: () => {}, }, ]} diff --git a/packages/react-components/src/components/ActionMenu/ActionMenu.stories.tsx b/packages/react-components/src/components/ActionMenu/ActionMenu.stories.tsx index 6a1656d24..90ad8f6ea 100644 --- a/packages/react-components/src/components/ActionMenu/ActionMenu.stories.tsx +++ b/packages/react-components/src/components/ActionMenu/ActionMenu.stories.tsx @@ -6,11 +6,11 @@ import { customHeightForChromatic } from '../../utils/chromatic-story-helpers'; import { Button } from '../Button'; import { Checkbox } from '../Checkbox'; import { Icon } from '../Icon'; +import { ListItem } from '../ListItem'; import { RadioButton } from '../RadioButton'; import { Switch } from '../Switch'; import { ActionMenu } from './ActionMenu'; -import { ActionMenuItem } from './ActionMenuItem'; import { exampleOptions } from './stories-constants'; import './ActionMenu.stories.css'; @@ -18,9 +18,6 @@ import './ActionMenu.stories.css'; export default { title: 'Components/ActionMenu', component: ActionMenu, - subcomponents: { - ActionMenuItem, - }, parameters: { chromatic: { delay: 300 }, }, @@ -61,11 +58,11 @@ export const KeepOpenOnItemClick = (): React.ReactElement => { { key: 'one', element: ( - + Radio label one - + ), onClick: () => setRadioButtonValue('one'), }, @@ -73,11 +70,11 @@ export const KeepOpenOnItemClick = (): React.ReactElement => { key: 'two', withDivider: true, element: ( - + Radio label two - + ), onClick: () => setRadioButtonValue('two'), }, @@ -85,26 +82,26 @@ export const KeepOpenOnItemClick = (): React.ReactElement => { key: 'three', withDivider: true, element: ( - } > Toggle label one - + ), onClick: () => setSwitchOneValue((s) => !s), }, { key: 'four', element: ( - } > Toggle label two - + ), onClick: () => setSwitchTwoValue((s) => !s), }, @@ -116,7 +113,7 @@ export const KeepOpenOnItemClick = (): React.ReactElement => { { key: 'five', element: ( - Checkbox label one @@ -129,7 +126,7 @@ export const KeepOpenOnItemClick = (): React.ReactElement => { { key: 'six', element: ( - Checkbox label two @@ -168,17 +165,17 @@ export const WithSelectedOptions = (): React.ReactElement => { options={[ { key: 'one', - element: Option one, + element: Option one, onClick: () => handleSelectOption('one'), }, { key: 'two', - element: Option two, + element: Option two, onClick: () => handleSelectOption('two'), }, { key: 'three', - element: Option three, + element: Option three, onClick: () => handleSelectOption('three'), }, ]} diff --git a/packages/react-components/src/components/ActionMenu/ActionMenuItem.tsx b/packages/react-components/src/components/ActionMenu/ActionMenuItem.tsx deleted file mode 100644 index a7f047d75..000000000 --- a/packages/react-components/src/components/ActionMenu/ActionMenuItem.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import * as React from 'react'; - -import cx from 'clsx'; - -import { Text } from '../Typography'; - -import { ActionMenuItemProps } from './types'; - -import styles from './ActionMenuItem.module.scss'; - -const baseClass = 'action-menu-item'; - -export const ActionMenuItem: React.FC< - React.PropsWithChildren -> = ({ leftNode, rightNode, children, kind }) => { - return ( -
- {leftNode && ( -
{leftNode}
- )} - - {children} - - {rightNode && ( -
{rightNode}
- )} -
- ); -}; diff --git a/packages/react-components/src/components/ActionMenu/index.ts b/packages/react-components/src/components/ActionMenu/index.ts index 1c90daa30..b48beed71 100644 --- a/packages/react-components/src/components/ActionMenu/index.ts +++ b/packages/react-components/src/components/ActionMenu/index.ts @@ -1,3 +1,2 @@ export { ActionMenu } from './ActionMenu'; -export { ActionMenuItem } from './ActionMenuItem'; export type { IActionMenuOption } from './types'; diff --git a/packages/react-components/src/components/ActionMenu/stories-constants.tsx b/packages/react-components/src/components/ActionMenu/stories-constants.tsx index a239d2ac7..50ad26401 100644 --- a/packages/react-components/src/components/ActionMenu/stories-constants.tsx +++ b/packages/react-components/src/components/ActionMenu/stories-constants.tsx @@ -8,40 +8,29 @@ import { import noop from '../../utils/noop'; import { Icon } from '../Icon'; - -import { ActionMenuItem } from './ActionMenuItem'; +import { ListItem } from '../ListItem'; export const exampleOptions = [ { key: 'one', - element: ( - }> - Copy - - ), + element: }>Copy, onClick: noop, }, { key: 'two', element: ( - }> - Move to... - + }>Move to... ), onClick: noop, }, { key: 'three', - element: ( - }>Edit - ), + element: }>Edit, onClick: noop, }, { key: 'four', - element: ( - }>Block - ), + element: }>Block, disabled: true, withDivider: true, onClick: noop, @@ -49,15 +38,15 @@ export const exampleOptions = [ { key: 'five', element: ( - }> + }> Delete item - + ), onClick: noop, }, ...[...Array(10)].map((_, index) => ({ key: `option${index + 8}`, - element: {`Menu item #${index + 8}`}, + element: {`Menu item #${index + 8}`}, onClick: noop, })), ]; diff --git a/packages/react-components/src/components/ActionMenu/types.ts b/packages/react-components/src/components/ActionMenu/types.ts index 08a3b11ff..8578def90 100644 --- a/packages/react-components/src/components/ActionMenu/types.ts +++ b/packages/react-components/src/components/ActionMenu/types.ts @@ -68,18 +68,3 @@ export interface IActionMenuProps extends ComponentCoreProps { */ footer?: React.ReactNode; } - -export interface ActionMenuItemProps { - /** - * Renders given element on the left of element - */ - leftNode?: React.ReactNode; - /** - * Renders given element on the right of element - */ - rightNode?: React.ReactNode; - /** - * Specify the kind of menu item - */ - kind?: 'warning' | undefined; -} diff --git a/packages/react-components/src/components/InviteAgents/InviteAgents.tsx b/packages/react-components/src/components/InviteAgents/InviteAgents.tsx index 80425332d..5b5e28725 100644 --- a/packages/react-components/src/components/InviteAgents/InviteAgents.tsx +++ b/packages/react-components/src/components/InviteAgents/InviteAgents.tsx @@ -11,9 +11,10 @@ import cx from 'clsx'; import { ThemeClassName } from '../../providers'; import { plural } from '../../utils/plural'; -import { ActionMenu, ActionMenuItem } from '../ActionMenu'; +import { ActionMenu } from '../ActionMenu'; import { Avatar } from '../Avatar'; import { Icon } from '../Icon'; +import { ListItem } from '../ListItem'; import { Interactive, Tooltip } from '../Tooltip'; import { Text } from '../Typography'; @@ -95,17 +96,17 @@ const InviteAgentsComponent: FC = ({ key: 'chatbot', onClick: handleSetUpChatbotClick, element: ( - }> + }> Set up ChatBot - + ), }, { key: 'teammate', element: ( - }> + }> Invite teammate - + ), onClick: handleAddTeammateClick, }, diff --git a/packages/react-components/src/components/ListItem/ListItem.mdx b/packages/react-components/src/components/ListItem/ListItem.mdx new file mode 100644 index 000000000..9d448afa4 --- /dev/null +++ b/packages/react-components/src/components/ListItem/ListItem.mdx @@ -0,0 +1,41 @@ +import { Canvas, ArgTypes, Meta, Title } from '@storybook/blocks'; + +import * as ListItemStories from './ListItem.stories.tsx'; + + + +ListItem + +[Intro](#Intro) | [Component API](#ComponentAPI) | [Content Spec](#ContentSpec) + +## Intro + +The List Item component is designed to be a universal styled element, suitable for use in various contexts such as menus, +navigation, or data presentation. It provides a structured way to display a collection of items, offering flexibility for customization. + +This component can include individual items that may feature labels, icons, or additional context, +which can be positioned on either the left or right side of the items. + + + +#### Example implementation + +```jsx +Simple Option 1 +// OR +} rightNode={}>Complex option 2 +``` + +## Component API + + + +## Content Spec + + + Go to Figma documentation + diff --git a/packages/react-components/src/components/ActionMenu/ActionMenuItem.module.scss b/packages/react-components/src/components/ListItem/ListItem.module.scss similarity index 92% rename from packages/react-components/src/components/ActionMenu/ActionMenuItem.module.scss rename to packages/react-components/src/components/ListItem/ListItem.module.scss index 554739f63..050b40064 100644 --- a/packages/react-components/src/components/ActionMenu/ActionMenuItem.module.scss +++ b/packages/react-components/src/components/ListItem/ListItem.module.scss @@ -1,4 +1,4 @@ -$base-class: 'action-menu-item'; +$base-class: 'list-item'; .#{$base-class} { display: flex; diff --git a/packages/react-components/src/components/ActionMenu/ActionMenuItem.spec.tsx b/packages/react-components/src/components/ListItem/ListItem.spec.tsx similarity index 52% rename from packages/react-components/src/components/ActionMenu/ActionMenuItem.spec.tsx rename to packages/react-components/src/components/ListItem/ListItem.spec.tsx index 6877a90e1..cbd80ba83 100644 --- a/packages/react-components/src/components/ActionMenu/ActionMenuItem.spec.tsx +++ b/packages/react-components/src/components/ListItem/ListItem.spec.tsx @@ -1,22 +1,22 @@ import { render } from 'test-utils'; -import { ActionMenuItem } from './ActionMenuItem'; -import { ActionMenuItemProps } from './types'; +import { ListItem } from './ListItem'; +import { ListItemProps } from './types'; const defaultProps = { leftNode:
Left node
, rightNode:
Right node
, }; -const renderComponent = (props: ActionMenuItemProps) => { - return render(Menu item); +const renderComponent = (props: ListItemProps) => { + return render(List item); }; -describe(' component', () => { +describe(' component', () => { it('should render given elements', () => { const { getByText } = renderComponent(defaultProps); - expect(getByText('Menu item')).toBeInTheDocument(); + expect(getByText('List item')).toBeInTheDocument(); expect(getByText('Left node')).toBeInTheDocument(); expect(getByText('Right node')).toBeInTheDocument(); }); diff --git a/packages/react-components/src/components/ListItem/ListItem.stories.css b/packages/react-components/src/components/ListItem/ListItem.stories.css new file mode 100644 index 000000000..bd1e67e1e --- /dev/null +++ b/packages/react-components/src/components/ListItem/ListItem.stories.css @@ -0,0 +1,6 @@ +.list-item-preview { + display: flex; + justify-content: center; + margin-bottom: 200px; + width: 300px; +} diff --git a/packages/react-components/src/components/ListItem/ListItem.stories.tsx b/packages/react-components/src/components/ListItem/ListItem.stories.tsx new file mode 100644 index 000000000..ce2f527a5 --- /dev/null +++ b/packages/react-components/src/components/ListItem/ListItem.stories.tsx @@ -0,0 +1,131 @@ +import { ReactElement } from 'react'; + +import { + CloseCircle, + ContentCopy, + MoreHoriz, +} from '@livechat/design-system-icons'; + +import { customHeightForChromatic } from '../../utils/chromatic-story-helpers'; +import noop from '../../utils/noop'; +import { ActionMenu } from '../ActionMenu'; +import { Button } from '../Button'; +import { Card } from '../Card'; +import { Icon } from '../Icon'; + +import { ListItem } from './ListItem'; + +import './ListItem.stories.css'; + +export default { + title: 'Components/ListItem', + component: ListItem, + parameters: { + chromatic: { delay: 300 }, + }, +}; + +const defaultOptions = [ + { + key: 'one', + element: Simple Item 1, + onClick: noop, + }, + { + key: 'two', + element: ( + } + rightNode={} + > + Complex Item 2 + + ), + onClick: noop, + }, +]; + +const warningOptions = [ + { + key: 'one', + element: Simple Item 1, + onClick: noop, + }, + { + key: 'two', + element: ( + } + rightNode={} + > + Complex Item 2 + + ), + onClick: noop, + }, +]; +export const Default = (): ReactElement => { + return ( +
+
+ } + rightNode={} + > + List Item + +
+
+ ); +}; + +export const CardList = (): ReactElement => { + return ( +
+ + } + rightNode={} + > + List Item 1 + + }> + List Item 2 + + +
+ ); +}; + +export const WithActionMenu = (): ReactElement => { + return ( +
+
+ } /> + } + openedOnInit + /> +
+
+ ); +}; + +export const WithActionMenuWarningKind = (): ReactElement => { + return ( +
+
+ } /> + } + openedOnInit + /> +
+
+ ); +}; diff --git a/packages/react-components/src/components/ListItem/ListItem.tsx b/packages/react-components/src/components/ListItem/ListItem.tsx new file mode 100644 index 000000000..a53d88a23 --- /dev/null +++ b/packages/react-components/src/components/ListItem/ListItem.tsx @@ -0,0 +1,48 @@ +import { FC, PropsWithChildren, useEffect } from 'react'; + +import cx from 'clsx'; + +import { Text } from '../Typography'; + +import { ListItemProps } from './types'; + +import styles from './ListItem.module.scss'; + +const baseClass = 'list-item'; + +export const ListItem: FC> = ({ + leftNode, + rightNode, + children, + kind, +}) => { + return ( +
+ {leftNode && ( +
{leftNode}
+ )} + + {children} + + {rightNode && ( +
{rightNode}
+ )} +
+ ); +}; + +/** + * @deprecated Use `ListItem` instead. This will be removed in a future release. + */ +export const ActionMenuItem: FC> = (props) => { + useEffect(() => { + // eslint-disable-next-line no-console + console.warn('`ActionMenuItem` is deprecated. Use `ListItem` instead.'); + }, []); + + return ; +}; diff --git a/packages/react-components/src/components/ListItem/index.ts b/packages/react-components/src/components/ListItem/index.ts new file mode 100644 index 000000000..f73bdb6dc --- /dev/null +++ b/packages/react-components/src/components/ListItem/index.ts @@ -0,0 +1 @@ +export { ListItem, ActionMenuItem } from './ListItem'; diff --git a/packages/react-components/src/components/ListItem/types.ts b/packages/react-components/src/components/ListItem/types.ts new file mode 100644 index 000000000..47218dca5 --- /dev/null +++ b/packages/react-components/src/components/ListItem/types.ts @@ -0,0 +1,16 @@ +import { ReactNode } from 'react'; + +export interface ListItemProps { + /** + * Renders given element on the left of element + */ + leftNode?: ReactNode; + /** + * Renders given element on the right of element + */ + rightNode?: ReactNode; + /** + * Specify the kind of menu item + */ + kind?: 'warning' | undefined; +} diff --git a/packages/react-components/src/index.ts b/packages/react-components/src/index.ts index 8bea54e78..bb7662514 100644 --- a/packages/react-components/src/index.ts +++ b/packages/react-components/src/index.ts @@ -30,6 +30,7 @@ export * from './components/Icon'; export * from './components/Input'; export * from './components/InviteAgents'; export * from './components/Link'; +export * from './components/ListItem'; export * from './components/Loader'; export * from './components/Modal'; export * from './components/NumericInput';