stories/atoms/components/listItem/ListItem.stories.mdx

Summary

Maintainability
Test Coverage
import { ArgsTable, Meta, Story, Canvas } from '@storybook/addon-docs'
import { ThemeProvider } from 'ui/atoms/theme/ThemeProvider'
import { ThemeSwitcher } from 'ui/atoms/theme/ThemeSwitcher'
import { List } from 'ui/atoms/list/List'
import { ListItem } from 'ui/atoms/listItem/ListItem'
import { Typography } from 'ui/atoms/typography/Typography'
import { ProgressBar } from 'ui/atoms/progressBar/ProgressBar'
import { Icon } from 'ui/atoms/icon/Icon'
import { Button } from 'ui/atoms/button/Button'
import { Toast } from 'ui/atoms/toast/Toast'
import { useState } from 'react'
import { useTheme } from 'hook/useTheme'

<Meta
  title="Atoms/ListItem"
  component={ListItem}
  parameters={{
    actions: { argTypesRegex: '^on.*' },
    docs: {
      source: {
        type: 'code'
      }
    }
  }}
  decorators={[
    Story => (
      <ThemeProvider>
        <div className="story-theme-switcher">
          <ThemeSwitcher />
        </div>
        <div className="component-story-main">
          <Story />
        </div>
      </ThemeProvider>
    )
  ]}
/>

# ListItem

> A primary UI component to display an item of a list of items.

[![stability-unstable](https://img.shields.io/badge/stability-unstable-yellow.svg)](https://github.com/emersion/stability-badges#unstable)

## Description

The `ListItem` allows to display an element of a [List](/docs/atoms-list--overview).

## Overview

<Story
  name="Overview"
  argTypes={{
    title: {
      table: {
        type: { summary: 'string | Node' }
      }
    },
    description: {
      table: {
        type: { summary: 'string | Node' }
      }
    },
    firstElement: {
      control: false,
      table: {
        type: { summary: 'Node' }
      }
    },
    lastElement: {
      control: false,
      table: {
        type: { summary: 'Node' }
      }
    }
  }}
  args={{
    title: 'Item',
    description: 'Description',
    layout: 'list',
    onClick: null
  }}
>
  {args => {
    const style = args.layout === 'list' ? null : { margin: 'auto' }
    return (
      <div style={style}>
        <List layout={args.layout}>
          <ListItem
            {...args}
            firstElement={
              <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                <Icon name="wallet" invertColor />
              </div>
            }
            lastElement={
              <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                <Icon name="close" invertColor />
              </div>
            }
          />
        </List>
      </div>
    )
  }}
</Story>

## Properties

<ArgsTable story="Overview" />

### Title

The property `title` allows you to display a text which is the main title of the item.
It can be either a simple string or a node element.

<Canvas>
  <Story name="Title">
    {() => (
      <List>
        <ListItem title="The title as a string" />
        <ListItem
          title={
            <Typography color="inverted-text" fontWeight="bold">
              The title as a node element
            </Typography>
          }
        />
      </List>
    )}
  </Story>
</Canvas>

### Description

The `description` property allows you to provide additional information. Like with `title` it can be a string or a node element.

<Canvas>
  <Story name="Description">
    {() => (
      <List>
        <ListItem title="Item 1" description="The additional information of the Item 1" />
        <ListItem
          title="Item 2"
          description={
            <div>
              <Typography as="div" color="inverted-text" fontSize="small">
                Additional information in Item 2 that may be more complex than a simple `string`.
                The description can display any content.
              </Typography>
              <ProgressBar label="okp4.pdf" minValue={0} maxValue={195} currentValue={23} />
            </div>
          }
        />
      </List>
    )}
  </Story>
</Canvas>

## Layout

The `layout` of the listItem can be changed by passing either **list** (default) or **grid** as value.
This rearranges how the listItem positions its content.

ListItem is responsive and will adapt according to your contents size. The `title` and `description` properties carry more importance
and are therefore given more space. Depending on your content you might need to do some additional styling to get your desired effect.

<Canvas>
  <Story name="Layout">
    {() => {
      const items = {
        okp4: {
          title: (
            <Typography color="inverted-text" fontWeight="bold">
              ØKP4
            </Typography>
          ),
          description: (
            <div style={{ display: 'grid', gap: '10px' }}>
              <Typography color="inverted-text">Dataset</Typography>
              <Typography color="inverted-text" fontSize="small">
                Data Spaces, Open source, Web3 design system
              </Typography>
            </div>
          ),
          firstElement: (
            <Typography as="div" color="inverted-text" fontSize="small" fontWeight="bold">
              PUBLIC
            </Typography>
          ),
          lastElement: (
            <Typography as="div" color="inverted-text" fontSize="x-small" fontWeight="x-light">
              By ØKP4 - The data sharing protocol
            </Typography>
          )
        },
        coloredSquares: {
          title: (
            <div style={{ borderRadius: '5px', backgroundColor: '#99ff99', height: '30px' }}></div>
          ),
          description: (
            <div style={{ borderRadius: '5px', backgroundColor: '#9999ff', height: '140px' }}></div>
          ),
          firstElement: (
            <div
              style={{
                backgroundColor: '#ffff99',
                borderRadius: '5px',
                height: '100%',
                width: '100%'
              }}
            >
              <div style={{ height: '18px', width: '61px' }}></div>
            </div>
          ),
          lastElement: (
            <div
              style={{
                backgroundColor: '#ff9999',
                borderRadius: '5px',
                height: '100%'
              }}
            >
              <div
                style={{
                  width: '192px',
                  height: '14px'
                }}
              ></div>
            </div>
          )
        }
      }
      const createList = layout => (
        <List layout={layout}>
          {Object.values(items).map(item => {
            const { title, description, firstElement, lastElement } = item
            return (
              <ListItem
                title={title}
                description={description}
                firstElement={firstElement}
                lastElement={lastElement}
                key={item}
              />
            )
          })}
        </List>
      )
      return (
        <div style={{ display: 'grid', gap: '50px' }}>
          {createList('list')}
          {createList('grid')}
        </div>
      )
    }}
  </Story>
</Canvas>

## On click

The `onClick` property makes the listItem clickable with a subtle animation and a change in cursor on hover.
This facilitates its use and makes it clear when something is clickable.

<Canvas>
  <Story name="On click">
    {() => {
      const [state, setState] = useState(false)
      const handleState = isOpened => () => {
        setState(isOpened)
      }
      return (
        <>
          <List layout="grid">
            <ListItem
              title={
                <Typography color="inverted-text" fontWeight="bold">
                  ØKP4
                </Typography>
              }
              description={
                <Typography
                  as="div"
                  style={{
                    display: 'grid',
                    textAlign: 'center',
                    alignItems: 'center',
                    height: '100%'
                  }}
                  color="inverted-text"
                >
                  The Next Generation of Data Applications
                </Typography>
              }
              lastElement={
                <Typography
                  as="div"
                  color="inverted-text"
                  fontSize="x-small"
                  fontWeight="x-light"
                  style={{ textAlign: 'right' }}
                >
                  Clickable
                </Typography>
              }
              onClick={handleState(true)}
            />
            <ListItem
              title={
                <Typography color="inverted-text" fontWeight="bold">
                  ØKP4
                </Typography>
              }
              description={
                <Typography
                  as="div"
                  style={{
                    display: 'grid',
                    textAlign: 'center',
                    alignItems: 'center',
                    height: '100%'
                  }}
                  color="inverted-text"
                >
                  The Next Generation of Data Applications
                </Typography>
              }
              lastElement={
                <Typography
                  as="div"
                  color="inverted-text"
                  fontSize="x-small"
                  fontWeight="x-light"
                  style={{ textAlign: 'right' }}
                >
                  Not clickable
                </Typography>
              }
            />
          </List>
          <Toast
            isOpened={state}
            onOpenChange={handleState(false)}
            title="ListItem clicked !"
            severityLevel="success"
          />
        </>
      )
    }}
  </Story>
</Canvas>

## First and last elements

The `firstElement` and `lastElement` properties allow you to provide any node element as icons, buttons, text, etc. to an item in the list.
The position of the properties changes depending on the `layout`, but `firstElement` will always position itself before `lastElement` in the reading flow.

They can be used to provide visual information or add interactions with an item in the list.

<Canvas>
  <Story name="First and last elements">
    {() => {
      const { theme } = useTheme()
      const isLightTheme = theme === 'light'
      const [state, setState] = useState({ name: '', isOpened: false })
      const handleState = (name, isOpened) => () => {
        setState({ name, isOpened })
      }
      return (
        <>
          <List>
            <ListItem
              title="Item without element"
              description="This item does not have any element."
            />
            <ListItem
              title="Item with first element"
              description="This item only has a first element."
              firstElement={
                <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                  <Icon name="profile" invertColor />
                </div>
              }
            />
            <ListItem
              title="Item with last element"
              description="This item only has a last icon."
              lastElement={
                <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                  <Icon name="check" invertColor />
                </div>
              }
            />
            <ListItem
              title="Item with both first and last elements"
              description="This item has first and last elements."
              firstElement={
                <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                  <Icon name="profile" invertColor />
                </div>
              }
              lastElement={
                <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                  <Icon name="check" invertColor />
                </div>
              }
            />
            <ListItem
              title="Item 5"
              description="Elements can be button or simple text."
              firstElement={
                <div style={{ display: 'flex', alignItems: 'center', height: '100%' }}>
                  <Button
                    label="A button"
                    backgroundColor="error"
                    size="small"
                    variant="icon"
                    icon={<Icon name="cross" size="15" invertColor={isLightTheme} />}
                    onClick={handleState('cross-button', true)}
                  />
                </div>
              }
              lastElement={
                <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                  <Typography as="div" color="inverted-text" fontSize="small" fontWeight="bold">
                    This is a simple text
                  </Typography>
                </div>
              }
            />
            <ListItem
              title="Item 6"
              description="An other example with button"
              firstElement={
                <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                  <Icon name="wallet" invertColor />
                </div>
              }
              lastElement={
                <div style={{ display: 'grid', alignItems: 'center', height: '100%' }}>
                  <Button
                    label="Select"
                    backgroundColor="success"
                    size="small"
                    variant="secondary"
                    onClick={handleState('select-button', true)}
                  />
                </div>
              }
            />
          </List>
          <Toast
            isOpened={Object.values(state).includes('cross-button') ? state['isOpened'] : false}
            onOpenChange={handleState('cross-button', false)}
            title="Cross button clicked !"
            severityLevel="error"
          />
          <Toast
            isOpened={Object.values(state).includes('select-button') ? state['isOpened'] : false}
            onOpenChange={handleState('select-button', false)}
            title="Select button clicked !"
            severityLevel="success"
          />
        </>
      )
    }}
  </Story>
</Canvas>