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

[Assistant]
Here's the implementation:

```markdown
#### The FilterOptions is a more horizontally compacted way of displaying filter atoms. Although designed to be used with the Modal component, it can also be used on its own with any other way you chose to display your data.

The FilterOptions is optimized for small viewport applications which do not provide enough horizontal space to work with. Making use of vertical collapsible components, it displays filter data to the user allowing them to choose which filter suits their needs.

### 👍 Dos

- Use the FilterOptions near of the content that will be filtered.
- Try offering as many filters and operators as possible. With the diversity of operations VTEX supports, we can never predict all the diverse use cases our merchants need.
- Hide the component when possible. After applying the filters is possible to display which filters are applied with other components.

### 👎 Don'ts

- Don't present too many filters in one single FilterOptions.

### Related components

- For applications with a larger viewport or when working with tables prefer using the <a href="#/Components/Display/FilterBar">FilterBar</a> component.

Simple product filter example

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

class MySimpleFilter extends React.Component {
  constructor() {
    super()
    this.state = { statements: [] }
    this.simpleInputObject = this.simpleInputObject.bind(this)
    this.getSimpleVerbs = this.getSimpleVerbs.bind(this)
    this.renderSimpleFilterLabel = this.renderSimpleFilterLabel.bind(this)
  }

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

  getSimpleVerbs() {
    return [
      {
        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: {},
        },
      },
    ]
  }

  renderSimpleFilterLabel(statement) {
    if (!statement || !statement.object) {
      // you should treat empty object cases only for alwaysVisibleFilters
      return 'Any'
    }
    return `${
      statement.verb === '='
        ? 'is'
        : statement.verb === '!='
        ? 'is not'
        : 'contains'
    } ${statement.object}`
  }

  render() {
    return (
      <FilterOptions
        alwaysVisibleFilters={['id', 'category', 'brand']}
        statements={this.state.statements}
        onChangeStatements={statements => this.setState({ statements })}
        clearAllFiltersButtonLabel="Clear Filters"
        options={{
          id: {
            label: 'ID',
            renderFilterLabel: this.renderSimpleFilterLabel,
            verbs: this.getSimpleVerbs(),
          },
          category: {
            label: 'Category',
            renderFilterLabel: this.renderSimpleFilterLabel,
            verbs: this.getSimpleVerbs(),
          },
          brand: {
            label: 'Brand',
            renderFilterLabel: this.renderSimpleFilterLabel,
            verbs: this.getSimpleVerbs(),
          },
        }}
      />
    )
  }
}
;<MySimpleFilter />
```

Filter users example

```js
class MyUsersFilter extends React.Component {
  constructor() {
    super()
    this.state = { statements: [] }
    this.simpleInputObject = this.simpleInputObject.bind(this)
    this.cpfInputObject = this.cpfInputObject.bind(this)
    this.ageInputObject = this.ageInputObject.bind(this)
    this.ageInputRangeObject = this.ageInputRangeObject.bind(this)
    this.classSelectorObject = this.classSelectorObject.bind(this)
    this.cpfValidationRegex = RegExp(
      '([0-9]{2}[.]?[0-9]{3}[.]?[0-9]{3}[/]?[0-9]{4}[-]?[0-9]{2})|([0-9]{3}[.]?[0-9]{3}[.]?[0-9]{3}[-]?[0-9]{2})'
    )
  }

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

  cpfInputObject(
    { statements, values, statementIndex, error, onChangeObjectCallback },
    shouldValidate = false
  ) {
    const errorMessage =
      shouldValidate && values
        ? this.cpfValidationRegex.test(values)
          ? null
          : 'Invalid CPF'
        : null
    return (
      <Input
        placeholder="Insert cpf…"
        type="number"
        errorMessage={errorMessage}
        min={0}
        value={values || ''}
        onChange={e => {
          onChangeObjectCallback(e.target.value.replace(/\D/g, ''))
        }}
      />
    )
  }

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

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

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

            onChangeObjectCallback(currentObject)
          }}
        />

    )
  }

  classSelectorObject({
    statements,
    values,
    statementIndex,
    error,
    extraParams,
    onChangeObjectCallback,
  }) {
    const initialValue = {
      vip: true,
      gold: true,
      silver: true,
      platinum: true,
    }

    const toCheckedMap = ([key, value]) => [key, { label: key, checked: value }]
    const toValues = ([key, value]) => [key, value.checked]

    const checkedMap = Object.fromEntries(
      Object.entries({ ...initialValue, ...(values || {}) }).map(toCheckedMap)
    )
    return (
      <CheckboxGroup
        name="simpleCheckboxGroup"
        label="All Filters"
        checkedMap={checkedMap}
        onGroupChange={checkedMap => {
          const newValues = Object.fromEntries(
            Object.entries(checkedMap).map(toValues)
          )
          onChangeObjectCallback(newValues)
        }}
      />
    )
  }

  render() {
    return (
      <FilterOptions
        alwaysVisibleFilters={['name', 'email', 'class']}
        statements={this.state.statements}
        onChangeStatements={statements => this.setState({ statements })}
        clearAllFiltersButtonLabel="Clear Filters"
        options={{
          name: {
            label: 'Name',
            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: {},
                },
              },
            ],
          },
          email: {
            label: 'Email',
            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: 'contains',
                value: 'contains',
                object: {
                  renderFn: this.simpleInputObject,
                  extraParams: {},
                },
              },
              {
                label: 'is',
                value: '=',
                object: {
                  renderFn: this.simpleInputObject,
                  extraParams: {},
                },
              },
              {
                label: 'is not',
                value: '!=',
                object: {
                  renderFn: this.simpleInputObject,
                  extraParams: {},
                },
              },
            ],
          },
          class: {
            label: 'Class',
            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.classSelectorObject,
                  extraParams: {},
                },
              },
            ],
          },
          age: {
            label: 'Age',
            renderFilterLabel: st =>
              `${
                st.verb === 'between'
                  ? `between ${st.object.first} and ${st.object.last}`
                  : `is ${st.object}`
              }`,
            verbs: [
              {
                label: 'is',
                value: '=',
                object: {
                  renderFn: this.ageInputObject,
                  extraParams: {},
                },
              },
              {
                label: 'is between',
                value: 'between',
                object: {
                  renderFn: this.ageInputRangeObject,
                  extraParams: {},
                },
              },
            ],
          },
          cpf: {
            label: 'Document',
            renderFilterLabel: st =>
              `${st.verb === '=' ? 'is' : 'contains'} ${st.object}`,
            verbs: [
              {
                label: 'is',
                value: '=',
                object: {
                  renderFn: obj => this.cpfInputObject(obj, true),
                  extraParams: {},
                },
              },
              {
                label: 'contains',
                value: 'contains',
                object: {
                  renderFn: this.cpfInputObject,
                  extraParams: {},
                },
              },
            ],
          },
        }}
      />
    )
  }
}
;<MyUsersFilter />
```

Filter orders example

```js
class MyOrdersFilter extends React.Component {
  constructor() {
    super()
    this.state = { statements: [] }
    this.simpleInputObject = this.simpleInputObject.bind(this)
    this.simpleInputVerbs = this.simpleInputVerbs.bind(this)
    this.datePickerObject = this.datePickerObject.bind(this)
    this.datePickerRangeObject = this.datePickerRangeObject.bind(this)
    this.statusSelectorObject = this.statusSelectorObject.bind(this)
  }

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

  simpleInputVerbs() {
    return [
      {
        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: {},
        },
      },
    ]
  }

  datePickerObject({
    statements,
    values,
    statementIndex,
    error,
    onChangeObjectCallback,
  }) {
    return (

        <DatePicker
          value={values || new Date()}
          onChange={date => {
            onChangeObjectCallback(date)
          }}
          locale="pt-BR"
        />

    )
  }

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

        <DatePicker
          label="from"
          value={(values && values.from) || new Date()}
          onChange={date => {
            onChangeObjectCallback({ ...(values || {}), from: date })
          }}
          locale="pt-BR"
        />

        <DatePicker
          label="to"
          value={(values && values.to) || new Date()}
          onChange={date => {
            onChangeObjectCallback({ ...(values || {}), to: date })
          }}
          locale="pt-BR"
        />

    )
  }

  statusSelectorObject({
    statements,
    values,
    statementIndex,
    error,
    extraParams,
    onChangeObjectCallback,
  }) {
    const initialValue = {
      'Window to cancelation': true,
      Canceling: true,
      Canceled: true,
      'Payment pending': true,
      'Payment approved': true,
      'Ready for handling': true,
      'Handling shipping': true,
      'Ready for invoice': true,
      Invoiced: true,
      Complete: true,
    }
    const toCheckedMap = ([key, value]) => [key, { label: key, checked: value }]
    const toValues = ([key, value]) => [key, value.checked]

    const checkedMap = Object.fromEntries(
      Object.entries({ ...initialValue, ...(values || {}) }).map(toCheckedMap)
    )
    return (
      <CheckboxGroup
        name="simpleCheckboxGroup"
        label="All Filters"
        checkedMap={checkedMap}
        onGroupChange={checkedMap => {
          const newValues = Object.fromEntries(
            Object.entries(checkedMap).map(toValues)
          )
          onChangeObjectCallback(newValues)
        }}
      />
    )
  }

  render() {
    return (
      <FilterOptions
        alwaysVisibleFilters={['id', 'email', 'status', 'invoicedate']}
        statements={this.state.statements}
        onChangeStatements={statements => this.setState({ statements })}
        clearAllFiltersButtonLabel="Clear Filters"
        options={{
          id: {
            label: 'Order ID',
            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: this.simpleInputVerbs(),
          },
          email: {
            label: 'Email',
            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: this.simpleInputVerbs(),
          },
          status: {
            label: 'Status',
            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.statusSelectorObject,
                  extraParams: {},
                },
              },
            ],
          },
          invoicedate: {
            label: 'Invoiced date',
            renderFilterLabel: st => {
              if (!st || !st.object) return 'All'
              return `${
                st.verb === 'between'
                  ? `between ${st.object.from} and ${st.object.to}`
                  : `is ${st.object}`
              }`
            },
            verbs: [
              {
                label: 'is',
                value: '=',
                object: {
                  renderFn: this.datePickerObject,
                  extraParams: {},
                },
              },
              {
                label: 'is between',
                value: 'between',
                object: {
                  renderFn: this.datePickerRangeObject,
                  extraParams: {},
                },
              },
            ],
          },
          utm: {
            label: 'UTM Source',
            renderFilterLabel: st =>
              `${st.verb === '=' ? 'is' : 'contains'} ${st.object}`,
            verbs: this.simpleInputVerbs(),
          },
          seller: {
            label: 'Seller',
            renderFilterLabel: st =>
              `${st.verb === '=' ? 'is' : 'contains'} ${st.object}`,
            verbs: this.simpleInputVerbs(),
          },
        }}
      />
    )
  }
}
;<MyOrdersFilter />
```

Filter Options with Modal Example

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

class MyUsersFilter extends React.Component {
  constructor() {
    super()
    this.state = { statements: [] }
    this.simpleInputObject = this.simpleInputObject.bind(this)
    this.cpfInputObject = this.cpfInputObject.bind(this)
    this.ageInputObject = this.ageInputObject.bind(this)
    this.ageInputRangeObject = this.ageInputRangeObject.bind(this)
    this.classSelectorObject = this.classSelectorObject.bind(this)
    this.cpfValidationRegex = RegExp(
      '([0-9]{2}[.]?[0-9]{3}[.]?[0-9]{3}[/]?[0-9]{4}[-]?[0-9]{2})|([0-9]{3}[.]?[0-9]{3}[.]?[0-9]{3}[-]?[0-9]{2})'
    )
  }

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

  cpfInputObject(
    { statements, values, statementIndex, error, onChangeObjectCallback },
    shouldValidate = false
  ) {
    const errorMessage =
      shouldValidate && values
        ? this.cpfValidationRegex.test(values)
          ? null
          : 'Invalid CPF'
        : null
    return (
      <Input
        placeholder="Insert cpf…"
        type="number"
        errorMessage={errorMessage}
        min={0}
        value={values || ''}
        onChange={e => {
          onChangeObjectCallback(e.target.value.replace(/\D/g, ''))
        }}
      />
    )
  }

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

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

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

            onChangeObjectCallback(currentObject)
          }}
        />

    )
  }

  classSelectorObject({
    statements,
    values,
    statementIndex,
    error,
    extraParams,
    onChangeObjectCallback,
  }) {
    const initialValue = {
      vip: true,
      gold: true,
      silver: true,
      platinum: true,
    }

    const toCheckedMap = ([key, value]) => [key, { label: key, checked: value }]
    const toValues = ([key, value]) => [key, value.checked]

    const checkedMap = Object.fromEntries(
      Object.entries({ ...initialValue, ...(values || {}) }).map(toCheckedMap)
    )
    return (
      <CheckboxGroup
        name="simpleCheckboxGroup"
        label="All Filters"
        checkedMap={checkedMap}
        onGroupChange={checkedMap => {
          const newValues = Object.fromEntries(
            Object.entries(checkedMap).map(toValues)
          )
          onChangeObjectCallback(newValues)
        }}
      />
    )
  }

  render() {
    return (
      <FilterOptions
        alwaysVisibleFilters={['name', 'email', 'class']}
        statements={this.state.statements}
        onChangeStatements={statements => this.setState({ statements })}
        clearAllFiltersButtonLabel="Clear Filters"
        options={{
          name: {
            label: 'Name',
            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: {},
                },
              },
            ],
          },
          email: {
            label: 'Email',
            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: 'contains',
                value: 'contains',
                object: {
                  renderFn: this.simpleInputObject,
                  extraParams: {},
                },
              },
              {
                label: 'is',
                value: '=',
                object: {
                  renderFn: this.simpleInputObject,
                  extraParams: {},
                },
              },
              {
                label: 'is not',
                value: '!=',
                object: {
                  renderFn: this.simpleInputObject,
                  extraParams: {},
                },
              },
            ],
          },
          class: {
            label: 'Class',
            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.classSelectorObject,
                  extraParams: {},
                },
              },
            ],
          },
          age: {
            label: 'Age',
            renderFilterLabel: st =>
              `${
                st.verb === 'between'
                  ? `between ${st.object.first} and ${st.object.last}`
                  : `is ${st.object}`
              }`,
            verbs: [
              {
                label: 'is',
                value: '=',
                object: {
                  renderFn: this.ageInputObject,
                  extraParams: {},
                },
              },
              {
                label: 'is between',
                value: 'between',
                object: {
                  renderFn: this.ageInputRangeObject,
                  extraParams: {},
                },
              },
            ],
          },
          cpf: {
            label: 'Document',
            renderFilterLabel: st =>
              `${st.verb === '=' ? 'is' : 'contains'} ${st.object}`,
            verbs: [
              {
                label: 'is',
                value: '=',
                object: {
                  renderFn: obj => this.cpfInputObject(obj, true),
                  extraParams: {},
                },
              },
              {
                label: 'contains',
                value: 'contains',
                object: {
                  renderFn: this.cpfInputObject,
                  extraParams: {},
                },
              },
            ],
          },
        }}
      />
    )
  }
}

class ModalExample extends React.Component {
  constructor() {
    super()
    this.state = { isModalOpen: false }
    this.handleOpenModal = this.handleOpenModal.bind(this)
    this.handleCloseModal = this.handleCloseModal.bind(this)
  }

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

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

  render() {
    return (
      <React.Fragment>
        <Button onClick={this.handleOpenModal}>Filters</Button>

        <Modal
          isOpen={this.state.isModalOpen}
          title="User Filters"
          responsiveFullScreen
          centered
          bottomBar={

                <Button variation="tertiary" onClick={this.handleCloseModal}>
                  Clear
                </Button>

                <Button variation="secondary" onClick={this.handleCloseModal}>
                  Apply
                </Button>

          }
          onClose={this.handleCloseModal}>

            <MyUsersFilter />

        </Modal>
      </React.Fragment>
    )
  }
}

;<ModalExample />
```

```


[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/FilterOptions/FilterColapsible.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 Statement from '../Statement'
import Collapsible from '../Collapsible'

class FilterColapsible extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      isCollapsibleOpen: props.beginWithOpenCollapsibles,
    }
  }

  handleChangeStatement = (newValue, structure) => {
    const { statement } = this.props
    return this.props.onChangeStatement({ ...statement, [structure]: newValue })
  }

  render() {
    const { options, subject, statement } = this.props
    const { isCollapsibleOpen } = this.state

    const shouldOmitVerb = options[subject].verbs.length === 1

    return (
      <div className="flex items-stretch">
        <Collapsible
          isOpen={isCollapsibleOpen}
          align="left"
          caretColor="base"
          header={
            <div className="flex items-center h-100 ph4 pv5">
              <span className="flex nl3 ">
                <span className="fw5 f4">{options[subject].label}</span>
              </span>
            </div>
          }
          onClick={e => this.setState({ isCollapsibleOpen: e.target.isOpen })}>
          <div className="ma5">
            {isCollapsibleOpen && (
              <Statement
                isFullWidth
                omitSubject
                omitVerbs={shouldOmitVerb}
                options={options}
                subjectPlaceholder={'…'}
                statements={[statement]}
                onChangeStatement={this.handleChangeStatement}
                onChangeObjectCallback={value =>
                  this.handleChangeStatement(value, 'object')
                }
              />
            )}
          </div>
        </Collapsible>
      </div>
    )
  }
}

FilterColapsible.defaultProps = {
  statement: {
    subject: null,
    verb: null,
    object: null,
    error: null,
  },
}

FilterColapsible.propTypes = {
  options: PropTypes.object.isRequired,
  subject: PropTypes.string.isRequired,
  statement: PropTypes.objectOf(
    PropTypes.shape({
      subject: PropTypes.string,
      verb: PropTypes.string.isRequired,
      object: PropTypes.object,
      error: PropTypes.any,
    })
  ),
  onChangeStatement: PropTypes.func.isRequired,
  beginWithOpenCollapsibles: PropTypes.bool,
}

export default FilterColapsible

```


[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/Button/index.tsx from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, Fragment } from 'react'
// import PropTypes from 'prop-types'

import Spinner from '../Spinner'
// import { withForwardedRef, refShape } from '../../modules/withForwardedRef'
import { withForwardedRef } from '../../modules/withForwardedRef'

class Button extends Component<any> {
  handleClick = event => {
    !this.props.disabled &&
      !this.props.isLoading &&
      this.props.onClick &&
      this.props.onClick(event)
  }

  componentDidMount() {
    if (this.props.icon) {
      console.warn(
        'Button: The prop "icon" of the "Button" component has been deprecated, and will be removed in a future version. Please use the component "ButtonWithIcon" instead'
      )
    }
    if (this.props.collapseLeft || this.props.collapseRight) {
      console.warn(
        'Button: The props "collapseLeft" and "collapseRight" of the "Button" component have been deprecated, and will be removed in a future version. Please use the component "ButtonPlain" instead'
      )
    }
  }

  render() {
    const {
      size,
      block,
      variation,
      icon,
      children,
      isLoading,
      collapseLeft,
      collapseRight,
      isGrouped,
      isActiveOfGroup,
      isFirstOfGroup,
      isLastOfGroup,
      href,
      target,
      rel,
      referrerPolicy,
      download,
      noUpperCase,
      noWrap,
    } = this.props

    const disabled = this.props.disabled || isLoading
    const iconOnly = icon || this.props.iconOnly

    const isTertiary =
      variation === 'tertiary' ||
      variation === 'inverted-tertiary' ||
      variation === 'danger-tertiary'

    let classes = 'vtex-button bw1 ba fw5 v-mid relative pa0 lh-solid '
    let labelClasses =
      'vtex-button__label flex items-center justify-center h-100 '
    let loaderSize = 15
    let horizontalPadding = 0

    classes += !isGrouped ? 'br2 ' : ''
    classes += isFirstOfGroup ? 'br2 br--left ' : ''
    classes += isLastOfGroup ? 'br2 br--right ' : ''

    switch (size) {
      case 'small':
        classes += 'min-h-small t-action--small '
        horizontalPadding = iconOnly ? 3 : 5
        break
      case 'large':
        classes += 'min-h-large t-action--large '
        horizontalPadding = iconOnly ? 5 : 7
        loaderSize = 25
        break
      default:
        classes += 'min-h-regular t-action '
        horizontalPadding = iconOnly ? 4 : 6
        loaderSize = 20
        break
    }

    if (isTertiary && (collapseLeft || collapseRight)) {
      horizontalPadding = 2
    }

    labelClasses += `ph${horizontalPadding} `

    if (isTertiary && noUpperCase) {
      labelClasses += 'ttn '
    }

    if (collapseLeft && isTertiary) {
      labelClasses += `nl1 `
    }
    if (collapseRight && isTertiary) {
      labelClasses += `nr1 `
    }
    if (noWrap) {
      labelClasses += 'nowrap '
    }

    if (iconOnly) {
      classes += 'icon-button dib '
    }

    const primaryEnabledClasses =
      'bg-action-primary b--action-primary c-on-action-primary hover-bg-action-primary hover-b--action-primary hover-c-on-action-primary '
    const secondaryEnabledClasses =
      'bg-action-secondary b--action-secondary c-on-action-secondary hover-bg-action-secondary hover-b--action-secondary hover-c-on-action-secondary '

    if (isGrouped && !disabled) {
      classes += isActiveOfGroup
        ? primaryEnabledClasses
        : secondaryEnabledClasses
    } else {
      switch (variation) {
        default:
        case 'primary': {
          if (disabled) {
            classes += 'bg-disabled b--muted-5 c-on-disabled '
          } else {
            classes += primaryEnabledClasses
          }
          break
        }
        case 'secondary': {
          if (disabled) {
            classes += 'bg-disabled b--muted-5 c-on-disabled '
          } else {
            classes += secondaryEnabledClasses
          }
          break
        }
        case 'tertiary': {
          if (disabled) {
            classes += 'bg-transparent b--transparent c-disabled '
          } else {
            classes +=
              'bg-transparent b--transparent c-action-primary hover-b--transparent '
          }
          if (!disabled) {
            classes += 'hover-bg-action-secondary hover-b--action-secondary '
          }
          break
        }
        case 'inverted-tertiary': {
          if (disabled) {
            classes += 'bg-transparent b--transparent c-disabled '
          } else {
            classes += 'bg-transparent b--transparent c-on-base--inverted '
          }
          break
        }
        case 'danger': {
          if (disabled) {
            classes += 'bg-disabled b--muted-5 c-on-disabled '
          } else {
            classes +=
              'bg-danger b--danger c-on-danger hover-bg-danger hover-b--danger hover-c-on-danger '
          }
          break
        }
        case 'danger-tertiary': {
          if (disabled) {
            classes += 'bg-transparent b--transparent c-disabled '
          } else {
            classes +=
              'bg-transparent b--transparent c-danger hover-bg-danger--faded hover-b--danger--faded '
          }
          break
        }
      }
    }

    if (!disabled) {
      classes += 'pointer '
    }

    if (block) {
      classes += 'w-100 '
      labelClasses += 'w-100 border-box '
    }

    if (href) {
      classes += 'inline-flex items-center no-underline '
    }

    const style: any = {}

    if (iconOnly) {
      style.fontSize = 0
    }

    // Active state receives the hover color of the Button.
    // No token available for this.
    if (isActiveOfGroup && !disabled) {
      style.backgroundColor = '#0c389f'
      style.borderColor = '#0c389f'
    }

    const linkModeProps = {
      target,
      rel,
      referrerPolicy,
      download,
    }

    const Element = href ? 'a' : 'button'

    return (
      <Element
        id={this.props.id}
        data-testid={this.props.testId}
        autoFocus={iconOnly ? undefined : this.props.autoFocus}
        disabled={iconOnly ? undefined : this.props.disabled}
        name={iconOnly ? undefined : this.props.name}
        value={iconOnly ? undefined : this.props.value}
        tabIndex={0}
        className={classes}
        href={href}
        onClick={this.handleClick}
        onMouseEnter={this.props.onMouseEnter}
        onMouseLeave={this.props.onMouseLeave}
        onMouseOver={this.props.onMouseOver}
        onMouseOut={this.props.onMouseOut}
        onMouseUp={this.props.onMouseUp}
        onMouseDown={this.props.onMouseDown}
        onFocus={this.props.onFocus}
        onBlur={this.props.onBlur}
        ref={this.props.forwardedRef}
        style={style}
        // Button-mode exclusive props
        type={href ? undefined : this.props.type}
        // Link-mode exclusive props
        {...(href && linkModeProps)}>
        {isLoading ? (
          <Fragment>
            <span className="vtex-button__spinner-container top-0 left-0 w-100 h-100 absolute flex justify-center items-center">
              <Spinner
                secondary={variation === 'primary' || variation === 'danger'}
                size={loaderSize}
              />
            </span>
            <span className={`${labelClasses} o-0`}>{children}</span>
          </Fragment>
        ) : (
          <div
            className={labelClasses}
            style={{
              paddingTop: '.25em',
              paddingBottom: '.32em',
            }}>
            {children}
          </div>
        )}
      </Element>
    )
  }
}

export default withForwardedRef(Button) as any

```


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

[Assistant]
Here's the implementation:

```markdown
#### Buttons are independent interactive elements used to trigger actions or to perform navigation.

### Chosing a variation
Our Styleguide defines 3 main variations based on the button visual proeminence. As with any UI element, the proeminence should be proportional to the importance of that element.
- Do mix variations as needed.
- Avoid having more than one Primary button, save this kind for the most important or most recommended action in a given screen.
- Use the Danger variation for destructive actions that delete data or lead to a state that is hard to recover. Danger buttons should _always_ have a confirmation dialog.
- In doubt, use Secondary buttons.
- Since reading speed can be slowed down by up to 20% when reading all caps (Breland & Breland - 1944), beware of "phrase long" button texts. Keep it short, and make your point in 3 words or less.

Types

```js

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

      <Button variation="secondary">Secondary</Button>

      <Button variation="tertiary">Tertiary</Button>

      <Button variation="danger">Danger</Button>

      <Button variation="danger-tertiary">Danger Tertiary</Button>

        <Button variation="inverted-tertiary">Inverted Tertiary</Button>

      <Button variation="primary" disabled>Primary</Button>

      <Button variation="secondary" disabled>Secondary</Button>

      <Button variation="tertiary" disabled>Tertiary</Button>

      <Button variation="danger" disabled>Danger</Button>

      <Button variation="danger-tertiary" disabled>Danger Tertiary</Button>

        <Button variation="inverted-tertiary" disabled>Inverted Tertiary</Button>

```

Sizes

```js

      <Button variation="primary" size="small">
        Small
      </Button>

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

      <Button variation="primary" size="large">
        Large
      </Button>

      <Button variation="secondary" size="small">
        Small
      </Button>

      <Button variation="secondary" size="regular">
        Regular
      </Button>

      <Button variation="secondary" size="large">
        Large
      </Button>

      <Button variation="tertiary" size="small">
        Small
      </Button>

      <Button variation="tertiary" size="regular">
        Regular
      </Button>

      <Button variation="tertiary" size="large">
        Large
      </Button>

      <Button variation="danger" size="small">
        Small
      </Button>

      <Button variation="danger" size="regular">
        Regular
      </Button>

      <Button variation="danger" size="large">
        Large
      </Button>

      <Button variation="danger-tertiary" size="small">
        Small
      </Button>

      <Button variation="danger-tertiary" size="regular">
        Regular
      </Button>

      <Button variation="danger-tertiary" size="large">
        Large
      </Button>

```

Box types

```js

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

    <Button variation="primary" block>
      Block
    </Button>

```

Link mode

```js

  <Button variation="primary" href="http://vtex.com" target="_blank">Sign in</Button>

```

Cancelling out button paddings. Useful for visually aligning tertiary buttons

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

;<>

Use collapse props when tertiary button is alone, to align it with the other elements

        <Box />

        <Button variation="tertiary" collapseLeft>Collapse left</Button>
        <Button variation="tertiary" collapseRight>Collapse right</Button>

Don't collapse when the button is not alone

        <Box />

        <Button variation="tertiary">Default</Button>

          <Button variation="secondary">Another Button</Button>

Collapse props don't work on primary and secondary variations (and it shouldn't)

        <Box />

        <Button variation="primary" collapseLeft>Collapse left</Button>
        <Button variation="primary" collapseRight>Collapse right</Button>

        <Box />

        <Button variation="secondary" collapseLeft>Collapse left</Button>
        <Button variation="secondary" collapseRight>Collapse right</Button>

</>;
```
Colored container background

```js

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

      <Button variation="secondary">Secondary</Button>

      <Button variation="tertiary">Neutral</Button>

      <Button variation="primary" disabled>Primary</Button>

      <Button variation="secondary" disabled>Secondary</Button>

      <Button variation="tertiary" disabled>Neutral</Button>

```

Loading state

```js
initialState = { isLoading1: true, isLoading2: true, isLoading3: true  };

    <Button
      variation="primary"
      onClick={() => setState({ isLoading2: !state.isLoading2 })}
      isLoading={state.isLoading2}
    >
      Toggle loading state
    </Button>

```

```


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

[Assistant]
Here's the implementation:

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

import ExternalLink from '../icon/ExternalLink'

const classes =
  'pointer c-link hover-c-link active-c-link no-underline underline-hover'

const Link = ({ children, href, target, mediumWeigth }) => (
  <a
    href={href}
    target={target}
    className={`${classes} ${mediumWeigth ? 'fw5' : ''}`}>
    {children}
    {target === '_blank' && (
      <span className="ml2">
        <ExternalLink size={12} />
      </span>
    )}
  </a>
)

Link.defaultProps = {
  target: '_self',
  mediumWeigth: false,
}

Link.propTypes = {
  /** Content of the link */
  children: PropTypes.string.isRequired,
  /** Spec attribute */
  href: PropTypes.string.isRequired,
  /** Spec attribute */
  target: PropTypes.oneOf(['_self', '_blank', '_parent', '_top']),
  /** Weight property */
  mediumWeigth: PropTypes.bool,
}

export default Link

```


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

[Assistant]
Here's the implementation:

```markdown
#### A Link is an anchor tag.

### 👍 Dos

- Use them for links. When user needs to take an action, swtich for the <a href="#/Components/Forms/Button">Button</a> component.
- Use normal and medium weights when outside a paragraph. Designers can choose which one is the best depending on the context and layout.

### 👎 Don'ts

- Use medium weigth when the link is inline, eg, within a paragraph.

### Related components

- <a href="#/Components/Forms/Button">Button</a>.

Within a paragraph:

```js

    It is simply dummy text of the printing and typesetting industry. Lorem
    Ipsum has been the{' '}
    <Link href="https://www.vtex.com/">industry's standard</Link> dummy text
    ever since the 1500s, when an unknown printer took a galley of type and
    scrambled it to make a type specimen book.

    <Link href="https://www.vtex.com/" target="_blank" mediumWeigth>
      External link
    </Link>

```

Within an unordered list:

```js

    <Link href="#">Item 1</Link>

    <Link href="#" mediumWeigth>
      Item 2
    </Link>

    <Link href="#">Item 3</Link>

```

```


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

[Assistant]
Here's the implementation:

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

import FileIcon from './FileIcon'
import Spinner from '../Spinner'
import ButtonWithIcon from '../ButtonWithIcon'
import IconDelete from '../icon/Delete'

const dropzoneRef = createRef()

class Dropzone extends PureComponent {
  state = {
    files: [],
    isHovering: false,
    fileDropped: false,
  }

  handleDragEnter = () => {
    this.setState({ isHovering: true })
  }

  handleDragLeave = () => {
    this.setState({ isHovering: false })
  }

  handleDropAccepted = files => {
    this.setState({ isHovering: false, fileDropped: true, files })
    this.props.onDropAccepted(files)
  }

  handleDropRejected = files => {
    this.setState({ isHovering: false })
    this.props.onDropRejected(files)
  }

  handleRemoveFile = fileIndex => {
    const { files } = this.state

    if (files.length > 1) {
      const filteredFiles = files.filter((_, i) => i !== fileIndex)
      this.setState({ files: filteredFiles }, () => {
        this.props.onFileReset(filteredFiles)
      })
    } else {
      this.handleReset()
      this.props.onFileReset()
    }
  }

  handleReset = () => {
    this.setState({ files: [], fileDropped: false })
  }

  render() {
    const { children, isLoading, icon, multiple } = this.props
    const { isHovering, fileDropped, files } = this.state
    const initialState = !isHovering && !fileDropped

    let dropzoneContainerClasses =
      'flex flex-column items-center justify-center b--light-blue ba br2 bw1 b--dashed '
    let iconHolderClasses =
      'flex items-center justify-center pa6  bg-near-white '
    const droppedContainerClasses = 'w-100 '

    const holderSize = '120px'
    const iconHolderStyles = {
      borderRadius: '100%',
      width: holderSize,
      height: holderSize,
    }

    if (initialState) {
      dropzoneContainerClasses += 'pa8 '
    }

    if (isHovering) {
      dropzoneContainerClasses += 'pa8 b--action-primary bg-action-secondary '
      iconHolderClasses += 'bg-action-primary '
    }

    if (fileDropped) {
      dropzoneContainerClasses += 'pv4 ph5 bg-muted-5 b--muted-3 '
    }

    return (
      <ReactDropZone
        ref={dropzoneRef}
        multiple={multiple}
        onDropAccepted={this.handleDropAccepted}
        onDropRejected={this.handleDropRejected}
        onDragEnter={this.handleDragEnter}
        onDragLeave={this.handleDragLeave}>
        {({ getRootProps, getInputProps }) => (
          <div className={dropzoneContainerClasses} {...getRootProps()}>
            {isLoading ? (
              <div className={iconHolderClasses} style={iconHolderStyles}>
                <Spinner size={32} />
              </div>
            ) : fileDropped ? (
              <div className={droppedContainerClasses}>
                {files.map((file, fileIndex) => (
                  <div
                    key={file.name}
                    className={`black-70 flex flex-row justify-between items-center ${
                      files.length > 1 && fileIndex !== files.length - 1
                        ? 'mb2'
                        : ''
                    }`}>
                    <span>{file.name}</span>
                    <ButtonWithIcon
                      onClick={() => this.handleRemoveFile(fileIndex)}
                      variation="tertiary"
                      icon={<IconDelete />}
                    />
                  </div>
                ))}
              </div>
            ) : (
              <>
                <input {...getInputProps()} />
                <div className={iconHolderClasses} style={iconHolderStyles}>
                  {icon || <FileIcon />}
                </div>
                {children && children}
              </>
            )}
          </div>
        )}
      </ReactDropZone>
    )
  }
}

Dropzone.defaultProps = {
  onDropRejected: () => {},
  children: null,
  icon: null,
  isLoading: false,
  onFileReset: () => {},
  multiple: false,
}

Dropzone.propTypes = {
  /** Callback when file(s) is(are) droppped and accepted */
  onDropAccepted: PropTypes.func.isRequired,
  /** Callback when file(s) is(are) droppped and rejected (e.g: when props.multiple is false and multiple files are dragged and dropped.*/
  onDropRejected: PropTypes.func,
  /** Callback when a file is removed */
  onFileReset: PropTypes.func,
  /** Content after the file icon */
  children: PropTypes.node,
  /** Loading state */
  isLoading: PropTypes.bool,
  /** Custom icon */
  icon: PropTypes.node,
  /** Allow multiple files drop */
  multiple: PropTypes.bool,
}

export default Dropzone

```


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

[Assistant]
Here's the implementation:

```javascript
import React from 'react'

const FileIcon = () => (
  <svg width="40" height="46" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path
      d="M24.793.793A1 1 0 0 0 24.086.5H1.5a1 1 0 0 0-1 1v43a1 1 0 0 0 1 1h37a1 1 0 0 0 1-1V15.914a1 1 0 0 0-.293-.707L24.793.793z"
      fill="#E3E4E6"
    />
    <path
      d="M9.5 33.5h21M9.5 24.5h21"
      stroke="#3F3F40"
      strokeWidth="1.2"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path
      d="M9.5 15.5h6"
      stroke="#F71963"
      strokeWidth="1.2"
      strokeMiterlimit="10"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <path d="M24.5.5v15h15" fill="#CACBCC" />
  </svg>
)

export default FileIcon

```


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

[Assistant]
Here's the implementation:

```markdown
#### A Dropzone is an area for a drag and drop of files.

Working example

```js
class MyDropZone extends React.Component {
  constructor() {
    super()
    this.state = { files: null }
    this.handleFile = this.handleFile.bind(this)
  }

  handleFile(files) {
    console.log(files)
  }

  handleReset(files) {
    if (files) {
      console.log(files)
    }
  }

  render() {
    return (
      <Dropzone onDropAccepted={this.handleFile} onFileReset={this.handleReset}>

            Drop here your XLS or

              choose a file

      </Dropzone>
    )
  }
}
;<MyDropZone />
```

Custom icon

```js
const TableIcon = (
  <svg width="44" height="36" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M0 24h44v11a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V24z" fill="#E3E4E6" />
    <rect x="4" y="29" width="8" height="2" rx="1" fill="#727273" />
    <rect x="18" y="29" width="8" height="2" rx="1" fill="#727273" />
    <rect x="36" y="29" width="4" height="2" rx="1" fill="#727273" />
    <path d="M44 12H0V1a1 1 0 0 1 1-1h42a1 1 0 0 1 1 1v11z" fill="#F71963" />
    <rect
      x="40"
      y="7"
      width="4"
      height="2"
      rx="1"
      transform="rotate(180 40 7)"
      fill="#fff"
    />
    <rect
      x="26"
      y="7"
      width="8"
      height="2"
      rx="1"
      transform="rotate(180 26 7)"
      fill="#fff"
    />
    <rect
      x="12"
      y="7"
      width="8"
      height="2"
      rx="1"
      transform="rotate(180 12 7)"
      fill="#fff"
    />
    <path d="M0 12h44v12H0V12z" fill="#CACBCC" />
    <rect x="4" y="17" width="8" height="2" rx="1" fill="#727273" />
    <rect x="18" y="17" width="8" height="2" rx="1" fill="#727273" />
    <rect x="36" y="17" width="4" height="2" rx="1" fill="#727273" />
  </svg>
)
;

  <Dropzone icon={TableIcon} />

```

Loading state

```js
<Dropzone isLoading />
```

```


[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/CheckboxContainer.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 Checkbox from '../Checkbox'

class CheckboxContainer extends Component {
  static defaultProps = {
    partial: false,
    disabled: false,
  }

  static propTypes = {
    checked: PropTypes.bool.isRequired,
    partial: PropTypes.bool,
    onClick: PropTypes.func.isRequired,
    disabled: PropTypes.bool,
    id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  }

  render() {
    const { checked, partial, id, onClick, disabled } = this.props

    return (
      <div className="flex items-center justify-center aspect-ratio--object">
        <div
          className="br2 h2 w2 flex items-center justify-center"
          onClick={e => {
            e.stopPropagation()
            onClick(id)
            // prevents the onRowClick event from happening
          }}>
          <Checkbox
            checked={checked}
            partial={partial}
            value={`${id}`}
            id={`${id}`}
            name={`row_${id}`}
            onChange={() => {}}
            disabled={disabled}
          />
        </div>
      </div>
    )
  }
}

export default CheckboxContainer

```


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

[Assistant]
Here's the implementation:

```javascript
const generateRandomColorObject = () => {
  const tagData = [
    {
      color: '#F71963',
      label: 'Rebel pink',
    },
    {
      color: '#00BBD4',
      label: 'Young blue',
    },
    {
      color: '#D6D8E0',
      label: 'Cold gray',
    },
    {
      color: '#142032',
      label: 'Serious black',
    },
  ]
  return tagData[Math.floor(Math.random() * tagData.length)]
}

export default {
  defaultSchema: {
    properties: {
      name: {
        type: 'string',
        title: 'Name',
      },
      email: {
        type: 'string',
        title: 'Email',
        width: 300,
      },
      number: {
        type: 'number',
        title: 'Number',
      },
    },
  },
  items: [
    {
      email: 'olen.stamm21@yahoo.com',
      name: 'Patrick Rothfuss',
      number: 1.52725,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'junius0@gmail.com',
      name: 'Hurricane Skywalker IV',
      number: 2.84639,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'judd_gulgowski22@yahoo.com',
      name: 'Tom Braddy',
      number: 4.10182,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'catharine.leuschke62@hotmail.com',
      name: 'Momochi Zabuza',
      number: 6.33245,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'candido_ryan@hotmail.com',
      name: 'Freddie Mercury',
      number: 7.96637,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'freda_ritchie26@yahoo.com',
      name: 'Dr. Lempi Mosciski',
      number: 8.42623,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'elissa28@gmail.com',
      name: 'Nikita Feeney',
      number: 9.11769,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'reginald1@yahoo.com',
      name: 'Janick Lesch',
      number: 10.85311,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'hailee.botsford24@gmail.com',
      name: 'Mr. Ron Smith',
      number: 3.23851,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'gaetano.mayert71@yahoo.com',
      name: 'Norris Murazik',
      number: 5.33725,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'jerad_hyatt@hotmail.com',
      name: 'Golda Carter',
      number: 11.44288,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'jaquan.fisher61@yahoo.com',
      name: 'Dr. Cassandra Jerde',
      number: 12.73372,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'marlee_ziemann@yahoo.com',
      name: 'Kvothe Kshlerin',
      number: 13.62416,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'erman_Beier@yahoo.com',
      name: 'Arch Hegmann',
      number: 14.8212,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'arturo_schroeder30@yahoo.com',
      name: 'Alejandro Strosin',
      number: 15.73701,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'santos_doyle36@gmail.com',
      name: 'Zidane Vandervort',
      number: 16.26124,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'yvonne.gleason@hotmail.com',
      name: 'Tonny Balistreri',
      number: 17.79484,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'michaela51@hotmail.com',
      name: 'Emanuel Ullrich',
      number: 18.57879,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'erik_kuvalis41@hotmail.com',
      name: 'Beto',
      number: 19.87262,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'edwardo_kuvalis@hotmail.com',
      name: 'Mrs. Deion Beahan',
      number: 20.13464,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'alexa_ondricka45@hotmail.com',
      name: 'Trent Kerluke',
      number: 21.9677,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'darron_brakus63@yahoo.com',
      name: 'Jacquelyn Flatley Jr.',
      number: 22.55405,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'jamie.hermann60@gmail.com',
      name: 'Ervin Kuphal',
      number: 23.76965,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
    {
      email: 'cleo.klein@hotmail.com',
      name: 'Terrence Gutmann',
      number: 24.55731,
      color: generateRandomColorObject(),
      color1: generateRandomColorObject(),
      color2: generateRandomColorObject(),
      color3: generateRandomColorObject(),
      color4: generateRandomColorObject(),
      color5: generateRandomColorObject(),
      color6: generateRandomColorObject(),
    },
  ],
}

```


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

[Assistant]
Here's the implementation:

```javascript
import React, { PureComponent } from 'react'
import { StickyContainer } from 'react-sticky'
import PropTypes from 'prop-types'
import reduce from 'lodash/reduce'
import map from 'lodash/map'
import isEmpty from 'lodash/isEmpty'

import Box from '../Box'
import EmptyState from '../EmptyState'
import FilterBar from '../FilterBar'
import SimpleTable from './SimpleTable'
import Toolbar from './Toolbar'
import CheckboxContainer from './CheckboxContainer'
import Totalizers from '../Totalizer'
import BulkActions from './BulkActions'
import SwitchablePagination from './SwitchablePagination'

class Table extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      hiddenFields: this.getInitialHiddenFieldsFromSchema(props.schema),
      tableRowHeight: this.getRowHeight(props.density),
      selectedDensity: props.density,
      allChecked: false,
      selectedRows: this.props.bulkActions.selectedRows || [],
      allLinesSelected: false,
    }
  }

  componentDidUpdate() {
    const { bulkActions } = this.props
    const { selectedRows } = this.state

    if (
      bulkActions &&
      bulkActions.selectedRows &&
      bulkActions.selectedRows !== selectedRows
    ) {
      this.setState({ selectedRows: bulkActions.selectedRows })
    }
  }

  getInitialHiddenFieldsFromSchema = schema => {
    const hiddenFields = []
    Object.keys(schema.properties).forEach(key => {
      if (schema.properties[key].hidden) {
        hiddenFields.push(key)
      }
    })
    return hiddenFields
  }

  getRowHeight = density => {
    switch (density) {
      case 'low':
        return 76
      case 'medium':
        return 48
      case 'high':
        return 32
      default:
        return 45
    }
  }

  handleTableRowHeight = density => {
    const { tableRowHeight } = this.state
    const newHeight = this.getRowHeight(density)
    if (tableRowHeight !== newHeight) {
      this.setState({
        tableRowHeight: newHeight,
        selectedDensity: density,
      })
    }
  }

  handleToggleColumn = key => {
    const { hiddenFields } = this.state
    const newFieldsArray = hiddenFields.slice()
    const index = hiddenFields.indexOf(key)
    index === -1 ? newFieldsArray.push(key) : newFieldsArray.splice(index, 1)
    this.setState({ hiddenFields: newFieldsArray })
  }

  handleShowAllColumns = () => {
    this.setState({ hiddenFields: [] })
  }

  handleHideAllColumns = () => {
    this.setState({ hiddenFields: Object.keys(this.props.schema.properties) })
  }

  handleSelectionChange = () => {
    if (this.props.bulkActions && this.props.bulkActions.onChange) {
      const selectedParameters = this.state.allLinesSelected
        ? { allLinesSelected: true }
        : { selectedRows: this.state.selectedRows }
      this.props.bulkActions.onChange(selectedParameters)
    }
  }

  handleSelectAllLines = () => {
    const { items: selectedRows } = this.props
    this.setState(
      { selectedRows, allLinesSelected: true },
      this.handleSelectionChange
    )
  }

  handleSelectAllVisibleLines = () => {
    const selectedRows = this.state.selectedRows.slice(0)
    const { items } = this.props
    const itemsLength = items.length

    if (selectedRows.length <= itemsLength && selectedRows.length !== 0) {
      this.handleDeselectAllLines()
    } else {
      this.setState({ selectedRows: items }, this.handleSelectionChange)
    }
  }

  handleSelectLine = rowData => {
    const selectedRows = this.state.selectedRows.slice(0)
    const { id } = rowData

    if (selectedRows.some(el => el.id === id)) {
      const filteredRows = selectedRows.filter(row => row.id !== id)
      this.setState({ selectedRows: filteredRows }, this.handleSelectionChange)
    } else {
      selectedRows.push({ ...rowData })
      this.setState({ selectedRows }, this.handleSelectionChange)
    }
  }

  handleDeselectAllLines = () => {
    this.setState(
      { selectedRows: [], allLinesSelected: false },
      this.handleSelectionChange
    )
  }

  handleInputSearchClearWithBulkAction = event => {
    const {
      toolbar: {
        inputSearch: { onClear = () => {} },
      },
    } = this.props
    this.handleDeselectAllLines()
    onClear(event)
  }

  render() {
    const {
      items,
      schema,
      disableHeader,
      emptyStateLabel,
      emptyStateChildren,
      fixFirstColumn,
      onRowClick,
      onRowHover,
      sort,
      onSort,
      updateTableKey,
      containerHeight,
      toolbar,
      pagination,
      fullWidth,
      dynamicRowHeight,
      lineActions,
      loading,
      bulkActions,
      totalizers,
      filters,
    } = this.props
    const {
      hiddenFields,
      tableRowHeight,
      selectedDensity,
      selectedRows,
      allLinesSelected,
    } = this.state

    const hasPrimaryBulkAction =
      bulkActions &&
      bulkActions.main &&
      typeof bulkActions.main.handleCallback === 'function'
    const hasSecondaryBulkActions =
      bulkActions.others && bulkActions.others.length > 0
    const hasBulkActions = hasPrimaryBulkAction || hasSecondaryBulkActions

    if (hasBulkActions) {
      schema.properties = {
        bulk: {
          width: 40,
          headerRenderer: () => {
            const { selectedRows } = this.state
            const selectedRowsLength = selectedRows.length
            const itemsLength = this.props.items.length

            const isChecked = selectedRowsLength === itemsLength
            const isPartial =
              selectedRowsLength > 0 && selectedRowsLength < itemsLength

            return (
              <CheckboxContainer
                checked={isChecked}
                onClick={this.handleSelectAllVisibleLines}
                id="all"
                partial={isPartial}
              />
            )
          },
          cellRenderer: ({ rowData }) => (
            <CheckboxContainer
              checked={this.state.selectedRows.some(
                row => row.id === rowData.id
              )}
              onClick={() => this.handleSelectLine(rowData)}
              id={rowData.id}
              disabled={this.state.allLinesSelected}
            />
          ),
        },
        ...schema.properties,
      }

      items.map((item, i) => (item.id = i))
    }

    const properties = Object.keys(schema.properties)
    const emptyState = !!(
      properties.length === 0 || properties.length === hiddenFields.length
    )
    const displayProperties = reduce(
      schema.properties,
      (acc, value, key) => {
        if (hiddenFields.includes && hiddenFields.includes(key)) {
          return acc
        }
        return { ...acc, [key]: value }
      },
      {}
    )
    const displaySchema = {
      ...schema,
      properties: displayProperties,
    }

    // if pagination and bulk actions features are active at the same time
    // when paginating, bulk actions active lines should be deselected
    const paginationClone = pagination ? Object.assign({}, pagination) : null
    if (paginationClone && hasBulkActions) {
      paginationClone.onNextClick = () => {
        this.handleDeselectAllLines()
        pagination.onNextClick()
      }
      paginationClone.onPrevClick = () => {
        this.handleDeselectAllLines()
        pagination.onPrevClick()
      }
    }

    // extend toolbar's input search onClear when bulk action is turned on to
    // deselect all lines when clearing to avoid weird unwanted behaviors
    // until a permanent solution for https://github.com/vtex/styleguide/issues/873
    // is found
    const extendedToolbar = toolbar
      ? {
          ...toolbar,
          inputSearch: toolbar.inputSearch
            ? {
                ...toolbar.inputSearch,
                onClear: hasBulkActions
                  ? this.handleInputSearchClearWithBulkAction
                  : toolbar.inputSearch.onClear,
              }
            : null,
        }
      : null

    return (
      <div className="vtex-table__container">
        {extendedToolbar && (
          <Toolbar
            loading={loading}
            hiddenFields={hiddenFields}
            onToggleColumn={this.handleToggleColumn}
            onDeselectAllLines={this.handleDeselectAllLines}
            onHideAllColumns={this.handleHideAllColumns}
            onShowAllColumns={this.handleShowAllColumns}
            onToggleDensity={this.handleTableRowHeight}
            selectedDensity={selectedDensity}
            schema={schema}
            actions={extendedToolbar}
          />
        )}

        {filters && (
          <div className="mb5">
            <FilterBar {...filters} />
          </div>
        )}

        {totalizers && (
          <div className="mb5">
            {Array.isArray(totalizers) ? (
              <Totalizers items={totalizers} />
            ) : (
              <Totalizers {...totalizers} />
            )}
          </div>
        )}

        <SwitchablePagination
          enabled={!loading && !isEmpty(paginationClone)}
          {...paginationClone}>
          <StickyContainer>
            <BulkActions
              hasPrimaryBulkAction={hasPrimaryBulkAction}
              hasSecondaryBulkActions={hasSecondaryBulkActions}
              selectedRows={selectedRows}
              bulkActions={bulkActions}
              allLinesSelected={allLinesSelected}
              onSelectAllLines={this.handleSelectAllLines}
              onDeselectAllLines={this.handleDeselectAllLines}
            />

            {emptyState ? (
              <Box>
                <EmptyState title={emptyStateLabel}>
                  {emptyStateChildren}
                </EmptyState>
              </Box>
            ) : (
              <SimpleTable
                fullWidth={fullWidth}
                items={items}
                schema={displaySchema}
                fixFirstColumn={fixFirstColumn}
                rowHeight={tableRowHeight}
                disableHeader={disableHeader}
                emptyStateLabel={emptyStateLabel}
                emptyStateChildren={emptyStateChildren}
                dynamicRowHeight={dynamicRowHeight}
                onRowClick={onRowClick}
                onRowHover={onRowHover}
                sort={sort}
                onSort={onSort}
                key={hiddenFields.toString()}
                updateTableKey={updateTableKey}
                lineActions={lineActions}
                loading={loading}
                containerHeight={containerHeight}
                selectedRowsIndexes={map(selectedRows, 'id')}
                density={selectedDensity}
              />
            )}
          </StickyContainer>
        </SwitchablePagination>
      </div>
    )
  }
}

Table.defaultProps = {
  loading: false,
  density: 'medium',
  fixFirstColumn: false,
  toolbar: null,
  emptyStateLabel: 'Nothing to show.',
  fullWidth: false,
  bulkActions: {},
  totalizers: [],
}

Table.propTypes = {
  /** Array of objects with data */
  items: PropTypes.array.isRequired,
  /** JSON defining the data model schema for the items (More info about it after the examples) */
  schema: PropTypes.object.isRequired,
  /** Do not render the table header (only the rows) */
  disableHeader: PropTypes.bool,
  /** Fix first column so only the following ones scroll horizontaly */
  fixFirstColumn: PropTypes.bool,
  /** Callback invoked when a user clicks on a table row. ({ event: Event, index: number, rowData: any }): void */
  onRowClick: PropTypes.func,
  /** Callback invoked when a user hovers a table row. (rowIndex): void */
  onRowHover: PropTypes.func,
  /** Sort order and which property (key in schema) is table data sorted by. */
  sort: PropTypes.shape({
    sortOrder: PropTypes.oneOf(['ASC', 'DESC']),
    sortedBy: PropTypes.string,
  }),
  /** Callback to handle sort ({ sortOrder, sortedBy }) : object */
  onSort: PropTypes.func,
  /** Forces table re-render when changed */
  updateTableKey: PropTypes.string,
  /** In case you need precise control of table container height (number in pixels)  */
  containerHeight: PropTypes.number,
  /** Row info visual density  */
  density: PropTypes.oneOf(['low', 'medium', 'high']),
  /** Label for emptystate  */
  emptyStateLabel: PropTypes.string,
  /** Children for emptystate  */
  emptyStateChildren: PropTypes.node,
  /** Full width property  */
  fullWidth: PropTypes.bool,
  /** Allows rows to show the full height of their content */
  dynamicRowHeight: PropTypes.bool,
  /** Line actions column */
  lineActions: PropTypes.arrayOf(
    PropTypes.shape({
      /** Function that returns a string for the action label */
      label: PropTypes.func,
      /** Mark whether the action performs a dangerous option or not */
      isDangerous: PropTypes.bool,
      /** Handles the callback function of the action */
      onClick: PropTypes.func,
    })
  ),
  /** Controls the table loading state */
  loading: PropTypes.bool,
  /** Toolbar (search and actions) */
  toolbar: PropTypes.shape({
    inputSearch: PropTypes.shape({
      onSubmit: PropTypes.func,
      onClear: PropTypes.func,
    }),
    density: PropTypes.shape({
      buttonLabel: PropTypes.string,
      lowOptionLabel: PropTypes.string,
      mediumOptionLabel: PropTypes.string,
      highOptionLabel: PropTypes.string,
      alignMenu: PropTypes.oneOf(['right', 'left']),
      handleCallback: PropTypes.func,
    }),
    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,
      disabled: PropTypes.bool,
    }),
    upload: PropTypes.shape({
      label: PropTypes.string,
      handleCallback: PropTypes.func,
      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']),
    }),
    newLine: PropTypes.shape({
      label: PropTypes.string.isRequired,
      handleCallback: PropTypes.func.isRequired,
      disabled: PropTypes.bool,
    }),
  }),
  pagination: PropTypes.shape({
    onNextClick: PropTypes.func,
    onPrevClick: PropTypes.func,
    currentItemFrom: PropTypes.number,
    currentItemTo: PropTypes.number,
    textOf: PropTypes.node,
    totalItems: PropTypes.number,
  }),
  bulkActions: PropTypes.shape({
    texts: PropTypes.shape({
      secondaryActionsLabel: PropTypes.string.isRequired,
      rowsSelected: PropTypes.func.isRequired,
      selectAll: PropTypes.string,
      allRowsSelected: PropTypes.func,
    }),
    totalItems: PropTypes.number,
    onChange: PropTypes.func,
    selectedRows: PropTypes.array,
    main: PropTypes.shape({
      label: PropTypes.string.isRequired,
      handleCallback: PropTypes.func.isRequired,
    }),
    others: PropTypes.arrayOf(
      PropTypes.shape({
        label: PropTypes.string.isRequired,
        handleCallback: PropTypes.func.isRequired,
      })
    ),
    fixed: PropTypes.bool,
  }),
  /** Totalizers property  */
  totalizers: PropTypes.oneOfType([
    PropTypes.array,
    PropTypes.shape({
      mobileScroll: PropTypes.bool,
      horizontalLayout: PropTypes.bool,
      items: PropTypes.array,
    }),
  ]),
  /** Filters property  */
  filters: PropTypes.shape({ ...FilterBar.propTypes }),
}

export default Table

```


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

[Assistant]
Here's the implementation:

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

import Button from '../Button'
import ButtonWithIcon from '../ButtonWithIcon'
import ActionMenu from '../ActionMenu'
import Close from '../icon/Close'
import BulkActionsSelectedRows from './BulkActionsSelectedRows'

const DEFAULT_BAR_HEIGHT_PX = '56px'
const close = <Close />

class BulkActions extends PureComponent {
  render() {
    const {
      hasPrimaryBulkAction,
      hasSecondaryBulkActions,
      selectedRows,
      bulkActions,
      allLinesSelected,
      onSelectAllLines,
      onDeselectAllLines,
    } = this.props

    const hasBulkActions = hasPrimaryBulkAction || hasSecondaryBulkActions
    const selectedRowsLength = selectedRows.length
    const hasRowsSelected = selectedRowsLength > 0
    const fixed = bulkActions && bulkActions.fixed
    const canStickToTop = fixed && hasRowsSelected

    const bulkActionsReturnedParameters = allLinesSelected
      ? { allLinesSelected: true }
      : { selectedRows }

    return (
      <Sticky disableCompensation={!canStickToTop}>
        {({ style: stickyStyle, isSticky }) => (
          <div
            className={classNames(
              'flex flex-row justify-between bg-action-primary c-on-action-primary ph4',
              {
                'pv4 overflow-auto': hasRowsSelected,
                'overflow-hidden': !hasRowsSelected,
                'shadow-4 z-1': canStickToTop && isSticky,
                'br3 br--top': !(canStickToTop && isSticky),
              }
            )}
            style={{
              ...(canStickToTop && stickyStyle),
              height: hasRowsSelected ? DEFAULT_BAR_HEIGHT_PX : 0,
              transition: 'height 0.2s ease-in-out, padding 0.2s ease-in-out',
            }}>
            {hasBulkActions && (
              <div className="flex flex-row">
                {hasPrimaryBulkAction && (
                  <div className="mr4">
                    <Button
                      variation="secondary"
                      size="small"
                      onClick={() =>
                        bulkActions.main.handleCallback(
                          bulkActionsReturnedParameters
                        )
                      }>
                      {bulkActions.main.label}
                    </Button>
                  </div>
                )}
                {hasSecondaryBulkActions && (
                  <ActionMenu
                    label={bulkActions.texts.secondaryActionsLabel}
                    buttonProps={{ variation: 'secondary', size: 'small' }}
                    options={bulkActions.others.map(el => ({
                      label: el.label,
                      isDangerous: el.isDangerous,
                      onClick: () =>
                        el.handleCallback(bulkActionsReturnedParameters),
                    }))}
                  />
                )}
              </div>
            )}
            <div className="tr flex flex-row items-center">
              {!allLinesSelected && bulkActions && bulkActions.texts && (
                <span className="mr4 c-muted-4">
                  {bulkActions.texts.rowsSelected(
                    <BulkActionsSelectedRows
                      selectedRowsLength={selectedRowsLength}
                    />
                  )}
                </span>
              )}
              {bulkActions &&
                bulkActions.texts &&
                bulkActions.texts.selectAll &&
                bulkActions.texts.allRowsSelected && (
                  <span className="mr2">
                    {allLinesSelected ? (
                      bulkActions.texts.allRowsSelected(
                        <span className="b">{bulkActions.totalItems}</span>
                      )
                    ) : (
                      <Button onClick={() => onSelectAllLines()}>
                        <span className="ttu">
                          {`${bulkActions.texts.selectAll} ${bulkActions.totalItems}`}
                        </span>
                      </Button>
                    )}
                  </span>
                )}
              <ButtonWithIcon
                icon={close}
                onClick={() => onDeselectAllLines()}
              />
            </div>
          </div>
        )}
      </Sticky>
    )
  }
}

BulkActions.defaultProps = {
  hasPrimaryBulkAction: false,
  hasSecondaryBulkActions: false,
  allLinesSelected: false,
  selectedRows: [],
  bulkActions: {},
  onSelectAllLines: () => {},
}

BulkActions.propTypes = {
  hasPrimaryBulkAction: PropTypes.bool,
  hasSecondaryBulkActions: PropTypes.bool,
  allLinesSelected: PropTypes.bool,

  selectedRows: PropTypes.array,
  bulkActions: PropTypes.object,

  onSelectAllLines: PropTypes.func,
  onDeselectAllLines: PropTypes.func.isRequired,
}

export default BulkActions

```