[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/CssHandles/__tests__/withCssHandles.test.tsx from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component } from 'react'
import withCssHandles from '../withCssHandles'
import { useExtension } from '../hooks/useExtension'
import { render } from '@vtex/test-tools/react'

type ValueOf<T extends readonly any[]> = T[number]

type CssHandlesInput = readonly string[]
type CssHandles<T extends CssHandlesInput> = Record<ValueOf<T>, string>
interface CssHandlesOptions {
  migrationFrom?: string | string[]
}

interface Props<T extends CssHandlesInput> {
  cssHandles?: CssHandles<T>
}

class ExampleComponent<T extends CssHandlesInput> extends Component<Props<T>> {
  public render() {
    const cssHandles = this.props.cssHandles as CssHandles<T>

    return (
      <div
        data-testid="test-div"
        className={Object.values(cssHandles).join(' ')}></div>
    )
  }
}

jest.mock('../hooks/useExtension', () => ({
  useExtension: jest.fn(() => ({
    component: 'vtex.app@2.1.0',
    props: {
      blockClass: 'blockClass',
    },
  })),
}))

describe('withCssHandles', () => {
  const renderComponent = (
    handlesNames: CssHandlesInput,
    options?: CssHandlesOptions
  ) => {
    const EnhancedComponent = withCssHandles<
      typeof handlesNames,
      Props<typeof handlesNames>
    >(handlesNames, options)(ExampleComponent)

    return render(<EnhancedComponent />)
  }

  it('should apply proper classes to proper handles', () => {
    const CSS_HANDLES = ['element1', 'element2'] as const
    const { getByTestId } = renderComponent(CSS_HANDLES)
    expect(getByTestId('test-div').className).toBe(
      'vtex-app-2-x-element1 vtex-app-2-x-element1--blockClass vtex-app-2-x-element2 vtex-app-2-x-element2--blockClass'
    )
  })
  it('should not apply blockClasses if not available', () => {
    const CSS_HANDLES = ['element1', 'element2'] as const
    ;(useExtension as any).mockImplementationOnce(() => ({
      component: 'vtex.app@2.1.0',
      props: {},
    }))

    const { getByTestId } = renderComponent(CSS_HANDLES)
    expect(getByTestId('test-div').className).toBe(
      'vtex-app-2-x-element1 vtex-app-2-x-element2'
    )
  })
  describe('migration', () => {
    it('should add both the current app and the migration app', () => {
      const CSS_HANDLES = ['element1', 'element2']
      const options = {
        migrationFrom: 'vtex.previous-app@3.0.0',
      }

      const { getByTestId } = renderComponent(CSS_HANDLES, options)
      expect(getByTestId('test-div').className).toBe(
        'vtex-app-2-x-element1 vtex-app-2-x-element1--blockClass vtex-previous-app-3-x-element1 vtex-previous-app-3-x-element1--blockClass vtex-app-2-x-element2 vtex-app-2-x-element2--blockClass vtex-previous-app-3-x-element2 vtex-previous-app-3-x-element2--blockClass'
      )
    })
    it('should add more than one migration if needed', () => {
      const CSS_HANDLES = ['element1', 'element2']
      const options = {
        migrationFrom: ['vtex.previous-app@2.0.0', 'vtex.previous-app@3.0.0'],
      }

      const { getByTestId } = renderComponent(CSS_HANDLES, options)
      expect(getByTestId('test-div').className).toBe(
        'vtex-app-2-x-element1 vtex-app-2-x-element1--blockClass vtex-previous-app-2-x-element1 vtex-previous-app-2-x-element1--blockClass vtex-previous-app-3-x-element1 vtex-previous-app-3-x-element1--blockClass vtex-app-2-x-element2 vtex-app-2-x-element2--blockClass vtex-previous-app-2-x-element2 vtex-previous-app-2-x-element2--blockClass vtex-previous-app-3-x-element2 vtex-previous-app-3-x-element2--blockClass'
      )
    })

    it('doesnt repeat the migration if the current app happens to be the same as the migration one', () => {
      const CSS_HANDLES = ['element1', 'element2']
      const options = {
        migrationFrom: [
          'vtex.previous-app@2.0.0',
          'vtex.previous-app@3.0.0',
          'vtex.app@2.1.0',
        ],
      }

      const { getByTestId } = renderComponent(CSS_HANDLES, options)
      expect(getByTestId('test-div').className).toBe(
        'vtex-app-2-x-element1 vtex-app-2-x-element1--blockClass vtex-previous-app-2-x-element1 vtex-previous-app-2-x-element1--blockClass vtex-previous-app-3-x-element1 vtex-previous-app-3-x-element1--blockClass vtex-app-2-x-element2 vtex-app-2-x-element2--blockClass vtex-previous-app-2-x-element2 vtex-previous-app-2-x-element2--blockClass vtex-previous-app-3-x-element2 vtex-previous-app-3-x-element2--blockClass'
      )
    })
  })
})

```


[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/CssHandles/hooks/useExtension.tsx from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { useRuntime, useTreePath } from '../../../../../vtex.render-runtime'

/** This code has been copied and adapted from vtex.render-runtime,
 * while it doesn't export this hook */

function mountTreePath(base: string, children: string[]) {
  return [base, ...children].filter(id => !!id).join('/')
}

interface Options {
  children?: string[] | string
}

interface Extension {
  /* eslint-disable @typescript-eslint/no-explicit-any */
  props?: Record<string, any>
  component?: string
  [key: string]: any
}

export const useExtension = ({ children }: Options = {}): Extension | null => {
  const { extensions } = useRuntime()

  const { treePath: baseTreePath } = useTreePath()

  const treePath = children
    ? mountTreePath(
        baseTreePath,
        Array.isArray(children) ? children : [children]
      )
    : baseTreePath

  const extension = treePath && extensions[treePath]

  return (extension as Extension) || null
}

```


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

[Assistant]
Here's the implementation:

```tsx
/* eslint-env jest */
import React from 'react'
import { render } from '@vtex/test-tools/react'
import RichText from '../index'

import { textPositionValues, textAlignmentValues } from '../typings/SchemaTypes'

describe('Rich text component', () => {
  const defaultProps = {
    font: 't-body',
    text: '',
    textPosition: textPositionValues.LEFT,
    textAlignment: textAlignmentValues.LEFT,
    textColor: 'c-on-base',
  }

  it('should render with bold', () => {
    const component = render(
      <RichText {...defaultProps} text={'**be bold**'} />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render with paragraph', () => {
    const component = render(
      <RichText {...defaultProps} text={'IAM BOLD'} />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render link with title', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          '[I\'m an inline-style link with title](https://www.google.com "Google\'s Homepage")\n**Bollllddd**\n*this is talic*'
        }
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render and sanitize malicious text', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          "Hi everyone! This is **how** you do XSS <script>alert('XSS')</script>"
        }
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render and sanitize malicious text', () => {
    const component = render(
      <RichText {...defaultProps} text={'[some text](javascript:exec())'} />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render and sanitize malicious javascript', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={'<a href="javascript:exec()">HEHEHE</a>'}
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render and sanitize malicious javascript', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          '<DIV STYLE="background-image: url(javascript:alert(\'XSS\'))">'
        }
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render with image', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          `Inline-style:
          ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png "Logo Title Text 1")`
        }
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render with image no title', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          `Inline-style:
          ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png)`
        }
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render with image block class props', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          `[I\'m an inline-style link with title](https://www.google.com "Google\'s Homepage")\n**Bollllddd**\n*this is talic*`
        }
        blockClass="home"
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render an iframe', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={ // It must be preceeded by Markdown tag or any other string, otherwise the lib marked escapes the iframe HTML tag.
          "__<iframe frameborder=\"0\" height=\"100%\" src=\"https://invictastores.typeform.com/to/KmycOT?typeform-embed=embed-widget&embed-hide-footer=true&embed-hide-headers=true&embed-opacity=50&typeform-embed-id=d0pe3\" width=\"100%\" style=\"border: 0px;\"></iframe>__"
        }
      />
    )

    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render a link with target', () => {
    const { asFragment } = render(
      <RichText
        {...defaultProps}
        text="<a href='https://google.com' target='_blank'>bc</a>"
      />
    )

    expect(asFragment()).toMatchSnapshot()
  })

  it('should render a link with target as query string on markdown and erase ?', () => {
    const { asFragment } = render(
      <RichText
        {...defaultProps}
        text="[text](https://www.google.com?target=_blank)  "
      />
    )
    expect(asFragment()).toMatchSnapshot()
  })

  it('should render a link with target as query string on markdown and keep others params', () => {
    const { asFragment } = render(
      <RichText
        {...defaultProps}
        text="[text](https://www.google.com?target=_blank&foo=tree\&bar=lala)"
      />
    )
    expect(asFragment()).toMatchSnapshot()
  })

  it('should render a link with target as query string on markdown and keep others params, remove trailing &', () => {
    const { asFragment } = render(
      <RichText
        {...defaultProps}
        text="[text](https://www.google.com?foo=tree\&bar=lala&target=_blank)"
      />
    )
    expect(asFragment()).toMatchSnapshot()
  })

  it('should render a table', () => {
    const { asFragment } = render(
      <RichText
        {...defaultProps}
        text={`
foo|bar
---|---
teste|abc
        `}
      />
    )

    expect(asFragment()).toMatchSnapshot()
  })

  it('should render with two different heading levels', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          `# Heading 1 # \n ### Heading level 3 ##`
        }
        blockClass="home"
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render list', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          `Teste \n * Item 1\n * Item 2\n * Item 3`
        }
        blockClass="home"
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should render ordered list', () => {
    const component = render(
      <RichText
        {...defaultProps}
        text={
          `Teste \n 1. Item 1\n 2. Item 2\n 3. Item 3`
        }
        blockClass="home"
      />
    )
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should sanitize the font prop', () => {
    const typography = 't-heading-1'
    const { container } = render(<RichText {...defaultProps} text="foo" font={`${typography} foo`} />)

    const element = container.querySelector(`.${typography}`)
    const notFound = container.querySelector('.foo')

    expect(element).toBeTruthy()
    expect(notFound).toBeFalsy()
  })

  it('should sanitize the textColor prop', () => {
    const color = 'c-muted-1'
    const { container } = render(<RichText {...defaultProps} text="foo" textColor={`${color} foo`} />)

    const element = container.querySelector(`.${color}`)
    const notFound = container.querySelector('.foo')

    expect(element).toBeTruthy()
    expect(notFound).toBeFalsy()
  })

  it('should allow tel scheme on link', () => {
    const component = render(<RichText {...defaultProps} text="[Telefone](tel:+18882165252)" />)
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should allow mailto scheme on link', () => {
    const component = render(<RichText {...defaultProps} text="[Mail me](mailto:test@vtex.com.br)" />)
    expect(component).toBeDefined()
    expect(component.asFragment()).toMatchSnapshot()
  })
})

```


[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/RichText/typings/vtex.css-handles.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.css-handles' {
  type CssHandlesInput = readonly string[]
  type ValueOf<T extends readonly any[]> = T[number]
  export type CssHandles<T extends CssHandlesInput> = Record<ValueOf<T>, string>
  interface CssHandlesOptions {
    migrationFrom?: string | string[]
  }

  export const useCssHandles: <T extends CssHandlesInput>(
    handles: T,
    options: CssHandlesOptions = {}
  ) => CssHandles<T>
  export const applyModifiers: (
    handles: string,
    modifier: string | string[]
  ) => string
}

```


[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/RichText/typings/SchemaTypes.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
export enum textPositionValues {
  CENTER = 'CENTER',
  LEFT = 'LEFT',
  RIGHT = 'RIGHT',
}

export enum textAlignmentValues {
  CENTER = 'CENTER',
  LEFT = 'LEFT',
  RIGHT = 'RIGHT',
}

export const textPositionTypes: Record<
  string,
  { name: string; value: textPositionValues }
> = {
  TEXT_POSITION_CENTER: {
    name: 'admin/editor.rich-text.textPosition.center',
    value: textPositionValues.CENTER,
  },
  TEXT_POSITION_LEFT: {
    name: 'admin/editor.rich-text.textPosition.left',
    value: textPositionValues.LEFT,
  },
  TEXT_POSITION_RIGHT: {
    name: 'admin/editor.rich-text.textPosition.right',
    value: textPositionValues.RIGHT,
  },
}

export const textAlignmentTypes: Record<
  string,
  { name: string; value: textAlignmentValues }
> = {
  TEXT_ALIGNMENT_CENTER: {
    name: 'admin/editor.rich-text.textAlignment.center',
    value: textAlignmentValues.CENTER,
  },
  TEXT_ALIGNMENT_LEFT: {
    name: 'admin/editor.rich-text.textAlignment.left',
    value: textAlignmentValues.LEFT,
  },
  TEXT_ALIGNMENT_RIGHT: {
    name: 'admin/editor.rich-text.textAlignment.right',
    value: textAlignmentValues.RIGHT,
  },
}

```


[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/RichText/typings/insane.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
// Insane is based on the sanitize-html package.
// So its types are extremely close to it.

declare function insane(dirty: string, options?: insane.IOptions): string

declare namespace insane {
  interface IOptions {
    allowedTags?: string[] | boolean
    allowedAttributes?: { [index: string]: string[] } | boolean
    allowedStyles?:  { [index: string]: { [index: string]: RegExp[] } }
    allowedClasses?: { [index: string]: string[] } | boolean
  }
}

declare module 'insane' {
  export = insane
}
```


[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/RichText/typings/vtex.native-types.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.native-types' {
  import { Component } from 'react'
  import { InjectedIntl, WrappedComponentProps } from 'react-intl'

  interface FormatIOMessageParams {
    id: string
    intl: InjectedIntl
  }

  export const formatIOMessage: (params: FormatIOMessageParams) => string

  interface IOMessageProps extends WrappedComponentProps {
    id: string
  }

  export const IOMessage: Component<IOMessageProps>
}

```


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

[Assistant]
Here's the implementation:

```css
.heading-level-1 {
}
.heading-level-2 {
}
.heading-level-3 {
}
.heading-level-4 {
}
.heading-level-5 {
}
.heading-level-6 {
}

```


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

[Assistant]
Here's the implementation:

```javascript
/* eslint-env jest */
import React from 'react'
import { render } from '@vtex/test-tools/react'
import ProductList from '../components/ProductList'
import { productMock } from '../__mocks__/productMock'
import Shelf from '../Shelf'
import { resolvePaginationDotsVisibility } from '../utils/resolvePaginationDots'
const fs = require('fs')

describe('Shelf component', () => {
  const renderComponent = customProps => {
    const props = {
      maxItems: 1,
      itemsPerPage: 1,
      arrows: true,
      showTitle: true,
    }

    const titleText = 'store/shelf.title'
    const wrapper = render(<ProductList titleText={titleText} {...props} {...customProps} />)
    return wrapper
  }

  it('should be rendered', () => {
    const component = renderComponent()
    expect(component).toBeDefined()
  })

  it('should render nothing if there is no products', () => {
    const component = renderComponent({ products: [] })

    expect(component.container.querySelector('.title')).toBeFalsy()
  })

  it('should match the snapshot', () => {
    const component = renderComponent({ products: productMock })
    expect(component.asFragment()).toMatchSnapshot()
  })

  it('should show in Site Editor', () => {
    const { schema } = Shelf
    expect(schema).toBeDefined()
    expect(typeof schema.title).toBe('string')
  })
})

describe('Util functions', () => {
  it('should return the correct value for pagination dots visibility', () => {
    const MOBILE_ONLY = 'mobileOnly'
    const DESKTOP_ONLY = 'desktopOnly'
    const VISIBLE = 'visible'
    const HIDDEN = 'hidden'

    /** resolvePaginationDotsVisibility(visibility: string, isMobile: boolean) */
    expect(resolvePaginationDotsVisibility(MOBILE_ONLY, false)).toBeFalsy()
    expect(resolvePaginationDotsVisibility(MOBILE_ONLY, true)).toBeTruthy()

    expect(resolvePaginationDotsVisibility(DESKTOP_ONLY, false)).toBeTruthy()
    expect(resolvePaginationDotsVisibility(DESKTOP_ONLY, true)).toBeFalsy()

    expect(resolvePaginationDotsVisibility(VISIBLE, false)).toBeTruthy()
    expect(resolvePaginationDotsVisibility(VISIBLE, true)).toBeTruthy()

    expect(resolvePaginationDotsVisibility(HIDDEN, false)).toBeFalsy()
    expect(resolvePaginationDotsVisibility(HIDDEN, true)).toBeFalsy()
  })
})

```


[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/Shelf/typings/vtex.native-types.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.native-types' {
    import { Component } from 'react'
    import { InjectedIntl, InjectedIntlProps } from 'react-intl'

    interface FormatIOMessageParams {
      id: string
      intl: InjectedIntl
    }

    export const formatIOMessage: (params: FormatIOMessageParams) => string

    interface IOMessageProps extends InjectedIntlProps {
      id: string
    }

    export const IOMessage: Component<IOMessageProps>
  }

```


[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/Shelf/queries/productRecommendations.gql from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```graphql
query ProductRecommendations(
  $identifier: ProductUniqueIdentifier
  $type: CrossSelingInputEnum
) {
  productRecommendations(identifier: $identifier, type: $type)
    @context(provider: "vtex.search-graphql") {
    cacheId
    productId
    productName
    description
    link
    linkText
    brand
    brandId
    categories
    priceRange {
      sellingPrice {
        highPrice
        lowPrice
      }
      listPrice {
        highPrice
        lowPrice
      }
    }
    specificationGroups {
      name
      specifications {
        name
        values
      }
    }
    items {
      name
      itemId
      measurementUnit
      unitMultiplier
      referenceId {
        Value
      }
      images {
        imageId
        imageLabel
        imageTag
        imageUrl
        imageText
      }
      sellers {
        sellerId
        sellerName
        addToCartLink
        sellerDefault
        commertialOffer {
          Price
          ListPrice
          PriceWithoutDiscount
          RewardValue
          PriceValidUntil
          AvailableQuantity
          Tax
          CacheVersionUsedToCallCheckout
          Installments(criteria: MAX) {
            Value
            InterestRate
            TotalValuePlusInterestRate
            NumberOfInstallments
            Name
          }
          teasers {
            name
          }
          discountHighlights {
            name
          }
        }
      }
    }
    properties {
      name
      values
    }
  }
}

```


[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/Shelf/queries/productsQuery.gql from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```graphql
query Products(
  $category: String
  $collection: String
  $specificationFilters: [String]
  $orderBy: String
  $from: Int
  $to: Int
  $hideUnavailableItems: Boolean = false
  $skusFilter: ItemsFilter = ALL_AVAILABLE
) {
  products(
    category: $category
    collection: $collection
    specificationFilters: $specificationFilters
    orderBy: $orderBy
    from: $from
    to: $to
    hideUnavailableItems: $hideUnavailableItems
  ) @context(provider: "vtex.search-graphql") {
    cacheId
    productId
    productName
    productReference
    description
    link
    linkText
    brand
    brandId
    categories
    priceRange {
      sellingPrice {
        highPrice
        lowPrice
      }
      listPrice {
        highPrice
        lowPrice
      }
    }
    specificationGroups {
      name
      specifications {
        name
        values
      }
    }
    items(filter: $skusFilter) {
      name
      itemId
      measurementUnit
      unitMultiplier
      referenceId {
        Value
      }
      images {
        imageUrl
        imageTag
        imageLabel
      }
      variations {
        name
        values
      }
      sellers {
        sellerId
        commertialOffer {
          Installments(criteria: MAX) {
            Value
            InterestRate
            TotalValuePlusInterestRate
            NumberOfInstallments
            Name
          }
          AvailableQuantity
          Price
          PriceWithoutDiscount
          ListPrice
          teasers {
            name
          }
          discountHighlights {
            name
          }
        }
      }
    }
    productClusters {
      id
      name
    }
    properties {
      name
      values
    }
  }
}

```


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

[Assistant]
Here's the implementation:

```javascript
function getBuyableSellers(sellers) {
  return sellers && sellers.length > 0
    ? sellers.filter(seller => seller.sellerId)
    : sellers
}

export function normalizeBuyable(product) {
  if (!product || !product.items || product.items.length === 0) {
    return product
  }

  const buyableItems = product.items
    .map(item => ({
      ...item,
      sellers: getBuyableSellers(item.sellers),
    }))
    .filter(item => item && item.sellers && item.sellers.length > 0)

  return buyableItems ? { ...product, items: buyableItems } : null
}

```


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

[Assistant]
Here's the implementation:

```javascript
import { pluck, values } from 'ramda'

const gapPaddingTypes = {
  NONE: {
    name: 'admin/editor.shelf.gapType.none',
    value:'ph0',
  },
  SMALL: {
    name: 'admin/editor.shelf.gapType.small',
    value:'ph3',
  },
  MEDIUM: {
    name: 'admin/editor.shelf.gapType.medium',
    value:'ph5',
  },
  LARGE: {
    name: 'admin/editor.shelf.gapType.large',
    value:'ph7',
  },
}

export const getGapPaddingNames = () => values(pluck('name', gapPaddingTypes))

export const getGapPaddingValues = () => values(pluck('value', gapPaddingTypes))

export default gapPaddingTypes
```


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

[Assistant]
Here's the implementation:

```javascript
const ordenationTypes = {
  ORDER_BY_RELEVANCE: {
    name: 'admin/editor.shelf.orderType.relevance',
    value: '',
  },
  ORDER_BY_TOP_SALE_DESC: {
    name: 'admin/editor.shelf.orderType.sales',
    value: 'OrderByTopSaleDESC',
  },
  ORDER_BY_PRICE_DESC: {
    name: 'admin/editor.shelf.orderType.priceDesc',
    value: 'OrderByPriceDESC',
  },
  ORDER_BY_PRICE_ASC: {
    name: 'admin/editor.shelf.orderType.priceAsc',
    value: 'OrderByPriceASC',
  },
  ORDER_BY_NAME_ASC: {
    name: 'admin/editor.shelf.orderType.nameAsc',
    value: 'OrderByNameASC',
  },
  ORDER_BY_NAME_DESC: {
    name: 'admin/editor.shelf.orderType.nameDesc',
    value: 'OrderByNameDESC',
  },
  ORDER_BY_RELEASE_DATE_DESC: {
    name: 'admin/editor.shelf.orderType.releaseDate',
    value: 'OrderByReleaseDateDESC',
  },
  ORDER_BY_BEST_DISCOUNT_DESC: {
    name: 'admin/editor.shelf.orderType.discount',
    value: 'OrderByBestDiscountDESC',
  },
}

export function getOrdenationNames() {
  const names = []
  for (const key in ordenationTypes) {
    names.push(ordenationTypes[key].name)
  }
  return names
}

export function getOrdenationValues() {
  const values = []
  for (const key in ordenationTypes) {
    values.push(ordenationTypes[key].value)
  }
  return values
}

export default ordenationTypes

```


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

[Assistant]
Here's the implementation:

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

import { getScrollValues } from './ScrollTypes'
import { getGapPaddingValues } from './paddingEnum'

export const shelfItemPropTypes = {
  item: PropTypes.shape({
    productId: PropTypes.string.isRequired,
    productName: PropTypes.string.isRequired,
    description: PropTypes.string.isRequired,
    categories: PropTypes.array,
    link: PropTypes.string.isRequired,
    linkText: PropTypes.string.isRequired,
    brandId: PropTypes.number,
    brand: PropTypes.string.isRequired,
    items: PropTypes.arrayOf(
      PropTypes.shape({
        itemId: PropTypes.string.isRequired,
        name: PropTypes.string.isRequired,
        referenceId: PropTypes.arrayOf(
          PropTypes.shape({
            Value: PropTypes.string.isRequired,
          })
        ),
        images: PropTypes.arrayOf(
          PropTypes.shape({
            imageUrl: PropTypes.string.isRequired,
            imageTag: PropTypes.string.isRequired,
          })
        ).isRequired,
        sellers: PropTypes.arrayOf(
          PropTypes.shape({
            commertialOffer: PropTypes.shape({
              /** SKU installments */
              Installments: PropTypes.arrayOf(
                PropTypes.shape({
                  /** Installment value */
                  Value: PropTypes.number.isRequired,
                  /** Interest rate (zero if interest-free) */
                  InterestRate: PropTypes.number.isRequired,
                  /** Calculated total value */
                  TotalValuePlusInterestRate: PropTypes.number,
                  /** Number of installments */
                  NumberOfInstallments: PropTypes.number.isRequired,
                  /** Installments offer name */
                  Name: PropTypes.string,
                })
              ),
              Price: PropTypes.number.isRequired,
              ListPrice: PropTypes.number.isRequired,
            }).isRequired,
          })
        ).isRequired,
      })
    ).isRequired,
  }),
  summary: PropTypes.any,
  position: PropTypes.number,
}

export const productListSchemaPropTypes = {
  /** Maximum number of items in the shelf. */
  maxItems: PropTypes.number.isRequired,
  /** Maximum number of items in a page. */
  itemsPerPage: PropTypes.number.isRequired,
  /** Minimum number of items in a page. */
  minItemsPerPage: PropTypes.number,
  /** Scroll options. */
  scroll: PropTypes.oneOf(getScrollValues()),
  /** If the arrows are showable or not. */
  arrows: PropTypes.bool.isRequired,
  /** Show value of the title. */
  showTitle: PropTypes.bool,
  /** Text value of the title. */
  titleText: PropTypes.string,
  /** Product Summary schema props */
  summary: PropTypes.any,
}

export const shelfContentPropTypes = {
  /** List of products */
  products: PropTypes.arrayOf(shelfItemPropTypes.item),
  /** Max Items per page */
  itemsPerPage: PropTypes.number,
  /** Minimum Items per page */
  minItemsPerPage: PropTypes.number,
  /** Max items in shelf */
  maxItems: PropTypes.number.isRequired,
  /** Show Arrows */
  arrows: PropTypes.bool.isRequired,
  /** Scroll type */
  scroll: PropTypes.string.isRequired,
  /** Should display navigation dots below the Shelf */
  paginationDotsVisibility: PropTypes.oneOf([
    'visible',
    'hidden',
    'desktopOnly',
    'mobileOnly',
  ]),
  /** Container width */
  width: PropTypes.number,
  /** Props to ProductsSummary */
  summary: PropTypes.any,
  /** Is mobile */
  isMobile: PropTypes.bool,
  /** Gap between Shelf Items */
  gap: PropTypes.oneOf(getGapPaddingValues()),
}

```


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

[Assistant]
Here's the implementation:

```javascript
const scrollTypes = {
  BY_PAGE: {
    name: 'admin/editor.shelf.scrollType.byPage',
    value: 'BY_PAGE',
  },
  ONE_BY_ONE: {
    name: 'admin/editor.shelf.scrollType.oneByOne',
    value: 'ONE_BY_ONE',
  },
}

export function getScrollNames() {
  const names = []
  for (const key in scrollTypes) {
    names.push(scrollTypes[key].name)
  }
  return names
}

export function getScrollValues() {
  const values = []
  for (const key in scrollTypes) {
    values.push(scrollTypes[key].value)
  }
  return values
}

export default scrollTypes

```


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

[Assistant]
Here's the implementation:

```javascript
export function resolvePaginationDotsVisibility(visibility, isMobile) {
  return !!(
    visibility === 'visible' ||
    (visibility === 'mobileOnly' && isMobile) ||
    (visibility === 'desktopOnly' && !isMobile)
  )
}

```


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

[Assistant]
Here's the implementation:

```javascript
import React, { useMemo, useCallback, useEffect } from 'react'
import { assocPath } from 'ramda'
import { useInView } from 'react-intersection-observer'
import ProductListContext from '../../ProductListContext/ProductListContext'

import {usePixel} from '../../PixelContext/PixelContext'
import ProductSummaryShelf from '../../ProductSummary/ProductSummaryCustom'
import ProductSummaryImage from '../../ProductSummary/ProductSummaryImage'
import ProductSummarySpecificationBadges from '../../ProductSummary/ProductSummarySpecificationBadges'
import ProductSummaryName from '../../ProductSummary/ProductSummaryName'
import ProductSummarySpacer from '../../ProductSummary/Spacer'
import ProductSummaryPrice from '../../ProductSummary/ProductSummaryPrice'
// import RatingInline from '../../ReviewsRatings/RatingInline'
import AddToCartBtn from '../../AddToCartButton/Wrapper'
import StackLayout from '../../StackLayout'

const { useProductListDispatch } = ProductListContext
/**
 * ShelfItem Component. Normalizes the item received in the props
 * to adapt to the extension point prop.
 */
const ShelfItem = ({ item, summary }) => {
  const { push } = usePixel()
  const newSummary = useMemo(() => assocPath(['name', 'tag'], 'h2', summary), [
    summary,
  ])
  const product = useMemo(
    () => ProductSummaryShelf.mapCatalogProductToProductSummary(item),
    [item]
  )

  const pushPixelProductClick = useCallback(() => {
    push({
      event: 'productClick',
      product: product,
    })
  }, [product, push])

  const [inViewRef, inView] = useInView({
    // Triggers the event when the element is 75% visible
    threshold: 0.75,
    triggerOnce: true,
  })

  const dispatch = useProductListDispatch()

  useEffect(() => {
    if (inView) {
      dispatch({ type: 'SEND_IMPRESSION', args: { product: product } })
    }
  }, [dispatch, inView, product])
  return (
    <ProductSummaryShelf containerRef={inViewRef} product={product} actionOnClick={pushPixelProductClick} {...newSummary}>
      <StackLayout>
        <ProductSummaryImage />
        <ProductSummarySpecificationBadges specificationGroupName="Group" specificationName="On Sale" visibleWhen="True" displayValue="SPECIFICATION_NAME" />
      </StackLayout>
      <ProductSummaryName />
      <ProductSummarySpacer />
      <ProductSummaryPrice />
      <AddToCartBtn />
    </ProductSummaryShelf>
  )

  // return (
  //   <ExtensionPoint
  //     id="product-summary"
  //     containerRef={inViewRef}
  //     product={product}
  //     actionOnClick={pushPixelProductClick}
  //     {...newSummary}
  //   />
  // )
}

export default ShelfItem

```


[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/Shelf/components/shelf.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
.arrow {
  top: 50%;
  transform: translate(0, -50%);
}

.dot {
  height: 0.625rem;
  width: 0.625rem;
}

```


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

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import React, { Fragment } from 'react'
import ReactResizeDetector from 'react-resize-detector'
import IOMessage from '../../NativeTypes/IOMessage'
import useCssHandles from '../../CssHandles/useCssHandles'

import {
  productListSchemaPropTypes,
  shelfItemPropTypes,
} from '../utils/propTypes'
import ScrollTypes from '../utils/ScrollTypes'
import GapPaddingTypes from '../utils/paddingEnum'
import ShelfContent from './ShelfContent'
import ProductListEventCaller from './ProductListEventCaller'

const CSS_HANDLES = ['title']

const DEFAULT_MAX_ITEMS = 10
const DEFAULT_ITEMS_PER_PAGE = 5
const DEFAULT_MIN_ITEMS_PER_PAGE = 1

/**
 * Product List Component. Shows a collection of products.
 */
const ProductList = ({
  gap,
  arrows,
  scroll,
  summary,
  maxItems,
  products,
  isMobile,
  autoplay,
  showTitle,
  titleText,
  itemsPerPage,
  minItemsPerPage,
  paginationDotsVisibility,
  navigationStep: navigationStepProp,
}) => {
  const handles = useCssHandles(CSS_HANDLES)
  const navigationStep = isNaN(parseInt(navigationStepProp)) ? navigationStepProp : parseInt(navigationStepProp)

  return products && !products.length ? null : (
    <Fragment>
      {showTitle && (
        <div
          className={`${handles.title} t-heading-2 fw3 w-100 flex justify-center pt7 pb6 c-muted-1`}
        >
          <IOMessage id={titleText} />
        </div>
      )}
      <ReactResizeDetector handleWidth>
        {width => (
          <ShelfContent
            gap={gap}
            width={width.width}
            arrows={arrows}
            scroll={scroll}
            summary={summary}
            products={products}
            maxItems={maxItems}
            isMobile={isMobile}
            autoplay={autoplay}
            itemsPerPage={itemsPerPage}
            navigationStep={navigationStep}
            minItemsPerPage={minItemsPerPage}
            paginationDotsVisibility={paginationDotsVisibility}
          />
        )}
      </ReactResizeDetector>
      <ProductListEventCaller />
    </Fragment>
  )
}

ProductList.defaultProps = {
  maxItems: DEFAULT_MAX_ITEMS,
  minItemsPerPage: DEFAULT_MIN_ITEMS_PER_PAGE,
  itemsPerPage: DEFAULT_ITEMS_PER_PAGE,
  scroll: ScrollTypes.BY_PAGE.value,
  gap: GapPaddingTypes.SMALL.value,
  arrows: true,
  showTitle: true,
  titleText: null,
  isMobile: false,
}

ProductList.propTypes = {
  /** Loading status */
  loading: PropTypes.bool,
  /** Graphql data response. */
  products: PropTypes.arrayOf(shelfItemPropTypes.item),
  /** Verifies if is a mobile device. */
  isMobile: PropTypes.bool,
  /** Should display navigation dots below the Shelf */
  paginationDotsVisibility: PropTypes.oneOf([
    'visible',
    'hidden',
    'desktopOnly',
    'mobileOnly',
  ]),
  ...productListSchemaPropTypes,
}

ProductList.schema = {
  title: 'admin/editor.shelf.title',
}

export default ProductList

```


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

[Assistant]
Here's the implementation:

```javascript
import useProductImpression from '../../ProductListContext/useProductImpression'

const ProductListEventCaller = () => {
  useProductImpression()
  return null
}

export default ProductListEventCaller

```


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

[Assistant]
Here's the implementation:

```javascript
import { path } from 'ramda'
import React, { Component } from 'react'
import IconCaret from '../../StoreIcons/IconCaret'
import classNames from 'classnames'
import { NoSSR } from '../../../../../vtex.render-runtime'
import Dots from '../../Slider/Dots'
import Slide from '../../Slider/Slide'
import Slider from '../../Slider/Slider'
import SliderContainer from '../../Slider/SliderContainer'
import withCssHandles from '../../CssHandles/withCssHandles'
import { resolvePaginationDotsVisibility } from '../utils/resolvePaginationDots'
import ScrollTypes from '../utils/ScrollTypes'
import ShelfItem from './ShelfItem'
import { shelfContentPropTypes } from '../utils/propTypes'
import shelf from './shelf.css'

const CSS_HANDLES = ['arrow', 'arrowLeft', 'arrowRight', 'shelfContentContainer', 'sliderContainer', 'slide']
const SLIDER_WIDTH_ONE_ELEMENT = 320
const SLIDER_WIDTH_TWO_ELEMENTS = 500
const SLIDER_WIDTH_THREE_ELEMENTS = 750
const SLIDER_WIDTH_FOUR_ELEMENTS = 1000
const SLIDER_WIDTH_FIVE_ELEMENTS = 1290
const DEFAULT_SHELF_ITEM_WIDTH = 260

/**
 * ShelfContent Component. Executes the interaction with react-slick
 * and render the properly content of the Shelf depending of edit mode state.
 */
class ShelfContent extends Component {
  constructor(props) {
    super(props)
    this.perPage = {
      [SLIDER_WIDTH_FIVE_ELEMENTS]: 5,
      [SLIDER_WIDTH_FOUR_ELEMENTS]: 4,
      [SLIDER_WIDTH_THREE_ELEMENTS]: 3,
      [SLIDER_WIDTH_TWO_ELEMENTS]: 2,
      [SLIDER_WIDTH_ONE_ELEMENT]: 1,
    }
    this.calcItemsPerPage()
    this.state = {
      currentSlide: 0,
      firstRender: true,
    }
  }

  calcItemsPerPage = () => {
    const { itemsPerPage } = this.props
    for (let key in this.perPage) {
      if (this.perPage[key] > itemsPerPage) delete this.perPage[key]
    }
  }

  handleChangeSlide = i => {
    this.setState({ currentSlide: i })
  }

  handleNextSlide = () => {
    const { currentSlide } = this.state
    const { itemsPerPage, maxItems, products, isMobile } = this.props
    const productList =
      !products || !products.length ? Array(maxItems).fill(null) : products
    const totalItems = productList.slice(0, maxItems).length
    const customPerPage = (!isMobile && itemsPerPage) || this.perPage
    const nextSlide = ((currentSlide) % totalItems) + customPerPage
    this.handleChangeSlide(nextSlide)
  }

  componentDidMount() {
    this.setState({
      firstRender: false,
    })
  }

  arrowRender = ({ orientation, onClick }) => {
    const { gap, cssHandles } = this.props
    const containerClasses = classNames(
      shelf.arrow,
      'pointer z-1 flex absolute',
      {
        [`${cssHandles.arrowLeft} left-0 ${gap}`]: orientation === 'left',
        [`${cssHandles.arrowRight} right-0 ${gap}`]: orientation === 'right',
      }
    )
    return (
      <div className={containerClasses} onClick={onClick}>
        <IconCaret orientation={orientation} thin size={20} />
      </div>
    )
  }

  roundHalf = num => Math.round(num * 2) / 2

  render() {
    const {
      gap,
      scroll,
      arrows,
      summary,
      autoplay,
      isMobile,
      maxItems,
      products,
      cssHandles,
      itemsPerPage,
      navigationStep,
      minItemsPerPage,
      paginationDotsVisibility,
    } = this.props

    const { currentSlide } = this.state
    const showPaginationDots = resolvePaginationDotsVisibility(
      paginationDotsVisibility,
      isMobile
    )

    const isScrollByPage = scroll === ScrollTypes.BY_PAGE.value

    const productList =
      !products || !products.length ? Array(maxItems).fill(null) : products

    const roundedMinItems = this.roundHalf(minItemsPerPage)
    const customPerPage = !isMobile && itemsPerPage

    return (
      <div className={`${cssHandles.shelfContentContainer} flex justify-center`}>
        <SliderContainer
          autoplay={autoplay}
          onNextSlide={this.handleNextSlide}
          className={`${cssHandles.sliderContainer} w-100 mw9`}
        >
          <Slider
            loop
            easing="ease"
            duration={500}
            minPerPage={roundedMinItems}
            scrollByPage={isScrollByPage}
            navigationStep={navigationStep}
            currentSlide={Math.ceil(currentSlide)}
            onChangeSlide={this.handleChangeSlide}
            perPage={customPerPage || this.perPage}
            arrowRender={arrows && this.arrowRender}
          >
            {productList.slice(0, maxItems).map((item, index) => (
              <Slide
                sliderTransitionDuration={500}
                className={classNames('justify-center h-100', gap, cssHandles.slide)}
                key={path(['productId'], item) || index}
                defaultWidth={DEFAULT_SHELF_ITEM_WIDTH}
              >
                <ShelfItem item={item} summary={summary} />
              </Slide>
            ))}
          </Slider>
          {showPaginationDots && (
            <NoSSR>
              <Dots
                loop
                showDotsPerPage={isScrollByPage}
                minPerPage={roundedMinItems}
                perPage={this.perPage}
                currentSlide={Math.ceil(currentSlide)}
                totalSlides={productList.slice(0, maxItems).length}
                onChangeSlide={this.handleChangeSlide}
                classes={{
                  root: 'pt4',
                  notActiveDot: 'bg-muted-3',
                  dot: classNames(shelf.dot, 'mh2 mv0 pointer br-100'),
                  activeDot: 'bg-emphasis',
                }}
              />
            </NoSSR>
          )}
        </SliderContainer>
      </div>
    )
  }
}

ShelfContent.defaultProps = {
  itemsPerPage: 5,
  minItemsPerPage: 1,
  paginationDotsVisibility: 'visible',
}

ShelfContent.propTypes = {
  /** List of products */
  products: shelfContentPropTypes.products,
  /** Max Items per page */
  itemsPerPage: shelfContentPropTypes.itemsPerPage,
  /** Minimum Items per page */
  minItemsPerPage: shelfContentPropTypes.minItemsPerPage,
  /** Max items in shelf */
  maxItems: shelfContentPropTypes.maxItems,
  /** Show Arrows */
  arrows: shelfContentPropTypes.arrows,
  /** Scroll type */
  scroll: shelfContentPropTypes.scroll,
  /** Should display navigation dots below the Shelf */
  paginationDotsVisibility: shelfContentPropTypes.paginationDotsVisibility,
  /** Container width */
  width: shelfContentPropTypes.width,
  /** Props to ProductsSummary */
  summary: shelfContentPropTypes.summary,
  /** Is mobile */
  isMobile: shelfContentPropTypes.isMobile,
  /** Gap between Shelf Items */
  gap: shelfContentPropTypes.gap,
}

export default withCssHandles(CSS_HANDLES)(ShelfContent)

```


[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/Shelf/components/TabbedShelf/Tabs.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
import classNames from 'classnames'
import useCssHandles from '../CssHandles/useCssHandles'

const CSS_HANDLES = ['itemContainer', 'itemContainerSelected', 'itemContainerUnselected', 'tabsContainer', 'tabsNamesContainer', 'shelfContainer', 'tabButton']

class Tabs extends Component {

  state = {
    selectedIndex: 0
  }

  handleClick = (index) => this.setState({ selectedIndex: index })

  render() {
    const { panes, cssHandles } = this.props
    const { selectedIndex } = this.state
    if (!panes || panes.length === 0) {
      return null
    }

    return (
      <div className="flex justify-center items-center">
      <div className={`${cssHandles.tabsContainer} flex-ns pa6-ns justify-between-ns w-100-s`}>
        <div className={`${cssHandles.tabsNamesContainer} flex flex-column mh6`}>
          {panes.map((pane, index) => {
            const isSelected = index === selectedIndex
            const isLast = index === panes.length - 1
            const itemContainer = classNames(`t-body pa4 tl bb b--muted-4 ${cssHandles.itemContainer}`, {
              [`bg-base--inverted c-on-muted-1 ${cssHandles.itemContainerSelected}`]: isSelected,
              [cssHandles.itemContainerUnselected]: !isSelected,
              'bw0': isLast,
            })
            return (
              <button
                type="button"
                onClick={() => this.handleClick(index)}
                className={`${cssHandles.tabButton} bn outline-0 bg-transparent pa0`}
              >
                <div className={itemContainer} key={pane.menuItem} >{pane.menuItem}</div>
              </button>
            )
          })}
        </div>
        <div className={`${cssHandles.shelfContainer} mh6-s mw-100 overflow-hidden`}>
          {panes[selectedIndex].render()}
        </div>
      </div>
      </div>
    )
  }
}

export default withCssHandles(CSS_HANDLES)(Tabs)

```


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

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import { injectIntl } from 'react-intl'
import React, { Fragment, Component } from 'react'
import Button from '../../../Styleguide/Button'
import { withRuntimeContext } from '../../../../../../vtex.render-runtime'
import Tabs from './Tabs'
import Shelf from '../../Shelf'
import withCssHandles from '../../../CssHandles/withCssHandles'
import tabbedShelf from './tabbedShelf.css'

const MAX_NUMBER_OF_MENUS = 6
const CSS_HANDLES = ['headline']

/**
 * Tabbed Shelf Module
 */
class TabbedShelf extends Component {

    /**
     * The prop types for this component
     * Used for type checking
     */
    static propTypes = {
        isEnabled: PropTypes.bool,
        headline: PropTypes.string,
        bottomText: PropTypes.string,
        tabs: PropTypes.PropTypes.arrayOf(PropTypes.shape({
          id: PropTypes.number,
          __editorItemTitle: PropTypes.string,
        })),
        shelf: PropTypes.object,
    };

    /**
     * Default prop types for this component
     */
    static defaultProps = {
        isEnabled: false,
        headline: '',
        bottomText: '',
        tabs: [],
        shelf: {},
    };

    /**
     * Handle primary call to action button click
     */
    handleButtonClick(url) {
        if(url != undefined && url != '') {
            window.open(url.buttunUrl,"_self");
        }
    }

    /**
     * Return the component
     *
     * @returns {*}
     */
    render() {
        const {
            isEnabled,
            headline,
            bottomText,
            buttunUrl,
            buttonText,
            tabs,
            cssHandles
        } = this.props

        const panes = (tabs.length > 0 ? tabs.map(tab => (
            { menuItem: tab.__editorItemTitle, render: () => (
              <Shelf
                {...{ ...this.props.shelf }}
                category={tab.id}
              />
            )
            }
        )) : '');

        return (
            <Fragment>
                {isEnabled ? (
                    <div className={`${tabbedShelf.container}`}>
                        <h3 className={`${cssHandles.headline}`}>{headline}</h3>
                          <Tabs panes={panes} />
                        <div className={`${tabbedShelf.blockContainer}`}>
                            <p className={`${tabbedShelf.blockText}`}>{bottomText}</p>
                            {buttonText ? (
                                <div className={`${tabbedShelf.buttonContainer}`}>
                                  <Button onClick={() => this.handleButtonClick({buttunUrl})}>{buttonText}</Button>
                                </div>
                            ) : ('')}
                        </div>
                    </div>
                ) : null
                }
            </Fragment>
        );
    }
}

/**
 * Schema for component to allow configuration of props from admin configuration
 */
TabbedShelf.getSchema = props => ({
    title: 'admin/editor.tabbed-shelf.title',
    description: 'admin/editor.tabbed-shelf.description',
    type: 'object',
    properties: {
        isEnabled: {
            title: 'admin/editor.tabbed-shelf.isEnabled.title',
            type: 'boolean',
            default: false,
        },
        headline: {
            title: 'admin/editor.tabbed-shelf.headline',
            type: 'string',
        },
        bottomText: {
            title: 'admin/editor.tabbed-shelf.bottomText',
            type: 'string',
            widget: {
                'ui:widget': 'textarea',
            },
        },
        buttonText: {
            title: 'admin/editor.tabbed-shelf.buttonText',
            type: 'string',
        },
        buttunUrl: {
            title: 'admin/editor.tabbed-shelf.buttonUrl',
            type: 'string',
        },
        tabs: {
            title:'admin/editor.tabbed-shelf.tabs',
            type: 'array',
            minItems: 0,
            maxItems: MAX_NUMBER_OF_MENUS,
            items: {
                title: 'admin/editor.tabbed-shelf.tabs.items',
                type: 'object',
                properties: {
                    id: {
                        title: 'admin/editor.tabbed-shelf.tabs.items.id',
                        type: 'number',
                    },
                },
            },
        },
        shelf: {
          title: 'admin/editor.tabbed-shelf.shelf.title',
          type: 'object',
          properties: Shelf.getSchema(props).properties,
        },
    },
});

export default withRuntimeContext(injectIntl(withCssHandles(CSS_HANDLES)(TabbedShelf)))

```


[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/Shelf/components/TabbedShelf/tabbedShelf.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
.container {
  display:block;
}

.container .mh7-ns,
.container .pv4,
.container .pb7 {
  margin:0 0 0 25px;
  padding:0;
}

.blockContainer {
  font-size: .625em;
  width: 15%;
  position: absolute;
  margin-top: -320px;
  color: #45474a;
  line-height: 17px;
}

.blockContainer:after {
  clear:both;
}

.blockText {
  margin:0 0 19px;
}

.buttonContainer {
  margin: 0 !important;
  padding: 8px 20px !important;
  text-transform: inherit;
  font-size: 16px !important;
}

@media only screen and (max-width: 1047px) {
  .blockContainer {
      margin-top: -300px;
  }
}

@media only screen and (max-width: 854px) {
  .blockContainer {
      margin-top: -280px;
  }
}

@media only screen and (max-width: 821px) {
  .blockContainer {
      margin-top: -320px;
  }
}

@media only screen and (max-width: 639px) {
  .blockContainer {
      margin-top: -220px;
      width:25%;
  }
}

/* Switch to mobile tabs */
@media only screen and (max-width: 570px) {
  .blockContainer {
      margin-top: 45px;
      width: 100%;
      position: relative;
  }
}

```


[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/StoreComponents/typings/storefront.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { FunctionComponent, ReactElement } from 'react'

declare global {
  interface StorefrontFC<P = {}> extends FunctionComponent<P> {
    getSchema?(props: P): object
    schema?: object
  }

  interface StorefrontComponent<P = {}, S = {}> extends Component<P, S> {
    getSchema?(props: P): object
    schema: object
  }
}

```


[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/StoreComponents/typings/jsx.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
import 'react'

declare module 'react' {
  interface ImgHTMLAttributes<T> extends HTMLAttributes<T> {
    loading?: 'lazy' | 'eager' | 'auto';
  }
}
```


[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/StoreComponents/typings/vtex.css-handles.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.css-handles' {
  const useCssHandles: 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/StoreComponents/typings/vtex.responsive-values.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.responsive-values' {
  const useResponsiveValues: (input: any) => any
  const useResponsiveValue: <T>(input: MaybeResponsiveInput<T>) => T

  enum InputDevices {
    mobile = 'mobile',
    phone = 'phone',
    tablet = 'tablet',
    desktop = 'desktop',
  }

  enum OutputDevices {
    phone = 'phone',
    tablet = 'tablet',
    desktop = 'desktop',
  }

  type ResponsiveInput<T> = { [P in keyof typeof InputDevices]?: T }
  type MaybeResponsiveInput<T> = T | ResponsiveInput<T>

  type ResponsiveOutput<T> = { [P in keyof typeof OutputDevices]: T }
}

```


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

[User] Show me the implementation of store-on-localhost/src/monobloco/components/new/StoreComponents/typings/vtex.product-context.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface Seller {
  commertialOffer: {
    AvailableQuantity: number
  }
}

interface ProductItem {
  itemId: string
  name: string
  images: Image[]
  variations: {
    name: string
    values: string[]
  }[]
  sellers: {
    commertialOffer: {
      Price: number
      ListPrice: number
      AvailableQuantity: number
    }
  }[]
}

interface Product {
  itemMetadata: ItemMetadata
  items: ProductItem[]
  skuSpecifications: SkuSpecification[]
}

interface SkuSpecification {
  field: SkuSpecificationField
  values: SkuSpecificationValues[]
}

interface SkuSpecificationField {
  name: string
}

interface SkuSpecificationValues {
  name: string
}

declare module 'vtex.product-context/useProduct' {
  type GroupId = string

  interface AssemblyOptionItem {
    id: string
    quantity: number
    seller: string
    initialQuantity: number
    choiceType: string
    name: string
    price: number
    children: Record<string, AssemblyOptionItem[]> | null
  }

  type InputValues = Record<string, string>

  export interface ProductContext {
    product?: Product,
    selectedItem: ProductItem | null,
    selectedQuantity: number
    skuSelector: {
      isVisible: boolean
      areAllVariationsSelected: boolean
    }
    buyButton: {
      clicked: boolean
    }
    assemblyOptions: {
      items: Record<GroupId, AssemblyOptionItem[]>
      inputValues: Record<GroupId, InputValues>
      areGroupsValid: Record<GroupId, boolean>
    }
  }

  const useProduct: () => ProductContext
  export default useProduct
}

declare module 'vtex.product-context/ProductDispatchContext' {
  type DispatchFunction = (payload: { type: string; args?: any }) => void
  export const useProductDispatch: () => DispatchFunction
}

declare module 'vtex.product-context' {
  export const ProductContext
}
```


[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/StoreComponents/typings/vtex.render-runtime.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.render-runtime' {
  interface Runtime {
    setQuery: (vars: { skuId: string }, options?: { replace?: boolean } ) => void
    query?: { skuId?: string }
  }
  export const useRuntime: () => Runtime
}

```


[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/StoreComponents/typings/vtex.native-types.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
// import { ComponentType } from 'react'

declare module 'vtex.native-types' {
  import { ComponentType } from 'react'
  export const IOMessage: ComponentType<{
    id: string
    values: Record<string, any>
    ['data-testid']: string
  }>
}

```


[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/StoreComponents/typings/css.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  const css: any
  export default css
}

```


[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/StoreComponents/typings/product.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface Product {
  linkText: string
  productName: string
  brand: string
  categoryId: string
  categoryTree: Category[]
  productId: string
  titleTag: string
  metaTagDescription: string
  items: SKU[]
  skuSpecifications: SkuSpecification[]
}

export interface Category {
  id: string
  name: string
}

export interface SKU {
  itemId: string
  ean: string
  referenceId: [{ Value: string }]
  sellers: Seller[]
}

interface Seller {
  commertialOffer: CommertialOffer
  sellerId: string
}

interface CommertialOffer {
  ListPrice: number
  Price: number
}

interface Seller {
  commertialOffer: CommertialOffer
}

interface CommertialOffer {
  AvailableQuantity: number
}

interface SkuSpecification {
  field: SkuSpecificationField
  values: SkuSpecificationValues[]
}

interface SkuSpecificationField {
  name: string
}

interface SkuSpecificationValues {
  name: string
}
```


[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/StoreComponents/typings/graphql.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.gql' {
  const graphql: any
  export default graphql
}

```


[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/StoreComponents/typings/vtex.store-resources.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.store-resources/OrderFormContext' {
  export const orderFormConsumer
}
```


[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/StoreComponents/typings/vtex.styleguide.d.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.styleguide' {
  export const Checkbox: any
  export const Radio: any
  export const NumericStepper: any
  export const Button: any
  export const Modal: any
  export const withToast: any
  export const Tooltip: any
  export const EXPERIMENTAL_Select: any
  export const Dropdown: 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/StoreComponents/components/SearchBar/styles.css from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```css
.searchBarContainer :global(.vtex-input-prefix__group) {
  height: 38px;
}

.searchBarContainer input {
  color: #727273;
  border-radius: 1px;
}

.compactMode input {
  border-top-style: none !important;
  border-left-style: none !important;
  border-right-style: none !important;
  border-bottom: 2px solid !important;
}

.paddingInput input {
  -webkit-appearance: none;
  padding-left: 40px !important;
}

.searchMobile .vtex-button {
  padding: 0.9rem;
  font-weight: 400;
  font-size: 0.875rem;
}

.searchMobile input {
  border: none;
}

@media only screen and (max-width: 49rem) {
  .searchBarContainer input {
    box-shadow: 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/StoreComponents/components/SearchBar/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 { injectIntl } from 'react-intl'

import SearchBar from './components/SearchBar'

/** Canonical search bar that uses the autocomplete endpoint to search for a specific product*/
class SearchBarContainer extends Component {
  state = {
    inputValue: '',
  }

  handleClearInput = () => {
    this.setState({ inputValue: '' })
  }

  handleGoToSearchPage = () => {
    const search = this.state.inputValue
    const { customSearchPageUrl } = this.props

    if (this.props.attemptPageTypeSearch) {
      window.location.href = `/${search}`
      return
    }

    if (customSearchPageUrl) {
      this.context.navigate({
        to: customSearchPageUrl.replace(/\$\{term\}/g, search),
      })

      return
    }

    // This param is only useful to track terms searched
    // See: https://support.google.com/analytics/answer/1012264
    const paramForSearchTracking = '&_q=' + search

    this.setState({ inputValue: '' })
    this.context.navigate({
      page: 'store.search',
      params: { term: search },
      query: 'map=ft' + paramForSearchTracking,
      fallbackToWindowLocation: false,
    })
  }

  handleInputChange = event => {
    this.setState({ inputValue: event.target.value })
  }

  render() {
    const {
      intl,
      compactMode,
      hasIconLeft,
      iconClasses,
      autoFocus,
      maxWidth,
      attemptPageTypeSearch,
      customSearchPageUrl,
      placeholder = intl.formatMessage({
        id: 'store/search.placeholder',
      }),
      autocompleteAlignment = 'right',
      openAutocompleteOnFocus = false,
      blurOnSubmit = false,
      submitOnIconClick = false,
      minSearchTermLength,
      autocompleteFullWidth = false,
    } = this.props

    const { inputValue } = this.state

    return (
      <SearchBar
        // eslint-disable-next-line jsx-a11y/no-autofocus
        autoFocus={autoFocus}
        placeholder={placeholder}
        inputValue={inputValue}
        onClearInput={this.handleClearInput}
        onGoToSearchPage={this.handleGoToSearchPage}
        onInputChange={this.handleInputChange}
        compactMode={compactMode}
        hasIconLeft={hasIconLeft}
        iconClasses={iconClasses}
        maxWidth={maxWidth}
        attemptPageTypeSearch={attemptPageTypeSearch}
        customSearchPageUrl={customSearchPageUrl}
        autocompleteAlignment={autocompleteAlignment}
        openAutocompleteOnFocus={openAutocompleteOnFocus}
        blurOnSubmit={blurOnSubmit}
        submitOnIconClick={submitOnIconClick}
        minSearchTermLength={minSearchTermLength}
        autocompleteFullWidth={autocompleteFullWidth}
        intl={intl}
      />
    )
  }
}

SearchBarContainer.contextTypes = {
  navigate: PropTypes.func,
}

SearchBarContainer.schema = {
  title: 'admin/editor.search-bar.title',
}

SearchBarContainer.propTypes = {
  /** Indentify when use the compact version of the component */
  compactMode: PropTypes.bool,
  /** Identify if the search icon is on left or right position */
  hasIconLeft: PropTypes.bool,
  /** Custom classes for the search icon */
  iconClasses: PropTypes.string,
  /** Identify if the search input should autofocus or not */
  autoFocus: PropTypes.bool,
  /** Max width of the search bar */
  maxWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /** Uses the term the user has inputed to try to navigate to the proper
   * page type (e.g. a department, a brand, a category)
   */
  attemptPageTypeSearch: PropTypes.bool,
  /** A template for a custom url. It can have a substring ${term} used as placeholder to interpolate the searched term. (e.g. `/search?query=${term}`) */
  customSearchPageUrl: PropTypes.string,
  placeholder: PropTypes.string,
  /* Autocomplete Horizontal alignment */
  autocompleteAlignment: PropTypes.string,
  /** Identify if autocomplete should be open on input focus or not */
  openAutocompleteOnFocus: PropTypes.bool,
  /** Identify if input should blur on submit */
  blurOnSubmit: PropTypes.bool,
  /** Identify if icon should submit on click */
  submitOnIconClick: PropTypes.bool,
  /** Minimum search term length allowed */
  minSearchTermLength: PropTypes.number,
  /** If true, the autocomplete will fill the whole window horizontally */
  autocompleteFullWidth: PropTypes.bool,
}

export default injectIntl(SearchBarContainer)

```


[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/StoreComponents/components/SearchBar/setupTests.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'

configure({ adapter: new Adapter() })

```


[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/StoreComponents/components/BuyButton/assemblyUtils.test.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  transformAssemblyOptions,
  ItemOption,
  InputValuesOption,
} from './assemblyUtils'
import { customBell, comboPizza, starColor } from './__mocks__/assemblyOptions'

test('should transform assemblyOptions', () => {
  const parentPrice = 450
  const parentQuantity = 1

  const resultBell = transformAssemblyOptions(
    customBell.items,
    {},
    parentPrice,
    parentQuantity
  )

  expect(resultBell.options).toHaveLength(5)
  const addonOption = resultBell.options[0] as ItemOption
  expect(addonOption.assemblyId).toBe('add-on_Add-on')
  expect(addonOption.id).toBe('2000588')
  expect(addonOption.quantity).toBe(1)
  expect(addonOption.seller).toBe('1')

  const resultPizza = transformAssemblyOptions(
    comboPizza.items,
    {},
    parentPrice,
    parentQuantity
  )

  expect(resultPizza.options).toHaveLength(2)
  const pizzaOption = resultPizza.options[0] as ItemOption
  expect(pizzaOption.assemblyId).toBe('pizza_composition_Pizza flavor')
  expect(pizzaOption.id).toBe('5101')
  expect(pizzaOption.quantity).toBe(1)
  expect(pizzaOption.seller).toBe('1')
  expect(pizzaOption.options).toHaveLength(3)

  const drinksOptions = resultPizza.options[1] as ItemOption
  expect(drinksOptions.options).toBeUndefined()
})

test('input values', () => {
  const parentPrice = 450
  const parentQuantity = 1

  const resultStar = transformAssemblyOptions(
    starColor.items,
    starColor.inputValues,
    parentPrice,
    parentQuantity
  )

  expect(resultStar.options).toHaveLength(1)
  const customization = resultStar.options[0] as InputValuesOption
  expect(customization.assemblyId).toBe('Customization')
  expect(customization.inputValues).toMatchObject({
    Font: 'Sans serif',
    'Front text': 'Frente',
    'Back text': 'Verso',
    'Glossy print': true,
  })
})

test('empty input values should result in empty options', () => {
  const parentPrice = 450
  const parentQuantity = 1

  const resultStar = transformAssemblyOptions(
    starColor.items,
    {
      Customization: {},
    },
    parentPrice,
    parentQuantity
  )
  expect(resultStar.options.length).toBe(0)
})

```


[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/StoreComponents/components/BuyButton/assemblyUtils.ts from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```typescript
type GroupId = string
type GroupTypes = 'SINGLE' | 'TOGGLE' | 'MULTIPLE'

export const sumAssembliesPrice = (
  assemblyOptions: Record<GroupId, AssemblyOptionItem[]>
) => {
  const cleanAssemblies = assemblyOptions || {}
  const assembliesGroupItems = Object.values(cleanAssemblies)
  return assembliesGroupItems.reduce<number>(
    (sum: number, groupItems: AssemblyOptionItem[]) => {
      const groupPrice = groupItems.reduce<number>((groupSum, item) => {
        const childrenPrice: number = item.children
          ? sumAssembliesPrice(item.children)
          : 0
        const itemCost = item.price * item.quantity
        return groupSum + itemCost + childrenPrice * item.quantity
      }, 0)
      return groupPrice + sum
    },
    0
  )
}

interface AssemblyOptionItem {
  price: number
  choiceType: GroupTypes
  children: Record<GroupId, AssemblyOptionItem[]> | null
  name: string
  id: string
  initialQuantity: number
  quantity: number
  seller: string
}

type InputValue = Record<string, string | boolean>

export interface AssemblyOptions {
  items: Record<GroupId, AssemblyOptionItem[]>
  inputValues: Record<GroupId, InputValue>
  areGroupsValid: Record<string, boolean>
}

type Option = ItemOption | InputValuesOption

export interface ItemOption {
  assemblyId: string
  id: string
  quantity: number
  seller: string
  options?: Option[]
}

export interface InputValuesOption {
  assemblyId: string
  inputValues: InputValue
}

interface AddedItem {
  id: string
  name: string
  quantity: number
  sellingPrice: number
  sellingPriceWithAssemblies: number
  assemblyOptions?: ParsedAssemblyOptionsMeta
}

interface CartAddedOption {
  normalizedQuantity: number
  extraQuantity: number
  choiceType: GroupTypes
  item: AddedItem
}

interface CartRemovedOption {
  name: string
  initialQuantity: number
  removedQuantity: number
}

interface ParsedAssemblyOptionsMeta {
  added: CartAddedOption[]
  removed: CartRemovedOption[]
  parentPrice: number
}

interface ParsedAssemblyOptions {
  options: Option[]
  assemblyOptions: ParsedAssemblyOptionsMeta
}

export const transformAssemblyOptions = (
  assemblyOptionsItems: Record<GroupId, AssemblyOptionItem[]> = {},
  inputValues: Record<GroupId, InputValue> = {},
  parentPrice: number,
  parentQuantity: number
): ParsedAssemblyOptions => {
  // contains options sent as arguments to graphql mutation
  const options: Option[] = []

  // array with added assemblies data to show in minicart optimistic preview
  const added: CartAddedOption[] = []

  // array with removed assemblies data to show in minicart optimistic preview
  const removed: CartRemovedOption[] = []

  const assemblyItemsKeys: GroupId[] = Object.keys(assemblyOptionsItems)

  for (const groupId of assemblyItemsKeys) {
    const items = assemblyOptionsItems[groupId]
    for (const item of items) {
      const childrenAddedData = item.children
        ? transformAssemblyOptions(
            item.children,
            {},
            item.price,
            item.quantity * parentQuantity
          )
        : null

      const {
        options: childrenOptions,
        assemblyOptions: childrenAssemblyOptions,
      } = childrenAddedData || {
        options: undefined,
        assemblyOptions: undefined,
      }

      const { quantity, initialQuantity } = item

      if (quantity >= initialQuantity && quantity > 0) {
        added.push({
          normalizedQuantity: quantity,
          extraQuantity: quantity - initialQuantity,
          choiceType: item.choiceType,
          item: {
            name: item.name,
            sellingPrice: item.price,
            quantity,
            sellingPriceWithAssemblies:
              item.price + sumAssembliesPrice(item.children || {}),
            id: item.id,
            ...(childrenAssemblyOptions
              ? { assemblyOptions: childrenAssemblyOptions }
              : {}),
          },
        })
      }

      if (quantity < initialQuantity && item.choiceType === 'TOGGLE') {
        removed.push({
          name: item.name,
          initialQuantity,
          removedQuantity: initialQuantity - quantity,
        })
      }

      const addedChildrenCount = childrenAddedData
        ? childrenAddedData.options.length
        : 0

      if (quantity !== initialQuantity || addedChildrenCount > 0) {
        options.push({
          assemblyId: groupId,
          id: item.id,
          quantity: quantity * parentQuantity,
          seller: item.seller,
          ...(childrenOptions && childrenOptions.length > 0
            ? { options: childrenOptions }
            : {}),
        })
      }
    }
  }

  const assemblyInputValuesKeys: GroupId[] = Object.keys(inputValues)

  for (const groupId of assemblyInputValuesKeys) {
    const inputValuesObject = inputValues[groupId] || {}
    if (Object.keys(inputValuesObject).length > 0) {
      options.push({
        assemblyId: groupId,
        inputValues: inputValues[groupId],
      })
    }
  }

  return {
    options,
    assemblyOptions: {
      added,
      removed,
      parentPrice,
    },
  }
}

```


[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/StoreComponents/components/BuyButton/Wrapper.js from the store-on-localhost VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'

import useProduct from '../../../ProductContext/useProduct'
import { path, isEmpty, compose } from 'ramda'
import { FormattedMessage, injectIntl } from 'react-intl'
import withToast from '../../../Styleguide/withToast'
import OrderFormContext from '../../../StoreResources/OrderFormContext'
import ProductPrice from '../ProductPrice'
import { graphql } from 'react-apollo'

import useCssHandles from '../../../CssHandles/useCssHandles'

import { BuyButton } from './index'
import { transformAssemblyOptions, sumAssembliesPrice } from './assemblyUtils'
import { loader } from 'graphql.macro'

const addToCartMutation = loader('./mutations/addToCart.gql')
const setOpenMinicartMutation = loader('./mutations/setOpenMinicart.gql')
const installedApp = loader('./queries/installedApp.gql')
const { useOrderForm } = OrderFormContext

const MESSAGE_CSS_HANDLES = [
  'buyButtonText',
  'buttonDataContainer',
  'buttonItemsPrice',
]

const CHECKOUT_URL = {
  V0: '/checkout/#/cart',
  V1: '/cart',
}

const BuyButtonMessage = ({ showItemsPrice, skuItems }) => {
  const handles = useCssHandles(MESSAGE_CSS_HANDLES)
  if (!showItemsPrice) {
    return (
      <FormattedMessage id="store/buy-button.add-to-cart">
        {message => <span className={handles.buyButtonText}>{message}</span>}
      </FormattedMessage>
    )
  }

  const totalPrice = skuItems.reduce((acc, item) => {
    const itemPrice =
      item.sellingPriceWithAssemblies != null
        ? item.sellingPriceWithAssemblies
        : item.price
    const itemCost = itemPrice * item.quantity
    return acc + itemCost
  }, 0)

  return (
    <div
      className={`${handles.buttonDataContainer} flex w-100 justify-between items-center`}
    >
      <FormattedMessage id="store/buy-button.add-to-cart">
        {message => <span className={handles.buyButtonText}>{message}</span>}
      </FormattedMessage>
      <ProductPrice
        showLabels={false}
        showListPrice={false}
        sellingPrice={totalPrice}
        className={handles.buttonItemsPrice}
      />
    </div>
  )
}
const BuyButtonWrapper = ({
  intl,
  addToCart,
  showToast,
  onAddStart,
  onAddFinish,
  children,
  isOneClickBuy,
  shouldOpenMinicart,
  setMinicartOpen,
  showItemsPrice,
  available: propAvailable,
  skuItems: propSkuItems,
  large: propLarge,
  disabled: propDisabled,
  shouldAddToCart,
  customToastURL,
  showTooltipOnSkuNotSelected,
  checkoutVersion,
}) => {
  const orderFormContext = useOrderForm()
  const valuesFromContext = useProduct()

  const isEmptyContext = !valuesFromContext || isEmpty(valuesFromContext)

  const product = valuesFromContext && valuesFromContext.product
  const selectedItem = valuesFromContext && valuesFromContext.selectedItem
  const assemblyOptions = valuesFromContext && valuesFromContext.assemblyOptions
  const selectedSeller = path(['selectedItem', 'sellers', 0], valuesFromContext)
  const selectedQuantity =
    valuesFromContext && valuesFromContext.selectedQuantity != null
      ? valuesFromContext.selectedQuantity
      : 1

  const skuItems =
    isEmptyContext || propSkuItems != null
      ? propSkuItems
      : EnhancedBuyButton.mapCatalogItemToCart({
          product,
          selectedItem,
          selectedQuantity,
          selectedSeller,
          assemblyOptions,
        })

  const large = isEmptyContext || propLarge != null ? propLarge : true

  const available =
    isEmptyContext || propAvailable != null
      ? propAvailable
      : selectedSeller &&
        selectedSeller.commertialOffer &&
        selectedSeller.commertialOffer.AvailableQuantity > 0

  const groupsValidArray =
    (assemblyOptions &&
      assemblyOptions.areGroupsValid &&
      Object.values(assemblyOptions.areGroupsValid)) ||
    []

  const areAssemblyGroupsValid = groupsValidArray.every(Boolean)
  const disabled =
    isEmptyContext || propDisabled != null
      ? propDisabled
      : !areAssemblyGroupsValid

  const version =
    checkoutVersion &&
    checkoutVersion.installedAppPublic &&
    checkoutVersion.installedAppPublic.version

  const checkoutUrl =
    version && parseInt(version.split('.')[0]) > 0
      ? CHECKOUT_URL.V1
      : CHECKOUT_URL.V0

  return (
    <BuyButton
      intl={intl}
      addToCart={addToCart}
      onAddStart={onAddStart}
      onAddFinish={onAddFinish}
      showToast={showToast}
      orderFormContext={orderFormContext}
      skuItems={skuItems}
      large={large}
      available={available}
      isOneClickBuy={isOneClickBuy}
      shouldOpenMinicart={shouldOpenMinicart}
      setMinicartOpen={setMinicartOpen}
      disabled={disabled}
      customToastURL={customToastURL}
      shouldAddToCart={shouldAddToCart}
      showTooltipOnSkuNotSelected={showTooltipOnSkuNotSelected}
      checkoutUrl={checkoutUrl}
    >
      {children || (
        <BuyButtonMessage showItemsPrice={showItemsPrice} skuItems={skuItems} />
      )}
    </BuyButton>
  )
}

const withAddToCart = graphql(addToCartMutation, {
  name: 'addToCart',
  props: ({ addToCart }) => ({
    addToCart: items => addToCart({ variables: { items } }),
  }),
})

const withOpenMinicart = graphql(setOpenMinicartMutation, {
  name: 'setMinicartOpen',
  props: ({ setMinicartOpen }) => ({
    setMinicartOpen: isOpen => setMinicartOpen({ variables: { isOpen } }),
  }),
})

const withCheckoutVersion = graphql(installedApp, {
  name: 'checkoutVersion',
  options: {
    ssr: false,
    variables: {
      slug: 'vtex.checkout',
    },
  },
})

const EnhancedBuyButton = compose(
  withAddToCart,
  withOpenMinicart,
  withCheckoutVersion,
  withToast,
  injectIntl
)(BuyButtonWrapper)

// This function is public available to be used only by vtex.product-summary.
// We do not garantee this API will not change and might happen breaking change anytime.
EnhancedBuyButton.mapCatalogItemToCart = function mapCatalogItemToCart({
  product,
  selectedItem,
  selectedQuantity,
  selectedSeller,
  assemblyOptions,
}) {
  return (
    product &&
    selectedItem &&
    selectedSeller &&
    selectedSeller.commertialOffer && [
      {
        index: 0,
        quantity: selectedQuantity,
        detailUrl: `/${product.linkText}/p`,
        name: product.productName,
        brand: product.brand,
        category:
          product.categories && product.categories.length > 0
            ? product.categories[0]
            : '',
        productRefId: product.productReference,
        seller: selectedSeller.sellerId,
        price: selectedSeller.commertialOffer.Price,
        listPrice: selectedSeller.commertialOffer.ListPrice,
        variant: selectedItem.name,
        skuId: selectedItem.itemId,
        imageUrl: path(['images', '0', 'imageUrl'], selectedItem),
        ...transformAssemblyOptions(
          path(['items'], assemblyOptions),
          path(['inputValues'], assemblyOptions),
          selectedSeller.commertialOffer.Price,
          selectedQuantity
        ),
        sellingPriceWithAssemblies:
          selectedSeller.commertialOffer.Price +
          sumAssembliesPrice(path(['items'], assemblyOptions) || {}),
      },
    ]
  )
}

export default EnhancedBuyButton

```