[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/EXPERIMENTAL_Table/README.md from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```markdown
#### A table displays any kind of structured data and offers controls to easily navigate, search and filter through it. Data may be from just numbers to complex entities that employ other components to represent themselves, like images, tags, links, etc.

# Columns

The `columns` property is an `Array` used to define the table columns and how they should behave visually. Each column describes each item field should be handled by the `Table`.

```ts
type ReturnedData = {
  data: unknown | object
  rowHeight: number
  currentDensity: Density
  motion: ReturnType<typeof useTableMotion>
}

type Column = {
  id?: string
  title?: string | Element | Function
  width?: number | string
  cellRenderer?: (data: ReturnedData) => React.ReactNode
  sortable?: boolean
  extended?: boolean
  condensed?: string[]
}
```

##### id

- Defines the property name that the column represents.
- This property is required and must be a string.

##### title

- Controls the title which appears on the table Header.
- It can receive either a string or an element.

##### width

- Defines a fixed width for the specific column.
- Receives either a string or number.
- By default, the column's width is defined to fit the available space without breaking the content.

##### cellRenderer

- Customize the render method of a single column cell.
- It receives a function that returns a node (react component).
- The function has the following params: ({ data, rowHeight, currentDensity, motion })
- data: the value of the current cell.
- rowHeight: current height of the row.
- currentDensity: current table density.
- motion: current row motion, that can be used by internal elements.
- The default is rendering the value as a string.

##### Sortable

- Indicates if the table can be sorted using this column as a reference.

##### Extended

- Indicates if a column is extended or not.
- It is generally used to express actions or general information about each row.

##### Condensed

- Indicates if a column is condensed or not.
- Condensed columns are a combination of two or more columns.
- It's important to notice that it must have it's ID to represent the join.
- It receives an array of string, which are the props that will compose the `data` object.

#### Live example

This live example features all columns props (unless `sortable`, which will be discussed in the `Sort` section).

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const ActionMenu = require('../ActionMenu/index.js').default
const OptionsDots = require('../icon/OptionsDots/index.js').default
const data = require('./sampleData.ts')

/** Columns definition, must be an array */
const columns = [
  {
    /** Prop that this column represents */
    id: 'id',
    /** Title that will appear on Header */
    title: 'ID',
    /** Fixed width */
    width: '3rem',
  },
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'qty',
    title: 'Qty',
  },
  {
    id: 'costPrice',
    title: 'Cost',
    /** Cellrenderer using the default data, which is costPrice in this case */
    cellRenderer: ({ data }) => <Currency value={data} />,
  },
  {
    id: 'retailPrice',
    title: 'Retail',
    cellRenderer: ({ data }) => <Currency value={data} />,
  },
  {
    id: 'profit',
    /** This is a custom title */
    title: <ProfitTitle />,
    /** Profit is a condensed column generated using retailPrice and costPrice props */
    condensed: ['retailPrice', 'costPrice'],
    cellRenderer: ({ data }) => {
      /** As you can see the data is the object of the two props */
      const { costPrice, retailPrice } = data
      const profit = parseFloat(retailPrice) - parseFloat(costPrice)
      return <Currency value={profit} />
    },
  },
  {
    id: 'actions',
    width: '3rem',
    cellRenderer: props => <Actions {...props} />,
    /** This column is extended, its data is the entire row */
    extended: true,
  },
]

function ProfitTitle() {
  return <>💸 Profit</>
}

const formatCurrency = value => parseFloat(value).toFixed(2)

function Currency({ value }) {
  return $ {formatCurrency(value)}
}

function Actions({ data }) {
  return (
    <ActionMenu
      buttonProps={{
        variation: 'tertiary',
        icon: <OptionsDots />,
      }}
      options={[
        {
          label: 'Action 1',
          onClick: () =>
            alert(
              `Executed action for ${data.name} of price ${data.retailPrice}`
            ),
        },
        {
          label: 'DANGEROUS Action',
          isDangerous: true,
          onClick: () =>
            alert(
              `Executed a DANGEROUS action for ${data.name} of price ${data.retailPrice}`
            ),
        },
      ]}
    />
  )
}

const items = data.products

function ColumnsExample() {
  /** The useTableMeasures hook will be discussed on the Measures section */
  const measures = useTableMeasures({ size: items.length })
  return

}
;<ColumnsExample />
```

# Table Props

This example, showcase the table using all of its simple props that are `loading`, `emptyState`, `onRowClick`, `columns`, `measures` and `items`

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const Toggle = require('../Toggle/index.js').default
const Button = require('../Button/index.js').default
const Spinner = require('../Spinner/index.js').default
const items = require('./sampleData.ts').customers.slice(0, 5)

const columns = [
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'location',
    title: 'Location',
  },
]

function reducer(state, action) {
  switch (action.type) {
    case 'loading':
      return {
        loading: !state.loading,
        customLoading: false,
        empty: false,
        customEmpty: false,
      }
    case 'custom-loading':
      return {
        loading: false,
        customLoading: !state.customLoading,
        empty: false,
        customEmpty: false,
      }
    case 'empty':
      return {
        loading: false,
        customLoading: false,
        empty: !state.empty,
        customEmpty: false,
      }
    case 'custom-empty':
      return {
        loading: false,
        customLoading: false,
        empty: false,
        customEmpty: !state.customEmpty,
      }
    default:
      return state
  }
}

function SimpleExample() {
  const measures = useTableMeasures({ size: items.length })
  const [
    { loading, customLoading, empty, customEmpty },
    dispatch,
  ] = React.useReducer(reducer, {
    loading: false,
    customLoading: false,
    empty: false,
    customEmpty: false,
  })

  const onRowClick = ({ rowData }) => {
    const { name, location } = rowData
    alert(`Your character is ${name}, from ${location}`)
  }

  const emptyState = {
    label: 'This is a default empty state for title',
  }

  const customEmptyState = {
    label: 'This is a default empty state for title',
    children: (
      <React.Fragment>

          A longer explanation of what should be here, and why should I care
          about what should be here.

          <Button variation="secondary" size="small">
            Suggested action
          </Button>

      </React.Fragment>
    ),
  }

  const tableLoading = customLoading
    ? {
        loading: {
          renderAs: () => {
            return <Spinner color="red" size={50} />
          },
        },
      }
    : { loading }

  return (
    <>

        <Toggle
          label="Toggle loading"
          checked={loading}
          onChange={() => dispatch({ type: 'loading' })}
        />
        <Toggle
          label="Toggle CUSTOM loading"
          checked={customLoading}
          onChange={() => dispatch({ type: 'custom-loading' })}
        />

        <Toggle
          label="Toggle empty state"
          checked={empty}
          onChange={() => dispatch({ type: 'empty' })}
        />
        <Toggle
          label="Toggle CUSTOM empty state"
          checked={customEmpty}
          onChange={() => dispatch({ type: 'custom-empty' })}
        />

    </>
  )
}
;<SimpleExample />
```

# State handlers

## useTableMeasures

```ts
enum Density {
  Compact = 'compact',
  Regular = 'regular',
  Comfortable = 'comfortable',
}
```

### Inputs

| Property | Type    | Required | Default         | Description               |
| -------- | ------- | -------- | --------------- | ------------------------- |
| size     | number  | ✅       | 0               | Length of the table items |
| density  | Density | 🚫       | Density.Regular | Initial Density           |

### Outputs

| Property          | Type                       | Description               |
| ----------------- | -------------------------- | ------------------------- |
| currentDensity    | Density                    | The table current density |
| rowHeight         | number                     | The height of each row    |
| tableHeight       | number                     | Table's full height       |
| setCurrentDensity | (density: Density) => void | Sets the current density  |

### Usage

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const data = require('./sampleData.ts')

const columns = [
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'email',
    title: 'Email',
  },
  {
    id: 'location',
    title: 'Location',
  },
]

const customers = data.customers.slice(0, 5)

function MeasuresExample() {
  const measures = useTableMeasures({ size: customers.length })
  return

}
;<MeasuresExample />
```

## useTableProportion

### Inputs

| Property | Type     | Required | Default | Description          |
| -------- | -------- | -------- | ------- | -------------------- |
| columns  | Column[] | ✅       | 🚫      | Columns of the table |
| ratio    | number[] | ✅       | 🚫      | Ratio of each column |

### Outputs

| Property     | Type     | Description                                   |
| ------------ | -------- | --------------------------------------------- |
| sizedColumns | Column[] | Columns of the table with the selected ratios |

### Usage

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const useTableProportion = require('./hooks/useTableProportion.ts').default
const ButtonGroup = require('../ButtonGroup/index.js').default
const Button = require('../Button/index.js').default
const data = require('./sampleData.ts')

const columns = [
  {
    id: 'id',
    title: 'ID',
  },
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'email',
    title: 'Email',
  },
]

const items = data.customers.slice(0, 3)

const ratios = [
  {
    id: 1,
    title: '1 | 1 | 1',
    value: [1, 1, 1],
  },
  {
    id: 2,
    title: '0.1 | 0.5 | 1',
    value: [0.1, 0.5, 1],
  },
  {
    id: 3,
    title: '0.5 | 0.5 | 1',
    value: [0.5, 0.5, 1],
  },
]

function ProportionExample() {
  const [currentRatio, setCurrentRatio] = React.useState(ratios[0])
  const measures = useTableMeasures({
    size: items.length,
  })

  const { sizedColumns } = useTableProportion({
    columns,
    ratio: currentRatio.value,
  })

  return (
    <>

        <ButtonGroup
          buttons={ratios.map(ratio => (
            <Button
              size="small"
              isActiveOfGroup={ratio.id === currentRatio.id}
              onClick={() => setCurrentRatio(ratio)}>
              Ratio: {ratio.title}
            </Button>
          ))}
        />

    </>
  )
}
;<ProportionExample />
```

## useTableSort

```ts
enum SortOrder {
  ASC = 'ASC',
  DSC = 'DSC',
}

type Sorted = {
  /** order of the sorting */
  order?: SortOrder
  /** reference prop */
  by?: string
}
```

### Outputs

| Property | Type                 | Description                             |
| -------- | -------------------- | --------------------------------------- |
| sorted   | Sorted               | Order and referecen prop of the sorting |
| clear    | () => void           | Clears sorting                          |
| sort     | (id: string) => void | Toggle sorting by some prop             |

### Usage

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const useTableSort = require('./hooks/useTableSort.ts').default
const data = require('./sampleData.ts')

const columns = [
  {
    id: 'name',
    title: 'Name',
    sortable: true,
  },
  {
    id: 'qty',
    title: 'Qty',
    sortable: true,
  },
  {
    id: 'costPrice',
    title: 'Cost',
    sortable: true,
  },
  {
    id: 'retailPrice',
    title: 'Retail',
    sortable: true,
  },
]

const products = data.products.slice(0, 5)

const ascOrdering = prop => (a, b) =>
  a[prop] > b[prop] ? 1 : a[prop]  (a, b) =>
  a[prop] > b[prop] ? -1 : a[prop]  {
    const {
      sorted: { order, by },
    } = sorting
    if (!order) {
      return products
    }
    const ascending = order === 'ASC'
    const comparator = ascending ? ascOrdering(by) : dscOrdering(by)
    return products.sort(comparator)
  }, [sorting.sorted, data.products])

  return (

  )
}
;<SortExample />
```

## useTableVisibility

### Inputs

| Property      | Type     | Required | Default | Description                      |
| ------------- | -------- | -------- | ------- | -------------------------------- |
| columns       | Column[] | ✅       | 🚫      | Columns of the table             |
| hiddenColumns | string[] | 🚫       | []      | Columns that are initally hidden |

### Outputs

| Property       | Type                 | Description                      |
| -------------- | -------------------- | -------------------------------- |
| visibleColumns | Column[]             | Columns that are visible         |
| hiddenColumns  | string[]             | Columns that are hidden          |
| toggleColumn   | (id: string) => void | Toggle a column visibility by id |
| showAllColumns | () => void           | Make all columns visible         |
| hideAllColumns | () => void           | Make all columns hidden          |

### Usage

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const useTableVisibility = require('./hooks/useTableVisibility.ts').default
const data = require('./sampleData.ts')

const columns = [
  {
    id: 'id',
    title: 'ID',
  },
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'email',
    title: 'Email',
  },
  {
    id: 'location',
    title: 'location',
  },
]

const items = data.customers.slice(0, 5)

function VisibilityExample() {
  const measures = useTableMeasures({
    size: items.length,
  })
  const visibility = useTableVisibility({
    columns,
    hiddenColumns: ['id', 'location'],
  })

  const buttonColumns = {
    label: 'Toggle visible fields',
    showAllLabel: 'Show All',
    hideAllLabel: 'Hide All',
    visibility,
  }

  return (
    <>

    </>
  )
}
;<VisibilityExample />
```

# Features

<div className="center mw7 pv6">
 ![](./table.png)
</div>

## Toolbar

The toolbar is a bundle of features, including search input, autocomplete, columns visibility toggle, density controls, import and export buttons, extra actions menu using ActionMenu component and a newLine button to help with entry creation (you can see the illustrative diagram in the beginning of the page for a better visualization of this structure)

#### InputSearch

- A wrapper around `InputSearch` component. The props are the same.

#### InputAutocomplete

- A wrapper around `AutocompleteInput` component. The props are the same.

#### Button Group

Represents the group of buttons located at the right. It has other composites that are described down below.

##### Columns

- A button that toggles columns visibility.
- It is recommended to combine it with the `useTableVisibility` hook.

```ts
enum Alignment {
  Left = 'left',
  Right = 'right',
}
```

| Property     | Type                         | Required | Default | Description                      |
| ------------ | ---------------------------- | -------- | ------- | -------------------------------- |
| label        | string                       | ✅       | 🚫      | General label                    |
| showAllLabel | string                       | ✅       | 🚫      | Label for the show all button    |
| hideAllLabel | string                       | ✅       | 🚫      | Label for the hide all button    |
| visibility   | Return of useTableVisibility | ✅       | 🚫      | Visibility of the columns        |
| alignMenu    | Alignment                    | 🚫       | 🚫      | Menu alignment                   |
| disabled     | boolean                      | 🚫       | false   | If the button is disabled or not |

##### Density

- A button that changes the row's density.
- It is recommended to combine it with the `useTableMeasures` hook.

```ts
enum Alignment {
  Left = 'left',
  Right = 'right',
}

enum Density {
  Compact = 'compact',
  Regular = 'regular',
  Comfortable = 'comfortable',
}
```

| Property         | Type                       | Required | Default | Description                      |
| ---------------- | -------------------------- | -------- | ------- | -------------------------------- |
| density          | Return of useTableMeasures | ✅       | 🚫      | Density object                   |
| compactLabel     | string                     | ✅       | 🚫      | Label of the compact option      |
| regularLabel     | string                     | ✅       | 🚫      | Label of the regular option      |
| comfortableLabel | string                     | ✅       | 🚫      | Label of the comfortable option  |
| alignMenu        | Alignment                  | 🚫       | 🚫      | Menu alignment                   |
| handleCallback   | (density: Density) => void | 🚫       | 🚫      | Triggered on change density      |
| disabled         | boolean                    | 🚫       | false   | If the button is disabled or not |

##### Download

- Button to handle download or export actions.

| Property | Type       | Required | Default | Description                      |
| -------- | ---------- | -------- | ------- | -------------------------------- |
| onClick  | () => void | ✅       | 🚫      | Action on click button           |
| label    | string     | 🚫       | ""      | Button text                      |
| disabled | boolean    | 🚫       | false   | If the button is disabled or not |

##### Upload

- Button to handle upload or import actions.

| Property | Type       | Required | Default | Description                      |
| -------- | ---------- | -------- | ------- | -------------------------------- |
| onClick  | () => void | ✅       | 🚫      | Action on click button           |
| label    | string     | 🚫       | 🚫      | Button text                      |
| disabled | boolean    | 🚫       | false   | If the button is disabled or not |

##### ExtraActions

- Button to perform extra actions.

```ts
enum Alignment {
  Left = 'left',
  Right = 'right',
}

type MenuAction = {
  label: string
  onClick: Function
  toggle?: {
    checked: boolean
    semantic: boolean
  }
  id?: number | string
}
```

| Property  | Type         | Required | Default | Description                      |
| --------- | ------------ | -------- | ------- | -------------------------------- |
| actions   | MenuAction[] | ✅       | 🚫      | Action on click button           |
| label     | string       | 🚫       | 🚫      | Button label                     |
| isLoading | boolean      | 🚫       | false   | If the button is loading or not  |
| disabled  | boolean      | 🚫       | false   | If the button is disabled or not |
| alignMenu | Alignment    | 🚫       | 🚫      | Menu alignment                   |

##### NewLine

- A button that represents creational purposes.

```ts
type MenuAction = {
  label: string
  onClick: Function
  toggle?: {
    checked: boolean
    semantic: boolean
  }
  id?: number | string
}
```

| Property  | Type         | Required | Default | Description                      |
| --------- | ------------ | -------- | ------- | -------------------------------- |
| onClick   | () => void   | ✅       | 🚫      | Action on click button           |
| label     | string       | 🚫       | 🚫      | Button text                      |
| actions   | MenuAction[] | 🚫       | 🚫      | Action on click button           |
| isLoading | boolean      | 🚫       | false   | If the button is loading or not  |
| disabled  | boolean      | 🚫       | false   | If the button is disabled or not |

#### Working example

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const useTableVisibility = require('./hooks/useTableVisibility.ts').default
const Tag = require('../Tag/index.js').default
const Icons = require('react-icons/fa')
const data = require('./sampleData.ts')

const columns = [
  {
    id: 'id',
    title: 'ID',
  },
  {
    id: 'icon',
    title: 'Icon',
    cellRenderer: ({ data, rowHeight, motion }) => (
      <Icon name={data} style={motion} height={rowHeight} />
    ),
  },
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'status',
    title: 'Status',
    cellRenderer: ({ data }) => <Status status={data} />,
  },
]

function Icon({ name, height, style }) {
  const SelectedIcon = Icons[name]
  return <SelectedIcon style={style} className="c-muted-1" size={height - 5} />
}

function Status({ status }) {
  const type = status === 'ACTIVE' ? 'success' : 'neutral'
  return <Tag type={type}>{status}</Tag>
}

const items = data.payments

function reducer(state, action) {
  switch (action.type) {
    case 'change': {
      const { value } = action
      return {
        ...state,
        inputValue: value,
      }
    }
    case 'clear': {
      return {
        inputValue: '',
        displayItems: items,
      }
    }
    case 'submit': {
      const inputClear = state.input === ''
      const filterFn = item =>
        item.name.toLowerCase().includes(state.inputValue.toLowerCase())
      return {
        ...state,
        displayItems: inputClear ? items : items.filter(filterFn),
      }
    }
    default: {
      return state
    }
  }
}

function ToolbarExample() {
  const [state, dispatch] = React.useReducer(reducer, {
    inputValue: '',
    displayItems: items,
  })

  const visibility = useTableVisibility({
    columns,
    items,
    hiddenColumns: ['id'],
  })

  const measures = useTableMeasures({
    size: state.displayItems.length,
  })

  const inputSearch = {
    value: state.inputValue,
    placeholder: 'Search stuff...',
    onChange: e => dispatch({ type: 'change', value: e.currentTarget.value }),
    onClear: () => {
      dispatch({ type: 'clear' })
    },
    onSubmit: e => {
      e.preventDefault()
      dispatch({ type: 'submit' })
    },
  }

  const buttonColumns = {
    label: 'Toggle visible fields',
    showAllLabel: 'Show All',
    hideAllLabel: 'Hide All',
    visibility,
  }

  const density = {
    label: 'Line density',
    compactLabel: 'Compact',
    regularLabel: 'Regular',
    comfortableLabel: 'Comfortable',
  }

  const download = {
    label: 'Export',
    onClick: () => alert('Clicked EXPORT'),
  }

  const upload = {
    label: 'Import',
    onClick: () => alert('Clicked IMPORT'),
  }

  const extraActions = {
    label: 'More options',
    actions: [
      {
        label: 'An action',
        onClick: () => alert('An action'),
      },
      {
        label: 'Another action',
        onClick: () => alert('Another action'),
      },
      {
        label: 'A third action',
        onClick: () => alert('A third action'),
      },
    ],
  }

  const newLine = {
    label: 'New',
    onClick: () => alert('handle new line callback'),
    actions: [
      'General',
      'Desktop & Screen Saver',
      'Dock',
      'Language & Region',
    ].map(label => ({
      label,
      onClick: () => alert(`Clicked ${label}`),
    })),
  }

  const emptyState = {
    label: 'The table is empty',
  }

  const empty = React.useMemo(
    () =>
      state.displayItems.length === 0 ||
      Object.keys(visibility.visibleColumns).length === 0,
    [visibility.visibleColumns, state.displayItems]
  )

  return (

  )
}
;<ToolbarExample />
```

## Totalizer

This uses the Totalizer component between the toolbar and the table content. You can find the full specs on the [Totalizer specific docs](https://styleguide.vtex.com/#/Components/Display/Totalizer).

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const ArrowUp = require('../icon/ArrowUp/index.js').default
const ArrowDown = require('../icon/ArrowDown/index.js').default

const data = require('./sampleData')

const columns = [
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'manufacturer',
    title: 'Manufacturer',
  },
  {
    id: 'qty',
    title: 'Qty',
  },
  {
    id: 'costPrice',
    title: 'Cost',
    cellRenderer: ({ data }) => <Currency value={data} />,
  },
  {
    id: 'retailPrice',
    title: 'Retail',
    cellRenderer: ({ data }) => <Currency value={data} />,
  },
]

const formatCurrency = value => parseFloat(value).toFixed(2)

function Currency({ value }) {
  return $ {formatCurrency(value)}
}

const items = data.products

function TotalizerExample() {
  const measures = useTableMeasures({
    size: items.length,
  })

  const totalizer = React.useMemo(() => {
    const sum = items.reduce(
      (acc, item) => {
        const { stock, cost, retail } = acc

        return {
          stock: stock + item.qty,
          cost: cost + item.qty * item.costPrice,
          retail: retail + item.qty * item.retailPrice,
        }
      },
      {
        stock: 0,
        cost: 0,
        retail: 0,
      }
    )
    return {
      items: [
        {
          label: 'Stock',
          value: sum.stock,
        },

        {
          label: 'Total cost',
          value: `$ ${formatCurrency(sum.cost)}`,
          iconBackgroundColor: '#fda4a4',
          icon: <ArrowDown color="#dd1616" size={14} />,
        },
        {
          label: 'Total retail',
          value: `$ ${formatCurrency(sum.retail)}`,
          iconBackgroundColor: '#eafce3',
          icon: <ArrowUp color="#79B03A" size={14} />,
        },
        {
          label: 'Expected Profit',
          value: `$ ${formatCurrency(sum.retail - sum.cost)}`,
        },
      ],
    }
  }, [items])

  return (

  )
}

;<TotalizerExample />
```

## Bulk Actions

- Bulk actions allow the user to select some or all the rows to apply an action.
- It is recommended the usage along with the `EXPERIMENTAL_useCheckboxTree` hook which [has its docs](https://styleguide.vtex.com/#/Components/%F0%9F%91%BB%20Experimental/EXPERIMENTALUseCheckboxTree).
- Like the `Toolbar`, `BulkActions` is a compound component.

#### Actions

##### Primary

- Button to handle primary.

| Property | Type       | Required | Default | Description            |
| -------- | ---------- | -------- | ------- | ---------------------- |
| label    | string     | ✅       | 🚫      | Button text            |
| onClick  | () => void | ✅       | 🚫      | Action on click button |

##### Secondary

- Button to handle secondary actions.

| Property      | Type                    | Required | Default | Description             |
| ------------- | ----------------------- | -------- | ------- | ----------------------- |
| label         | string                  | ✅       | 🚫      | Button text             |
| onClick       | () => void              | ✅       | 🚫      | Action on click button  |
| onActionClick | (e: MenuAction) => void | 🚫       | 🚫      | Action on click actions |

#### Tail

##### Info

- Displays information of any kind.
- Often used to display selected rows count.

| Property | Type           | Required | Default | Description     |
| -------- | -------------- | -------- | ------- | --------------- |
| children | React.ReacNode | 🚫       | 🚫      | Info to display |

##### Toggle

- Action that hidden when active, showing it's children.
- It is inactive, shows a Button.

```ts
type Button = {
  text: string
  onClick: () => void
}
```

| Property | Type           | Required | Default | Description                   |
| -------- | -------------- | -------- | ------- | ----------------------------- |
| button   | Button         | ✅       | 🚫      | Button props                  |
| active   | boolean        | 🚫       | false   | Action on click button        |
| children | React.ReacNode | 🚫       | 🚫      | Item to show when is inactive |

##### Dismiss

- Button to handle download or export actions.

| Property | Type       | Required | Default | Description            |
| -------- | ---------- | -------- | ------- | ---------------------- |
| onClick  | () => void | ✅       | 🚫      | Action on click button |

#### Working Example

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const useCheckboxTree = require('../EXPERIMENTAL_useCheckboxTree').default
const data = require('./sampleData')

const columns = [
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'manufacturer',
    title: 'Manufacturer',
  },
  {
    id: 'qty',
    title: 'Qty',
  },
  {
    id: 'costPrice',
    title: 'Cost',
    cellRenderer: props => <Currency {...props} />,
  },
  {
    id: 'wholesalePrice',
    title: 'Wholesale',
    cellRenderer: props => <Currency {...props} />,
  },
  {
    id: 'retailPrice',
    title: 'Retail',
    cellRenderer: props => <Currency {...props} />,
  },
]

function Currency({ data }) {
  return <>$ {parseFloat(data).toFixed(2)}</>
}

function useProducts() {
  const [items, setItems] = React.useState(data.products)

  const bulkUpdate = group => positive => {
    const groupIds = group.map(item => item.id)
    setItems(oldItems =>
      oldItems.map(item => (groupIds.includes(item.id) ? positive(item) : item))
    )
  }

  const discountCurry = amt => value => value - value * amt

  const applyDiscount = React.useCallback(
    (group, amt) => {
      const calcDiscount = discountCurry(amt)
      const update = bulkUpdate(group)
      update(item => ({
        ...item,
        retailPrice: calcDiscount(item.retailPrice),
        wholesalePrice: calcDiscount(item.wholesalePrice),
      }))
    },
    [items]
  )

  const increaseQty = React.useCallback(
    (group, amt) => {
      const update = bulkUpdate(group)
      update(item => ({
        ...item,
        qty: item.qty + amt,
      }))
    },
    [items]
  )

  const decreaseQty = React.useCallback(
    (group, amt) => {
      const update = bulkUpdate(group)
      update(item => ({
        ...item,
        qty: item.qty - amt,
      }))
    },
    [items]
  )

  return { items, applyDiscount, increaseQty, decreaseQty }
}

function BulkFullExample() {
  const { items, applyDiscount, increaseQty, decreaseQty } = useProducts()

  const primaryAction = {
    label: 'Apply 50% Discount',
    onClick: () => applyDiscount(checkboxes.checkedItems, 0.5),
  }

  const secondaryActions = {
    label: 'Quantity',
    actions: [
      {
        label: 'Increase 50',
        onClick: checked => increaseQty(checked, 50),
      },
      {
        label: 'Decrease 50',
        onClick: checked => decreaseQty(checked, 50),
      },
    ],
    onActionClick: action => action.onClick(checkboxes.checkedItems),
  }

  const measures = useTableMeasures({
    size: items.length,
  })

  const checkboxes = useCheckboxTree({
    items,
    onToggle: ({ checkedItems }) => console.table(checkedItems),
  })

  return (
    <>

            {!checkboxes.allChecked && (

                All rows selected: {checkboxes.checkedItems.length}

            )}
            <Table.Bulk.Tail.Toggle
              button={{
                text: `Select all ${items.length}`,
                onClick: checkboxes.checkAll,
              }}
              active={checkboxes.allChecked}>
              Selected rows: {items.length}

    </>
  )
}

;<BulkFullExample />
```

## Autocomplete, Filters & Pagination

Each component has it's own documentation ([Autocomplete](https://styleguide.vtex.com/#/Components/Forms/AutocompleteInput), [Filters](https://styleguide.vtex.com/#/Components/Display/FilterBar), [Pagination](https://styleguide.vtex.com/#/Components/Navigation/Pagination)), so it's important to you check it out to see the full capabilities.

```js
const useTableMeasures = require('./hooks/useTableMeasures.tsx').default
const useTableProportion = require('./hooks/useTableProportion.ts').default
const Input = require('../Input/index.js').default
const Checkbox = require('../Checkbox/index.js').default
const data = require('./sampleData.ts')

const columns = [
  {
    id: 'name',
    title: 'Name',
  },
  {
    id: 'email',
    title: 'Email',
  },
  {
    id: 'location',
    title: 'Location',
  },
]

const items = data.customers

/** hook that handles pagination state */
function usePagination(initialSize, items) {
  const [state, setState] = React.useState({
    tableSize: initialSize,
    currentPage: 1,
    currentItemFrom: 1,
    currentItemTo: initialSize,
    slicedItems: [...items].slice(0, initialSize),
  })

  /** resets state on items change */
  React.useEffect(() => {
    setState({
      tableSize: initialSize,
      currentPage: 1,
      currentItemFrom: 1,
      currentItemTo: initialSize,
      slicedItems: [...items].slice(0, initialSize),
    })
  }, [items])

  /** gets the next page */
  const onNextClick = React.useCallback(() => {
    const newPage = state.currentPage + 1
    const itemFrom = state.currentItemTo + 1
    const itemTo = state.tableSize * newPage
    const newItems = [...items].slice(itemFrom - 1, itemTo)
    setState(state => ({
      ...state,
      currentPage: newPage,
      currentItemFrom: itemFrom,
      currentItemTo: itemTo,
      slicedItems: newItems,
    }))
  }, [state, items])

  /** gets the previous page */
  const onPrevClick = React.useCallback(() => {
    if (state.currentPage === 0) return
    const newPage = state.currentPage - 1
    const itemFrom = state.currentItemFrom - state.tableSize
    const itemTo = state.currentItemFrom - 1
    const newItems = [...items].slice(itemFrom - 1, itemTo)
    setState(state => ({
      ...state,
      currentPage: newPage,
      currentItemFrom: itemFrom,
      currentItemTo: itemTo,
      slicedItems: newItems,
    }))
  }, [state, items])

  /** deals rows change of Pagination component */
  const onRowsChange = React.useCallback(
    (e, value) => {
      const rowValue = parseInt(value)
      setState(state => ({
        ...state,
        tableSize: rowValue,
        currentItemTo: rowValue,
        slicedItems: [...items].slice(state.currentItemFrom - 1, rowValue),
      }))
    },
    [state, items]
  )

  return {
    onNextClick,
    onPrevClick,
    onRowsChange,
    ...state,
  }
}

function PaginationExample() {
  const ITEMS_PER_PAGE = 5

  /** handles sizes */
  const measures = useTableMeasures({ size: ITEMS_PER_PAGE })

  /** handles filtering */
  const [filteredItems, setFilteredItems] = React.useState(items)
  const [filterStatements, setFilterStatements] = React.useState([])

  /* handles pagination */
  const { slicedItems, ...paginationProps } = usePagination(
    ITEMS_PER_PAGE,
    filteredItems
  )

  /** handles proportion consistency on change pages */
  const { sizedColumns } = useTableProportion({ columns, ratio: [1, 1] })

  /** handles autocomplete */
  const [term, setTerm] = React.useState('')
  const [loading, setLoading] = React.useState(false)
  const timeoutRef = React.useRef(null)

  /** props of the PaginationComponent */
  const pagination = {
    ...paginationProps,
    textOf: 'of',
    rowsOptions: [5, 10, 15],
    textShowRows: 'Show rows',
    totalItems: filteredItems.length,
  }

  /** function to handle filter changes more info @ Styleguides FilterBar docs */
  function handleFiltersChange(statements = []) {
    let newData = items.slice()
    statements.forEach(st => {
      if (!st || !st.object) return
      const { subject, verb, object } = st
      switch (subject) {
        case 'name':
        case 'email':
          if (verb === 'contains') {
            newData = newData.filter(item => item[subject].includes(object))
          } else if (verb === '=') {
            newData = newData.filter(item => item[subject] === object)
          } else if (verb === '!=') {
            newData = newData.filter(item => item[subject] !== object)
          }
          break

        case 'location':
          if (!object) return
          const selectedLocations = Object.keys(object).reduce(
            (acc, item) => (object[item] ? [...acc, item] : acc),
            []
          )
          newData = newData.filter(item =>
            selectedLocations.includes(item[subject])
          )
          break
      }
    })
    setFilteredItems(newData)
    setFilterStatements(statements)
  }

  /** FilterBar props */
  const filters = {
    alwaysVisibleFilters: ['name', 'location'],
    statements: filterStatements,
    onChangeStatements: handleFiltersChange,
    clearAllFiltersButtonLabel: 'Clear Filters',
    collapseLeft: true,
    options: {
      name: {
        label: 'Name',
        ...simpleInputVerbsAndLabel(),
      },
      email: {
        label: 'Email',
        ...simpleInputVerbsAndLabel(),
      },
      location: {
        label: 'Location',
        renderFilterLabel: st => {
          if (!st || !st.object) {
            return 'All'
          }
          const keys = st.object ? Object.keys(st.object) : {}
          const isAllTrue = !keys.some(key => !st.object[key])
          const isAllFalse = !keys.some(key => st.object[key])
          const trueKeys = keys.filter(key => st.object[key])
          let trueKeysLabel = ''
          trueKeys.forEach((key, index) => {
            trueKeysLabel += `${key}${
              index === trueKeys.length - 1 ? '' : ', '
            }`
          })
          return `${
            isAllTrue ? 'All' : isAllFalse ? 'None' : `${trueKeysLabel}`
          }`
        },
        verbs: [
          {
            label: 'includes',
            value: 'includes',
            object: {
              renderFn: locationSelectorObject,
              extraParams: {},
            },
          },
        ],
      },
    },
  }

  /** Autocomplete options prop */
  const options = {
    onSelect: (...args) => console.log('onSelect: ', ...args),
    loading,
    value: !term.length
      ? []
      : filteredItems
          .map(item => item.name)
          .filter(name =>
            typeof name === 'string'
              ? name.toLowerCase().includes(term.toLowerCase())
              : name.label.toLowerCase().includes(term.toLowerCase())
          ),
  }

  /** Autocomplete input prop */
  const input = {
    onChange: term => {
      if (term) {
        setLoading(true)
        if (timeoutRef.current) {
          clearTimeout(timeoutRef.current)
        }
        timeoutRef.current = setTimeout(() => {
          setLoading(false)
          setTerm(term)
          timeoutRef.current = null
        }, 1000)
      } else {
        setTerm(term)
      }
    },
    onSearch: (...args) => console.log('onSearch:', ...args),
    onClear: () => setTerm(''),
    placeholder: 'Search name... (e.g.: Peter)',
    value: term,
  }

  return (

  )
}

/** simple input renderer */
function simpleInputObject({ values, onChangeObjectCallback }) {
  return (
    <Input
      value={values || ''}
      onChange={e => onChangeObjectCallback(e.target.value)}
    />
  )
}

/** simple input verbs renderer */
function simpleInputVerbsAndLabel() {
  return {
    renderFilterLabel: st => {
      if (!st || !st.object) {
        // you should treat empty object cases only for alwaysVisibleFilters
        return 'Any'
      }
      return `${
        st.verb === '=' ? 'is' : st.verb === '!=' ? 'is not' : 'contains'
      } ${st.object}`
    },
    verbs: [
      {
        label: 'is',
        value: '=',
        object: {
          renderFn: simpleInputObject,
          extraParams: {},
        },
      },
      {
        label: 'is not',
        value: '!=',
        object: {
          renderFn: simpleInputObject,
          extraParams: {},
        },
      },
      {
        label: 'contains',
        value: 'contains',
        object: {
          renderFn: simpleInputObject,
          extraParams: {},
        },
      },
    ],
  }
}

/** location input renderer */
function locationSelectorObject({ values, onChangeObjectCallback }) {
  const initialValue = {
    '🇰🇪Wakanda': true,
    '🇺🇸USA': true,
    '🇨🇳China': true,
    '🇷🇺Russia': true,
    '🇬🇧Great Britain': true,
    '🇸🇦Saudi Arabia': true,
    '🇨🇺Cuba': true,
    ...(values || {}),
  }
  const toggleValueByKey = key => {
    const newValues = {
      ...(values || initialValue),
      [key]: values ? !values[key] : false,
    }
    return newValues
  }
  return (

      {Object.keys(initialValue).map((opt, index) => {
        return (
          <div className="mb3" key={`class-statment-object-${opt}-${index}`}>
            <Checkbox
              checked={values ? values[opt] : initialValue[opt]}
              label={opt}
              name="default-checkbox-group"
              onChange={() => {
                const newValue = toggleValueByKey(`${opt}`)
                const newValueKeys = Object.keys(newValue)
                const isEmptyFilter = !newValueKeys.some(key => !newValue[key])
                onChangeObjectCallback(isEmptyFilter ? null : newValue)
              }}
              value={opt}
            />

        )
      })}

  )
}

;<PaginationExample />
```

# 🧪 E2E Testing

The Table has a lot of internal components. To avoid receiving a huge object containing id's of each component, they are semantically created based on the `testId` property.

You can reference as `[data-testid]=id` on your e2e testing tool, such as cypress.

#### Table of semantic generated data-testid's:

💡[id] represents the value of `testId` property, which is `vtex-table-v2` by default.

| Targets                               | Decription                                                        |
| ------------------------------------- | ----------------------------------------------------------------- |
| `[id]`                                | Internal `table`                                                  |
| `[id]__container`                     | `table`'s container                                               |
| `[id]__header`                        | `table`'s `thead`                                                 |
| `[id]__body`                          | `table`'s `tbody`                                                 |
| `[id]__loading`                       | `Loading` container                                               |
| `[id]__empty-state`                   | `EmptyState` component                                            |
| `[id]__filter-bar`                    | `Table.FilterBar` wrapper                                         |
| `[id]__totalizer`                     | `Table.Totalizer` wrapper                                         |
| `[id]__pagination`                    | `Table.Pagination` wrapper                                        |
| `[id]__toolbar`                       | `Table.Toolbar` root                                              |
| `[toolbar]__search-form`              | `Table.Toolbar.InputSearch` `form` tag                            |
| `[toolbar]__search-form__input`       | `Table.Toolbar.InputSearch` input                                 |
| `[toolbar]__input-autocomplete`       | `Table.Toolbar.InputAutocomplete` wrapper                         |
| `[toolbar]__button-group`             | `Table.Toolbar.ButtonGroup` root                                  |
| `[button-group]__columns`             | `Table.Toolbar.ButtonGroup.Columns` button                        |
| `[button-group]__columns__box`        | `Table.Toolbar.ButtonGroup.Columns` box                           |
| `[columns-box]__group-actions`        | `Table.Toolbar.ButtonGroup.Columns` box actions                   |
| `[columns-box]__group-actions--[key]` | `Table.Toolbar.ButtonGroup.Columns` box action of key (1, 2, ...) |
| `[columns-box]__items`                | `Table.Toolbar.ButtonGroup.Columns` box items                     |
| `[button-group]__density`             | `Table.Toolbar.ButtonGroup.Density` button                        |
| `[button-group]__density__box`        | `Table.Toolbar.ButtonGroup.Density` box                           |
| `[density-box]__items`                | `Table.Toolbar.ButtonGroup.Density` box items                     |
| `[button-group]__download`            | `Table.Toolbar.ButtonGroup.Download` button                       |
| `[button-group]__upload`              | `Table.Toolbar.ButtonGroup.Upload` button                         |
| `[button-group]__extra-actions`       | `Table.Toolbar.ButtonGroup.ExtraActions` button                   |
| `[button-group]__new-line`            | `Table.Toolbar.ButtonGroup.NewLine` button                        |

# 📚 Migration Guide

This section is designed to `Table V1` users that desire to upgrade and enjoy the `V2` benefits. Will be discussed the key differences between the two. The reading of the V2 documentation is essential, though.

### Schema vs Columns

The V1 introduced the concept of the table schema, which was a JSON object of type:

```ts
type Schema = {
  properties: {
    [key: string]: {
      title: string
      width: number
      minWidth: number
      cellRenderer: ({
        cellData,
        rowData,
        updateCellMeasurements,
      }) => React.ReactNode
      headerRight: boolean
      sortable: boolean
      headerRenderer: ({ columnIndex, key, title }) => React.ReactNode
    }
  }
}
```

The V2 take on the same problem is the `columns` property, which is an array of columns of type:

```ts
type Column = {
  id?: string
  title?: string | Element | Function
  width?: number | string
  cellRenderer?: (cellData: {
    data: unknown | object
    rowHeight: number
    currentDensity: Density
    motion: ReturnType<typeof useTableMotion>
  }) => React.ReactNode
  sortable?: boolean
  extended?: boolean
  condensed?: string[]
}
```

We can conclude that:

- ➕ The `title` now supports strings or objects.
- ➕ The `width` that was a number, is now a string or a number.
- ➕ We have two new props: `extended` and `condensed`, to handle what rowData was supposed to deal with.
- ♻ The `key` was converted to an `id` prop of the column object.
- ♻ `sortable` is kept and did not even change its purpose or type.
- The `cellRenderer` props:
- ➕ The cell can react to `rowHeight`, `currentDensity` and `motion`.
- ♻ `cellData` is now called `data`
- 🚫 `rowData` is deprecated
- 🚫 `updateCellMeasurements` is deprecated
- 🚫 The `minWidth` is deprecated.
- 🚫 `headerRight` is deprecated
- 🚫 `headerRenderer` is deprecated since its job is done by title.

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/EXPERIMENTAL_Table/types.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Density } from './hooks/useTableMeasures'
import useTableMotion from './hooks/useTableMotion'

export type Items = object[]

export type ReturnedData = {
  data: unknown | object
  rowHeight: number
  currentDensity: Density
  motion: ReturnType<typeof useTableMotion>
}

export type Column = {
  id?: string
  title?: string | Element | Function
  width?: number | string
  sortable?: boolean
  cellRenderer?: (data: ReturnedData) => React.ReactNode
  extended?: boolean
  condensed?: string[]
}

export type E2ETestable<T = string> = {
  testId?: T
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/EXPERIMENTAL_Table/constants.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const ORDER_CLASSNAMES = {
  TOOLBAR: 'order-0',
  TOOLBAR_CHILD: {
    INPUT: 'order-0',
    POSITION_FIXER: 'order-1',
    BUTTON_GROUP: 'order-2',
    BUTTON_GROUP_CHILD: {
      COLUMNS: 'order-0',
      DENSITY: 'order-1',
      DOWNLOAD: 'order-2',
      UPLOAD: 'order-3',
      EXTRA_ACTIONS: 'order-4',
      NEWLINE: 'order-5',
    },
  },
  FILTER_BAR: 'order-1',
  TOTALIZER: 'order-2',
  GENERIC_ACTION_BAR: 'order-3',
  BULK: 'order-4',
  BULK_CHILD: {
    ACTIONS: 'order-0',
    ACTIONS_CHILD: {
      PRIMARY: 'order-0',
      SECONDARY: 'order-1',
    },
    POSITION_FIXER: 'order-1',
    RIGHT: 'order-2',
  },
  TABLE: 'order-5',
  PAGINATION: 'order-6',
}

export const NAMESPACE_PREFIX = 'vtex-table-v2'
export const NAMESPACES = {
  CONTAINER: `${NAMESPACE_PREFIX}__container`,
  TABLE: `${NAMESPACE_PREFIX}`,
  HEADER: `${NAMESPACE_PREFIX}__header`,
  BODY: `${NAMESPACE_PREFIX}__body`,
  ROW: `${NAMESPACE_PREFIX}__row`,
  CELL: `${NAMESPACE_PREFIX}__cell`,
  TOOLBAR: {
    CONTAINER: `${NAMESPACE_PREFIX}__toolbar`,
    INPUT_SEARCH: `${NAMESPACE_PREFIX}__toolbar__input-search`,
    BUTTON_GROUP: `${NAMESPACE_PREFIX}__toolbar__button-group`,
    BUTTON_COLUMNS: `${NAMESPACE_PREFIX}__toolbar__button-columns`,
    BUTTON_DENSITY: `${NAMESPACE_PREFIX}__toolbar__button-density`,
    BUTTON_DOWNLOAD: `${NAMESPACE_PREFIX}__toolbar__button-download`,
    BUTTON_UPLOAD: `${NAMESPACE_PREFIX}__toolbar__button-upload`,
    BUTTON_NEWLINE: `${NAMESPACE_PREFIX}__toolbar__button-newline`,
    BUTTON_EXTRA_ACTIONS: `${NAMESPACE_PREFIX}__toolbar__button-extra-actions`,
  },
  FILTER_BAR: `${NAMESPACE_PREFIX}__filter-bar`,
  TOTALIZER: `${NAMESPACE_PREFIX}__totalizer`,
  BULK_ACTIONS: `${NAMESPACE_PREFIX}__bulk-actions`,
  CHECKBOX: `${NAMESPACE_PREFIX}__checkbox`,
  PAGINATION: `${NAMESPACE_PREFIX}__pagination`,
  GENERIC_ACTION_BAR: `${NAMESPACE_PREFIX}__action-bar`,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/EXPERIMENTAL_Table/context.tsx from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, useContext, createContext } from 'react'

import { E2ETestable } from './types'

const TestingContext = createContext<E2ETestable>(null)

export const TableProvider: FC<E2ETestable> = ({ children, testId }) => {
  return (
    <TestingContext.Provider value={{ testId }}>
      {children}
    </TestingContext.Provider>
  )
}

export function useTestingContext() {
  const context = useContext(TestingContext)
  if (!context) {
    throw new Error(
      'Do not use E2ETestable components outside of the testing context'
    )
  }
  return context
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/EXPERIMENTAL_Table/Checkbox.tsx from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'

import CheckboxBase from '../Checkbox/index.js'

const Checkbox: FC<CheckboxProps> = ({ id, onClick, ...props }) => (
  <div onClick={e => e.stopPropagation()}>
    <CheckboxBase
      id={id}
      name={`row_${id}`}
      onChange={() => onClick(id)}
      {...props}
    />
  </div>
)

export type CheckboxProps = {
  id?: string
  onClick?: (id: string) => void
  checked?: boolean
  disabled?: boolean
  partial?: boolean
}

Checkbox.defaultProps = {
  id: '',
  onClick: () => {},
  checked: false,
  partial: false,
  disabled: false,
}

export default Checkbox

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/EXPERIMENTAL_Table/sampleData.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const products = [
  {
    id: 1,
    name: 'Macbook Pro 13 inch',
    categories: ['laptop'],
    manufacturer: 'apple',
    qty: 200,
    costPrice: 800,
    retailPrice: 1299,
    wholesalePrice: 1000,
  },
  {
    id: 2,
    name: 'Pixel 4',
    categories: ['smartphone'],
    manufacturer: 'google',
    qty: 100,
    costPrice: 600,
    retailPrice: 799,
    wholesalePrice: 700,
  },
  {
    id: 3,
    name: 'Macbook Pro 16 inch',
    categories: ['laptop'],
    manufacturer: 'apple',
    qty: 10,
    costPrice: 1800,
    retailPrice: 2399,
    wholesalePrice: 2000,
  },
  {
    id: 4,
    name: 'Ipad Pro 12.9 inch',
    categories: ['tablet'],
    manufacturer: 'apple',
    qty: 210,
    costPrice: 600,
    retailPrice: 999,
    wholesalePrice: 750,
  },
  {
    id: 5,
    name: 'XPS 15',
    categories: ['laptop'],
    manufacturer: 'dell',
    qty: 500,
    costPrice: 500,
    retailPrice: 1050,
    wholesalePrice: 950,
  },
  {
    id: 6,
    name: 'Disabled Item',
    categories: ['laptop'],
    manufacturer: 'example',
    qty: 0,
    costPrice: 500,
    retailPrice: 1050,
    wholesalePrice: 950,
  },
  {
    id: 7,
    name: 'IPhone 11 Pro',
    categories: ['smartphone'],
    manufacturer: 'apple',
    qty: 450,
    costPrice: 700,
    retailPrice: 1100,
    wholesalePrice: 950,
  },
]

export const payments = [
  {
    id: 1,
    icon: 'FaCcPaypal',
    name: 'Paypal',
    status: 'ACTIVE',
  },
  {
    id: 2,
    icon: 'FaCcMastercard',
    name: 'Mastercard',
    status: 'ACTIVE',
  },
  {
    id: 3,
    icon: 'FaCcDinersClub',
    name: 'Dinners Club',
    status: 'ACTIVE',
  },
  {
    id: 4,
    icon: 'FaCcDiscover',
    name: 'Discover',
    status: 'INACTIVE',
  },
  {
    id: 5,
    icon: 'FaCcAmazonPay',
    name: 'Amazon Pay',
    status: 'INACTIVE',
  },
  {
    id: 6,
    icon: 'FaCcApplePay',
    name: 'Apple Pay',
    status: 'ACTIVE',
  },
  {
    id: 7,
    icon: 'FaCcVisa',
    name: 'Visa',
    status: 'ACTIVE',
  },
  {
    id: 8,
    icon: 'FaCcAmex',
    name: 'American Express',
    status: 'INACTIVE',
  },
]

export const customers = [
  {
    id: 1,
    name: "T'Chala",
    email: 'black.panther@gmail.com',
    location: '🇰🇪Wakanda',
  },
  {
    id: 2,
    name: 'Peter Parker',
    email: 'spider.man@gmail.com',
    location: '🇺🇸USA',
  },
  {
    id: 3,
    name: 'Shang-Chi',
    email: 'kung.fu@gmail.com',
    location: '🇨🇳China',
  },
  {
    id: 4,
    name: 'Natasha Romanoff',
    email: 'black.widown@gmail.com',
    location: '🇷🇺Russia',
  },
  {
    id: 5,
    name: 'Stephen Strange',
    email: 'dr.strange@gmail.com',
    location: '🇺🇸USA',
  },
  {
    id: 6,
    name: 'Steve Rogers',
    email: 'cap.america@gmail.com',
    location: '🇺🇸USA',
  },
  {
    id: 7,
    name: 'Abdul Alhazred',
    email: 'abdul@gmail.com',
    location: '🇸🇦Saudi Arabia',
  },
  {
    id: 8,
    name: 'Carol Danvers',
    email: 'cap.marvel@gmail.com',
    location: '🇺🇸USA',
  },
  {
    id: 9,
    name: 'Scott Lang',
    email: 'ant.man@gmail.com',
    location: '🇺🇸USA',
  },
  {
    id: 10,
    name: 'En Sabah Nuh',
    email: 'sabah@gmail.com',
    location: '🇨🇺Cuba',
  },
  {
    id: 11,
    name: 'Abdul Qamar',
    email: 'qamar@gmail.com',
    location: '🇸🇦Saudi Arabia',
  },
  {
    id: 12,
    name: 'Goose the Cat',
    email: 'meow@gmail.com',
    location: '🇺🇸USA',
  },
  {
    id: 13,
    name: 'Brian Braddock',
    email: 'cap.britain@gmail.com',
    location: '🇬🇧Great Britain',
  },
  {
    id: 14,
    name: 'Marc Spector',
    email: 'moon.knight@gmail.com',
    location: '🇺🇸USA',
  },
  {
    id: 15,
    name: 'John Walker',
    email: 'american.agent@gmail.com',
    location: '🇺🇸USA',
  },
  {
    id: 16,
    name: 'Dane Whitman',
    email: 'black.knight@gmail.com',
    location: '🇺🇸USA',
  },
]

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/PageBlock/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component, Fragment } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

import Box from '../Box/index'

class PageBlock extends Component {
  render() {
    const {
      title,
      subtitle,
      variation,
      titleAside,
      testId,
      boxProps,
    } = this.props
    const isAnnotated = variation === 'annotated'

    const headerClasses = classNames({
      'w-third': isAnnotated,
      'flex flex-row justify-between': !isAnnotated && titleAside,
      'flex flex-column': isAnnotated && titleAside,
    })

    let titleClasses = 'styleguide__pageBlock_title t-heading-3 mb3 ml5 ml3-ns '
    titleClasses += titleAside ? 'mt0' : 'mt4'

    return (
      <div
        className={`styleguide__pageBlock flex ${
          isAnnotated ? 'flex-row' : 'flex-column'
        }`}
        data-testid={testId}>
        {/* Title, subtitle & aside */}
        {(title || subtitle) && (
          <div className={headerClasses}>
            <div className="flex-grow-1">
              {title && <h2 className={titleClasses}>{title}</h2>}
              {subtitle && (
                <div
                  className={`t-body lh-copy c-muted-1 mb7 ml3 ${!isAnnotated &&
                    'w-two-thirds-ns w-100'}`}>
                  {subtitle}
                </div>
              )}
            </div>
            {titleAside && !isAnnotated && (
              <div className="flex-grow-1">{titleAside}</div>
            )}
          </div>
        )}

        {/* Boxes and the content itself */}
        <div
          className={`flex flex-column flex-row-ns ${isAnnotated &&
            'w-two-thirds'}`}>
          {variation === 'half' ? (
            <Fragment>
              <div className="w-50-ns w-100 mr3-ns mb0-ns mb5">
                <Box {...boxProps}>
                  {this.props.children && this.props.children[0]}
                </Box>
              </div>
              <div className="w-50-ns w-100 ml3-ns mb5">
                <Box {...boxProps}>
                  {this.props.children && this.props.children[1]}
                </Box>
              </div>
            </Fragment>
          ) : variation === 'aside' ? (
            <Fragment>
              <div className="w-two-thirds-ns w-100 mr3-ns mb0-ns mb5">
                <Box {...boxProps}>
                  {this.props.children && this.props.children[0]}
                </Box>
              </div>
              <div className="w-third-ns w-100 ml3-ns mb5">
                <Box {...boxProps}>
                  {this.props.children && this.props.children[1]}
                </Box>
              </div>
            </Fragment>
          ) : (
            <div className="w-100 mb5">
              <Box {...boxProps}>{this.props.children}</Box>
            </div>
          )}
        </div>
      </div>
    )
  }
}

PageBlock.defaultProps = {
  variation: 'full',
}

PageBlock.propTypes = {
  /** Type of layout grid for the content. */
  variation: PropTypes.oneOf(['full', 'half', 'annotated', 'aside']),
  /** Title for the block. */
  title: PropTypes.string,
  /** Data attribute */
  testId: PropTypes.string,
  /** Subtitle for the block. */
  subtitle: PropTypes.string,
  /** Content on the right side of the title. */
  titleAside: PropTypes.node,
  /** Contents of the boxes. Can be 1 or 2 nodes depending on the variation chosen. */
  children: function(props, propName, componentName) {
    PropTypes.checkPropTypes(
      {
        children: PropTypes.node.isRequired,
      },
      props,
      'props',
      componentName
    )

    const isAsideOrHalf = ['half', 'aside'].indexOf(props.variation) !== -1
    const hasRequiredChildren = props[propName] && props[propName].length === 2

    if (isAsideOrHalf && !hasRequiredChildren) {
      return new Error(
        `Invalid prop \`children\` supplied to \`${componentName}\` with variation "${
          props.variation
        }", it must have 2 nodes as children. It was passed ${
          props.children
            ? props.children.length
              ? props.children.length
              : 'one'
            : 'nothing'
        }.`
      )
    }
  },
  boxProps: PropTypes.shape({
    noPadding: PropTypes.bool,
    title: PropTypes.string,
  }),
}

export default PageBlock

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/PageBlock/README.md from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```markdown
#### Page blocks are the building blocks of a page. After choosing a layout you can use the Page Blocks to design the page as you will. There are four main kinds of Page blocks, full, Half, Aside and annotated. They are off to be used to fulfill design needs and to display the page content with the due visual hierarchy.

### 👍 Dos

- Use to build a page layout
- Use to create information hierarchy

### 👎 Don'ts

- PageBlock title is not the box title, it’s the title of a whole section. Boxes titles should be inside them.
- Over complicate the header aside. It should be very concise such as a button or a dropdown.

Full

```js

  <PageBlock variation="full">

  </PageBlock>

```

Half

```js

  <PageBlock variation="half">

  </PageBlock>

```

Aside

```js

  <PageBlock variation="aside">

  </PageBlock>

```

Annotated

```js

  <PageBlock variation="annotated" title="Section title" subtitle="Subtitle">

  </PageBlock>

```

With Title and subtitle

```js

  <PageBlock
    title="Section"
    subtitle="Some explanation for your section."
    variation="full">

  </PageBlock>

```

With Title, subtitle and aside children

```js
const Dropdown = require('../Dropdown').default

;

  <PageBlock
    title="Section"
    subtitle="Some explanation for your section."
    variation="full"
    titleAside={

          <Dropdown
            options={[
              { value: 'chagall', label: 'Chagall' },
              { value: 'dali', label: 'Dali' },
            ]}
            value="dali"
            onChange={() => {}}
          />

    }>

  </PageBlock>

```

Full-blown example

```js
const Tab = require('../Tabs/Tab').default
const Tabs = require('../Tabs').default
const Dropdown = require('../Dropdown').default
const Input = require('../Input').default
const Button = require('../Button').default
const Table = require('../Table').default

;

  <PageBlock variation="full">

 Fill this form

        <Input label="Lorem ipsum" />

        <Input label="Lorem ipsum" />

        <Input label="Lorem ipsum" />

      <Button variation="primary">button</Button>

  </PageBlock>
  <PageBlock variation="half">

        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus et
        neque sit amet urna rhoncus ultrices. Praesent feugiat congue ligula, ut
        eleifend ligula laoreet vel.

      <Button variation="tertiary" collapseLeft>
        button
      </Button>
      <Button variation="tertiary">button</Button>

        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus et
        neque sit amet urna rhoncus ultrices. Praesent feugiat congue ligula, ut
        eleifend ligula laoreet vel.

      <Button variation="tertiary" collapseLeft>
        button
      </Button>
      <Button variation="tertiary">button</Button>

  </PageBlock>

  <PageBlock
    variation="full"
    title="Section"
    subtitle="Some explanation for your section. It can be used as a helper text to guide user troughout the interface. ">

 List of people

 {},
                placeholder: 'Search stuff...',
                onChange: () => {},
                onClear: () => {},
                onSubmit: () => {},
              },
              newLine: {
                label: 'New',
                handleCallback: () => {},
              },
            }}
            pagination={{
              onNextClick: () => {},
              onPrevClick: () => {},
              currentItemFrom: 1,
              currentItemTo: 7,
              onRowsChange: () => {},
              textShowRows: 'Show rows',
              textOf: 'of',
              totalItems: 7,
              rowsOptions: [7],
            }}
            items={[
              {
                email: 'olen.stamm21@yahoo.com',
                name: 'Patrick Rothfuss',
              },
              {
                email: 'junius0@gmail.com',
                name: 'Hurricane Skywalker IV',
              },
              {
                email: 'judd_gulgowski22@yahoo.com',
                name: 'Tom Braddy',
              },
              {
                email: 'catharine.leuschke62@hotmail.com',
                name: 'Momochi Zabuza',
              },
              {
                email: 'candido_ryan@hotmail.com',
                name: 'Freddie Mercury',
              },
              {
                email: 'freda_ritchie26@yahoo.com',
                name: 'Dr. Lempi Mosciski',
              },
              {
                email: 'elissa28@gmail.com',
                name: 'Nikita Feeney',
              },
            ]}
          />

  </PageBlock>
  <PageBlock variation="aside">

      <Tabs>
        <Tab label="A tab" active onClick={() => {}} />
        <Tab label="Another tab" onClick={() => {}} />
      </Tabs>

        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce elementum
        tellus in mauris aliquet, at vestibulum elit scelerisque. Fusce dapibus
        consectetur scelerisque. Nam vitae orci id metus aliquam efficitur at
        non tortor. Etiam nec tincidunt mi, sit amet ultrices ipsum. Sed mattis
        eu dui at ultrices. Pellentesque sit amet neque in nibh malesuada
        elementum. Integer condimentum, enim non vehicula ultricies, dui eros
        lobortis nunc, id convallis orci est vel nunc. Aenean iaculis vehicula
        turpis sed consequat.

        Praesent consequat ligula at auctor lobortis. Ut facilisis, odio ut
        consequat consectetur, enim nisi hendrerit lectus, nec consectetur magna
        ante id arcu. Proin id dolor id enim viverra ornare et ac mauris.
        Integer efficitur egestas magna vel iaculis. Sed faucibus congue nisi,
        cursus porta erat malesuada in. Vestibulum id sollicitudin libero.
        Vivamus eu aliquet ipsum. Proin in rutrum eros.

 Summary

 Lorem ipsum

 Lorem ipsum

 Lorem ipsum

 Lorem ipsum

 Lorem ipsum

  </PageBlock>

```

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/Tag/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import config from 'vtex-tachyons/config.json'

import Close from '../icon/Close'

class Tag extends PureComponent {
  constructor() {
    super()

    this.state = {
      hover: false,
    }
  }

  handleMouseEnter = () => {
    this.setState({ hover: true })
  }

  handleMouseLeave = () => {
    this.setState({ hover: false })
  }

  render() {
    const {
      bgColor,
      children,
      color,
      disabled,
      onClick,
      size,
      type,
      variation,
    } = this.props

    let sizeClasses = ''
    switch (size) {
      case 'small':
        sizeClasses = 't-mini pv2 ph4'
        break
      case 'regular':
        sizeClasses = 't-small pv2 ph4'
        break
      case 'large':
        sizeClasses = 't-body pv3 ph5'
        break
      default:
        sizeClasses = 't-small pv2 ph4'
        break
    }

    const baseClasses = `br-pill dib fw5 ${sizeClasses}`

    let theme = ''
    const variationIsLow = variation === 'low'

    switch (type) {
      case 'success':
        theme = variationIsLow
          ? 'bg-transparent ba c-success '
          : 'bg-success c-on-success '
        break
      case 'error':
        theme = variationIsLow
          ? 'bg-transparent ba c-danger '
          : 'bg-danger c-on-danger '
        break
      case 'warning':
        theme = variationIsLow
          ? 'bg-transparent ba c-warning '
          : 'bg-warning c-on-warning '
        break
      default:
        theme = variationIsLow
          ? 'bg-transparent ba c-muted-2 '
          : 'bg-muted-2 c-on-muted-2 '
        break
    }

    const btnClasses = disabled ? 'bg-muted-4 c-muted-2 ' : 'pointer '

    let hoverClass = ''
    if (!disabled) {
      hoverClass = this.state.hover && 'o-80'
    }

    return onClick ? (
      <button
        className={`${baseClasses} bn ${btnClasses} ${theme} ${hoverClass}`}
        style={{
          backgroundColor: bgColor,
          color: disabled ? config.semanticColors.text.disabled : color,
        }}
        disabled={disabled}
        onClick={onClick}
        onMouseEnter={this.handleMouseEnter}
        onMouseLeave={this.handleMouseLeave}>
        <div className="flex items-stretch">
          <span>{children}</span>
          <div className="ml2 flex items-center">
            <Close color={color} size={12} />
          </div>
        </div>
      </button>
    ) : (
      <div
        className={`${baseClasses} ${theme}`}
        style={{
          backgroundColor: bgColor,
          color: color,
        }}>
        {children}
      </div>
    )
  }
}

Tag.defaultProps = {
  disabled: false,
  size: 'regular',
}

Tag.propTypes = {
  bgColor: PropTypes.string,
  children: PropTypes.node.isRequired,
  color: PropTypes.string,
  disabled: PropTypes.bool,
  onClick: PropTypes.func,
  /** Input size */
  size: PropTypes.oneOf(['small', 'regular', 'large']),
  type: PropTypes.oneOf(['success', 'error', 'warning']),
  variation: PropTypes.oneOf(['default', 'low']),
}

export default Tag

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/Tag/README.md from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```markdown
#### A Tag represents a status or a tag.

### 👍 Dos

- Do use semantic colors that have a clear and contextualized meaning in your application, such as _green_ for success and _red_ for errors. However, if your application employs a richer palette with unambiguous usage of colors don't hesitate to use your Tags with them.
- Do play with contrast to convey the visual prominence more suited to your application. Use high contrast for high prominence, and low contrast for low prominence.

### 👎 Don'ts

- Don't do make the Tag other than the optional close button, otherwise they should always be read-only.
- Don't use color contrast combinations that don't pass Accessibility tests.

** @todo: update examples to use VTEX Tachyons colors without hardcoding them **

Default

```js
<Tag>Neutral</Tag>
```

Sizes

```js

  <Tag size="small">
    Small
  </Tag>

  <Tag size="regular">
    Regular
  </Tag>

  <Tag size="large">
    Large
  </Tag>

```

Types

```js

  <Tag type="error">
    Error
  </Tag>

  <Tag type="warning">
    Warning
  </Tag>

  <Tag type="success">
    Success
  </Tag>

```

Low prominence

```js

    <Tag type="error" variation="low">
      Error
    </Tag>

    <Tag type="warning" variation="low">
      Warning
    </Tag>

    <Tag type="success" variation="low">
      Success
    </Tag>

    <Tag variation="low">Neutral</Tag>

```

Custom colors

```js

  <Tag bgColor="#F71963">
    Label
  </Tag>

  <Tag bgColor="#134CD8" color="#ffffff">
    Label
  </Tag>

  <Tag bgColor="#FFF6E0" color="#0C389F">
    Label
  </Tag>

```

With remove

```js

  <Tag onClick={() => console.log('callback')}>Default</Tag>

  <Tag  disabled onClick={() => console.log('callback')}>Disabled</Tag>

  <Tag onClick={() => console.log('callback')} bgColor="#134CD8" color="#fff">With color</Tag>

  <Tag onClick={() => console.log('callback')} type="error">
    Error
  </Tag>

  <Tag onClick={() => console.log('callback')} type="warning">
    Warning
  </Tag>

  <Tag onClick={() => console.log('callback')} type="success">
    Success
  </Tag>

```

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/NumericStepper/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
import PropTypes from 'prop-types'

import styles from '../Input/Input.css'

const normalizeMin = min => (min == null ? -Infinity : min)
const normalizeMax = max => (max == null ? Infinity : max)

const validateValue = (value, min, max, defaultValue) => {
  // This function always return a valid numeric value from the current input.
  // Compare with the function validateDisplayValue
  min = normalizeMin(min)
  max = normalizeMax(max)

  if (isNaN(value) || value == null) {
    if (defaultValue < min) return min
    if (defaultValue > max) return max
    return defaultValue
  } else if (value < min) {
    return min
  } else if (value > max) {
    return max
  }
  return parseInt(value, 10)
}

const validateDisplayValue = (value, min, max) => {
  // This function validates the input as the user types
  // It allows for temporarily invalid values (namely, empty string and minus sign without a number following it)
  // However, it prevents values out of boundaries, and invalid characters, e.g. letters

  min = normalizeMin(min)
  max = normalizeMax(max)

  const parsedValue = parseInt(value, 10)

  if (value === '') {
    return value
  }
  // Only allows typing the negative sign if negative values are allowed
  if (value === '-' && min < 0) {
    return value
  }
  if (isNaN(parsedValue)) {
    return ''
  }
  // Only limit by lower bounds if the min value is 1
  // Otherwise, it could prevent typing, for example, 10 if the min value is 2
  if (parsedValue < min && min === 1) {
    return min
  }
  if (parsedValue > max) {
    return max
  }
  return parsedValue
}

class NumericStepper extends Component {
  state = {
    inputFocused: false,
    // used for comparison whether to trigger onChange or not
    value: 0,
    // used for temporarily invalid values during typing--specifically, when it's empty
    displayValue: 0,
  }

  componentDidMount() {
    if (this.props.size === 'x-large') {
      console.warn(
        'NumericStepper: The value "x-large" for the prop "size" is deprecated. In the next major version, it will be equivalent to "large", and removed altogether in future versions'
      )
    }
  }

  static getDerivedStateFromProps(props, state) {
    const { value, minValue, maxValue, defaultValue } = props

    const validatedValue = validateValue(
      value,
      minValue,
      maxValue,
      defaultValue
    )

    return {
      value: validatedValue,
      ...(!state.inputFocused && {
        displayValue: validateDisplayValue(value, minValue, maxValue),
      }),
    }
  }

  changeValue = (value, event) => {
    const parsedValue = parseInt(value, 10)

    const { minValue, maxValue, defaultValue, onChange } = this.props

    const validatedValue = validateValue(
      parsedValue,
      minValue,
      maxValue,
      defaultValue
    )

    const displayValue = validateDisplayValue(value, minValue, maxValue)

    this.setState({
      value: validatedValue,
      displayValue,
    })

    if (this.state.value !== validatedValue && onChange) {
      // React synthetic events are reused for performance reasons.
      // New properties added to it are never released.
      // Calling event.persist() releases the event from the pool
      // https://reactjs.org/docs/events.html#event-pooling
      event.persist()
      event.value = validatedValue
      onChange(event)
    }
  }

  handleTypeQuantity = event => {
    this.changeValue(event.target.value, event)
  }

  handleIncreaseValue = event => {
    this.changeValue(this.state.value + 1, event)
  }

  handleDecreaseValue = event => {
    this.changeValue(this.state.value - 1, event)
  }

  handleFocusInput = e => {
    e.target.select()
    this.setState({ inputFocused: true })
  }

  handleBlurInput = () => {
    this.setState({
      displayValue: this.state.value,
      inputFocused: false,
    })
  }

  render() {
    const { value, displayValue } = this.state
    const {
      maxValue,
      minValue,
      size,
      block,
      label,
      lean,
      readOnly,
    } = this.props

    const isMin = value <= normalizeMin(minValue)
    const isMax = value >= normalizeMax(maxValue)

    let labelClasses = ''
    let buttonClasses = ''
    let inputClasses = ''

    switch (size) {
      case 'small': {
        buttonClasses += `h-small ${lean ? 'f4' : 'f6'} `
        const inputWidth = lean ? 'w1' : 'w3'
        inputClasses += `h-small t-small ${block ? 'flex-grow-1' : inputWidth} `
        labelClasses += 't-small '
        break
      }
      case 'large': {
        buttonClasses += `h-large ${lean ? 'f3' : 'f5'} `
        const inputWidth = lean ? 'w2' : 'w3'
        inputClasses += `h-large t-body ${block ? 'flex-grow-1' : inputWidth} `
        labelClasses += 't-body '
        break
      }
      case 'x-large': {
        // DEPRECATED
        buttonClasses += `pv5 ${lean ? 'f2' : 'f4'} `
        const inputWidth = lean ? 'w3' : 'w4'
        inputClasses += `pv5 t-body ${block ? 'flex-grow-1' : inputWidth} `
        labelClasses += 't-body '
        break
      }
      default: {
        buttonClasses += `h-regular ${lean ? 'f4' : 'f6'} `
        const inputWidth = lean ? 'w2' : 'w3'
        inputClasses += `h-regular t-body ${
          block ? 'flex-grow-1' : inputWidth
        } `
        labelClasses += 't-small '
        break
      }
    }

    if (lean) inputClasses += 'br2 hover-b--muted-4 ba outline-transparent'

    const borderClasses = lean ? 'b--transparent ' : 'ba b--muted-4 bw1 '

    const buttonDisabledClasses = lean
      ? 'c-disabled bg-transparent '
      : 'bg-muted-5 c-disabled o-100 '

    const buttonEnabledClasses = `pointer bg-base c-action-primary ${
      lean ? 'outline-0' : ''
    } `

    const content = (
      <React.Fragment>
        {label && (
          <span
            className={`numeric-stepper__label db mb3 w-100 c-on-base ${labelClasses}`}>
            {label}
          </span>
        )}
        <div className="numeric-stepper-container flex self-start">
          <input
            type="tel"
            readOnly={readOnly}
            className={`numeric-stepper__input z-1 order-1 tc bw1 ${borderClasses} br0 ${inputClasses} ${styles.hideDecorators}`}
            style={{
              ...(block && {
                width: 0,
              }),
              WebkitAppearance: 'none',
            }}
            value={displayValue}
            onChange={this.handleTypeQuantity}
            onFocus={this.handleFocusInput}
            onBlur={this.handleBlurInput}
          />
          <div className="numeric-stepper__plus-button-container z-2 order-2 flex-none">
            <button
              type="button"
              className={`numeric-stepper__plus-button br2 pa0 bl-0 flex items-center justify-center ${borderClasses} ${buttonClasses} ${
                readOnly || isMax ? buttonDisabledClasses : buttonEnabledClasses
              }`}
              style={{
                borderTopLeftRadius: 0,
                borderBottomLeftRadius: 0,
                width: lean ? '2em' : '3em',
                transition: 'opacity 150ms',
              }}
              disabled={readOnly || isMax}
              aria-label="+"
              tabIndex={0}
              onClick={this.handleIncreaseValue}>
              <div className="numeric-stepper__plus-button__text b">
                {/* fullwidth plus sign (U+FF0B) http://graphemica.com/%EF%BC%8B */}
                ＋
              </div>
            </button>
          </div>
          <div className="numeric-stepper__minus-button-container z-2 order-0 flex-none">
            <button
              type="button"
              className={`numeric-stepper__minus-button br2 pa0 br-0 flex items-center justify-center ${borderClasses} ${buttonClasses} ${
                readOnly || isMin ? buttonDisabledClasses : buttonEnabledClasses
              }`}
              style={{
                borderTopRightRadius: 0,
                borderBottomRightRadius: 0,
                width: lean ? '2em' : '3em',
                transition: 'opacity 150ms',
              }}
              disabled={readOnly || isMin}
              aria-label="−"
              // This is a minus sign (U+2212), not a regular hyphen (-, U+002D),
              // which is the default keyboard character.
              // Used for screen readers.
              tabIndex={0}
              onClick={this.handleDecreaseValue}>
              <span className="numeric-stepper__minus-button__text b">
                {/* fullwidth hyphen-minus (U+FF0D) http://graphemica.com/%EF%BC%8D */}
                －
              </span>
            </button>
          </div>
        </div>
      </React.Fragment>
    )

    // Refrain from using label tag if not needed, to prevent
    // iOS from focusing on the text field and popping up the
    // keyboard when increment/decrement is pressed
    if (label && !lean) {
      return <label className="numeric-stepper-wrapper">{content}</label>
    }
    return <div className="numeric-stepper-wrapper">{content}</div>
  }
}

NumericStepper.propTypes = {
  /** Value of the input */
  value: PropTypes.number,
  /** onChange event handler */
  onChange: PropTypes.func.isRequired,
  /** Minimum value (will be the default value in case of invalid input, e.g. letters).
    Set to null or -Infinity in case there is no miminum. Default is 0.
  */
  minValue: PropTypes.number,
  /** Maximum value (null or Infinity in case there is no maximum. Default is Infinity) */
  maxValue: PropTypes.number,
  /** Default value in case of invalid input (e.g. letters) and there is no minimum value */
  defaultValue: PropTypes.number,
  /** Makes input readonly and disables buttons */
  readOnly: PropTypes.bool,
  /** Input size */
  size: PropTypes.oneOf(['small', 'regular', 'large']),
  /** Block or default size. */
  block: PropTypes.bool,
  /** Input label */
  label: PropTypes.string,
  /** Lean mode, with subtler styling */
  lean: PropTypes.bool,
}

NumericStepper.defaultProps = {
  minValue: 0,
  maxValue: Infinity,
  defaultValue: 0,
  readOnly: false,
  size: 'regular',
  block: false,
}

export default NumericStepper

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/NumericStepper/README.md from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```markdown
#### This is a special case of a regular numerical input where you expect the user to modify it by a few incremental steps.

### 👍 Dos

- Do use a default value. If you don't know which do use, use 1. This way the user is not forced to type something and then click the buttons.
- The lean mode is intended to be used on lists, that: have a high density of content, and where being able to type the number is not important. It is especially useful in mobile contexts or cramped containers.

### 👎 Don'ts

- Don't use for big numbers. Steppers make more sense for small numbers that might be adjusted with a few clicks.
- Don't stretch it too much, the buttons in the extremes and the centered label will look really weird.

### Related components

In doubt, use a regular <a href="#/Components/Forms/Input">Input</a>.

Sizes

```js
initialState = {
  value1: 0,
  value2: 0,
  value3: 0,
}

;<React.Fragment>

    <NumericStepper
      label="Small"
      size="small"
      value={state.value1}
      onChange={event => setState({ value1: event.value })}
    />

    <NumericStepper
      label="Regular"
      value={state.value2}
      onChange={event => setState({ value2: event.value })}
    />

    <NumericStepper
      label="Large"
      size="large"
      value={state.value3}
      onChange={event => setState({ value3: event.value })}
    />

</React.Fragment>
```

Minimum and maximum values

```js
initialState = {
  value1: 1,
  value2: 2,
  value3: 0,
}

;<React.Fragment>

    <NumericStepper
      label="Minimum value = 1"
      minValue={1}
      value={state.value1}
      onChange={event => setState({ value1: event.value })}
    />

    <NumericStepper
      label="Minimum 2, maximum 6"
      minValue={2}
      maxValue={6}
      value={state.value2}
      onChange={event => setState({ value2: event.value })}
    />

</React.Fragment>
```

Styling

```js
initialState = {
  value1: 1,
  value2: 1,
  value3: 1,
  value4: 1,
}

;<React.Fragment>

    <NumericStepper
      label="Default"
      minValue={1}
      value={state.value1}
      onChange={event => setState({ value1: event.value })}
    />

    <NumericStepper
      label="Lean mode"
      minValue={1}
      lean
      value={state.value2}
      onChange={event => setState({ value2: event.value })}
    />

    <NumericStepper
      label="Read Only"
      minValue={1}
      readOnly
      value={state.value3}
      onChange={event => setState({ value3: event.value })}
    />

    <NumericStepper
      label="lean Read Only"
      minValue={1}
      lean
      readOnly
      value={state.value4}
      onChange={event => setState({ value4: event.value })}
    />

</React.Fragment>
```

Box types

Used to make the component fit the parent container's width, either larger or smaller than the default size.

```js
<React.Fragment>

    <NumericStepper
      label="Default"
      size="small"
      value={1}
      onChange={() => {}}
    />

    <NumericStepper size="large" value={1} onChange={() => {}} />

    <NumericStepper
      label="Block"
      size="small"
      value={1}
      block
      onChange={() => {}}
    />

    <NumericStepper size="large" value={1} block onChange={() => {}} />

</React.Fragment>
```

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/Totalizer/TotalizerIcon.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'

const CIRCLE_DIAMETER = '2.4rem'

class TotalizerIcon extends PureComponent {
  render() {
    const {
      item: { icon, iconBackgroundColor },
    } = this.props

    return (
      <div className="flex items-center mr3">
        <div
          className="flex items-center justify-center br-100 pa3 bg-light-silver c-muted-1"
          style={{
            height: CIRCLE_DIAMETER,
            width: CIRCLE_DIAMETER,
            backgroundColor: iconBackgroundColor,
          }}>
          {icon}
        </div>
      </div>
    )
  }
}

TotalizerIcon.propTypes = {
  item: PropTypes.shape({
    icon: PropTypes.node.isRequired,
    iconBackgroundColor: PropTypes.string,
  }).isRequired,
}

export default TotalizerIcon

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/Totalizer/index.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

import TotalizerLabel from './TotalizerLabel'
import TotalizerValue from './TotalizerValue'
import TotalizerIcon from './TotalizerIcon'

const BORDER_COLOR = 'b--muted-4'

class Totalizer extends PureComponent {
  render() {
    const { items, mobileScroll, horizontalLayout } = this.props

    const OUTER_CONTAINER_CLASSES = classNames({
      'overflow-x-hidden': mobileScroll,
    })

    const INNER_CONTAINER_CLASSES = classNames(
      `w-100 flex flex-row-ns ba br3 ${BORDER_COLOR}`,
      {
        'flex-row overflow-y-hidden overflow-x-scroll overflow-x-hidden-ns': mobileScroll,
        'flex-column': !mobileScroll,
      }
    )

    const TOTALIZER_BASE_CLASSES = classNames(
      `flex flex-column pa4 ${BORDER_COLOR}`,
      {
        'flex-auto-ns': mobileScroll,
        'flex-auto': !mobileScroll,
      }
    )

    const invertedMargin = 2

    if (items.length === 0) {
      return null
    }

    return (
      <div className={OUTER_CONTAINER_CLASSES}>
        <div className={INNER_CONTAINER_CLASSES}>
          {items.map((item, i) => {
            const IS_NOT_LAST = items.length > 1 && i < items.length - 1

            const EXTRA_BORDER = classNames({
              'bb bb-0-ns br-ns ': IS_NOT_LAST,
              'bb-0 ': mobileScroll,
              br: mobileScroll && IS_NOT_LAST,
            })

            const ITEM_CLASSES = classNames(
              `${TOTALIZER_BASE_CLASSES} ${EXTRA_BORDER}`
            )

            const WITH_ICON_CONTAINER_CLASSES = classNames(
              'item_container flex flex-row',
              {
                'items-center': horizontalLayout,
              }
            )

            const WITH_ICON_ITEM_CONTAINER_CLASSES = classNames(
              'item_container',
              {
                'flex flex-row items-baseline': horizontalLayout,
              }
            )

            const WITHOUT_ICON_ITEM_CONTAINER_CLASSES = classNames(
              'item_container',
              {
                'flex flex-row items-baseline': horizontalLayout,
              }
            )

            return (
              <div className={ITEM_CLASSES} key={item.label}>
                {item.icon ? (
                  <div className={WITH_ICON_CONTAINER_CLASSES}>
                    <TotalizerIcon item={item} />

                    <div className={WITH_ICON_ITEM_CONTAINER_CLASSES}>
                      {item.inverted && (
                        <div className={horizontalLayout ? 'mr3' : ''}>
                          <TotalizerValue
                            item={item}
                            mobileScroll={mobileScroll}
                          />
                        </div>
                      )}
                      <TotalizerLabel
                        label={item.label}
                        mobileScroll={mobileScroll}
                      />
                      {!item.inverted && (
                        <TotalizerValue
                          item={item}
                          mobileScroll={mobileScroll}
                        />
                      )}
                    </div>
                  </div>
                ) : (
                  <div className={WITHOUT_ICON_ITEM_CONTAINER_CLASSES}>
                    {item.inverted && (
                      <div
                        className={`m${
                          horizontalLayout ? 'r' : 'b'
                        }${invertedMargin}`}>
                        <TotalizerValue
                          item={item}
                          mobileScroll={mobileScroll}
                        />
                      </div>
                    )}
                    <TotalizerLabel
                      label={item.label}
                      mobileScroll={mobileScroll}
                    />
                    {!item.inverted && (
                      <div className={`mt${invertedMargin}`}>
                        <TotalizerValue
                          item={item}
                          mobileScroll={mobileScroll}
                        />
                      </div>
                    )}
                  </div>
                )}
              </div>
            )
          })}
        </div>
      </div>
    )
  }
}

Totalizer.defaultProps = {
  value: null,
  iconBackgroundColor: '',
  icon: null,
  isLoading: false,
  mobileScroll: false,
  horizontalLayout: false,
}

export const totalizerPropTypes = {
  /** Sets the items horizontally instead of vertically on mobile. */
  mobileScroll: PropTypes.bool,
  horizontalLayout: PropTypes.bool,
  items: PropTypes.arrayOf(
    PropTypes.shape({
      label: PropTypes.string.isRequired,
      value: PropTypes.node,
      iconBackgroundColor: PropTypes.string,
      icon: PropTypes.node,
      isLoading: PropTypes.bool,
      invertedWeight: PropTypes.bool,
    })
  ).isRequired,
}

Totalizer.propTypes = totalizerPropTypes

export default Totalizer

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/Styleguide/components/Totalizer/README.md from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```markdown
#### Totalizers are used to consolidate important numeric data. It usually displays numbers that the user what to access fastly.

### 👍 Dos

- Use icons to support the user scanning the information
- Create clear clues with titles
- Display the most important, and needed, numbers

### 👎 Don'ts

- Never replace the number with text
- Avoid using seccondary information
- Avoid using on the bottom of the page

### Related components

- Consider using a <a href="#/Components/Display/Table">Table</a> if the information on totalizer is followed by a list of data.

#### Default

```js
<Totalizer
  items={[
    {
      label: 'Saldo em conta',
      value: 23837,
    },
    {
      label: 'Entradas',
      value: 'R$ 36239,05',
    },

    {
      label: 'Saídas',
      value: '- R$ 13.485,26',
    },
    {
      label: 'Vendas',
      value: 23837,
      isLoading: true,
    },
  ]}
/>
```

#### Horizontal layout

```js
const ShoppingCart = require('../icon/ShoppingCart').default
;<Totalizer
  horizontalLayout
  items={[
    {
      label: 'Orders',
      value: '566',
      inverted: true,
    },
    {
      label: 'Average Ticket',
      value: 'R$55.47',
      inverted: true,
    },
    {
      label: 'Gross',
      value: 'R$554.70',
      inverted: true,
    },
  ]}
/>
```

#### Horizontal layout with icons

```js
const ArrowDown = require('../icon/ArrowDown').default
const ArrowUp = require('../icon/ArrowUp').default
const ShoppingCart = require('../icon/ShoppingCart').default
;<Totalizer
  horizontalLayout
  items={[
    {
      label: 'Orders',
      value: '566',
      inverted: true,
      iconBackgroundColor: '#eafce3',
      icon: <ArrowUp color="#79B03A" size={14} />,
    },
    {
      label: 'Average Ticket',
      value: 'R$55.47',
      inverted: true,
      iconBackgroundColor: '#cce8ff',
      icon: <ShoppingCart color="#368df7" size={14} />,
    },
    {
      label: 'Gross',
      value: 'R$554.70',
      inverted: true,
      iconBackgroundColor: '#fda4a4',
      icon: <ArrowDown color="#dd1616" size={14} />,
    },
  ]}
/>
```

#### With inverted first item

```js
<Totalizer
  items={[
    {
      inverted: true,
      value: 'Total Orders',
      label: 'Until 10h10',
    },
    {
      label: 'Today',
      value: 12364,
    },
    {
      label: 'Yesterday',
      value: 11980,
    },

    {
      label: 'Last Week',
      value: 10776,
    },
    {
      label: 'Last Year',
      value: 9802,
    },
  ]}
/>
```

#### With Icons

```js
const ArrowDown = require('../icon/ArrowDown').default
const ArrowUp = require('../icon/ArrowUp').default
const ShoppingCart = require('../icon/ShoppingCart').default
;<Totalizer
  items={[
    {
      label: 'Pedidos',
      value: 23837,
      iconBackgroundColor: '#cce8ff',
      icon: <ShoppingCart color="#368df7" size={14} />,
    },
    {
      label: 'Entradas',
      value: 'R$ 36239,05',
      iconBackgroundColor: '#eafce3',
      icon: <ArrowUp color="#79B03A" size={14} />,
    },

    {
      label: 'Saídas',
      value: '- R$ 13.485,26',
      iconBackgroundColor: '#fda4a4',
      icon: <ArrowDown color="#dd1616" size={14} />,
    },
  ]}
/>
```

#### Mobile scroll

```js
<Totalizer
  mobileScroll
  items={[
    {
      label: 'Saldo em conta',
      value: 23837,
    },
    {
      label: 'Entradas',
      value: 'R$ 36239,05',
    },

    {
      label: 'Saídas',
      value: '- R$ 13.485,26',
    },
    {
      label: 'Vendas',
      value: 23837,
      isLoading: true,
    },
  ]}
/>
```

#### Icons & mobile scroll

```js
const ArrowDown = require('../icon/ArrowDown').default
const ArrowUp = require('../icon/ArrowUp').default
const ShoppingCart = require('../icon/ShoppingCart').default
;<Totalizer
  mobileScroll
  items={[
    {
      label: 'Pedidos',
      value: 23837,
      iconBackgroundColor: '#cce8ff',
      icon: <ShoppingCart color="#368df7" size={14} />,
    },
    {
      label: 'Entradas',
      value: 'R$ 36239,05',
      iconBackgroundColor: '#eafce3',
      icon: <ArrowUp color="#79B03A" size={14} />,
    },

    {
      label: 'Saídas',
      value: '- R$ 13.485,26',
      iconBackgroundColor: '#fda4a4',
      icon: <ArrowDown color="#dd1616" size={14} />,
    },
  ]}
/>
```

```