[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/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 itself, like images, tags, links, etc.

Our Table was built to be highly composable and flexible. All parts are optional, and you can compose your table with any other Styleguide components. A Table may be used from a small table with numbers to full CRUD-like functionalities, from a small data display to the main screen of a complex module. All parts are plug'n'play parts that you can turn on and off to match your needs.

### 👍 Dos

- Try to support as many of the Table features as you can in your system - if we designed there it's because it's highly recommended to have.
- Provide as many domain-specific actions as you want in the dropdown slot.
- Line actions: should be mostly for actions that are resolved in the same screen, or if it's was identified to be a very recurrent action.

Simple example

```js
const sampleData = require('./sampleData').default
const itemsCopy = sampleData.items
  .slice()
  .reverse()
  .splice(15)
const defaultSchema = {
  properties: {
    name: {
      title: 'Name',
      width: 300,
    },
    email: {
      title: 'Email',
      minWidth: 350,
    },
    number: {
      title: 'Number',
      // default is 200px
      minWidth: 100,
    },
  },
}

;

 {
        alert(
          `you just clicked ${rowData.name}, number is ${rowData.number} and email ${rowData.email}`
        )
      }}
    />

```

# Table schema

The Schema property is a JSON used to define the table columns and how they should behave visually. The Schema has properties and each one of them defines a column in the table.
Example with simple structure:

```md
{
properties: {
column1: {
title: "First Column"
},
column2: {
title: "Second Column",
width: 350
}
}
}
```

##### title

- Control the title which appears on table Header.
- It receives only strings.
- If you want to customize it with a component, you can use the `headerRenderer` prop.

##### width

- Control the column width.
- It receives only numbers, which are values in pixels.
- Default value is 200px

##### minWidth

- Fix a minimum width to the column.
- It receives only numbers, which are values in pixels.
- Default value is 200px

##### 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: ({ cellData, rowData, updateCellMeasurements })
- Default is render the value as a string.
- If you use `dynamicRowHeights` option in your table, you may need to use `updateCellMeasurements` to update the cell measurement cache (e.g. in an `onLoad` prop for images).
- If you have a custom cell component that has a click interaction and at the same time you use the onRowClick Table prop, you might stumble uppon the problem of both click actions being fired. We can work around that by doing a wrapper around cellRenderer to stop click event propagation, like so:

##### headerRight

- Use this boolean property to align right the text of a header. Useful for monetary values.
- Usage: `headerRight: true`.
- You will have to use the `cellRenderer` proerty to also align right the content of the column.

```jsx noeditor static
{
  properties: {
    column1: {
      cellRenderer: ({ cellData, rowData }) => {
        return (

 {
              e.stopPropagation()
              // the click event propagation start on the checkbox click below, and propagates up the DOM tree.
              // this wrapper is going to catch the event right after it fires and stop it's propagation.
              // stoping the click event from propagating until the row component node,
              // so the onRowClick will not be fired.
              // you can learm more about DOM event propagation here: http://tiny.cc/c1625y
            }}>
            <Checkbox
              checked={this.state.check}
              id="select-option"
              name="select-option"
              onChange={() => this.setState({ check: !this.state.check })}
            />

        )
      }
    }
  }
}
```

Example customizing color column cell, with clickable badges

```js
const sampleData = require('./sampleData').default
const itemsCopy = sampleData.items
  .slice()
  .reverse()
  .splice(20)
const Tag = require('../Tag').default
class CustomTableExample extends React.Component {
  constructor() {
    super()
    this.state = {
      orderedItems: itemsCopy,
    }
  }

  render() {
    const customSchema = {
      properties: {
        name: {
          title: 'Name',
          width: 300,
        },
        email: {
          title: 'Email',
          width: 350,
        },
        color: {
          title: 'Color',
          // you can customize cell component render (also header component with headerRenderer)
          cellRenderer: ({ cellData }) => {
            return (
              <Tag
                bgColor={cellData.color}
                color="#fff"
                onClick={e => {
                  // if you use cellRender click event AND onRowclick event
                  // you should stop the event propagation so the cell click fires and row click don't
                  e.stopPropagation()
                  alert(
                    `you just clicked a cell to remove ${cellData.label}, HEX: ${cellData.color}`
                  )
                }}>
                {cellData.label}
              </Tag>
            )
          },
        },
      },
    }

    return (

 {
              alert(`you just clicked the row with ${rowData.name}`)
            }}
          />

    )
  }
}
;<CustomTableExample />
```

##### sortable

- Sinalize that a column is sortable, so the header will be clickable.
- This prop receives a boolean.
- On sortable header's click the Table `onSort` callback will be fired.

Example sortable by Name

```js
const sampleData = require('./sampleData').default
const itemsCopy = sampleData.items
  .slice()
  .reverse()
  .splice(20)
const Tag = require('../Tag').default
class CustomTableExample extends React.Component {
  constructor() {
    super()
    this.state = {
      orderedItems: itemsCopy,
      dataSort: {
        sortedBy: null,
        sortOrder: null,
      },
    }

    this.sortNameAlphapeticallyASC = this.sortNameAlphapeticallyASC.bind(this)
    this.sortNameAlphapeticallyDESC = this.sortNameAlphapeticallyDESC.bind(this)
    this.handleSort = this.handleSort.bind(this)
  }

  sortNameAlphapeticallyASC(a, b) {
    return a.name  b.name ? 1 : 0
  }
  sortNameAlphapeticallyDESC(a, b) {
    return a.name  b.name ? -1 : 0
  }

  handleSort({ sortOrder, sortedBy }) {
    // I'll just handle sort by 'name', but I could handle multiple properties
    if (sortedBy === 'name') {
      const orderedItems =
        sortOrder === 'ASC'
          ? itemsCopy.slice().sort(this.sortNameAlphapeticallyASC)
          : itemsCopy.slice().sort(this.sortNameAlphapeticallyDESC)
      // the above const could come out of an API call to sort items for example
      this.setState({
        orderedItems,
        dataSort: {
          sortedBy,
          sortOrder,
        },
      })
    }
  }

  render() {
    const customSchema = {
      properties: {
        name: {
          title: 'Name',
          width: 300,
          // sortable boolean in a schema property makes it sortable,
          // (clicking header triggers onSort callback).
          sortable: true,
        },
        email: {
          title: 'Email',
          width: 300,
        },
        number: {
          title: 'Value',
          headerRight: true,
          cellRenderer: data => (

$ {data.cellData}

          ),
        },
      },
    }

    return (

    )
  }
}
;<CustomTableExample />
```

##### headerRenderer

- Customized the render method of a single header cell.
- It receives a function that returns a node (react component).
- The function has the following params: ({ columnIndex, key, title })
- This prop will not work if the `sortable` prop for the same header is active.

example customizing number column header to use intl FormattedMessage

```js
const sampleData = require('./sampleData').default
const itemsCopy = sampleData.items
  .slice()
  .reverse()
  .splice(20)
const Tag = require('../Tag').default
class FormattedMessage extends React.Component {
  render() {
    const renderTextByIntlId = id => {
      switch (id) {
        case 'some.intl.message.id':
          return 'Number'
          break
        default:
          return 'Deafult Header title'
          break
      }
    }
    return {renderTextByIntlId(this.props.id)}
  }
}

class CustomTableExample extends React.Component {
  constructor() {
    super()
    this.state = {
      orderedItems: itemsCopy,
    }
  }

  render() {
    const customSchema = {
      properties: {
        name: {
          title: 'Name',
          width: 250,
        },
        email: {
          title: 'Email',
          width: 300,
        },
        number: {
          title: 'some.intl.message.id',
          headerRenderer: ({ title }) => {
            return <FormattedMessage id={title} />
          },
        },
      },
    }

    return (

    )
  }
}
;<CustomTableExample />
```

# Features

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

##### Custom empty state

Empty states can also be customized, the passed children will be rendered inside an EmptyState component.
It's worth to customize empty state using this prop so the other table features will behave accordingly (e.g. the topbar, pagination and totalizers).

```js
const sampleData = require('./sampleData').default
const Button = require('../Button').default
const itemsCopy = sampleData.items
  .slice()
  .reverse()
  .splice(15)
const defaultSchema = {
  properties: {
    name: {
      title: 'Name',
      width: 300,
    },
    email: {
      title: 'Email',
      minWidth: 350,
    },
    number: {
      title: 'Number',
      // default is 200px
      minWidth: 100,
    },
  },
}

;

    <Table
      fullWidth
      schema={defaultSchema}
      items={[]}
      emptyStateLabel="This is my custom empty state title"
      emptyStateChildren={
        <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>
      }
      onRowClick={({ rowData }) => {
        alert(
          `you just clicked ${rowData.name}, number is ${rowData.number} and email ${rowData.email}`
        )
      }}
    />

```

##### Pagination

This feature uses the pagination component in the bottom, after the table content.

```js
const ArrowDown = require('../icon/ArrowDown').default
const ArrowUp = require('../icon/ArrowUp').default
const sampleData = require('./sampleData').default
const Tag = require('../Tag').default
const tableLength = 5
const initialState = {
  tableLength,
  currentPage: 1,
  slicedData: sampleData.items.slice(0, tableLength),
  currentItemFrom: 1,
  currentItemTo: tableLength,
  itemsLength: sampleData.items.length,
  emptyStateLabel: 'Nothing to show.',
}
const jsonschema = {
  properties: {
    name: {
      title: 'Name',
    },
    email: {
      title: 'Email',
      width: 300,
    },
    number: {
      title: 'Number',
      width: 150,
    },
    color: {
      title: 'Color',
      cellRenderer: ({ cellData }) => {
        return (
          <Tag bgColor={cellData.color} color="#fff">
            {cellData.label}
          </Tag>
        )
      },
    },
  },
}

class ResourceListExample extends React.Component {
  constructor() {
    super()
    this.state = initialState

    this.handleNextClick = this.handleNextClick.bind(this)
    this.handlePrevClick = this.handlePrevClick.bind(this)
    this.goToPage = this.goToPage.bind(this)
    this.handleRowsChange = this.handleRowsChange.bind(this)
  }

  handleNextClick() {
    const newPage = this.state.currentPage + 1
    const itemFrom = this.state.currentItemTo + 1
    const itemTo = tableLength * newPage
    const data = sampleData.items.slice(itemFrom - 1, itemTo)
    this.goToPage(newPage, itemFrom, itemTo, data)
  }

  handlePrevClick() {
    if (this.state.currentPage === 0) return
    const newPage = this.state.currentPage - 1
    const itemFrom = this.state.currentItemFrom - tableLength
    const itemTo = this.state.currentItemFrom - 1
    const data = sampleData.items.slice(itemFrom - 1, itemTo)
    this.goToPage(newPage, itemFrom, itemTo, data)
  }

  goToPage(currentPage, currentItemFrom, currentItemTo, slicedData) {
    this.setState({
      currentPage,
      currentItemFrom,
      currentItemTo,
      slicedData,
    })
  }

  handleRowsChange(e, value) {
    this.setState(
      {
        tableLength: parseInt(value),
        currentItemTo: parseInt(value),
      },
      () => {
        // this callback garantees new sliced items respect filters and tableLength
        const { filterStatements } = this.state
        this.handleFiltersChange(filterStatements)
      }
    )
  }

  render() {
    return (
      <Table
        schema={jsonschema}
        items={this.state.slicedData}
        emptyStateLabel={this.state.emptyStateLabel}
        pagination={{
          onNextClick: this.handleNextClick,
          onPrevClick: this.handlePrevClick,
          currentItemFrom: this.state.currentItemFrom,
          currentItemTo: this.state.currentItemTo,
          onRowsChange: this.handleRowsChange,
          textShowRows: 'Show rows',
          textOf: 'of',
          totalItems: this.state.itemsLength,
          rowsOptions: [5, 10, 15, 25],
        }}
      />
    )
  }
}
;<ResourceListExample />
```

##### Line actions

This feature creates a last extra column with an ActionMenu component per line.

```js
const sampleData = require('./sampleData').default
const itemsCopy = sampleData.items
  .slice()
  .reverse()
  .splice(15)
const defaultSchema = {
  properties: {
    name: {
      title: 'Name',
    },
    email: {
      title: 'Email',
    },
    number: {
      title: 'Number',
    },
  },
}

const lineActions = [
  {
    label: ({ rowData }) => `Action for ${rowData.name}`,
    onClick: ({ rowData }) => alert(`Executed action for ${rowData.name}`),
  },
  {
    label: ({ rowData }) => `DANGEROUS action for ${rowData.name}`,
    isDangerous: true,
    onClick: ({ rowData }) =>
      alert(`Executed a DANGEROUS action for ${rowData.name}`),
  },
]

;

```

##### Fixed first column

This case is recomended if you have lots of columns, so the most important information could be fixed

```js
const sampleData = require('./sampleData').default
const Tag = require('../Tag').default
const tableLength = 5
const initialState = {
  tableLength,
  currentPage: 1,
  slicedData: sampleData.items.slice(0, tableLength),
  currentItemFrom: 1,
  currentItemTo: tableLength,
  searchValue: '',
  itemsLength: sampleData.items.length,
  emptyStateLabel: 'Nothing to show.',
}

class ResourceListExample extends React.Component {
  constructor() {
    super()
    this.state = initialState
    this.customColorTagProperty = this.customColorTagProperty.bind(this)
  }

  customColorTagProperty(index) {
    return {
      title: `Color${index ? ` ${index}` : ''}`,
      cellRenderer: ({ cellData }) => {
        return (
          <Tag bgColor={cellData.color} color="#fff">
            {cellData.label}
          </Tag>
        )
      },
    }
  }

  render() {
    const customSchema = {
      properties: {
        name: {
          title: 'Name',
        },
        email: {
          title: 'Email',
          width: 300,
        },
        number: {
          title: 'Number',
        },
        color: this.customColorTagProperty(),
        color1: this.customColorTagProperty(1),
        color2: this.customColorTagProperty(2),
        color3: this.customColorTagProperty(3),
        color4: this.customColorTagProperty(4),
        color5: this.customColorTagProperty(5),
        color6: this.customColorTagProperty(6),
      },
    }

    return (

    )
  }
}
;<ResourceListExample />
```

##### Toolbar

The toolbar is a bundle of features, including search input, columns visibility toggler, 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 begining of the page for a better visualization of this structure)

```js
const ArrowDown = require('../icon/ArrowDown').default
const ArrowUp = require('../icon/ArrowUp').default
const sampleData = require('./sampleData').default
const Tag = require('../Tag').default
const tableLength = 5
const initialState = {
  slicedData: sampleData.items.slice(0, tableLength),
  searchValue: '',
  emptyStateLabel: 'Nothing to show.',
}
const jsonschema = {
  properties: {
    name: {
      title: 'Name',
      width: 170,
    },
    email: {
      title: 'Email',
      width: 300,
    },
    number: {
      title: 'Number',
      width: 150,
    },
    color: {
      title: 'Color',
      width: 170,
      cellRenderer: ({ cellData }) => {
        return (
          <Tag bgColor={cellData.color} color="#fff">
            {cellData.label}
          </Tag>
        )
      },
    },
  },
}

class ResourceListExample extends React.Component {
  constructor() {
    super()
    this.state = initialState

    this.handleInputSearchChange = this.handleInputSearchChange.bind(this)
    this.handleInputSearchSubmit = this.handleInputSearchSubmit.bind(this)
    this.handleInputSearchClear = this.handleInputSearchClear.bind(this)
  }

  handleInputSearchChange(e) {
    this.setState({ searchValue: e.target.value })
  }

  handleInputSearchClear(e) {
    this.setState({ ...initialState })
  }

  handleInputSearchSubmit(e) {
    e.preventDefault()

    if (!this.state.searchValue) {
      this.setState({ ...initialState })
    } else {
      this.setState({
        slicedData: [],
        emptyStateLabel: 'No results found.',
      })
    }
  }

  render() {
    return (
      <Table
        schema={jsonschema}
        items={this.state.slicedData}
        toolbar={{
          inputSearch: {
            value: this.state.searchValue,
            placeholder: 'Search stuff...',
            onChange: this.handleInputSearchChange,
            onClear: this.handleInputSearchClear,
            onSubmit: this.handleInputSearchSubmit,
          },
          density: {
            buttonLabel: 'Line density',
            lowOptionLabel: 'Low',
            mediumOptionLabel: 'Medium',
            highOptionLabel: 'High',
          },
          download: {
            label: 'Export',
            handleCallback: () => alert('Callback()'),
          },
          upload: {
            label: 'Import',
            handleCallback: () => alert('Callback()'),
          },
          fields: {
            label: 'Toggle visible fields',
            showAllLabel: 'Show All',
            hideAllLabel: 'Hide All',
          },
          extraActions: {
            label: 'More options',
            actions: [
              {
                label: 'An action',
                handleCallback: () => alert('An action'),
              },
              {
                label: 'Another action',
                handleCallback: () => alert('Another action'),
              },
              {
                label: 'A third action',
                handleCallback: () => alert('A third action'),
              },
            ],
          },
          newLine: {
            label: 'New',
            handleCallback: () => alert('handle new line callback'),
          },
        }}
      />
    )
  }
}
;<ResourceListExample />
```

##### Totalizers

This uses the Totalizer component between the toolbar and the table content

```js
const ArrowDown = require('../icon/ArrowDown').default
const ArrowUp = require('../icon/ArrowUp').default
const sampleData = require('./sampleData').default
const Tag = require('../Tag').default
const tableLength = 5
const initialState = {
  slicedData: sampleData.items.slice(0, tableLength),
  emptyStateLabel: 'Nothing to show.',
}
const jsonschema = {
  properties: {
    name: {
      title: 'Name',
    },
    email: {
      title: 'Email',
      width: 300,
    },
    number: {
      title: 'Number',
      width: 150,
    },
    color: {
      title: 'Color',
      cellRenderer: ({ cellData }) => {
        return (
          <Tag bgColor={cellData.color} color="#fff">
            {cellData.label}
          </Tag>
        )
      },
    },
  },
}

class ResourceListExample extends React.Component {
  constructor() {
    super()
    this.state = initialState
  }

  render() {
    return (
      <Table
        schema={jsonschema}
        items={this.state.slicedData}
        emptyStateLabel={this.state.emptyStateLabel}
        totalizers={[
          {
            label: 'Saldo em conta',
            value: 23837,
          },
          {
            label: 'Entradas',
            value: 'R$ 36239,05',
            iconBackgroundColor: '#eafce3',
            icon: <ArrowUp color="#79B03A" size={14} />,
          },

          {
            label: 'Saídas',
            value: '- R$ 13.485,26',
            icon: <ArrowDown size={14} />,
          },
          {
            label: 'Vendas',
            value: 23837,
            isLoading: true,
          },
        ]}
      />
    )
  }
}
;<ResourceListExample />
```

##### Filters

This feature uses FilterBar component inserting it between the toolbar and table content (just like the Totalizers, it's a separate component).

```js
const ArrowDown = require('../icon/ArrowDown').default
const ArrowUp = require('../icon/ArrowUp').default
const Checkbox = require('../Checkbox').default
const Input = require('../Input').default
const sampleData = require('./sampleData').default
const Tag = require('../Tag').default
const tableLength = 7
const initialState = {
  tableLength,
  slicedData: sampleData.items.slice(0, tableLength),
  emptyStateLabel: 'Nothing to show.',
  filterStatements: [],
}
const jsonschema = {
  properties: {
    name: {
      title: 'Name',
    },
    email: {
      title: 'Email',
      width: 300,
    },
    number: {
      title: 'Number',
      width: 150,
    },
    color: {
      title: 'Color',
      cellRenderer: ({ cellData }) => {
        return (
          <Tag bgColor={cellData.color} color="#fff">
            {cellData.label}
          </Tag>
        )
      },
    },
  },
}

class ResourceListExample extends React.Component {
  constructor() {
    super()
    this.state = initialState

    this.simpleInputObject = this.simpleInputObject.bind(this)
    this.simpleInputVerbsAndLabel = this.simpleInputVerbsAndLabel.bind(this)
    this.numberInputObject = this.numberInputObject.bind(this)
    this.numberInputRangeObject = this.numberInputRangeObject.bind(this)
    this.colorSelectorObject = this.colorSelectorObject.bind(this)
    this.handleFiltersChange = this.handleFiltersChange.bind(this)
  }

  simpleInputObject({ value, onChange }) {
    return (
      <Input value={value || ''} onChange={e => onChange(e.target.value)} />
    )
  }

  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: this.simpleInputObject,
        },
        {
          label: 'is not',
          value: '!=',
          object: this.simpleInputObject,
        },
        {
          label: 'contains',
          value: 'contains',
          object: this.simpleInputObject,
        },
      ],
    }
  }

  numberInputObject({ value, onChange }) {
    return (
      <Input
        placeholder="Insert number…"
        type="number"
        min="0"
        max="180"
        value={value || ''}
        onChange={e => {
          onChange(e.target.value.replace(/\D/g, ''))
        }}
      />
    )
  }

  numberInputRangeObject({ value, onChange }) {
    return (

        <Input
          placeholder="from…"
          errorMessage={
            value &&
            value.first &&
            value.last &&
            parseInt(value.first) >= parseInt(value.last)
              ? 'Must be smaller than other input'
              : ''
          }
          value={value && value.first ? value.first : ''}
          onChange={e => {
            const currentObject = value || {}
            currentObject.first = e.target.value.replace(/\D/g, '')

            onChange(currentObject)
          }}
        />

and

        <Input
          placeholder="to…"
          value={value && value.last ? value.last : ''}
          onChange={e => {
            const currentObject = value || {}
            currentObject.last = e.target.value.replace(/\D/g, '')

            onChange(currentObject)
          }}
        />

    )
  }

  colorSelectorObject({ value, onChange }) {
    const initialValue = {
      pink: true,
      black: true,
      blue: true,
      gray: true,
      ...(value || {}),
    }
    const toggleValueByKey = key => {
      const newValue = {
        ...(value || initialValue),
        [key]: value ? !value[key] : false,
      }
      return newValue
    }
    return (

        {Object.keys(initialValue).map((opt, index) => {
          return (
            <div className="mb3" key={`class-statment-object-${opt}-${index}`}>
              <Checkbox
                checked={value ? value[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]
                  )
                  onChange(isEmptyFilter ? null : newValue)
                }}
                value={opt}
              />

          )
        })}

    )
  }

  handleFiltersChange(statements = []) {
    // here you should receive filter values, so you can fire mutations ou fetch filtered data from APIs
    // For the sake of example I'll filter the data manually since there is no API
    const { tableLength } = this.state
    let newData = sampleData.items.slice()
    statements.forEach(st => {
      if (!st || !st.object) return
      const { subject, verb, object } = st
      switch (subject) {
        case 'color':
          if (!object) return
          const colorsMap = {
            '#F71963': 'pink',
            '#00BBD4': 'blue',
            '#D6D8E0': 'gray',
            '#142032': 'black',
          }
          newData = newData.filter(item => object[colorsMap[item.color.color]])
          break
        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 'number':
          if (verb === '=') {
            newData = newData.filter(item => item.number === parseInt(object))
          } else if (verb === 'between') {
            newData = newData.filter(
              item =>
                item.number >= parseInt(object.first) &&
                item.number <= parseInt(object.last)
            )
          }
          break
      }
    })
    const newDataLength = newData.length
    const newSlicedData = newData.slice(0, tableLength)
    this.setState({
      filterStatements: statements,
      slicedData: newSlicedData,
      itemsLength: newDataLength,
      currentItemTo: tableLength > newDataLength ? newDataLength : tableLength,
    })
  }

  render() {
    return (

 {
                if (!st || !st.object) {
                  // you should treat empty object cases only for alwaysVisibleFilters
                  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: this.colorSelectorObject,
                },
              ],
            },
            name: {
              label: 'Name',
              ...this.simpleInputVerbsAndLabel(),
            },
            email: {
              label: 'Email',
              ...this.simpleInputVerbsAndLabel(),
            },
            number: {
              label: 'Number',
              renderFilterLabel: st =>
                `${
                  st.verb === 'between'
                    ? `between ${st.object.first} and ${st.object.last}`
                    : `is ${st.object}`
                }`,
              verbs: [
                {
                  label: 'is',
                  value: '=',
                  object: this.numberInputObject,
                },
                {
                  label: 'is between',
                  value: 'between',
                  object: this.numberInputRangeObject,
                },
              ],
            },
          },
        }}
      />
    )
  }
}
;<ResourceListExample />
```

##### Bulk actions

Bulk actions allow the user to select some or all the rows to apply an action. Texts have to be given to the component via a `texts` object.
Actions are passed via the `main` object and the `others` array props. Each object is composed of a `label` and the action event via `onClick` key.

The returned value for all lines selected is an object `allLinesSelected: true` otherwise the data of the rows are returned in the key `selectedRows` as an array.

##### NOTE 1:

`onRowClick` actions are not happening when clicking the checkbox.

##### NOTE 2:

There are two "select all" items.

- The **upper checkbox** on the left side selects the currently visible items, in the example below, 5.
- The **Select all** button on the right side, selects all items from the database (by concept, since you will probably only load the visible items). Since not all items might be loaded in the table, the callback will only return a flag telling your app to handle all items for the next database operation.

Check the console when selecting/unselecting rows or clicking an action button in the example below to see the action parameters

```js
const itemsCopy = [
  {
    email: 'olen.stamm21@yahoo.com',
    name: 'Patrick Rothfuss',
    number: 1.52725,
  },
  {
    email: 'junius0@gmail.com',
    name: 'Hurricane Skywalker IV',
    number: 2.84639,
  },
  {
    email: 'judd_gulgowski22@yahoo.com',
    name: 'Tom Braddy',
    number: 4.10182,
  },
  {
    email: 'catharine.leuschke62@hotmail.com',
    name: 'Momochi Zabuza',
    number: 6.33245,
  },
  {
    email: 'candido_ryan@hotmail.com',
    name: 'Freddie Mercury',
    number: 7.96637,
  },
]
const defaultSchema = {
  properties: {
    name: {
      type: 'string',
      title: 'Name',
    },
    email: {
      type: 'string',
      title: 'Email',
    },
    number: {
      type: 'number',
      title: 'Number',
    },
  },
}

;

 (
          <React.Fragment>Selected rows: {qty}</React.Fragment>
        ),
        selectAll: 'Select all',
        allRowsSelected: qty => (
          <React.Fragment>All rows selected: {qty}</React.Fragment>
        ),
      },
      totalItems: 122,
      onChange: params => console.log(params),
      main: {
        label: 'Main Action',
        handleCallback: params => console.log(params),
      },
      others: [
        {
          label: 'Action 1',
          handleCallback: params => console.log(params),
        },
        {
          label: 'Action 2',
          handleCallback: params => console.log(params),
        },
        {
          label: 'Dangerous action',
          isDangerous: true,
          handleCallback: params => console.log(params),
        },
      ],
    }}
  />

```

##### Full blown example

With Toolbar, Totalizers, Pagination and Filters

```js
const ArrowDown = require('../icon/ArrowDown').default
const ArrowUp = require('../icon/ArrowUp').default
const Checkbox = require('../Checkbox').default
const Input = require('../Input').default
const sampleData = require('./sampleData').default
const Tag = require('../Tag').default
const tableLength = 5
const initialState = {
  tableLength,
  currentPage: 1,
  slicedData: sampleData.items.slice(0, tableLength),
  currentItemFrom: 1,
  currentItemTo: tableLength,
  searchValue: '',
  itemsLength: sampleData.items.length,
  emptyStateLabel: 'Nothing to show.',
  filterStatements: [],
}
const jsonschema = {
  properties: {
    name: {
      title: 'Name',
    },
    email: {
      title: 'Email',
      width: 300,
    },
    number: {
      title: 'Number',
      width: 150,
    },
    color: {
      title: 'Color',
      cellRenderer: ({ cellData }) => {
        return (
          <Tag bgColor={cellData.color} color="#fff">
            {cellData.label}
          </Tag>
        )
      },
    },
  },
}

class ResourceListExample extends React.Component {
  constructor() {
    super()
    this.state = initialState

    this.handleNextClick = this.handleNextClick.bind(this)
    this.handlePrevClick = this.handlePrevClick.bind(this)
    this.goToPage = this.goToPage.bind(this)
    this.handleInputSearchChange = this.handleInputSearchChange.bind(this)
    this.handleInputSearchSubmit = this.handleInputSearchSubmit.bind(this)
    this.handleInputSearchClear = this.handleInputSearchClear.bind(this)
    this.handleRowsChange = this.handleRowsChange.bind(this)
    this.simpleInputObject = this.simpleInputObject.bind(this)
    this.simpleInputVerbsAndLabel = this.simpleInputVerbsAndLabel.bind(this)
    this.numberInputObject = this.numberInputObject.bind(this)
    this.numberInputRangeObject = this.numberInputRangeObject.bind(this)
    this.colorSelectorObject = this.colorSelectorObject.bind(this)
    this.handleFiltersChange = this.handleFiltersChange.bind(this)
  }

  handleNextClick() {
    const newPage = this.state.currentPage + 1
    const itemFrom = this.state.currentItemTo + 1
    const itemTo = tableLength * newPage
    const data = sampleData.items.slice(itemFrom - 1, itemTo)
    this.goToPage(newPage, itemFrom, itemTo, data)
  }

  handlePrevClick() {
    if (this.state.currentPage === 0) return
    const newPage = this.state.currentPage - 1
    const itemFrom = this.state.currentItemFrom - tableLength
    const itemTo = this.state.currentItemFrom - 1
    const data = sampleData.items.slice(itemFrom - 1, itemTo)
    this.goToPage(newPage, itemFrom, itemTo, data)
  }

  goToPage(currentPage, currentItemFrom, currentItemTo, slicedData) {
    this.setState({
      currentPage,
      currentItemFrom,
      currentItemTo,
      slicedData,
    })
  }

  handleRowsChange(e, value) {
    this.setState(
      {
        tableLength: parseInt(value),
        currentItemTo: parseInt(value),
      },
      () => {
        // this callback garantees new sliced items respect filters and tableLength
        const { filterStatements } = this.state
        this.handleFiltersChange(filterStatements)
      }
    )
  }

  handleInputSearchChange(e) {
    this.setState({ searchValue: e.target.value })
  }

  handleInputSearchClear(e) {
    this.setState({ ...initialState })
  }

  handleInputSearchSubmit(e) {
    const value = e && e.target && e.target.value
    const regex = new RegExp(value, 'i')
    if (!value) {
      this.setState({ ...initialState })
    } else {
      this.setState({
        slicedData: initialState.slicedData
          .slice()
          .filter(item => regex.test(item.name) || regex.test(item.email)),
      })
    }
  }

  simpleInputObject({
    statements,
    values,
    statementIndex,
    error,
    extraParams,
    onChangeObjectCallback,
  }) {
    return (
      <Input
        value={values || ''}
        onChange={e => onChangeObjectCallback(e.target.value)}
      />
    )
  }

  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: this.simpleInputObject,
            extraParams: {},
          },
        },
        {
          label: 'is not',
          value: '!=',
          object: {
            renderFn: this.simpleInputObject,
            extraParams: {},
          },
        },
        {
          label: 'contains',
          value: 'contains',
          object: {
            renderFn: this.simpleInputObject,
            extraParams: {},
          },
        },
      ],
    }
  }

  numberInputObject({
    statements,
    values,
    statementIndex,
    error,
    onChangeObjectCallback,
  }) {
    return (
      <Input
        placeholder="Insert number…"
        type="number"
        min="0"
        max="180"
        value={values || ''}
        onChange={e => {
          onChangeObjectCallback(e.target.value.replace(/\D/g, ''))
        }}
      />
    )
  }

  numberInputRangeObject({
    statements,
    values,
    statementIndex,
    error,
    extraParams,
    onChangeObjectCallback,
  }) {
    return (

        <Input
          placeholder="Number from…"
          errorMessage={
            statements[statementIndex].object &&
            parseInt(statements[statementIndex].object.first) >=
              parseInt(statements[statementIndex].object.last)
              ? 'Must be smaller than other input'
              : ''
          }
          value={values && values.first ? values.first : ''}
          onChange={e => {
            const currentObject = values || {}
            currentObject.first = e.target.value.replace(/\D/g, '')

            onChangeObjectCallback(currentObject)
          }}
        />

and

        <Input
          placeholder="Number to…"
          value={values && values.last ? values.last : ''}
          onChange={e => {
            const currentObject = values || {}
            currentObject.last = e.target.value.replace(/\D/g, '')

            onChangeObjectCallback(currentObject)
          }}
        />

    )
  }

  colorSelectorObject({
    statements,
    values,
    statementIndex,
    error,
    extraParams,
    onChangeObjectCallback,
  }) {
    const initialValue = {
      pink: true,
      black: true,
      blue: true,
      gray: 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}
              />

          )
        })}

    )
  }

  handleFiltersChange(statements = []) {
    // here you should receive filter values, so you can fire mutations ou fetch filtered data from APIs
    // For the sake of example I'll filter the data manually since there is no API
    const { tableLength } = this.state
    let newData = sampleData.items.slice()
    statements.forEach(st => {
      if (!st || !st.object) return
      const { subject, verb, object } = st
      switch (subject) {
        case 'color':
          if (!object) return
          const colorsMap = {
            '#F71963': 'pink',
            '#00BBD4': 'blue',
            '#D6D8E0': 'gray',
            '#142032': 'black',
          }
          newData = newData.filter(item => object[colorsMap[item.color.color]])
          break
        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 'number':
          if (verb === '=') {
            newData = newData.filter(item => item.number === parseInt(object))
          } else if (verb === 'between') {
            newData = newData.filter(
              item =>
                item.number >= parseInt(object.first) &&
                item.number <= parseInt(object.last)
            )
          }
          break
      }
    })
    const newDataLength = newData.length
    const newSlicedData = newData.slice(0, tableLength)
    this.setState({
      filterStatements: statements,
      slicedData: newSlicedData,
      itemsLength: newDataLength,
      currentItemTo: tableLength > newDataLength ? newDataLength : tableLength,
    })
  }

  render() {
    return (
      <Table
        schema={jsonschema}
        items={this.state.slicedData}
        emptyStateLabel={this.state.emptyStateLabel}
        toolbar={{
          inputSearch: {
            value: this.state.searchValue,
            placeholder: 'Search stuff...',
            onChange: this.handleInputSearchChange,
            onClear: this.handleInputSearchClear,
            onSubmit: this.handleInputSearchSubmit,
          },
          density: {
            buttonLabel: 'Line density',
            lowOptionLabel: 'Low',
            mediumOptionLabel: 'Medium',
            highOptionLabel: 'High',
          },
          download: {
            label: 'Export',
            handleCallback: () => alert('Callback()'),
          },
          upload: {
            label: 'Import',
            handleCallback: () => alert('Callback()'),
          },
          fields: {
            label: 'Toggle visible fields',
            showAllLabel: 'Show All',
            hideAllLabel: 'Hide All',
          },
          extraActions: {
            label: 'More options',
            actions: [
              {
                label: 'An action',
                handleCallback: () => alert('An action'),
              },
              {
                label: 'Another action',
                handleCallback: () => alert('Another action'),
              },
              {
                label: 'A third action',
                handleCallback: () => alert('A third action'),
              },
            ],
          },
          newLine: {
            label: 'New',
            handleCallback: () => alert('handle new line callback'),
            actions: [
              'General',
              'Desktop & Screen Saver',
              'Dock',
              'Language & Region',
            ].map(label => ({
              label,
              onClick: () => {},
            })),
          },
        }}
        pagination={{
          onNextClick: this.handleNextClick,
          onPrevClick: this.handlePrevClick,
          currentItemFrom: this.state.currentItemFrom,
          currentItemTo: this.state.currentItemTo,
          onRowsChange: this.handleRowsChange,
          textShowRows: 'Show rows',
          textOf: 'of',
          totalItems: this.state.itemsLength,
          rowsOptions: [5, 10, 15, 25],
        }}
        totalizers={[
          {
            label: 'Saldo em conta',
            value: 23837,
          },
          {
            label: 'Entradas',
            value: 'R$ 36239,05',
            iconBackgroundColor: '#eafce3',
            icon: <ArrowUp color="#79B03A" size={14} />,
          },

          {
            label: 'Saídas',
            value: '- R$ 13.485,26',
            icon: <ArrowDown size={14} />,
          },
          {
            label: 'Vendas',
            value: 23837,
            isLoading: true,
          },
        ]}
        bulkActions={{
          texts: {
            secondaryActionsLabel: 'Actions',
            rowsSelected: qty => (
              <React.Fragment>Selected rows: {qty}</React.Fragment>
            ),
            selectAll: 'Select all',
            allRowsSelected: qty => (
              <React.Fragment>All rows selected: {qty}</React.Fragment>
            ),
          },
          totalItems: 122,
          onChange: params => console.log(params),
          main: {
            label: 'Main Action',
            handleCallback: params => console.log(params),
          },
          others: [
            {
              label: 'Action 1',
              handleCallback: params => console.log(params),
            },
            {
              label: 'Action 2',
              handleCallback: params => console.log(params),
            },
            {
              label: 'Dangerous action',
              isDangerous: true,
              handleCallback: params => console.log(params),
            },
          ],
        }}
        filters={{
          alwaysVisibleFilters: ['color', 'name'],
          statements: this.state.filterStatements,
          onChangeStatements: this.handleFiltersChange,
          clearAllFiltersButtonLabel: 'Clear Filters',
          collapseLeft: true,
          options: {
            color: {
              label: 'Color',
              renderFilterLabel: st => {
                if (!st || !st.object) {
                  // you should treat empty object cases only for alwaysVisibleFilters
                  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: this.colorSelectorObject,
                    extraParams: {},
                  },
                },
              ],
            },
            name: {
              label: 'Name',
              ...this.simpleInputVerbsAndLabel(),
            },
            email: {
              label: 'Email',
              ...this.simpleInputVerbsAndLabel(),
            },
            number: {
              label: 'Number',
              renderFilterLabel: st =>
                `${
                  st.verb === 'between'
                    ? `between ${st.object.first} and ${st.object.last}`
                    : `is ${st.object}`
                }`,
              verbs: [
                {
                  label: 'is',
                  value: '=',
                  object: {
                    renderFn: this.numberInputObject,
                    extraParams: {},
                  },
                },
                {
                  label: 'is between',
                  value: 'between',
                  object: {
                    renderFn: this.numberInputRangeObject,
                    extraParams: {},
                  },
                },
              ],
            },
          },
        }}
      />
    )
  }
}
;<ResourceListExample />
```

```


[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/Table/SimpleTable.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 {
  MultiGrid,
  AutoSizer,
  CellMeasurer,
  CellMeasurerCache,
} from 'react-virtualized'

import ArrowDown from '../icon/ArrowDown'
import ArrowUp from '../icon/ArrowUp'
import OptionsDots from '../icon/OptionsDots'
import EmptyState from '../EmptyState'
import Spinner from '../Spinner'
import ActionMenu from '../ActionMenu'

const ARROW_SIZE = 11
const HEADER_HEIGHT = 36
const DEFAULT_COLUMN_WIDTH = 200
const LINE_ACTIONS_COLUMN_WIDTH = 70
const NO_TITLE_COLUMN = ' '
const SELECTED_ROW_BACKGROUND = '#dbe9fd'
const EMPTY_STATE_SIZE_IN_ROWS = 5

class SimpleTable extends Component {
  constructor(props) {
    super(props)

    this.state = {
      hoverRowIndex: -1,
      tableHeight: 0,
      scrollbarWidth: 0,
    }

    this._cache = new CellMeasurerCache({
      defaultHeight: props.rowHeight,
      minHeight: props.rowHeight,
      fixedWidth: true,
    })
  }

  componentDidMount() {
    this.setState({
      tableHeight:
        this.props.containerHeight || this.calculateContainerHeight(),
    })
  }

  componentDidUpdate() {
    if (!this.props.containerHeight) {
      this.updateTableHeight()
    }
  }

  updateTableHeight() {
    const calculatedContainerHeight = this.calculateContainerHeight()
    if (this.state.tableHeight !== calculatedContainerHeight) {
      this.setState({
        tableHeight: calculatedContainerHeight,
      })
    }
  }

  toggleSortType = key => {
    const {
      sort: { preferentialSortOrder = 'ASC', sortOrder, sortedBy },
    } = this.props

    if (sortedBy !== key) {
      return {
        sortOrder: preferentialSortOrder,
        sortedBy: key,
      }
    }

    return {
      sortOrder: sortOrder === 'ASC' ? 'DESC' : 'ASC',
      sortedBy: key,
    }
  }

  handleRowHover = rowIndex => {
    const { onRowClick, onRowHover } = this.props
    const { isLineActionsHovered } = this.state
    if (onRowClick && !isLineActionsHovered) {
      this.setState({
        hoverRowIndex: rowIndex,
      })
    } else {
      this.setState({
        hoverRowIndex: -1,
      })
    }

    onRowHover(rowIndex)
  }

  calculateColWidth = (
    schema,
    properties,
    index,
    fullWidth,
    fullWidthColWidth
  ) => {
    const col = schema.properties[properties[index]]
    return col.width
      ? col.width
      : fullWidth
      ? Math.max(col.minWidth || 100, fullWidthColWidth)
      : DEFAULT_COLUMN_WIDTH
  }

  calculateRowHeight = index => {
    const { disableHeader, dynamicRowHeight, rowHeight } = this.props
    const isHeader = !disableHeader && index === 0
    if (isHeader) {
      return HEADER_HEIGHT
    }

    if (dynamicRowHeight) {
      return this._cache.rowHeight({ index })
    }

    return rowHeight
  }

  addLineActionsToSchema = (schema, lineActions) => {
    return {
      ...schema.properties,
      // eslint-disable-next-line camelcase
      _VTEX_Table_Internal_lineActions: {
        title: NO_TITLE_COLUMN,
        width: LINE_ACTIONS_COLUMN_WIDTH,
        cellRenderer: ({ rowData }) => {
          return (
            <ActionMenu
              buttonProps={{
                variation: 'tertiary',
                icon: <OptionsDots />,
                onMouseEnter: () =>
                  this.setState({ isLineActionsHovered: true }),
                onMouseLeave: () =>
                  this.setState({ isLineActionsHovered: false }),
              }}
              options={lineActions.map(action => ({
                ...action,
                label: action.label({ rowData }),
                onClick: () => action.onClick({ rowData }),
              }))}
            />
          )
        },
      },
    }
  }

  handleScrollbarPresenceChange = ({ horizontal, size }) => {
    if (horizontal) {
      this.setState({ scrollbarWidth: size || 0 })
    }
  }

  calculateContainerHeight = () => {
    const { rowHeight, items, disableHeader } = this.props
    const { scrollbarWidth } = this.state

    if (items.length === 0) {
      return (
        HEADER_HEIGHT + rowHeight * EMPTY_STATE_SIZE_IN_ROWS + scrollbarWidth
      )
    }

    const rowCount = disableHeader ? items.length : items.length + 1

    let rawTableHeight = 0
    for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
      rawTableHeight += this.calculateRowHeight(rowIndex)
    }

    return rawTableHeight + scrollbarWidth
  }

  render() {
    const {
      schema,
      items,
      fixFirstColumn,
      disableHeader,
      density,
      emptyStateLabel,
      emptyStateChildren,
      onRowClick,
      sort: { sortOrder, sortedBy },
      onSort,
      updateTableKey,
      fullWidth,
      lineActions,
      loading,
      selectedRowsIndexes,
    } = this.props
    const { hoverRowIndex, tableHeight } = this.state

    if (lineActions)
      schema.properties = this.addLineActionsToSchema(schema, lineActions)
    const properties = Object.keys(schema.properties)

    const tableKey = `vtex-table--${updateTableKey}--${density}`

    return (
      <div className="vh-100 w-100 dt" style={{ height: tableHeight }}>
        {loading ? (
          <div
            className="dtc v-mid tc"
            style={{ height: tableHeight - HEADER_HEIGHT }}>
            <Spinner />
          </div>
        ) : (
          <div>
            <AutoSizer key={tableKey}>
              {({ width }) => {
                const colsWidth = Object.keys(schema.properties).reduce(
                  (acc, curr) => {
                    const col = schema.properties[curr]
                    return acc + (col.width ? col.width : 0)
                  },
                  0
                )

                const colsWithoutWidth = Object.keys(schema.properties).filter(
                  curr => {
                    const col = schema.properties[curr]
                    return !col.width
                  }
                )

                const fullWidthColWidth =
                  (width - colsWidth) / colsWithoutWidth.length

                return (
                  <MultiGrid
                    onScrollbarPresenceChange={
                      this.handleScrollbarPresenceChange
                    }
                    height={tableHeight}
                    width={width}
                    deferredMeasurementCache={this._cache}
                    tabIndex={null}
                    fixedRowCount={disableHeader ? 0 : 1}
                    rowCount={disableHeader ? items.length : items.length + 1}
                    rowHeight={({ index }) => this.calculateRowHeight(index)}
                    enableFixedRowScroll={!disableHeader}
                    hideTopRightGridScrollbar={!disableHeader}
                    overscanRowCount={0}
                    styleTopRightGrid={
                      fixFirstColumn ? { overflow: 'hidden' } : {}
                    }
                    styleTopLeftGrid={
                      fixFirstColumn ? { overflow: 'hidden' } : {}
                    }
                    fixedColumnCount={fixFirstColumn ? 1 : 0}
                    columnCount={properties.length}
                    columnWidth={({ index }) =>
                      this.calculateColWidth(
                        schema,
                        properties,
                        index,
                        fullWidth,
                        fullWidthColWidth
                      )
                    }
                    enableFixedColumnScroll
                    overscanColumnCount={0}
                    cellRenderer={({
                      columnIndex,
                      key,
                      rowIndex,
                      style,
                      parent,
                    }) => {
                      const property = properties[columnIndex]
                      if (!disableHeader && rowIndex === 0) {
                        // Header row
                        const title =
                          schema.properties[property].title || property
                        const headerRight =
                          schema.properties[property].headerRight || false
                        const headerRenderer =
                          schema.properties[property].headerRenderer
                        const arrowIsDown =
                          sortOrder === 'ASC' && sortedBy === property
                        const arrowIsUp =
                          sortOrder === 'DESC' && sortedBy === property
                        return (
                          <CellMeasurer
                            cache={this._cache}
                            columnIndex={columnIndex}
                            key={key}
                            parent={parent}
                            rowIndex={rowIndex}>
                            <div
                              key={key}
                              style={{
                                ...style,
                                height: HEADER_HEIGHT,
                              }}
                              className={`flex items-center w-100 h-100 c-muted-2 f6 truncate ph4 ${
                                columnIndex === 0 && fixFirstColumn ? 'br' : ''
                              } bt bb b--muted-4 overflow-x-hidden ${
                                headerRight ? 'tr' : 'tl'
                              }`}>
                              {schema.properties[property].sortable ? (
                                <div
                                  className="w-100 pointer c-muted-1 b t-small"
                                  onClick={() => {
                                    onSort(this.toggleSortType(property))
                                  }}>
                                  {!headerRight && title}
                                  <div
                                    className={`inline-flex ${
                                      headerRight ? 'pr2' : 'pl3'
                                    }`}>
                                    {arrowIsDown ? (
                                      <ArrowDown size={ARROW_SIZE} />
                                    ) : arrowIsUp ? (
                                      <ArrowUp size={ARROW_SIZE} />
                                    ) : null}
                                  </div>
                                  {headerRight && title}
                                </div>
                              ) : columnIndex === 0 && fixFirstColumn ? (
                                <div className="w-100">{title}</div>
                              ) : headerRenderer ? (
                                headerRenderer({
                                  columnIndex,
                                  key,
                                  rowIndex,
                                  style,
                                  title,
                                })
                              ) : (
                                <span className="w-100">{title}</span>
                              )}
                            </div>
                          </CellMeasurer>
                        )
                      }
                      const cellRenderer =
                        schema.properties[property].cellRenderer
                      const rowData =
                        items[disableHeader ? rowIndex : rowIndex - 1]
                      const cellData = rowData[property]

                      const cellClassNames = `flex items-center w-100 h-100 ph4 bb
                                    b--muted-4 truncate ${
                                      disableHeader && rowIndex === 0
                                        ? 'bt'
                                        : ''
                                    } ${
                        onRowClick && rowIndex === hoverRowIndex
                          ? 'pointer bg-near-white c-link'
                          : ''
                      } ${columnIndex === 0 && fixFirstColumn ? 'br' : ''}`

                      return (
                        <CellMeasurer
                          cache={this._cache}
                          columnIndex={columnIndex}
                          key={key}
                          parent={parent}
                          rowIndex={rowIndex}>
                          {({ measure: updateCellMeasurements }) => (
                            <div
                              key={key}
                              style={{
                                ...style,
                                height: this.calculateRowHeight(rowIndex),
                                width: this.calculateColWidth(
                                  schema,
                                  properties,
                                  columnIndex,
                                  fullWidth,
                                  fullWidthColWidth
                                ),
                                backgroundColor: selectedRowsIndexes.includes(
                                  rowIndex - 1
                                )
                                  ? SELECTED_ROW_BACKGROUND
                                  : '',
                              }}
                              className={cellClassNames}
                              onClick={
                                onRowClick &&
                                property !== '_VTEX_Table_Internal_lineActions'
                                  ? event =>
                                      onRowClick({
                                        event,
                                        index: rowIndex,
                                        rowData,
                                      })
                                  : null
                              }
                              onMouseEnter={
                                onRowClick
                                  ? () => this.handleRowHover(rowIndex)
                                  : null
                              }
                              onMouseLeave={
                                onRowClick
                                  ? () => this.handleRowHover(-1)
                                  : null
                              }>
                              {cellRenderer
                                ? cellRenderer({
                                    cellData,
                                    rowData,
                                    updateCellMeasurements,
                                  })
                                : cellData}
                            </div>
                          )}
                        </CellMeasurer>
                      )
                    }}
                  />
                )
              }}
            </AutoSizer>
            {items.length === 0 && (
              <div style={{ marginTop: HEADER_HEIGHT }}>
                <EmptyState title={emptyStateLabel}>
                  {emptyStateChildren}
                </EmptyState>
              </div>
            )}
          </div>
        )}
      </div>
    )
  }
}

SimpleTable.defaultProps = {
  indexColumnLabel: null,
  fixFirstColumn: false,
  items: [],
  disableHeader: false,
  sort: {
    sortOrder: null,
    sortedBy: null,
  },
  fullWidth: false,
  dynamicRowHeight: false,
  selectedRowsIndexes: [],
  onRowHover: () => {},
}

SimpleTable.propTypes = {
  items: PropTypes.array.isRequired,
  schema: PropTypes.object.isRequired,
  indexColumnLabel: PropTypes.string,
  fixFirstColumn: PropTypes.bool,
  density: PropTypes.string,
  disableHeader: PropTypes.bool,
  onRowClick: PropTypes.func,
  onRowHover: PropTypes.func,
  emptyStateLabel: PropTypes.string,
  emptyStateChildren: PropTypes.node,
  sort: PropTypes.shape({
    sortOrder: PropTypes.oneOf(['ASC', 'DESC']),
    sortedBy: PropTypes.string,
    preferentialSortOrder: PropTypes.oneOf(['ASC', 'DESC']),
  }),
  onSort: PropTypes.func,
  updateTableKey: PropTypes.string,
  containerHeight: PropTypes.number,
  rowHeight: PropTypes.number.isRequired,
  dynamicRowHeight: PropTypes.bool,
  fullWidth: PropTypes.bool,
  lineActions: PropTypes.arrayOf(
    PropTypes.shape({
      label: PropTypes.func,
      isDangerous: PropTypes.bool,
      onClick: PropTypes.func,
    })
  ),
  loading: PropTypes.bool,
  selectedRowsIndexes: PropTypes.array,
}

export default SimpleTable

```


[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/Table/Toolbar.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 ActionMenu from '../ActionMenu'
import InputSearch from '../InputSearch'
import Button from '../Button'
import ButtonWithIcon from '../ButtonWithIcon'
import ButtonGroup from '../ButtonGroup'
import Toggle from '../Toggle'
import IconColumns from '../icon/Columns'
import IconDensity from '../icon/Density'
import IconDownload from '../icon/Download'
import IconPlus from '../icon/Plus'
import IconUpload from '../icon/Upload'
import IconOptionsDots from '../icon/OptionsDots'

const MAX_FIELDS_BOX_HEIGHT = 192
const FIELDS_BOX_ITEM_HEIGHT = 36
const FIELDS_BOX_WIDTH = 292
const BOX_SHADOW_STYLE = { boxShadow: '0px 1px 18px rgba(0, 0, 0, 0.14)' }
const DENSITY_OPTIONS = ['low', 'medium', 'high']
const ICON_OPTICAL_COMPENSATION = { marginTop: '1.5px' }
const LIGHT_ICON_SIZE = 16
const MEDIUM_ICON_SIZE = 14
const HEAVY_ICON_SIZE = 13

class Toolbar extends PureComponent {
  constructor(props) {
    super(props)
    this.fieldsBtnRef = React.createRef()
    this.extraActionsBtnRef = React.createRef()
    this.densityBtnRef = React.createRef()
    this.state = {
      isFieldsBoxVisible: false,
      isDensityBoxVisible: false,
    }
  }

  handleToggleBox = boxKey => {
    const isBoxVisible = this.state[boxKey]
    if (isBoxVisible) {
      document.removeEventListener('mousedown', this.handleClickOutside)
    } else {
      document.addEventListener('mousedown', this.handleClickOutside)
    }
    this.setState({ [`${boxKey}`]: !isBoxVisible })
  }

  handleClickOutside = e => {
    if (
      // handle clicks outside the show/hide fields btn or box
      this.fieldsBtnRef &&
      this.fieldsBtnRef.current &&
      !this.fieldsBtnRef.current.contains(e.target) &&
      this.state.isFieldsBoxVisible
    ) {
      // closes the box if it's open
      this.handleToggleBox('isFieldsBoxVisible')
    }
    if (
      this.densityBtnRef &&
      this.densityBtnRef.current &&
      !this.densityBtnRef.current.contains(e.target) &&
      this.state.isDensityBoxVisible
    ) {
      this.handleToggleBox('isDensityBoxVisible')
    }
  }

  calculateFieldsBoxHeight = () => {
    const { schema } = this.props
    const estimate =
      Object.keys(schema.properties).length * FIELDS_BOX_ITEM_HEIGHT
    return estimate > MAX_FIELDS_BOX_HEIGHT ? MAX_FIELDS_BOX_HEIGHT : estimate
  }

  calculateExtraActionsBoxHeight = () => {
    const {
      actions: { extraActions },
    } = this.props
    const estimate = extraActions.actions.length * FIELDS_BOX_ITEM_HEIGHT
    return estimate > MAX_FIELDS_BOX_HEIGHT ? MAX_FIELDS_BOX_HEIGHT : estimate
  }

  handleInputSearchSubmit = e => {
    this.props.onDeselectAllLines && this.props.onDeselectAllLines()

    this.props.actions.inputSearch.onSubmit &&
      this.props.actions.inputSearch.onSubmit(e)
  }

  render() {
    const {
      actions: {
        inputSearch,
        download,
        upload,
        fields,
        extraActions,
        newLine,
        density,
      },
      hiddenFields,
      schema,
      onHideAllColumns,
      onShowAllColumns,
      onToggleColumn,
      onToggleDensity,
      selectedDensity,
      loading,
    } = this.props
    const { isFieldsBoxVisible, isDensityBoxVisible } = this.state
    const isDownloadVisible = download && download.handleCallback
    const isUploadVisible = upload && upload.handleCallback
    const isFieldsVisible = fields && fields.showAllLabel && fields.hideAllLabel
    const isExtraActionsVisible =
      extraActions && extraActions.label && extraActions.actions.length > 0
    const isNewLineVisible = newLine && newLine.label
    const isSearchBarVisible = !!inputSearch
    const isDensityVisible =
      density &&
      density.buttonLabel &&
      density.lowOptionLabel &&
      density.mediumOptionLabel &&
      density.highOptionLabel

    const newLineButtonProps = {
      disabled: loading || (newLine && newLine.disabled),
      isLoading: newLine && newLine.isLoading,
      variation: 'primary',
      size: 'regular',
    }

    const forcedColor = 'c-on-base'

    const inputSearchAlone =
      inputSearch &&
      !isDensityVisible &&
      !isFieldsVisible &&
      !isDownloadVisible &&
      !isUploadVisible &&
      !isExtraActionsVisible &&
      !isNewLineVisible

    return (
      <div
        id="toolbar"
        className={`mb5 flex flex-row w-100 ${
          isSearchBarVisible ? 'justify-between' : 'justify-end'
        }`}>
        {inputSearch && (
          <div className={inputSearchAlone ? 'w-100 w-40-ns' : 'w-40'}>
            <InputSearch
              disabled={loading}
              {...inputSearch}
              onSubmit={this.handleInputSearchSubmit}
            />
          </div>
        )}
        <div className="flex flex-row items-center">
          {isDensityVisible && (
            <div
              id="toggleDensity"
              title={density.buttonLabel}
              ref={this.densityBtnRef}
              className="relative mh1">
              <ButtonWithIcon
                icon={
                  <span
                    className="c-on-base mh2"
                    style={ICON_OPTICAL_COMPENSATION}>
                    <IconDensity size={MEDIUM_ICON_SIZE} />
                  </span>
                }
                disabled={loading}
                variation="tertiary"
                size="regular"
                onClick={() => this.handleToggleBox('isDensityBoxVisible')}
              />
              {isDensityBoxVisible && (
                <div
                  className={`absolute ${
                    density.alignMenu === 'right' ? 'right-0' : 'left-0'
                  } z-999 ba b--muted-4 br2 mt2 mh2`}
                  style={BOX_SHADOW_STYLE}>
                  <div className="w-100 b2 br2 bg-base">
                    <div
                      style={{ height: 3 * FIELDS_BOX_ITEM_HEIGHT }}
                      className="overflow-auto">
                      {DENSITY_OPTIONS.map((key, index) => {
                        const isKeySelected = selectedDensity === key
                        return (
                          <div
                            key={index}
                            className={`flex justify-between ph6 pv3 ${
                              isKeySelected ? 'b--emphasis' : 'b--transparent'
                            } pointer hover-bg-muted-5 bl bw1`}
                            onClick={() => {
                              onToggleDensity(key)
                              this.handleToggleBox('isDensityBoxVisible')
                              density.handleCallback &&
                                density.handleCallback(key)
                            }}>
                            <span
                              className={`w-100 ${isKeySelected ? 'fw5' : ''}`}>
                              {density[`${key}OptionLabel`]}
                            </span>
                          </div>
                        )
                      })}
                    </div>
                  </div>
                </div>
              )}
            </div>
          )}
          {isFieldsVisible && (
            <div
              id="toggleFieldsBtn"
              title={fields.label}
              ref={this.fieldsBtnRef}
              className="relative mh1">
              <ButtonWithIcon
                icon={
                  <span
                    className="c-on-base mh2"
                    style={ICON_OPTICAL_COMPENSATION}>
                    <IconColumns size={MEDIUM_ICON_SIZE} />
                  </span>
                }
                disabled={loading}
                variation="tertiary"
                size="regular"
                onClick={() => this.handleToggleBox('isFieldsBoxVisible')}
              />
              {isFieldsBoxVisible && (
                <div
                  className={`absolute ${
                    fields.alignMenu === 'right' ? 'right-0' : 'left-0'
                  } z-999 ba b--muted-4 br2 mt2 mh2`}>
                  <div
                    className="w-100 b2 br2 bg-base"
                    style={{
                      ...BOX_SHADOW_STYLE,
                      width: FIELDS_BOX_WIDTH,
                    }}>
                    <div className="flex inline-flex bb b--muted-4 w-100 pl6 pv4">
                      <Button
                        variation="secondary"
                        size="regular"
                        onClick={onShowAllColumns}>
                        {fields.showAllLabel}
                      </Button>
                      <div className="mh4">
                        <Button
                          variation="secondary"
                          size="regular"
                          onClick={onHideAllColumns}>
                          {fields.hideAllLabel}
                        </Button>
                      </div>
                    </div>
                    <div
                      style={{ height: this.calculateFieldsBoxHeight() }}
                      className="overflow-auto">
                      {Object.keys(schema.properties)
                        .filter(
                          field =>
                            !!(schema.properties[field].title || field).trim()
                        )
                        .map((field, index) => (
                          <div
                            key={index}
                            className="flex justify-between ph6 pv3 pointer hover-bg-muted-5"
                            onClick={() => onToggleColumn(field)}>
                            <span className="w-70 truncate">
                              {schema.properties[field].title || field}
                            </span>
                            <Toggle
                              size="regular"
                              checked={!hiddenFields.includes(field)}
                            />
                          </div>
                        ))}
                    </div>
                  </div>
                </div>
              )}
            </div>
          )}
          {isDownloadVisible && (
            <div title={download.label} className="mh2">
              <ButtonWithIcon
                icon={
                  <span
                    className={`${download.disabled ? '' : forcedColor} mh2`}>
                    <IconDownload size={MEDIUM_ICON_SIZE} />
                  </span>
                }
                variation="tertiary"
                disabled={download.disabled}
                isLoading={download.isLoading}
                size="regular"
                onClick={download.handleCallback}>
                {download.label && (
                  <span className={`${download.disabled ? '' : forcedColor}`}>
                    {download.label}
                  </span>
                )}
              </ButtonWithIcon>
            </div>
          )}
          {isUploadVisible && (
            <div title={upload.label} className="mh2">
              <ButtonWithIcon
                icon={
                  <span
                    className={`${upload.disabled ? '' : forcedColor} mh2`}
                    style={ICON_OPTICAL_COMPENSATION}>
                    <IconUpload size={HEAVY_ICON_SIZE} />
                  </span>
                }
                variation="tertiary"
                disabled={upload.disabled}
                isLoading={upload.isLoading}
                size="regular"
                onClick={upload.handleCallback}>
                {upload.label && (
                  <span className={`${upload.disabled ? '' : forcedColor}`}>
                    {upload.label}
                  </span>
                )}
              </ButtonWithIcon>
            </div>
          )}
          {isExtraActionsVisible && (
            <div title={extraActions.label} className="mh2">
              <ActionMenu
                hideCaretIcon
                buttonProps={{
                  variation: 'tertiary',
                  icon: (
                    <span className="c-on-base">
                      <IconOptionsDots />
                    </span>
                  ),
                  size: 'regular',
                }}
                options={extraActions.actions.map(action => {
                  return {
                    label: action.label,
                    onClick: action.handleCallback,
                  }
                })}
              />
            </div>
          )}
          {isNewLineVisible &&
            (newLine.actions ? (
              <ButtonGroup
                buttons={[
                  <ButtonWithIcon
                    isActiveOfGroup
                    key="new-line-button"
                    icon={<IconPlus solid size={LIGHT_ICON_SIZE} />}
                    onClick={newLine.handleCallback}
                    {...newLineButtonProps}>
                    {newLine.label}
                  </ButtonWithIcon>,
                  <ActionMenu
                    isActiveOfGroup
                    key="actions-button"
                    buttonProps={newLineButtonProps}
                    options={newLine.actions}
                  />,
                ]}
              />
            ) : (
              <ButtonWithIcon
                icon={<IconPlus solid size={LIGHT_ICON_SIZE} />}
                onClick={newLine.handleCallback}
                {...newLineButtonProps}>
                {newLine.label}
              </ButtonWithIcon>
            ))}
        </div>
      </div>
    )
  }
}

Toolbar.defaultProps = {
  actions: {
    extraActions: {
      actions: [],
    },
  },
}

Toolbar.propTypes = {
  actions: PropTypes.shape({
    inputSearch: PropTypes.shape({
      onSubmit: PropTypes.func,
    }),
    density: PropTypes.shape({
      buttonLabel: PropTypes.string,
      lowOptionLabel: PropTypes.string,
      handleCallback: PropTypes.func,
      mediumOptionLabel: PropTypes.string,
      highOptionLabel: PropTypes.string,
      alignMenu: PropTypes.oneOf(['right', 'left']),
    }),
    fields: PropTypes.shape({
      label: PropTypes.string,
      showAllLabel: PropTypes.string,
      hideAllLabel: PropTypes.string,
      alignMenu: PropTypes.oneOf(['right', 'left']),
    }),
    download: PropTypes.shape({
      label: PropTypes.string,
      handleCallback: PropTypes.func,
      isLoading: PropTypes.bool,
      disabled: PropTypes.bool,
    }),
    upload: PropTypes.shape({
      label: PropTypes.string,
      handleCallback: PropTypes.func,
      isLoading: PropTypes.bool,
      disabled: PropTypes.bool,
    }),
    extraActions: PropTypes.shape({
      label: PropTypes.string,
      actions: PropTypes.arrayOf(
        PropTypes.shape({
          label: PropTypes.string,
          handleCallback: PropTypes.func,
        })
      ),
      alignMenu: PropTypes.oneOf(['right', 'left']),
      isLoading: PropTypes.bool,
    }),
    newLine: PropTypes.shape({
      label: PropTypes.string.isRequired,
      handleCallback: PropTypes.func.isRequired,
      disabled: PropTypes.bool,
      isLoading: PropTypes.bool,
      actions: PropTypes.arrayOf(
        PropTypes.shape({
          label: PropTypes.string,
          handleCallback: PropTypes.func,
          toggle: PropTypes.shape({
            checked: PropTypes.bool,
            semantic: PropTypes.bool,
          }),
        })
      ),
    }),
  }),
  schema: PropTypes.object.isRequired,
  hiddenFields: PropTypes.array,
  onToggleColumn: PropTypes.func,
  onDeselectAllLines: PropTypes.func,
  onHideAllColumns: PropTypes.func,
  onShowAllColumns: PropTypes.func,
  onToggleDensity: PropTypes.func,
  selectedDensity: PropTypes.string,
  loading: PropTypes.bool,
}

export default Toolbar

```


[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/Table/BulkActionsSelectedRows.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

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

class BulkActionsSelectedRows extends Component {
  shouldComponentUpdate(nextProps) {
    // This prevents the value from showing 0 when bar is closing
    // It reduces the "noise" given to the user from a ux point of view
    if (nextProps.selectedRowsLength === 0) {
      return false
    }
    return true
  }

  render() {
    return this.props.selectedRowsLength
  }
}

BulkActionsSelectedRows.propTypes = {
  selectedRowsLength: PropTypes.number.isRequired,
}

export default BulkActionsSelectedRows

```


[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/Table/SwitchablePagination.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import PropTypes from 'prop-types'

import Pagination from '../Pagination'

function SwitchablePagination({ children, enabled, ...paginationProps }) {
  if (enabled) {
    return <Pagination {...paginationProps}>{children}</Pagination>
  }
  return children
}

SwitchablePagination.defaultProps = {
  enabled: false,
}

SwitchablePagination.propTypes = {
  currentItemFrom: PropTypes.number,
  currentItemTo: PropTypes.number,
  enabled: PropTypes.bool,
  onNextClick: PropTypes.func,
  onPrevClick: PropTypes.func,
  onRowsChange: PropTypes.func,
  rowsOptions: PropTypes.array,
  textOf: PropTypes.node,
  textShowRows: PropTypes.node,
  totalItems: PropTypes.number,
}

export default SwitchablePagination

```


[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/ModalDialog/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 Modal from '../Modal'
import Button from '../Button'

const NOOP = () => {}

class ModalDialog extends Component {
  handleCloseTransitionFinish = () => {
    this.props.onCloseTransitionFinish && this.props.onCloseTransitionFinish()
  }

  handleConfirmation = () => {
    this.props.confirmation &&
      this.props.confirmation.onClick &&
      this.props.confirmation.onClick()
  }

  handleCancelation = () => {
    this.props.cancelation &&
      this.props.cancelation.onClick &&
      this.props.cancelation.onClick()
  }

  render() {
    const { confirmation, cancelation, loading, onClose } = this.props

    return (
      <Modal
        {...this.props}
        onClose={loading ? NOOP : onClose}
        onCloseTransitionFinish={this.handleCloseTransitionFinish}>
        {this.props.children}
        <div className="vtex-modal__confirmation flex justify-end mt8">
          <span className="mr4">
            <Button
              size="small"
              variation="tertiary"
              disabled={loading}
              onClick={this.handleCancelation}>
              {cancelation.label}
            </Button>
          </span>

          <Button
            size="small"
            variation={confirmation.isDangerous ? 'danger' : 'primary'}
            isLoading={loading}
            onClick={this.handleConfirmation}>
            {confirmation.label}
          </Button>
        </div>
      </Modal>
    )
  }
}

ModalDialog.propTypes = {
  children: PropTypes.node,
  confirmation: PropTypes.shape({
    label: PropTypes.string.isRequired,
    onClick: PropTypes.func.isRequired,
    isDangerous: PropTypes.bool,
  }).isRequired,
  cancelation: PropTypes.shape({
    label: PropTypes.string.isRequired,
    onClick: PropTypes.func.isRequired,
  }).isRequired,
  onClose: PropTypes.func,
  loading: PropTypes.bool,
  onCloseTransitionFinish: PropTypes.func,
}

export default ModalDialog

```


[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/ModalDialog/README.md from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```markdown
Default

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

class ModalDialogExample extends React.Component {
  constructor() {
    super()
    this.state = { isModalOpen: false }
    this.handleModalToggle = this.handleModalToggle.bind(this)
  }

  handleModalToggle() {
    this.setState({ isModalOpen: !this.state.isModalOpen })
  }

  render() {
    return (

        <Button onClick={this.handleModalToggle}>Open modal</Button>

        <ModalDialog
          centered
          confirmation={{
            onClick: this.handleModalToggle,
            label: 'Ok',
          }}
          cancelation={{
            onClick: this.handleModalToggle,
            label: 'Cancel',
          }}
          isOpen={this.state.isModalOpen}
          onClose={this.handleModalToggle}>

Confirm?

            Some child content before the action buttons. Lorem ipsum dolor sit
            amet, consectetur adipiscing elit. Praesent semper eget magna sit
            amet maximus. In rutrum, justo sodales euismod dignissim, justo orci
            venenatis lectus, vel semper turpis nunc a justo.

        </ModalDialog>

    )
  }
}

;<ModalDialogExample />
```

Loading

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

class ModalDialogExample extends React.Component {
  constructor() {
    super()
    this.state = { isModalOpen: false, loading: false }
    this.handleOpen = this.handleOpen.bind(this)
    this.handleConfirmation = this.handleConfirmation.bind(this)
    this.handleCancelation = this.handleCancelation.bind(this)
  }

  handleOpen() {
    this.setState({ isModalOpen: true })
  }

  handleConfirmation() {
    this.setState({ loading: true })
    setTimeout(
      () => this.setState({ isModalOpen: false, loading: false }),
      1500
    )
  }

  handleCancelation() {
    this.setState({ isModalOpen: false })
  }

  render() {
    const { isModalOpen, loading } = this.state
    return (

        <Button onClick={this.handleOpen}>Open modal</Button>

        <ModalDialog
          centered
          loading={loading}
          confirmation={{
            onClick: this.handleConfirmation,
            label: 'Ok',
          }}
          cancelation={{
            onClick: this.handleCancelation,
            label: 'Cancel',
          }}
          isOpen={isModalOpen}
          onClose={this.handleCancelation}>

Confirm?

            Some child content before the action buttons. Lorem ipsum dolor sit
            amet, consectetur adipiscing elit. Praesent semper eget magna sit
            amet maximus. In rutrum, justo sodales euismod dignissim, justo orci
            venenatis lectus, vel semper turpis nunc a justo.

        </ModalDialog>

    )
  }
}

;<ModalDialogExample />
```

Dangerous

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

class ModalDialogExample extends React.Component {
  constructor() {
    super()
    this.state = { isModalOpen: false, loading: false }
    this.handleOpen = this.handleOpen.bind(this)
    this.handleConfirmation = this.handleConfirmation.bind(this)
    this.handleCancelation = this.handleCancelation.bind(this)
  }

  handleOpen() {
    this.setState({ isModalOpen: true })
  }

  handleConfirmation() {
    this.setState({ loading: true })
    setTimeout(
      () => this.setState({ isModalOpen: false, loading: false }),
      1500
    )
  }

  handleCancelation() {
    this.setState({ isModalOpen: false })
  }

  render() {
    const { isModalOpen, loading } = this.state
    return (

        <Button onClick={this.handleOpen}>Open modal</Button>

        <ModalDialog
          centered
          loading={loading}
          confirmation={{
            onClick: this.handleConfirmation,
            label: 'Are you sure?',
            isDangerous: true,
          }}
          cancelation={{
            onClick: this.handleCancelation,
            label: 'Cancel',
          }}
          isOpen={isModalOpen}
          onClose={this.handleCancelation}>

Do you really, really, want to do that?

            Some child content before the action buttons. Lorem ipsum dolor sit
            amet, consectetur adipiscing elit. Praesent semper eget magna sit
            amet maximus. In rutrum, justo sodales euismod dignissim, justo orci
            venenatis lectus, vel semper turpis nunc a justo.

        </ModalDialog>

    )
  }
}

;<ModalDialogExample />
```

```


[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/Collapsible/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
.jsFocusVisible :focus:not([data-focus-visible-added]) {
  outline: none;
}

```


[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/Collapsible/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 CaretDown from '../icon/CaretDown'
import CaretUp from '../icon/CaretUp'
import { jsFocusVisible } from './styles.css'

const colorMap = {
  base: 'c-on-base',
  primary: 'c-action-primary',
  muted: 'c-muted-3',
}

function handleClick(callback, isOpen) {
  callback &&
    callback({
      target: {
        isOpen,
      },
    })
}

function mapToCSSClass(color) {
  return colorMap[color]
}

class Collapsible extends Component {
  constructor(props) {
    super(props)
    this.childrenRef = React.createRef()
    this.openTimeout = null
    this.closeTimeout = null
    this.state = {
      height: 0,
    }
  }

  handleTransitionEnd = () => {
    this.setState({
      height: this.props.isOpen ? 'auto' : 0,
    })
  }

  openCard = () => {
    this.childrenRef.current.style.height = 'auto'
    const childrenHeight = this.childrenRef.current.offsetHeight
    this.childrenRef.current.style.height = 0
    /** after force setting element height like the line above
     * you have to force layout / reflow so the height value
     * may actually apply. You can do this by requesting
     * element offsetHeight again, like the line below
     */
    this.childrenRef.current.offsetHeight
    this.setState({
      height: childrenHeight,
    })
  }

  closeCard = () => {
    const childrenHeight = this.childrenRef.current.offsetHeight
    this.setState(
      {
        height: childrenHeight,
      },
      () => {
        window.requestAnimationFrame(() => this.setState({ height: 0 }))
      }
    )
  }

  componentDidMount() {
    if (this.props.isOpen) {
      this.setState({ height: 'auto' })
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.isOpen !== this.props.isOpen) {
      if (this.props.isOpen) {
        this.openCard()
      } else {
        this.closeCard()
      }
    }
  }

  render() {
    const {
      align,
      children,
      header,
      muted,
      onClick: callback,
      isOpen,
      arrowAlign,
    } = this.props
    let { caretColor } = this.props
    const { height } = this.state
    const childrenContainerStyle = {
      height,
      overflow: 'hidden',
      transition: 'height 250ms ease-in-out',
    }
    if (muted) {
      caretColor = caretColor || 'muted'
      console.warn(
        `The "muted" prop on the "Collapsible" component is depreacted and will be removed in a future version. Use "caretColor='muted'" instead.`
      )
    }

    const color = caretColor ? mapToCSSClass(caretColor) : 'c-action-primary'

    return (
      <div className={jsFocusVisible}>
        <div
          className="flex flex-row items-center pointer"
          tabIndex={0}
          role="button"
          onClick={() => handleClick(callback, !isOpen)}
          onKeyDown={e => e.key === 'Enter' && handleClick(callback, !isOpen)}
          aria-expanded={isOpen}>
          {align === 'left' ? (
            <Fragment>
              <div className={`${color} mr3 self-${arrowAlign}`}>
                {isOpen ? <CaretUp /> : <CaretDown />}
              </div>
              <div className="flex-grow-1">{header}</div>
            </Fragment>
          ) : (
            <Fragment>
              <div className="flex-grow-1">{header}</div>
              <div className={`${color} ml3  self-${arrowAlign}`}>
                {isOpen ? <CaretUp /> : <CaretDown />}
              </div>
            </Fragment>
          )}
        </div>
        <div
          ref={this.childrenRef}
          style={childrenContainerStyle}
          role="region"
          onTransitionEnd={this.handleTransitionEnd}>
          {children}
        </div>
      </div>
    )
  }
}

Collapsible.defaultProps = {
  align: 'left',
  isOpen: false,
  muted: false,
  arrowAlign: 'center',
}

Collapsible.propTypes = {
  /** Caret alignment.
   * Use _right_ alignment only in small width scenarios. */
  align: PropTypes.oneOf(['right', 'left']),
  /** Content of the collapsible */
  children: PropTypes.node.isRequired,
  /** Component to be used as the header of the collapsible. */
  header: PropTypes.node.isRequired,
  /** @deprecated Use the 'muted' option in the caretColor prop instead.
   * To be used only in dense scenarios, or when the affordance is clearly
   * conveyed by the context. */
  muted: PropTypes.bool,
  /** Controls whether the collapsible is open or not. */
  isOpen: PropTypes.bool,
  /** _onClick_ event. */
  onClick: PropTypes.func,
  /** Color or semantic to be applied to the Caret Icon in the Collapsible header.*/
  caretColor: PropTypes.oneOf(Object.keys(colorMap)),
  /** Vertical position of arrow icon.*/
  arrowAlign: PropTypes.oneOf([
    'start',
    'center',
    'end',
    'baseline',
    'stretch',
  ]),
}

export default Collapsible

```