import { Meta, Story, ArgsTable, Canvas } from '@storybook/addon-docs/blocks'
import Table from '../'
import {
  columnsChooser,
  compoundColumns,
  createFakeCustomers,
  defaultColumns,
} from '../Table.testUtils'
import ConvoList from './ConvoList/ConvoList'
import TableWithPagination from './TableWithPagination'
import TableWithSorting from './TableWithSorting'
import TableWithAnimatedRows from './TableWithAnimatedRows'

<Meta
  title="Components/Structural/Table"
  component={Table}
  parameters={{
    design: {
      type: 'figma',
      url:
        'https://www.figma.com/file/Wp9mDxTvWicSTWtezj2ImkMP/HSDS-Product?node-id=2582%3A10821',
    },
  }}
/>

# Table

Renders a table, you know with columns and rows and such.

It has some special powers though.

At it simplest:

```jsx
<Table tableDescription="Example table" columns={columns} data={data} />
```

`columns` is a list (array) of objects that describe each column to render.

`data` is a list of objects, one row per object will be rendered. Columns and Data should be related.

<Canvas>
  <Story name="default">
    <TableWithPagination />
  </Story>
</Canvas>

#### Reference

- **Designer**: Buzz
- **Engineering**: Juan Pablo
- **Usage**: CustomerList, member-plan, customers, reports (docs)

<br />
<br />

### Props

<ArgsTable of={Table} />

## Columns

Columns is a list of objects that configure each column, here you can customize how to render cells, sortability and more.

Full list of acceptable fields 👇:

- `columnKey`: (required) This value ties to corresponding data field(s), can be a string for a single data field, or a list (array) to get multiple data fields. If you have nested data you'd like to access, use a "dot notation", for example `customer.email`
- `title`: The string that renders by default in the header for this column
- `width`: Column width, ideally the sum of all the columns should be 100%
- `align`: Horizontal cell alignment for this column, one of "left" (default), "center" or "right"
- `renderHeaderCell`: To customize how to render the column header.
  - A function that takes the column object and returns a React Component/Element
  - If you need an icon, you can pass on object `{ iconName: 'chat' }` to get the `<Icon />` with the correct styles applied.
- `renderCell`: To customize how each cell renders it corresponding data on this column. A function that takes the corresponding data and returns a React Component/Element, its arguments are the data provided by `columnKey` above, if using nested data, access it by replaceing the dot (`.`) with an underscore (`_`), for example: `customer.email` in `columnKey` becomes `customer_email` in `renderCell` (see code example below). It also gives you access to the `row`
- `sorter`: A function that instructs how to sort the data based on this column. If you return a promise, the table will be able to refocus the button when triggered via pressing the `enter` key
- `sortKey`: If this column contains more than one columnKey, sorting will be based on this value which should exist in the list of Column Keys for this column.
- `clearCellPadding`: setting this to `true` will remove the default cell padding for all cells in this column.
  - [Optional] For convenience, if using this plus `renderCell`, add a class name of `fill-table-cell` to the root element returned in `renderCell` to apply styles that will fill the cell with the correct padding (see default story).

> Reminder: only `columnKey` is required

Example:

```jsx
const columns = [
  {
    title: 'Company',
    columnKey: 'companyName',
    width: '50%',
    align: 'center',
    renderCell: ({ companyName }) => <strong>companyName</strong>
    renderHeaderCell: column => <h3>{column.title}</h3>
  },
  {
    title: 'Customer',
    columnKey: ['customer.name', 'customer.email'], // nested
    width: '50%',
    align: 'left',
    renderCell: ({ customer_name, customer_email, row }) => ( // replace . with _
        <div>
          <strong>{customer_name}</strong>
          <br />
          <span>{customer_email}</span>
        </div>
      ),
    sorter: function sortByName() {/*...*/},
    sortKey: 'name'
  },
]

const data = [{
  id: 0,
  customer: {
    name: 'Walter White',
    email: 'walt@babyblue.net',
  },
  companyName: 'Heisenberg Inc',
  className: 'criminal' // Note: classNames on row objects can be used to target row specific styling
}, {}, {}]
```

## Themes

Built in themes: "default" and "alternative"

Example of custom theme:

```js
const purpleTheme = {
  fontColorHeader: 'rebeccapurple',
  fontColorBody: 'rebeccapurple',
  fontColorAlternate: 'plum',
  bgHeader: 'gold',
  bgColor: 'plum',
  bgColorHover: 'violet',
  bgAlternate: 'rebeccapurple',
  borderTableBody: '1px solid blueviolet',
  borderTableHeader: '1px solid blueviolet',
  borderRows: '1px solid blueviolet',
  borderColumns: '1px solid blueviolet',
}
```

### Stories

#### With compound columns

<Canvas>
  <Story name="with compound columns">
    <Table
      columns={compoundColumns}
      data={createFakeCustomers({ amount: 10 })}
    />
  </Story>
</Canvas>

#### With animated row addition/removal

Uses [react-transition-group](https://reactcommunity.org/react-transition-group) under the hood to animate rows.

Using `animateRows = true` will enable it with a some defaults, you can customize the animation by overriding the css that is applied on the `<tbody>`

Default class names and css:

```css
.animated-row-enter {
  opacity: 0;
}

.animated-row-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

.animated-row-exit {
  opacity: 1;
}

.animated-row-exit-active {
  opacity: 0;
  transition: opacity 500ms ease-in;
}
```

Example override:

```js
const TableUI = styled(Table)`
  .TableBody--animated {
    .animated-row-enter {
      background-color: red !important;
    }

    .animated-row-enter-active {
      background-color: transparent !important;
      transition: background-color 500ms ease-in;
    }

    .animated-row-exit {
      background-color: transparent !important;
    }

    .animated-row-exit-active {
      background-color: red !important;
      transition: background-color 500ms ease-in;
    }
  }
`
```

If you need more control over the animation, pass an object to `animateRows` with any valid prop for [CSSTransition](https://reactcommunity.org/react-transition-group/css-transition) and [Transition](https://reactcommunity.org/react-transition-group/transition).

For example:

```jsx
<Table animateRows={{ timeout: 300, onExit: () => console.log('Exit!') }} />
```

<Canvas>
  <Story name="with animated row addition/removal">
    <TableWithAnimatedRows />
  </Story>
</Canvas>

#### With column chooser

Enable by setting `withColumnChooser` to `true`.

When setting your columns:

- Add a `show: true` to each column you want to show by default
- Add a `group: 'Something'` to group columns in the Dropdown, if skipped, they will be grouped under 'Other'
- If you need a column to always be there, add `disabledForChoosing: true`, this way the option will be disabled for the user.

Example:

```js
const columns = [
  {
    title: 'Name',
    columnKey: 'name',
    width: '25%',
    group: 'Customer',
    show: true,
    disabledForChoosing: true,
  },
  {
    title: 'Company',
    columnKey: 'companyName',
    width: '25%',
    group: 'Customer',
    show: false,
  },
]
```

Use `columnChooserResetLabel` to customize the reset item label

<Canvas>
  <Story name="with column chooser">
    <Table
      columns={columnsChooser}
      data={createFakeCustomers({ amount: 10 })}
      tableDescription="Example table"
      onColumnChoose={(clickedItem, currentSelection) => {
        {
          console.log('🚀 ~ clickedItem', clickedItem)
          console.log('🚀 ~ currentSelection', currentSelection)
        }
      }}
      withColumnChooser
      headerContent={<h2>Such a cool table</h2>}
    />
  </Story>
</Canvas>

#### With sorting

<Canvas>
  <Story name="with sorting">
    <TableWithSorting />
  </Story>
</Canvas>

#### Expandable

<Canvas>
  <Story name="expandable">
    <Table
      columns={defaultColumns}
      data={createFakeCustomers({ amount: 10 })}
      maxRowsToDisplay={4}
      tableDescription="Example table"
      onRowClick={(e, row) => {
        console.log(row)
      }}
      onExpand={expanded => {
        console.log('🚀 ~ expanded', expanded)
      }}
    />
  </Story>
</Canvas>

#### Selecting rows

To enable selection you have these props available to you:

- `withSelectableRows`: Enable the feature
- `selectionKey`: A unique key from your data to identify selection, defaults to `id`
- `onSelectRow`: Callback that gives you an array of selected items (by the key provided in `selectionKey` which by default is `id`)

<Canvas>
  <Story name="Selecting Rows">
    <ConvoList />
  </Story>
</Canvas>

### Wrapping rows

Sometimes you might need to wrap a row or add attributes to it based on certain conditions, for this we have added the prop `rowWrapper`. Works like this:

```jsx

// children: the "row" react component (Table.Row)
// The row for this particular row
rowWrapper={(children, row) => {
  if (row.id === 281796231) {
	// Wrap with a `Popover`
    return (
      <Popover
        triggerOn="mouseenter"
        appendTo={() => document.body}
        withTriggerWrapper={false}
        placement="left"
        renderContent={() => (
          <Avatar
            size="xs"
            name={AvatarSpec.generate().name}
            image={AvatarSpec.generate().image}
          />
        )}
      >
        {
			// Add a `title` attribute on the row
			React.cloneElement(children, {
	          title: 'Someone is replying',
    	    })
		}
      </Popover>
    )
  }
  return children
}}
```

Note that while you can technically wrap the Row with any element, you are dealing with a `<tr />` and generally they don't like to be inside other stuff (like a span for example).
