[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 search-result/react/components/FilterNavigator/legacy/SearchFilter.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import React from 'react'
import { useIntl } from 'react-intl'

import FilterOptionTemplate from './FilterOptionTemplate'
import FacetItem from './FacetItem'
import { facetOptionShape } from '../../../constants/propTypes'
import { getFilterTitle } from '../../../constants/SearchHelpers'
import useSelectedFilters from './hooks/useSelectedFilters'

/**
 * Search Filter Component.
 */
const SearchFilter = ({
  title = 'Default Title',
  facets = [],
  preventRouteChange = false,
}) => {
  const intl = useIntl()
  const filtersWithSelected = useSelectedFilters(facets)

  return (
    <FilterOptionTemplate
      title={getFilterTitle(title, intl)}
      filters={filtersWithSelected}
    >
      {facet => (
        <FacetItem
          key={facet.name}
          facet={facet}
          preventRouteChange={preventRouteChange}
        />
      )}
    </FilterOptionTemplate>
  )
}

SearchFilter.propTypes = {
  /** SearchFilter's title. */
  title: PropTypes.string.isRequired,
  /** SearchFilter's options. */
  facets: PropTypes.arrayOf(facetOptionShape),
  /** Prevents changing route when setting filters (uses URL search params instead) */
  preventRouteChange: PropTypes.bool,
}

export default SearchFilter

```


[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 search-result/react/components/FilterNavigator/legacy/utils/getFilters.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import { flatten, path, contains, isEmpty } from 'ramda'

const getCategories = (tree = []) => {
  return [
    ...tree,
    ...flatten(tree.map(node => node.children && getCategories(node.children))),
  ].filter(Boolean)
}

const CATEGORIES_TITLE = 'store/search.filter.title.categories'
const BRANDS_TITLE = 'store/search.filter.title.brands'
const PRICE_RANGES_TITLE = 'store/search.filter.title.price-ranges'

const CATEGORIES_TYPE = 'Categories'
const BRANDS_TYPE = 'Brands'
const PRICE_RANGES_TYPE = 'PriceRanges'
const SPECIFICATION_FILTERS_TYPE = 'SpecificationFilters'

const getFilters = ({
  tree = [],
  specificationFilters = [],
  priceRanges = [],
  brands = [],
  hiddenFacets = {},
}) => {
  const categories = getCategories(tree)

  const hiddenFacetsNames = (
    path(['specificationFilters', 'hiddenFilters'], hiddenFacets) || []
  ).map(filter => filter.name)

  const mappedSpecificationFilters = !path(
    ['specificationFilters', 'hideAll'],
    hiddenFacets
  )
    ? specificationFilters
        .filter(spec => !contains(spec.name, hiddenFacetsNames))
        .map(spec => ({
          type: SPECIFICATION_FILTERS_TYPE,
          title: spec.name,
          facets: spec.facets,
        }))
    : []

  return [
    !hiddenFacets.categories &&
      !isEmpty(categories) && {
        type: CATEGORIES_TYPE,
        title: CATEGORIES_TITLE,
        facets: categories,
      },
    ...mappedSpecificationFilters,
    !hiddenFacets.brands &&
      !isEmpty(brands) && {
        type: BRANDS_TYPE,
        title: BRANDS_TITLE,
        facets: brands,
      },
    !hiddenFacets.priceRange &&
      !isEmpty(priceRanges) && {
        type: PRICE_RANGES_TYPE,
        title: PRICE_RANGES_TITLE,
        facets: priceRanges,
      },
  ].filter(Boolean)
}

export default getFilters

```


[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 search-result/react/components/FilterNavigator/legacy/hooks/useSelectedFilters.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import { zip } from 'ramda'
import { useContext } from 'react'
import slugify from 'slugify'

import QueryContext from '../../../QueryContext'

export function generateSlug(str) {
  return slugify(str, { lower: true, remove: /[*+~.()'"!:@]/g })
}

/**
 * This hook is required because we make the facets query
 * with only the categories and fulltext parameters, so we
 * need to calculate manually if the other filters are selected
 */
const useSelectedFilters = facets => {
  const { query, map } = useContext(QueryContext)

  const queryAndMap = zip(
    query
      .toLowerCase()
      .split('/')
      .map(str => generateSlug(decodeURIComponent(str))),
    map.split(',')
  )

  return facets.map(facet => {
    const currentFacetSlug = decodeURIComponent(facet.value).toLowerCase()

    const isSelected =
      queryAndMap.find(
        ([slug, slugMap]) => slug === currentFacetSlug && slugMap === facet.map
      ) !== undefined

    return {
      ...facet,
      selected: isSelected,
    }
  })
}

export default useSelectedFilters

```


[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 search-result/react/components/FilterNavigator/legacy/hooks/useOutsideClick.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import { useLayoutEffect, useCallback } from 'react'

const useOutsideClick = (ref, handler, activeWhen) => {
  const handle = useCallback(
    e => ref && ref.current && !ref.current.contains(e.target) && handler(e),
    [handler, ref]
  )

  useLayoutEffect(() => {
    activeWhen && document.addEventListener('mousedown', handle)

    return () => document.removeEventListener('mousedown', handle)
  }, [activeWhen, handle])
}

export default useOutsideClick

```


[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 search-result/react/__tests__/components/FilterOptionTemplate.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable jest/no-mocks-import */
import React from 'react'
import { fireEvent, render } from '@vtex/test-tools/react'
import { useRuntime } from 'vtex.render-runtime'
import { mockAllIsIntersecting } from 'react-intersection-observer/test-utils'

import FilterOptionTemplate from '../../components/FilterOptionTemplate'
import FacetItem from '../../components/FacetItem'
import SettingsContext from '../../components/SettingsContext'
import '../../__mocks__/intersectionObserverMock'
import specifications from '../../__mocks__/specifications'

beforeEach(() => {
  jest.clearAllMocks()

  useRuntime.mockImplementation(() => ({
    getSettings: () => ({}),
  }))
})

const mockNavigate = jest.fn()
const mockSetTruncatedFacetsFetched = jest.fn()

const mockProps = {
  id: 'color',
  title: 'Color',
  quantity: 13,
  filters: [
    {
      id: '',
      quantity: 2,
      name: 'White',
      key: 'color',
      selected: false,
      value: 'white',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/white?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 1,
      name: 'Grey',
      key: 'color',
      selected: true,
      value: 'grey',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/grey?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 2,
      name: 'Pink',
      key: 'color',
      selected: true,
      value: 'pink',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/pink?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 6,
      name: 'Green',
      key: 'color',
      selected: false,
      value: 'green',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/green?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 1,
      name: 'Light green',
      key: 'color',
      selected: false,
      value: 'light-green',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/light-green?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 2,
      name: 'Blue',
      key: 'color',
      selected: false,
      value: 'blue',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/blue?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 1,
      name: 'Light blue',
      key: 'color',
      selected: false,
      value: 'light-blue',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/light-blue?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 2,
      name: 'Brown',
      key: 'color',
      selected: false,
      value: 'brown',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/brown?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 1,
      name: 'Fuchsia',
      key: 'color',
      selected: false,
      value: 'fuchsia',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/fuchsia?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 1,
      name: 'Yellow',
      key: 'color',
      selected: false,
      value: 'yellow',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/yellow?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 3,
      name: 'Red',
      key: 'color',
      selected: false,
      value: 'red',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/red?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 2,
      name: 'Black',
      key: 'color',
      selected: false,
      value: 'black',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/black?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
    {
      id: '',
      quantity: 1,
      name: 'Rose',
      key: 'color',
      selected: false,
      value: 'rose',
      link: null,
      linkEncoded: null,
      href: 'apparel---accessories/rose?map=department,color',
      range: null,
      children: null,
      __typename: 'FacetValue',
      map: 'color',
    },
  ],
  lastOpenFilter: undefined,
  setLastOpenFilter: () => {},
  openFiltersMode: 'many',
  truncatedFacetsFetched: false,
  setTruncatedFacetsFetched: mockSetTruncatedFacetsFetched,
  appliedFiltersOverview: 'hide',
  navigateToFacet: mockNavigate,
  showClearByFilter: false,
  preventRouteChange: true,
  lazyRender: false,
}

describe('<FilterOptionTemplate />', () => {
  const renderComponent = customProps => {
    const props = { ...mockProps, ...customProps }

    return render(
      <SettingsContext.Provider value={{ thresholdForFacetSearch: 10 }}>
        <FilterOptionTemplate {...props}>
          {facet => (
            <FacetItem
              key={facet.name}
              facetTitle={facet.title}
              facet={facet}
              preventRouteChange={mockProps.preventRouteChange}
              navigateToFacet={mockNavigate}
            />
          )}
        </FilterOptionTemplate>
      </SettingsContext.Provider>
    )
  }

  const radioFiltersMock = [
    {
      key: 'shipping',
      name: 'Delivery',
      value: 'shipping',
      selected: false,
      quantity: 1,
      map: 'shipping',
    },
    {
      key: 'shipping',
      name: 'Pickup',
      value: 'pickup',
      selected: false,
      quantity: 1,
      map: 'shipping',
    },
  ]

  const deliveryOptionsFiltersMock = [
    {
      key: 'delivery-options',
      name: 'Express',
      value: 'express',
      selected: false,
      quantity: 1,
      map: 'delivery-options',
    },
    {
      key: 'delivery-options',
      name: 'Standard',
      value: 'standard',
      selected: false,
      quantity: 1,
      map: 'delivery-options',
    },
  ]

  const toggleFiltersMock = [
    {
      key: 'dynamic-estimate',
      name: 'Same Day',
      value: 'same-day',
      selected: false,
      quantity: 1,
      map: 'dynamic-estimate',
    },
    {
      key: 'dynamic-estimate',
      name: 'Next Day',
      value: 'next-day',
      selected: false,
      quantity: 1,
      map: 'dynamic-estimate',
    },
  ]

  it('should render RadioFilters and handle radio selection', () => {
    const mockNavigateRadio = jest.fn()
    const radioProps = {
      ...mockProps,
      filters: radioFiltersMock,
      title: 'Shipping',
      navigateToFacet: mockNavigateRadio,
      isSelectedFiltersSection: false,
    }

    const { getByLabelText, getByTestId } = render(
      <SettingsContext.Provider value={{ thresholdForFacetSearch: 10 }}>
        <FilterOptionTemplate {...radioProps}>
          {/* children required */}
          {() => null}
        </FilterOptionTemplate>
      </SettingsContext.Provider>
    )

    expect(getByTestId('radio-filters')).toBeInTheDocument()

    const deliveryRadio = getByLabelText('Delivery')
    const pickupRadio = getByLabelText('Pickup')

    fireEvent.click(deliveryRadio)
    expect(mockNavigateRadio).toHaveBeenCalled()

    fireEvent.click(pickupRadio)
    expect(mockNavigateRadio).toHaveBeenCalled()
  })

  it('should render RadioButton for shipping filter', () => {
    const shippingProps = {
      ...mockProps,
      filters: radioFiltersMock,
      title: 'Shipping',
    }

    const { getByText, getByTestId } = renderComponent(shippingProps)

    expect(getByTestId('radio-filters')).toBeInTheDocument()

    expect(getByText('Delivery')).toBeInTheDocument()
    expect(getByText('Pickup')).toBeInTheDocument()
  })

  it('should render RadioButton for delivery-options filter', () => {
    const deliveryOptionsProps = {
      ...mockProps,
      filters: deliveryOptionsFiltersMock,
      title: 'Delivery Options',
    }

    const { getByText, getByTestId } = renderComponent(deliveryOptionsProps)

    expect(getByTestId('radio-filters')).toBeInTheDocument()
    expect(getByText('Express')).toBeInTheDocument()
    expect(getByText('Standard')).toBeInTheDocument()
  })

  it('should render ToggleFilters for dynamic-estimate filter', () => {
    const toggleProps = {
      ...mockProps,
      filters: toggleFiltersMock,
      title: 'Delivery Estimate',
    }

    const { getByText, getByTestId } = renderComponent(toggleProps)

    expect(getByTestId('toggle-filters')).toBeInTheDocument()
    expect(getByText('Same Day')).toBeInTheDocument()
    expect(getByText('Next Day')).toBeInTheDocument()
  })

  it('should handle toggle interactions', () => {
    const mockNavigateToggle = jest.fn()
    const toggleProps = {
      ...mockProps,
      filters: toggleFiltersMock,
      title: 'Delivery Estimate',
      navigateToFacet: mockNavigateToggle,
      isSelectedFiltersSection: false,
    }

    const { getByLabelText, getByTestId } = render(
      <SettingsContext.Provider value={{ thresholdForFacetSearch: 10 }}>
        <FilterOptionTemplate {...toggleProps}>
          {() => null}
        </FilterOptionTemplate>
      </SettingsContext.Provider>
    )

    expect(getByTestId('toggle-filters')).toBeInTheDocument()

    const sameDayToggle = getByLabelText('Same Day')
    const nextDayToggle = getByLabelText('Next Day')

    fireEvent.click(sameDayToggle)
    expect(mockNavigateToggle).toHaveBeenCalled()

    fireEvent.click(nextDayToggle)
    expect(mockNavigateToggle).toHaveBeenCalled()
  })

  it('should lazy render items', () => {
    useRuntime.mockImplementation(() => ({
      getSettings: () => {
        return { enableSearchRenderingOptimization: true }
      },
    }))

    const { queryByTestId } = renderComponent({ lazyRender: true })

    expect(queryByTestId('check-specification-color-white')).toBeNull()

    mockAllIsIntersecting(true)

    expect(queryByTestId('check-specification-color-white')).toBeInTheDocument()
  })

  it('should truncate items', () => {
    const { queryByTestId, getByText } = renderComponent({
      truncateFilters: true,
    })

    expect(
      queryByTestId('check-specification-color-yellow')
    ).toBeInTheDocument()

    expect(queryByTestId('check-specification-color-red')).toBeNull()

    fireEvent.click(getByText(/See 3 more/))

    expect(queryByTestId('check-specification-color-red')).toBeInTheDocument()

    fireEvent.click(getByText(/See less/))

    expect(queryByTestId('check-specification-color-red')).toBeNull()
  })

  it('should be able to collapse', () => {
    const { getByText, queryByTestId } = renderComponent()

    expect(queryByTestId('check-specification-color-white')).toBeInTheDocument()

    fireEvent.click(getByText('Color'))

    expect(queryByTestId('check-specification-color-white')).toBeNull()
  })

  it('should search for options', () => {
    const { getByTestId, queryByTestId } = renderComponent()

    expect(getByTestId('check-specification-color-white')).toBeInTheDocument()

    fireEvent.change(getByTestId('filter-search-bar'), {
      target: { value: 'yellow' },
    })

    expect(queryByTestId('check-specification-color-white')).toBeNull()
    expect(getByTestId('check-specification-color-yellow')).toBeInTheDocument()
  })

  it('should show filter overview', () => {
    const { getByText } = renderComponent({ appliedFiltersOverview: 'show' })

    expect(getByText('Grey, Pink')).toBeInTheDocument()
  })

  it('should throw an error if the openFiltersMode does not exist', () => {
    jest.spyOn(console, 'error').mockImplementation()

    const { getByText } = renderComponent({
      openFiltersMode: 'this-option-does-not-exist',
      fiters: specifications,
      specificationFilters: specifications,
    })

    fireEvent.click(getByText('Color'))

    expect(console.error).toHaveBeenLastCalledWith(
      'Invalid openFiltersMode value: this-option-does-not-exist\nCheck the documentation for the values available'
    )
  })

  it('should collapse on space keyDown', () => {
    const { getByText, queryByTestId } = renderComponent()

    expect(queryByTestId('check-specification-color-white')).toBeInTheDocument()

    fireEvent.keyDown(getByText('Color'), { key: 'a', code: 'KeyA' })

    expect(queryByTestId('check-specification-color-white')).toBeInTheDocument()

    fireEvent.keyDown(getByText('Color'), { key: ' ', code: 'Space' })

    expect(queryByTestId('check-specification-color-white')).toBeNull()
  })

  it('should render children even if it is not a function', () => {
    const { getByText } = render(
      <FilterOptionTemplate {...mockProps}>
        <span>Child component</span>
      </FilterOptionTemplate>
    )

    expect(getByText('Child component')).toBeInTheDocument()
  })

  it('should call setTruncatedFacetsFetched', () => {
    useRuntime.mockImplementation(() => ({
      getSettings: () => {
        return { enableFiltersFetchOptimization: true }
      },
    }))

    const { getByText } = renderComponent({
      truncatedFacetsFetched: false,
    })

    fireEvent.click(getByText(/See 3 more/))

    expect(mockSetTruncatedFacetsFetched).toHaveBeenCalledTimes(1)
  })

  it('should render options on scroll', () => {
    useRuntime.mockImplementation(() => ({
      getSettings: () => {
        return { enableSearchRenderingOptimization: true }
      },
    }))

    const { queryByTestId } = renderComponent({ lazyRender: true })

    mockAllIsIntersecting(true)

    expect(queryByTestId('check-specification-color-white')).toBeInTheDocument()
    expect(queryByTestId('check-specification-color-rose')).toBeNull()

    fireEvent.scroll(queryByTestId('scrollable-element'), {
      target: { scrollY: 100 },
    })

    expect(queryByTestId('check-specification-color-rose')).toBeInTheDocument()
  })
})

```


[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 search-result/react/__tests__/components/FacetItem.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { render, fireEvent } from '@vtex/test-tools/react'

import FacetItem from '../../components/FacetItem'
import SettingsContext from '../../components/SettingsContext'

beforeEach(() => {
  jest.clearAllMocks()
})

const mockNavigate = jest.fn()

const mockProps = {
  navigateToFacet: mockNavigate,
  facetTitle: 'Color',
  facet: {
    id: '',
    quantity: 2,
    name: 'White',
    key: 'color',
    selected: false,
    value: 'white',
    link: null,
    linkEncoded: null,
    href: 'apparel---accessories/white?map=department,color',
    range: null,
    children: null,
    __typename: 'FacetValue',
    map: 'color',
  },
  className: 'color-classname',
  preventRouteChange: true,
}

describe('<FacetItem />', () => {
  const renderComponent = customProps => {
    const props = { ...mockProps, ...customProps }

    return render(<FacetItem {...props} />)
  }

  it('should call navigation callback on select', () => {
    const { getByTestId } = renderComponent()

    fireEvent.click(getByTestId('check-specification-color-white'))

    expect(mockNavigate).toHaveBeenCalledTimes(1)
    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        ...mockProps.facet,
        title: mockProps.facetTitle,
      }),
      mockProps.preventRouteChange
    )
  })

  it('should call navigation callback on unselect', () => {
    const clonedFacet = { ...mockProps.facet }

    clonedFacet.selected = true

    const { getByTestId } = renderComponent({ facet: clonedFacet })

    fireEvent.click(getByTestId('check-specification-color-white'))

    expect(mockNavigate).toHaveBeenCalledTimes(1)
    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        ...clonedFacet,
        title: mockProps.facetTitle,
      }),
      mockProps.preventRouteChange
    )
  })

  it('should give a feedback on select/unselect', () => {
    const { rerender, getByTestId } = renderComponent()

    expect(getByTestId('check-specification-color-white').checked).toEqual(
      false
    )

    const clonedFacet = { ...mockProps.facet }

    clonedFacet.selected = true

    rerender(<FacetItem {...mockProps} facet={clonedFacet} />)

    expect(getByTestId('check-specification-color-white').checked).toEqual(true)
  })

  it('should render the quantity when the showFacetQuantity is true', () => {
    const { getByTestId } = render(
      <SettingsContext.Provider value={{ showFacetQuantity: true }}>
        <FacetItem {...mockProps} />
      </SettingsContext.Provider>
    )

    expect(getByTestId('facet-quantity-white-2')).toBeInTheDocument()
  })

  it('should add a suffix when the id is a reserved variable', () => {
    const clonedFacet = { ...mockProps.facet }

    clonedFacet.value = 'global'

    const { getByTestId } = renderComponent({ facet: clonedFacet })

    expect(
      getByTestId('check-specification-filterItem--color-global')
    ).toBeInTheDocument()
  })
})

```


[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 search-result/react/__tests__/components/FacetCheckBoxList.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { fireEvent, render } from '@vtex/test-tools/react'
import specifications from 'specifications'
import { useRuntime } from 'vtex.render-runtime'

import FacetCheckboxList from '../../components/FacetCheckboxList'
import SettingsContext from '../../components/SettingsContext'

const mockProps = {
  facetTitle: 'Color',
  facets: specifications[0].facets,
  navigationType: 'page',
  quantity: 13,
  setTruncatedFacetsFetched: () => {},
  truncateFilters: true,
  tuncatedFacetsFetched: false,
}

beforeEach(() => {
  jest.clearAllMocks()

  useRuntime.mockImplementation(() => ({
    getSettings: () => ({ thresholdForFacetSearch: 10 }),
  }))
})

describe('<FacetCheckBoxList />', () => {
  const renderComponent = customProps => {
    const props = { ...mockProps, ...customProps }

    return render(
      <SettingsContext.Provider value={{ thresholdForFacetSearch: 10 }}>
        <FacetCheckboxList {...props} />
      </SettingsContext.Provider>
    )
  }

  it('should filter items by the search bar', () => {
    const { getByTestId, queryByText } = renderComponent()

    expect(queryByText('White')).toBeInTheDocument()

    fireEvent.change(getByTestId('filter-search-bar'), {
      target: { value: 'yellow' },
    })

    expect(queryByText('White')).toBeNull()
    expect(queryByText('Yellow')).toBeInTheDocument()
  })

  it('should truncate filters', () => {
    const { queryByText, getByText } = renderComponent({
      navigationType: 'collapsible',
      truncateFilters: true,
    })

    expect(queryByText('Rose')).toBeNull()

    fireEvent.click(getByText('See 3 more'))

    expect(getByText('Rose')).toBeInTheDocument()
  })
})

```


[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 search-result/react/__tests__/components/RadioFilters.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { render, fireEvent } from '@vtex/test-tools/react'

import RadioFilters from '../../components/RadioFilters'

const radioFacetsMock = [
  {
    key: 'shipping',
    name: 'Delivery',
    value: 'shipping',
    selected: false,
    quantity: 1,
    map: 'shipping',
  },
  {
    key: 'shipping',
    name: 'Pickup',
    value: 'pickup',
    selected: false,
    quantity: 1,
    map: 'shipping',
  },
]

describe('<RadioFilters />', () => {
  it('should render radio options', () => {
    const { getByLabelText } = render(
      <RadioFilters facets={radioFacetsMock} onChange={() => {}} />
    )

    expect(getByLabelText('Delivery')).toBeInTheDocument()
    expect(getByLabelText('Pickup')).toBeInTheDocument()
  })

  it('should call onChange when selecting an option', () => {
    const onChangeMock = jest.fn()
    const { getByLabelText } = render(
      <RadioFilters facets={radioFacetsMock} onChange={onChangeMock} />
    )

    fireEvent.click(getByLabelText('Pickup'))
    expect(onChangeMock).toHaveBeenCalledWith(
      expect.objectContaining({ value: 'pickup' })
    )
  })

  it('should set selected radio', () => {
    const selectedFacets = [
      { ...radioFacetsMock[0], selected: true },
      radioFacetsMock[1],
    ]

    const { getByLabelText } = render(
      <RadioFilters facets={selectedFacets} onChange={() => {}} />
    )

    expect(getByLabelText('Delivery')).toBeChecked()
    expect(getByLabelText('Pickup')).not.toBeChecked()
  })

  it('should disable radio when quantity is 0', () => {
    const disabledFacets = [
      { ...radioFacetsMock[0], quantity: 0 },
      radioFacetsMock[1],
    ]

    const { getByLabelText } = render(
      <RadioFilters facets={disabledFacets} onChange={() => {}} />
    )

    expect(getByLabelText('Delivery')).toBeDisabled()
    expect(getByLabelText('Pickup')).not.toBeDisabled()
  })
})

```


[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 search-result/react/__tests__/components/ToggleFilters.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { render, fireEvent } from '@vtex/test-tools/react'

import ToggleFilters from '../../components/ToggleFilters'

const toggleFacetsMock = [
  {
    key: 'dynamic-estimate',
    name: 'Same Day',
    value: 'same-day',
    selected: false,
    quantity: 5,
    map: 'dynamic-estimate',
  },
  {
    key: 'dynamic-estimate',
    name: 'Next Day',
    value: 'next-day',
    selected: false,
    quantity: 3,
    map: 'dynamic-estimate',
  },
]

describe('<ToggleFilters />', () => {
  it('should render toggle options', () => {
    const { getByLabelText } = render(
      <ToggleFilters facets={toggleFacetsMock} onChange={() => {}} />
    )

    expect(getByLabelText('Same Day')).toBeInTheDocument()
    expect(getByLabelText('Next Day')).toBeInTheDocument()
  })

  it('should call onChange when toggling an option', () => {
    const onChangeMock = jest.fn()
    const { getByLabelText } = render(
      <ToggleFilters facets={toggleFacetsMock} onChange={onChangeMock} />
    )

    fireEvent.click(getByLabelText('Same Day'))

    expect(onChangeMock).toHaveBeenCalledWith(
      expect.objectContaining({
        value: 'same-day',
        selected: true,
      })
    )
  })

  it('should set selected state correctly', () => {
    const selectedFacets = [
      { ...toggleFacetsMock[0], selected: true },
      { ...toggleFacetsMock[1], selected: false },
    ]

    const { getByLabelText } = render(
      <ToggleFilters facets={selectedFacets} onChange={() => {}} />
    )

    expect(getByLabelText('Same Day')).toBeChecked()
    expect(getByLabelText('Next Day')).not.toBeChecked()
  })

  it('should disable toggle when quantity is 0', () => {
    const disabledFacets = [
      { ...toggleFacetsMock[0], quantity: 0 },
      toggleFacetsMock[1],
    ]

    const { getByLabelText } = render(
      <ToggleFilters facets={disabledFacets} onChange={() => {}} />
    )

    expect(getByLabelText('Same Day')).toBeDisabled()
    expect(getByLabelText('Next Day')).not.toBeDisabled()
  })

  it('should render with data-testid', () => {
    const { getByTestId } = render(
      <ToggleFilters facets={toggleFacetsMock} onChange={() => {}} />
    )

    expect(getByTestId('toggle-filters')).toBeInTheDocument()
  })

  it('should have exclusive behavior like radio buttons', () => {
    const mockOnChange = jest.fn()
    const { getByLabelText } = render(
      <ToggleFilters facets={toggleFacetsMock} onChange={mockOnChange} />
    )

    // Primeiro, seleciona 'Same Day'
    fireEvent.click(getByLabelText('Same Day'))
    expect(mockOnChange).toHaveBeenCalledWith(
      expect.objectContaining({
        value: 'same-day',
        selected: true,
      })
    )

    // Reset do mock
    mockOnChange.mockClear()

    // Depois, seleciona 'Next Day' - deve ser excludente
    fireEvent.click(getByLabelText('Next Day'))
    expect(mockOnChange).toHaveBeenCalledWith(
      expect.objectContaining({
        value: 'next-day',
        selected: true,
      })
    )
  })

  it('should handle selected as boolean even when it comes as object', () => {
    const facetsWithObjectSelected = [
      { ...toggleFacetsMock[0], selected: { value: true } }, // Objeto ao invés de booleano
      { ...toggleFacetsMock[1], selected: false },
    ]

    const mockOnChange = jest.fn()
    const { getByLabelText } = render(
      <ToggleFilters
        facets={facetsWithObjectSelected}
        onChange={mockOnChange}
      />
    )

    // O componente deve tratar o objeto como truthy e converter para boolean
    expect(getByLabelText('Same Day')).toBeChecked()
    expect(getByLabelText('Next Day')).not.toBeChecked()

    // Clique deve funcionar normalmente
    fireEvent.click(getByLabelText('Next Day'))
    expect(mockOnChange).toHaveBeenCalledWith(
      expect.objectContaining({
        value: 'next-day',
        selected: true, // Deve ser booleano true
      })
    )
  })

  it('should handle SyntheticEvent from Toggle onChange correctly', () => {
    const mockOnChange = jest.fn()
    const { getByLabelText } = render(
      <ToggleFilters facets={toggleFacetsMock} onChange={mockOnChange} />
    )

    // Usar fireEvent.click que é como nosso mock funciona
    fireEvent.click(getByLabelText('Same Day'))

    expect(mockOnChange).toHaveBeenCalledWith(
      expect.objectContaining({
        value: 'same-day',
        selected: true, // Deve extrair corretamente o boolean
      })
    )
  })
})

```


[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 search-result/react/__tests__/components/FilterSideBar.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable jest/no-mocks-import */
/* eslint-env jest */
import React from 'react'
import { fireEvent, render } from '@vtex/test-tools/react'
import categoriesTree from 'categoriesTree'
import { useDevice } from 'vtex.device-detector'

import QueryContext from '../../components/QueryContext'
import { useRuntime } from '../../__mocks__/vtex.render-runtime'
import FilterNavigatorContext from '../../components/FilterNavigatorContext'
import specifications from '../../__mocks__/specifications'
import FilterNavigator from '../../FilterNavigator'

const mockUseRuntime = useRuntime

const mockNavigate = jest.fn()
const mockSetQuery = jest.fn()

beforeEach(() => {
  jest.clearAllMocks()

  mockUseRuntime.mockImplementation(() => ({
    navigate: mockNavigate,
    setQuery: mockSetQuery,
    getSettings: () => ({}),
    query: {},
  }))

  useDevice.mockImplementation(() => ({
    isMobile: true,
  }))
})

describe('<FilterNavigator />', () => {
  const renderComponent = (
    customProps = { query: 'fulltextterm', map: 'ft' }
  ) => {
    const props = {
      map: customProps.map,
      tree: categoriesTree,
      queryArgs: { query: customProps.query, map: customProps.map },
      specificationFilters: specifications,
      filters: specifications,
      ...customProps,
    }

    return render(
      <QueryContext.Provider
        value={{ query: customProps.query, map: props.map }}
      >
        <FilterNavigatorContext.Provider
          value={{ query: customProps.query, map: props.map }}
        >
          <FilterNavigator {...props} />
        </FilterNavigatorContext.Provider>
      </QueryContext.Provider>
    )
  }

  it('should add a unselected specification', () => {
    const { getByText } = renderComponent()

    fireEvent.click(getByText('Color'))
    fireEvent.click(getByText('White'))
    fireEvent.click(getByText('Apply'))

    expect(mockNavigate).toHaveBeenCalledTimes(1)
    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        to: '/fulltextterm/white',
      })
    )
  })

  it('should remove a selected specification', () => {
    const filters = JSON.parse(JSON.stringify(specifications))

    filters
      .find(filter => filter.title === 'store/search.filter.title.color')
      .facets.find(
        filterOption => filterOption.value === 'white'
      ).selected = true

    const { getByText } = renderComponent({
      specificationFilters: filters,
      filters,
      query: 'fulltextterm/white',
      map: 'ft,color',
    })

    fireEvent.click(getByText('Color'))
    fireEvent.click(getByText('White'))
    fireEvent.click(getByText('Apply'))

    expect(mockNavigate).toHaveBeenCalledTimes(1)
    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        to: '/fulltextterm',
      })
    )
  })

  it('should add a unselected category', () => {
    const { getByText } = renderComponent()

    fireEvent.click(getByText('Departments'))
    fireEvent.click(getByText('Eletrônicos'))
    fireEvent.click(getByText('Smartphones'))
    fireEvent.click(getByText('Apply'))

    expect(mockNavigate).toHaveBeenCalledTimes(1)
    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        to: '/fulltextterm/eletronicos/smartphones',
      })
    )
  })

  it('should not add a unselected specification when it is selected and unselected right after', () => {
    const { getByText } = renderComponent()

    fireEvent.click(getByText('Color'))
    fireEvent.click(getByText('White'))
    fireEvent.click(getByText('White'))
    fireEvent.click(getByText('Apply'))

    expect(mockNavigate).toHaveBeenCalledTimes(1)
    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        to: '/fulltextterm',
      })
    )
  })

  it('should clear all specifications and categories on button click', () => {
    const filters = JSON.parse(JSON.stringify(specifications))

    filters
      .find(filter => filter.title === 'store/search.filter.title.color')
      .facets.find(
        filterOption => filterOption.value === 'white'
      ).selected = true

    filters
      .find(filter => filter.title === 'store/search.filter.title.color')
      .facets.find(
        filterOption => filterOption.value === 'green'
      ).selected = true

    const { getByText } = renderComponent({
      specificationFilters: filters,
      filters,
      query: 'fulltextterm/green/white',
      map: 'ft,color,color',
    })

    fireEvent.click(getByText('Clear'))
    expect(mockNavigate).toHaveBeenCalledTimes(1)
    fireEvent.click(getByText('Apply'))
    expect(mockNavigate).toHaveBeenCalledTimes(2)

    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        to: '/fulltextterm',
      })
    )
  })

  it('should navigate right after specification selection when updateOnFilterSelectionOnMobile is true', () => {
    const { getByText } = renderComponent({
      query: 'fulltextterm',
      map: 'ft',
      updateOnFilterSelectionOnMobile: true,
      preventRouteChange: true,
    })

    fireEvent.click(getByText('Color'))
    fireEvent.click(getByText('White'))

    expect(mockSetQuery).toHaveBeenCalledTimes(1)
    expect(mockSetQuery).toHaveBeenCalledWith(
      expect.objectContaining({
        map: 'ft,color',
        query: '/fulltextterm/white',
      })
    )

    fireEvent.click(getByText('Apply'))

    expect(mockSetQuery).toHaveBeenCalledTimes(1)
  })

  it('should navigate right after category selection when updateOnFilterSelectionOnMobile is true', () => {
    const { getByText } = renderComponent({
      query: 'fulltextterm',
      map: 'ft',
      updateOnFilterSelectionOnMobile: true,
      preventRouteChange: true,
    })

    fireEvent.click(getByText('Departments'))
    fireEvent.click(getByText('Smartphones'))

    expect(mockSetQuery).toHaveBeenCalledTimes(1)
    expect(mockSetQuery).toHaveBeenCalledWith(
      expect.objectContaining({
        map: 'ft,c,c',
        query: '/fulltextterm/eletronicos/smartphones',
      })
    )

    fireEvent.click(getByText('Apply'))

    expect(mockSetQuery).toHaveBeenCalledTimes(1)
  })

  it('should clear all specifications right after click on Clear button when the updateOnFilterSelectionOnMobile is true', () => {
    const filters = JSON.parse(JSON.stringify(specifications))

    filters
      .find(filter => filter.title === 'store/search.filter.title.color')
      .facets.find(
        filterOption => filterOption.value === 'white'
      ).selected = true

    const { getByText } = renderComponent({
      specificationFilters: filters,
      filters,
      query: 'fulltextterm/white',
      map: 'ft,color',
      preventRouteChange: true,
      updateOnFilterSelectionOnMobile: true,
    })

    fireEvent.click(getByText('Clear'))

    expect(mockSetQuery).toHaveBeenCalledTimes(1)
    expect(mockSetQuery).toHaveBeenCalledWith(
      expect.objectContaining({
        query: '/fulltextterm',
        map: 'ft',
      })
    )
  })

  it('should not clear categories on Clear click', () => {
    const { getByText } = renderComponent()

    fireEvent.click(getByText('Departments'))
    fireEvent.click(getByText('Eletrônicos'))
    fireEvent.click(getByText('Smartphones'))

    fireEvent.click(getByText('Clear'))
    fireEvent.click(getByText('Apply'))

    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        to: '/fulltextterm/eletronicos/smartphones',
      })
    )
  })

  it('2', () => {
    const filters = JSON.parse(JSON.stringify(specifications))

    filters
      .find(filter => filter.title === 'store/search.filter.title.color')
      .facets.find(
        filterOption => filterOption.value === 'white'
      ).selected = true

    const { getByText, getAllByText } = renderComponent({
      specificationFilters: filters,
      filters,
      query: 'fulltextterm/white',
      map: 'ft,color',
      showClearByFilter: true,
    })

    fireEvent.click(getAllByText('Clear')[0])
    expect(mockNavigate).toHaveBeenCalledTimes(1)
    fireEvent.click(getByText('Apply'))
    expect(mockNavigate).toHaveBeenCalledTimes(2)

    expect(mockNavigate).toHaveBeenCalledWith(
      expect.objectContaining({
        to: '/fulltextterm',
      })
    )
  })
})

```


[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 search-result/react/__tests__/components/AvailableFilters.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { useRuntime } from 'vtex.render-runtime'
import { render } from '@vtex/test-tools/react'

import AvailableFilters from '../../components/AvailableFilters'

beforeEach(() => {
  jest.clearAllMocks()

  useRuntime.mockImplementation(() => ({
    getSettings: () => ({}),
    culture: { country: 'USA', currency: 'USD' },
  }))
})

const mockProps = {
  filters: [
    {
      type: 'PriceRanges',
      title: 'store/search.filter.title.price-ranges',
      facets: [
        {
          id: null,
          quantity: 1,
          name: '',
          key: 'price',
          selected: false,
          value: null,
          link: null,
          linkEncoded: null,
          href: null,
          range: {
            from: 1.11,
            to: 10,
            __typename: 'Range',
          },
          children: null,
          __typename: 'FacetValue',
          map: 'price',
          slug: 'de-1.11-a-10',
        },
      ],
    },
  ],
  preventRouteChange: false,
  initiallyCollapsed: false,
  navigateToFacet: () => {},
  truncatedFacetsFetched: false,
  setTruncatedFacetsFetched: () => {},
  closeOnOutsideClick: false,
  appliedFiltersOverview: 'hide',
  showClearByFilter: false,
}

describe('<AvailableFilters />', () => {
  const renderComponent = customProps => {
    const props = { ...mockProps, ...customProps }

    return render(<AvailableFilters {...props} />)
  }

  it('should render priceRange', () => {
    const { getByText } = renderComponent()

    expect(getByText('$1.00 $10.00')).toBeInTheDocument()
  })
})

```


[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 search-result/react/__tests__/components/PriceRange.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import { fireEvent, render } from '@vtex/test-tools/react'
import React from 'react'
import { useRuntime } from 'vtex.render-runtime'

import PriceRange from '../../components/PriceRange'

const mockUseRuntime = useRuntime
const mockOnChangePriceRange = jest.fn()

jest.spyOn(window, 'scroll').mockImplementation(jest.fn())

beforeEach(() => {
  jest.clearAllMocks()

  mockUseRuntime.mockImplementation(() => ({
    query: {
      priceRange: {},
    },
    getSettings: () => ({}),
    culture: { country: 'USA', currency: 'USD' },
  }))
})

const mockProps = {
  title: 'Price Range',
  facets: [
    {
      id: null,
      quantity: 1,
      name: '',
      key: 'price',
      selected: false,
      value: null,
      link: null,
      linkEncoded: null,
      href: null,
      range: {
        from: 1.11,
        to: 10,
        __typename: 'Range',
      },
      children: null,
      __typename: 'FacetValue',
      map: 'price',
      slug: 'de-1.11-a-10',
    },
    {
      id: null,
      quantity: 1,
      name: '',
      key: 'price',
      selected: false,
      value: null,
      link: null,
      linkEncoded: null,
      href: null,
      range: {
        from: 2,
        to: 9,
        __typename: 'Range',
      },
      children: null,
      __typename: 'FacetValue',
      map: 'price',
      slug: 'de-2-a-9',
    },
  ],
  preventRouteChange: false,
  onChangePriceRange: mockOnChangePriceRange,
}

describe('<PriceRange />', () => {
  const renderComponent = customProps => {
    const props = { ...mockProps, ...customProps }

    return render(<PriceRange {...props} />)
  }

  it('should call setQuery on Slider change', () => {
    jest.useFakeTimers()

    const { getByText } = renderComponent()

    fireEvent.click(getByText('$1.00 $10.00'))

    expect(mockOnChangePriceRange).not.toBeCalled()

    jest.runAllTimers()

    expect(mockOnChangePriceRange).toHaveBeenCalledTimes(1)
  })

  it('should call setQuery on Slider change only once after multiple interactions', () => {
    jest.useFakeTimers()

    const { getByText } = renderComponent()

    fireEvent.click(getByText('$1.00 $10.00'))
    fireEvent.click(getByText('$1.00 $10.00'))

    expect(mockOnChangePriceRange).not.toBeCalled()

    jest.runAllTimers()

    expect(mockOnChangePriceRange).toHaveBeenCalledTimes(1)
  })

  it('should render defaultValues', () => {
    const { getByText } = renderComponent({ priceRange: '2 TO 9' })

    expect(getByText('$2.00 $9.00')).toBeInTheDocument()
  })

  it('sould not render Slider if there are no options', () => {
    const { queryByText } = renderComponent({ facets: [] })

    expect(queryByText('Slider')).toBeNull()
  })
})

```


[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 search-result/react/__tests__/components/AccordionFilterContainer.test.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { fireEvent, render } from '@vtex/test-tools/react'
import specifications from 'specifications'
import { useRuntime } from 'vtex.render-runtime'

import AccordionFilterContainer from '../../components/AccordionFilterContainer'
import FilterNavigatorContext from '../../components/FilterNavigatorContext'

const mockProps = {
  filters: specifications,
  onFilterCheck: () => {},
  tree: [],
  onCategorySelect: () => {},
  appliedFiltersOverview: 'hide',
  navigationType: 'collapsible',
  initiallyCollapsed: false,
  truncateFilters: false,
  truncatedFacetsFetched: false,
  setTruncatedFacetsFetched: () => {},
  categoryFiltersMode: 'default',
  loading: false,
  onClearFilter: () => {},
  showClearByFilter: false,
  updateOnFilterSelectionOnMobile: false,
}

beforeEach(() => {
  jest.clearAllMocks()

  useRuntime.mockImplementation(() => ({
    getSettings: () => ({}),
  }))
})

describe('<AccordionFilterContainer />', () => {
  const renderComponent = () =>
    render(
      <FilterNavigatorContext.Provider
        value={{ query: 'fulltextterm', map: 'ft' }}
      >
        <AccordionFilterContainer {...mockProps} />
      </FilterNavigatorContext.Provider>
    )

  it('should be able to collapse filters when navigationTypeOnMobile is collapsible', () => {
    const { queryByText, getByText } = renderComponent()

    expect(getByText('White')).toBeInTheDocument()

    fireEvent.click(getByText('Color'))

    expect(queryByText('White')).toBeNull()

    fireEvent.click(getByText('Color'))

    expect(getByText('White')).toBeInTheDocument()
  })

  it('should be able to collapse filters on click when navigationTypeOnMobile is collapsible', () => {
    const { queryByText, getByText } = renderComponent()

    expect(getByText('White')).toBeInTheDocument()

    fireEvent.keyDown(getByText('Color'), {
      key: ' ',
      code: 'Space',
    })

    expect(queryByText('White')).toBeNull()
  })
})

```


[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 wishlist/messages/pt.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store/wishlist-new": "Criar Lista",
  "store/wishlist-add-button": "Criar",
  "store/wishlist-list-name-label": "Nome da lista",
  "store/wishlist-list-name-placeholder": "Ex: Para o meu escritório",
  "store/wishlist-is-public": "Privada",
  "store/wishlist-is-public-hint": "Apenas você poderá ver esta lista",
  "store/wishlist-add-to-list": "Salvar item em",
  "store/wishlist-my-lists": "Minhas Listas",
  "store/wishlist-apply": "aplicar",
  "store/wishlist-add-product-fail": "Produto não foi adicionado",
  "store/wishlist-see-lists": "VER LISTAS",
  "store/wishlist-product-added-to-list": "Item adicionado à lista",
  "store/wishlist-no-list-created": "Você tem nenhuma lista criada",
  "store/wishlist-default-list-name": "Todos os itens",
  "store/wishlist-option-delete": "Excluir",
  "store/wishlist-dialog-confirm": "sim",
  "store/wishlist-dialog-cancel": "não",
  "store/wishlist-delete-confirmation-message": "Você tem certeza que deseja excluir a lista {listName}?",
  "store/wishlist-option-configuration": "Editar",
  "store/wishlist-save": "Salvar",
  "store/wishlist-buy-items": "Adicionar ao carrinho",
  "store/wishlist-quantity-selected-items": "{selectedItemsQuantity} itens selecionados",
  "store/wishlist-total": "Total",
  "store/wishlist-list-empty": "Esta lista está vazia",
  "store/wishlist-add-items": "Adicionar produtos",
  "store/wishlist-list-updated": "Sua lista foi editada",
  "store/wishlist-quantity-of-items": "{itemsQuantity} items nesta lista",
  "store/wishlist-quantity-products": "{productsQuantity} produtos",
  "store/wishlist-copy-to": "Copiar para",
  "store/wishlist-copied": "Item copiado para {listName}",
  "store/wishlist-see": "Ver",
  "store/wishlist-login": "entrar",
  "store/wishlist-not-logged": "Você precisa estar loggado"
}
```


[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 wishlist/messages/en.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store/wishlist-new": "Create List",
  "store/wishlist-add-button": "Create",
  "store/wishlist-list-name-label": "LIST NAME",
  "store/wishlist-list-name-placeholder": "Ex: To my office",
  "store/wishlist-is-public": "Private",
  "store/wishlist-is-public-hint": "Only you can see this list",
  "store/wishlist-add-to-list": "Save item in",
  "store/wishlist-my-lists": "My Lists",
  "store/wishlist-apply": "apply",
  "store/wishlist-add-product-fail": "Product not added",
  "store/wishlist-see-lists": "SEE LISTS",
  "store/wishlist-product-added-to-list": "Item added to list",
  "store/wishlist-no-list-created": "You have no list created",
  "store/wishlist-default-list-name": "All items",
  "store/wishlist-option-delete": "Delete",
  "store/wishlist-dialog-confirm": "yes",
  "store/wishlist-dialog-cancel": "no",
  "store/wishlist-delete-confirmation-message": "Are you sure that you wish delete the list {listName}?",
  "store/wishlist-option-configuration": "Edit",
  "store/wishlist-save": "Save",
  "store/wishlist-buy-items": "Add to the cart",
  "store/wishlist-quantity-selected-items": "{selectedItemsQuantity} selected items",
  "store/wishlist-total": "Total",
  "store/wishlist-list-empty": "This list is empty",
  "store/wishlist-add-items": "Add products",
  "store/wishlist-list-updated": "Your list was updated",
  "store/wishlist-quantity-of-items": "{itemsQuantity} items in this list",
  "store/wishlist-quantity-products": "{productsQuantity} products",
  "store/wishlist-copy-to": "Copy to",
  "store/wishlist-copied": "Item copied to {listName}",
  "store/wishlist-see": "See",
  "store/wishlist-login": "login",
  "store/wishlist-not-logged": "You must be logged"
}
```


[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 wishlist/messages/context.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store/wishlist-new": "",
  "store/wishlist-add-button": "",
  "store/wishlist-list-name-label": "",
  "store/wishlist-list-name-placeholder": "",
  "store/wishlist-is-public": "",
  "store/wishlist-is-public-hint": "",
  "store/wishlist-add-to-list": "",
  "store/wishlist-my-lists": "",
  "store/wishlist-apply": "",
  "store/wishlist-add-product-fail": "",
  "store/wishlist-see-lists": "",
  "store/wishlist-product-added-to-list": "",
  "store/wishlist-no-list-created": "",
  "store/wishlist-default-list-name": "",
  "store/wishlist-option-delete": "",
  "store/wishlist-dialog-confirm": "",
  "store/wishlist-dialog-cancel": "",
  "store/wishlist-delete-confirmation-message": "",
  "store/wishlist-option-configuration": "",
  "store/wishlist-save": "",
  "store/wishlist-buy-items": "",
  "store/wishlist-quantity-selected-items": "",
  "store/wishlist-total": "",
  "store/wishlist-list-empty": "",
  "store/wishlist-add-items": "",
  "store/wishlist-list-updated": "",
  "store/wishlist-quantity-of-items": "",
  "store/wishlist-quantity-products": "",
  "store/wishlist-copy-to": "",
  "store/wishlist-copied": "",
  "store/wishlist-see": "",
  "store/wishlist-login": "",
  "store/wishlist-not-logged": ""
}
```


[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 wishlist/messages/es.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store/wishlist-new": "Crear Lista",
  "store/wishlist-add-button": "Crear",
  "store/wishlist-list-name-label": "NOMBRE DE LA LISTA",
  "store/wishlist-list-name-placeholder": "Ex: Para mi oficina",
  "store/wishlist-is-public": "Privado",
  "store/wishlist-is-public-hint": "Sólo usted puede ver esta lista",
  "store/wishlist-add-to-list": "Guardar elemento en",
  "store/wishlist-my-lists": "Mis Listas",
  "store/wishlist-apply": "aplicar",
  "store/wishlist-add-product-fail": "No se ha agregado producto",
  "store/wishlist-see-lists": "VER LISTAS",
  "store/wishlist-product-added-to-list": "Elemento agregado a la lista",
  "store/wishlist-no-list-created": "No tienes lista creada",
  "store/wishlist-default-list-name": "Todos los artículos",
  "store/wishlist-option-delete": "Borrar",
  "store/wishlist-dialog-confirm": "si",
  "store/wishlist-dialog-cancel": "no",
  "store/wishlist-delete-confirmation-message": "¿Estás seguro de que deseas borrar la lista {listName}?",
  "store/wishlist-option-configuration": "Editar",
  "store/wishlist-save": "Guardar",
  "store/wishlist-buy-items": "Añadir a la cesta de compra",
  "store/wishlist-quantity-selected-items": "{selectedItemsQuantity} elementos seleccionados",
  "store/wishlist-total": "Total",
  "store/wishlist-list-empty": "Esta lista esta vacia",
  "store/wishlist-add-items": "Añadir productos",
  "store/wishlist-list-updated": "Su lista fue editada",
  "store/wishlist-quantity-of-items": "{itemsQuantity} elementos en esta lista",
  "store/wishlist-quantity-products": "{productsQuantity} productos",
  "store/wishlist-copy-to": "Copiar a",
  "store/wishlist-copied": "Artículo copiado a {listName}",
  "store/wishlist-see": "Ver",
  "store/wishlist-login": "entrar",
  "store/wishlist-not-logged": "Usted debe estar conectado."
}
```


[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 wishlist/graphql/schema.graphql from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Query {
  appSettings: Settings @cacheControl(scope: PUBLIC, maxAge: MEDIUM)
}

type Settings {
  enableMultipleLists: Boolean
  defaultListName: 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 wishlist/node/package.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "dependencies": {
    "ramda": "^0.25.0"
  },
  "devDependencies": {
    "@types/node": "8.x",
    "@types/ramda": "types/npm-ramda#dist",
    "@vtex/api": "3.8.1",
    "eslint": "^5.16.0",
    "eslint-config-vtex": "^10.1.0",
    "prettier": "^1.18.2",
    "typescript": "3.5.2"
  },
  "scripts": {
    "lint": "tsc --noEmit --pretty && tslint -c tslint.json --fix './**/*.ts'"
  },
  "version": "0.3.1"
}

```


[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 wishlist/node/index.ts from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { Service, IOClients } from '@vtex/api'

import settingsResolver from './resolvers/settings'

// Export a service that defines route handlers and client options.
export default new Service<IOClients>({
  graphql: {
    resolvers: {
      Query: {
        appSettings: settingsResolver,
      },
    },
  },
})

```


[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 wishlist/react/GraphqlClient.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { ApolloClient } from 'apollo-client'
import { append, concat, contains, filter, map, path, without } from 'ramda'
import createListMutation from './graphql/mutations/createList.gql'
import deleteListMutation from './graphql/mutations/deleteList.gql'
import updateListMutation from './graphql/mutations/updateList.gql'
import getListQuery from './graphql/queries/getList.gql'
import getListDetailedQuery from './graphql/queries/getListDetails.gql'
import getListsByOwnerQuery from './graphql/queries/getListsByOwner.gql'

const WISHLIST_STORAGE_KEY = 'vtexwishlists'

export const getListsIdFromCookies = () => {
  const lists = localStorage.getItem(WISHLIST_STORAGE_KEY)
  return (
    (lists &&
      lists
        .split(',')
        .map((id: string) => id.replace('"', '').replace('"', ''))) ||
    []
  )
}

export const saveListIdInLocalStorage = (id: string | undefined): void => {
  if (id) {
    const lists = localStorage.getItem(WISHLIST_STORAGE_KEY)
    const newLists = lists ? lists + ',' + id : id
    localStorage.setItem(WISHLIST_STORAGE_KEY, newLists)
  }
}

export const removeListIdFromLocalStorage = (listId: string): void => {
  const listsId = getListsIdFromCookies()
  const listsIdWithoutRemoved = filter((id: string) => id !== listId, listsId)
  localStorage.setItem(WISHLIST_STORAGE_KEY, listsIdWithoutRemoved.toString())
}

export const getList = (
  client: ApolloClient<ResponseList>,
  id: string
): Promise<ResponseList> => {
  return client.query({
    fetchPolicy: 'network-only',
    query: getListQuery,
    variables: { id },
  })
}

const fetchListsByOwner = (
  client: ApolloClient<ResponseList>,
  owner: string,
  page?: number,
  pageSize?: number
): Promise<ResponseList> =>
  client.query({
    fetchPolicy: 'network-only',
    query: getListsByOwnerQuery,
    variables: { owner, page, pageSize },
  })

const joinLists = (
  listsByOwner: List[] | undefined,
  listsNotIndexed: ResponseList[]
) => {
  const localListsId = getListsIdFromCookies()
  const notIndexed = map(item => item.data.list || {}, listsNotIndexed)
  const lists = filter(
    item => contains(item.id, localListsId),
    listsByOwner || []
  )
  return concat(lists, notIndexed)
}

const isListFromOwner = (listsNotIndexed: ResponseList[], owner: string) => {
  return (
    listsNotIndexed.length > 0 &&
    path(['data', 'list', 'owner'], listsNotIndexed[0]) !== owner
  )
}

const getSyncLists = async (
  client: ApolloClient<ResponseList>,
  owner: string
): Promise<ResponseList> => {
  let {
    data: { listsByOwner },
  } = await fetchListsByOwner(client, owner)

  const listsId = map(item => item.id, listsByOwner || [])
  const listIdFromLocal = getListsIdFromCookies()
  if (!listIdFromLocal || !listIdFromLocal.length) {
    map(saveListIdInLocalStorage, listsId)
  } else {
    const listsIdNotIndexed = without(listsId, listIdFromLocal)
    const listsNotIndexed = await Promise.all(
      map(id => getList(client, id || ''), listsIdNotIndexed)
    )

    if (isListFromOwner(listsNotIndexed, owner)) {
      localStorage.removeItem(WISHLIST_STORAGE_KEY)
      map(saveListIdInLocalStorage, listsId)
    } else {
      listsByOwner = joinLists(listsByOwner, listsNotIndexed)
    }
  }
  return { data: { listsByOwner } }
}

export const getListsByOwner = (
  client: ApolloClient<ResponseList>,
  owner: string
): Promise<ResponseList> => getSyncLists(client, owner)

export const updateList = (
  client: ApolloClient<ResponseList>,
  id: string,
  { name, isPublic, items }: List
): Promise<ResponseList> => {
  return client.mutate({
    mutation: updateListMutation,
    variables: {
      id,
      list: {
        isPublic,
        items,
        name,
      },
    },
  })
}

export const createList = (
  client: ApolloClient<ResponseList>,
  list: List
): Promise<ResponseList> =>
  client
    .mutate({
      mutation: createListMutation,
      variables: {
        ...list,
      },
    })
    .then((response: ResponseList) => {
      const {
        data: { createList },
      } = response
      saveListIdInLocalStorage(createList ? createList.id : '')
      return response
    })

export const addProductToDefaultList = async (
  client: ApolloClient<ResponseList>,
  owner: string,
  listName: string,
  product: ListItem
): Promise<ResponseList> => {
  const {
    data: { listsByOwner },
  } = await getSyncLists(client, owner)
  if (listsByOwner && listsByOwner.length) {
    const list = listsByOwner[0]
    return updateList(client, list.id || '', {
      items: append(product, list ? list.items || [] : []),
      name: list ? list.name : '',
    })
  }
  return createList(client, {
    isEditable: false,
    items: [product],
    name: listName,
    owner,
  })
}

export const getListsFromLocaleStorage = (
  client: ApolloClient<ResponseList>
): Promise<ResponseList[]> => {
  const listsId = getListsIdFromCookies() || []
  return Promise.all(
    map((id: string) => {
      return getList(client, id)
    }, listsId)
  )
}

export const deleteList = (
  client: ApolloClient<ResponseList>,
  listId: string
): Promise<void> => {
  return client
    .mutate({
      mutation: deleteListMutation,
      variables: {
        id: listId,
      },
    })
    .then(() => removeListIdFromLocalStorage(listId))
}

export const getListDetailed = (
  client: ApolloClient<ResponseList>,
  listId: string
): Promise<ResponseList> =>
  client.query({
    fetchPolicy: 'network-only',
    query: getListDetailedQuery,
    variables: { id: listId },
  })

```


[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 wishlist/react/ListDetails.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import ListDetails from './components/ListDetails'

export default ListDetails

```


[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 wishlist/react/ListsPage.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import ListsPage from './components/ListsPage'

export default ListsPage

```


[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 wishlist/react/wishList.css from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```css
:global(.vtex-lists-page) :global(.vtex-store__template) {
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.listsPage {
}

.listPageItemsContainer {
  max-height: 100%;
}

.listPage {
}

.confirmSelectedListsBtn > button {
  border-radius: 0px;
  padding-bottom: 0.5625em;
  padding-top: 0.5625em;
}

.contentContainer {
}

.contentContainerMobile {
  max-height: 40vh;
}

.contentContainerDesktop {
  max-height: 16vh;
}

.popover {
  top: 100%;
}

.popoverLeft {
  width: 322px;
}

.open {
  overflow: hidden;
}

.formViewDialog {
  min-width: 480px;
}

.summaryContainer {}

.summaryContainerLarge {
  height: 200px;
}

.addToListContent {
}

.addToListListsToSwitch {
}

.createList {
}

.createListButtonContainer {
}

.isPublicLabel {
}

.isPublicHint {
}

.isPublicContainer {
}

.nameInputContainer {
}

.form {
}

.updateList {
}

.listEmptyContainer {
}

.listEmptyLabel{
}

.goToAddProductsButtonContainer {
}

.listDetailsContent {
}

.buySelectedItemsBtnContainer {
}

.totalPriceLabel {
}

.pricesContainer {
}

.quantityOfSelectedItemsLabel {
}

.ListDetailsFooter {
}

```


[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 wishlist/react/AddProductBtn.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, ReactNode } from 'react'
import classNames from 'classnames'
import { compose, withApollo, WithApolloClient, graphql } from 'react-apollo'
import { isMobile } from 'react-device-detect'
import { InjectedIntlProps, injectIntl, defineMessages } from 'react-intl'
import { withRuntimeContext, withSession } from 'vtex.render-runtime'
import { IconHeart } from 'vtex.store-icons'
import { ButtonWithIcon, withToast } from 'vtex.styleguide'
import { session } from 'vtex.store-resources/Queries'

import { getProfile } from './utils/profile'
import AddToList from './components/AddToList/index'
import MyLists from './MyLists'
import withSettings from './withSettings'

import { addProductToDefaultList } from './GraphqlClient'

interface AddProductBtnProps
  extends InjectedIntlProps,
    WithApolloClient<{}>,
    SettingsProps {
  icon?: ReactNode
  large?: boolean
  product: ListItem
  showToast: (toastInput: ToastInput) => void
  runtime: Runtime
  session: Session
}

interface AddProductBtnState {
  showContent?: boolean
  showLists?: boolean
  isLoading?: boolean
}

const ICON_SIZE_SMALL = 16
const ICON_SIZE_LARGE = 32
const messages = defineMessages({
  seeLists: {
    defaultMessage: '',
    id: 'store/wishlist-see-lists',
  },
  productAddedToList: {
    defaultMessage: '',
    id: 'store/wishlist-product-added-to-list',
  },
  addProductFail: {
    defaultMessage: '',
    id: 'store/wishlist-add-product-fail',
  },
  listNameDefault: {
    defaultMessage: '',
    id: 'store/wishlist-default-list-name',
  },
  login: {
    defaultMessage: '',
    id: 'store/wishlist-login',
  },
  notLogged: {
    defaultMessage: '',
    id: 'store/wishlist-not-logged',
  },
})

class AddProductBtn extends Component<AddProductBtnProps, AddProductBtnState> {
  public state: AddProductBtnState = {}

  public render() {
    const { product, large, icon } = this.props
    const { showContent, showLists, isLoading } = this.state

    const addProductBtnClasses = classNames('absolute z-5', {
      'ph6 pv7': large,
    })

    return (
      <div className={addProductBtnClasses}>
        <ButtonWithIcon
          variation="tertiary"
          onClick={this.handleAddProductClick}
          isLoading={isLoading}
          icon={
            icon || (
              <IconHeart
                color="c-muted-3"
                size={large ? ICON_SIZE_LARGE : ICON_SIZE_SMALL}
              />
            )
          }
        />
        {showContent && (
          <AddToList
            onAddToListsFail={this.handleAddToListsFail}
            onAddToListsSuccess={this.handleAddToListsSuccess}
            product={product}
            onClose={() => this.setState({ showContent: false })}
          />
        )}
        {showLists && (
          <MyLists
            {...this.props}
            onClose={() => this.setState({ showLists: false })}
          />
        )}
      </div>
    )
  }

  private handleAddProductSuccess = () => {
    const {
      showToast,
      intl,
      runtime: { navigate },
      settings: { appSettings },
    } = this.props
    const enableMultipleLists = appSettings && appSettings.enableMultipleLists

    this.setState({
      showContent: isMobile && enableMultipleLists,
      isLoading: false,
    })

    if (!isMobile || !enableMultipleLists) {
      showToast({
        action: {
          label: intl.formatMessage(messages.seeLists),
          onClick: () =>
            navigate({
              page: 'store.lists',
            }),
        },
        message: intl.formatMessage(messages.productAddedToList),
      })
    }
  }

  private handleAddProductFailed = (error: string): void => {
    const { intl } = this.props
    this.setState({ isLoading: false })
    console.error(error)
    this.props.showToast({
      message: intl.formatMessage(messages.addProductFail),
    })
  }

  private handleAddProductClick = (): void => {
    const { isLoading } = this.state
    if (isLoading) {
      return
    }
    const {
      client,
      product,
      intl,
      session,
      showToast,
      runtime: { navigate },
    } = this.props
    const profile = getProfile(session)
    if (profile) {
      this.setState({ isLoading: true })
      addProductToDefaultList(
        client,
        profile.email,
        intl.formatMessage(messages.listNameDefault),
        product
      )
        .then(this.handleAddProductSuccess)
        .catch(this.handleAddProductFailed)
    } else {
      showToast({
        action: {
          label: intl.formatMessage(messages.login),
          onClick: () => navigate({ page: 'store.login' }),
        },
        message: intl.formatMessage(messages.notLogged),
      })
    }
  }

  private handleAddToListsFail = (): void => {
    const { showToast, intl } = this.props
    showToast({
      message: intl.formatMessage(messages.addProductFail),
    })
  }

  private handleAddToListsSuccess = (): void => {
    const {
      showToast,
      intl,
      runtime: { navigate },
    } = this.props
    showToast({
      action: {
        label: intl.formatMessage(messages.seeLists),
        onClick: () => navigate({ page: 'store.lists' }),
      },
      message: intl.formatMessage(messages.productAddedToList),
    })
  }
}

const options = {
  name: 'session',
  options: () => ({ ssr: false }),
}

export default withSession()(
  compose(
    withRuntimeContext,
    injectIntl,
    withToast,
    withApollo,
    graphql(session, options),
    withSettings
  )(AddProductBtn)
)

```


[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 wishlist/react/Footer.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
/**
 * This component is necessary to hide the inherit footer
 * from the store.
 */

export default () => 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 wishlist/react/tests-setup.js from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable */
window.matchMedia = window.matchMedia || function () {
  return {
    matches: false,
    addListener: function () { },
    removeListener: function () { },
  }
}
```


[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 wishlist/react/package.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.wishlist",
  "description": "A WishList component.",
  "scripts": {
    "lint": "eslint --ext .tsx,.ts .",
    "pretest": "yarn",
    "test": "jest"
  },
  "author": "VTEX",
  "license": "ISC",
  "dependencies": {
    "classnames": "^2.2.6",
    "query-string": "^6.1.0",
    "ramda": "^0.26.1",
    "react-collapse": "^4.0.3",
    "react-device-detect": "^1.6.2",
    "react-infinite-scroll-component": "^4.1.0",
    "react-intl": "^2.7.2",
    "react-motion": "^0.5.2",
    "react-outside-click-handler": "^1.2.2",
    "route-parser": "^0.0.5"
  },
  "devDependencies": {
    "@types/classnames": "^2.2.7",
    "@types/node": "^11.13.8",
    "@types/ramda": "^0.26.3",
    "@types/react": "^16.4.18",
    "@types/react-dom": "^16.0.9",
    "@types/react-intl": "^2.3.17",
    "@types/react-outside-click-handler": "^1.2.0",
    "apollo-cache-inmemory": "^1.2.2",
    "apollo-client": "^2.3.2",
    "apollo-client-preset": "^1.0.8",
    "babel": "^6.23.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.16.0",
    "eslint-config-vtex-react": "^4.1.0",
    "eslint-plugin-import": "^2.17.2",
    "graphql": "^0.13.2",
    "graphql-tag": "^2.8.0",
    "jsdom": "^11.11.0",
    "prettier": "^1.17.0",
    "react": "^16.3.1",
    "react-apollo": "^2.1.3",
    "react-dom": "^16.3.1",
    "typescript": "3.5.2"
  },
  "version": "0.3.1"
}

```


[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 wishlist/react/withSettings.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import settings from './graphql/queries/appSettings.gql'
import { graphql } from 'react-apollo'

const withSettings = graphql<{}, Settings>(settings, { name: 'settings' })

export default withSettings

```


[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 wishlist/react/ContainerWrapper.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { ExtensionPoint } from 'vtex.render-runtime'

const ContainerWrapper = () => {
  return <ExtensionPoint id="lists" />
}

export default ContainerWrapper

```


[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 wishlist/react/MyLists.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import Lists from './components/Lists/'

export default Lists

```


[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 wishlist/store/blocks.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.lists": {
    "blocks": [
      "lists"
    ]
  }
}
```


[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 wishlist/store/interfaces.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "addon-summary-btn.add-to-list": {
    "component": "AddProductBtn"
  },
  "addon-details-btn.add-to-list": {
    "component": "AddProductBtn",
    "required": ["lists", "product-summary"]
  },
  "add-to-list-btn": {
    "component": "AddProductBtn",
    "required": ["lists", "product-summary"]
  },
  "lists": {
    "component": "ListsPage",
    "required": ["product-summary"]
  },
  "store.lists": {
    "component": "ContainerWrapper",
    "required": [
      "lists"
    ],
    "after": [
      "footer"
    ]
  },
  "footer": {
    "component": "Footer"
  }
}

```


[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 wishlist/store/plugins.json from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "product-add-to-list-button > addon-details-btn": "addon-details-btn.add-to-list",
  "product-summary-add-to-list-button > addon-summary-btn": "addon-summary-btn.add-to-list"
}

```


[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 wishlist/node/resolvers/settings.ts from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ServiceContext } from '@vtex/api'

declare var process: {
  env: {
    VTEX_APP_ID: string
  }
}

const appId = process.env.VTEX_APP_ID

export default async function settings(
  _root: {},
  _args: {},
  ctx: ServiceContext
) {
  const settings = await ctx.clients.apps.getAppSettings(appId)

  return settings
}

```


[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 wishlist/react/typings/vtex.store-components.d.ts from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.store-components/BuyButton'
declare module 'vtex.store-components/ProductPrice'

```


[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 wishlist/react/typings/vtex.render-runtime.d.ts from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.render-runtime' {
  export const withRuntimeContext
  export const withSession
  export const ExtensionPoint
}

```


[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 wishlist/react/typings/vtex.store-icons.d.ts from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.store-icons' {
  export const IconHeart
}

```


[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 wishlist/react/typings/global.d.ts from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface ToastInput {
  message: string
  action?: {
    label: string
    onClick: () => void
  }
}

interface CommertialOffer {
  AvailableQuantity: number
  Price: number
}

interface Seller {
  commertialOffer: CommertialOffer
}

interface Item {
  sellers: Seller[]
}

interface Product {
  items?: Item[]
  sku?: {}
  brand?: string
  detailUrl?: string
  imageUrl?: string
  listPrice?: {}
  name?: string
  price?: number
  quantity?: number
  seller?: {}
  skuId?: string
  variant?: {}
  linkText?: string
  productName?: string
}

interface ListItem {
  id?: string
  quantity?: number
  productId: string
  skuId: string
  product?: Product
}

interface List {
  id?: string
  name?: string
  isPublic?: boolean
  isEditable?: boolean
  owner?: string
  items?: ListItem[]
}

interface NavigateInput {
  fallbackToWindowLocation?: boolean
  to?: string
  page?: string
  params?: {
    listId?: string
  }
  query?: string
}

interface Query {
  listId?: string
}

interface Options {
  merge?: boolean
  replace?: boolean
}

interface Runtime {
  navigate: (navigateInput: NavigateInput) => void
  query: {
    listId?: string
  }
  setQuery: (query: Query, options: Options) => void
  goBack: () => void
}

interface Option {
  title: string
  onClick: (params?: {}) => void
  disabled?: boolean
}

interface ResponseList {
  data: {
    list?: List
    createList?: List
    updateList?: List
    listsByOwner?: List[]
  }
  errors?: {}
}

interface ListItemWithProduct {
  itemId: string
  product: Product
}

interface ActionMenuItem {
  label?: string
  onClick: () => void
}

interface Profile {
  email: string
}

interface GetSession {
  profile: Profile
}

interface Session {
  loading: boolean
  getSession: GetSession
}

interface Settings {
  enableMultipleLists: boolean
  defaultListName: string
}

interface SettingsProps {
  settings: NamedProps<{ appSettings: Settings }, {}>
}

```


[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 wishlist/react/typings/graphql.d.ts from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.gql' {
  import { DocumentNode } from 'graphql'

  const value: DocumentNode
  export default value
}

```


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

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.store-resources/Queries' {
  export const session
}

```


[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 wishlist/react/typings/vtex.styleguide.d.ts from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.styleguide' {
  export const ButtonWithIcon
  export const withToast
  export const Button
  export const Input
  export const Toggle
  export const ActionMenu
  export const IconOptionsDots
  export const Checkbox
  export const IconDelete
  export const Spinner
  export const IconPlusLines
  export const IconCaretLeft
  export const IconClose
  export const IconVisibilityOff
  export const IconVisibilityOn
}

```


[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 wishlist/react/utils/profile.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import { path } from 'ramda'

export const getProfile = (data: Session): Profile | undefined =>
  path(['getSession', 'profile'], data)

```


[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 wishlist/react/components/Popover.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import classNames from 'classnames'
import React from 'react'
import OutsideClickHandler from 'react-outside-click-handler'

import styles from '../wishList.css'

interface PopoverProps {
  onOutsideClick: () => void
  children: JSX.Element
  left?: boolean
}

const Popover = (props: PopoverProps): JSX.Element => {
  const { children, onOutsideClick, left } = props
  const className = classNames(styles.popover, 'absolute z-max', {
    [`${styles.popoverLeft} left-0 ml4 shadow-3 tl`]: left,
    'right-0': !left,
  })
  return (
    <OutsideClickHandler onOutsideClick={onOutsideClick}>
      <div className={className}>{children}</div>
    </OutsideClickHandler>
  )
}

export default Popover

```


[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 wishlist/react/components/BottomBar.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component, ReactNode } from 'react'
import { createPortal } from 'react-dom'

import styles from '../wishList.css'

const OPEN_BOTTOM_BAR_CLASS = styles.open

interface BottomBarProps {
  children: ReactNode
  onOutsideClick: () => void
}

/**
 * Bottom bar component.
 */
class BottomBar extends Component<BottomBarProps> {
  public componentDidMount() {
    document.body.classList.add(OPEN_BOTTOM_BAR_CLASS)
  }

  public componentWillUnmount() {
    document.body.classList.remove(OPEN_BOTTOM_BAR_CLASS)
  }

  public render(): ReactNode {
    const { children, onOutsideClick } = this.props

    return createPortal(
      <div className="fixed top-0 left-0 z-999 vh-100 vw-100 flex flex-column">
        <div
          tabIndex={0}
          role="button"
          onClick={() => onOutsideClick()}
          onKeyPress={this.handleKeyPress}
          className="h-100 w-100 bg-base--inverted z-4 o-40"
        />
        <div className="w-100">{children}</div>
      </div>,
      document.body
    )
  }

  private handleKeyPress = (e: React.KeyboardEvent<{}>) => {
    if (e.key == 'Enter') {
      this.props.onOutsideClick()
    }
  }
}

export default BottomBar

```


[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 wishlist/react/components/Screen.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

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

interface SreenProps {
  children: ReactNode
}

const Screen = ({ children }: SreenProps): JSX.Element => (
  <div className="fixed vh-100 w-100 left-0 top-0 z-999 bg-base">
    {children}
  </div>
)

export default Screen

```


[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 wishlist/react/components/Header.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

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

import { isMobile } from 'react-device-detect'
import { IconCaretLeft, IconClose, IconPlusLines } from 'vtex.styleguide'
import classNames from 'classnames'

interface HeaderProps {
  title?: string
  onClose: () => void
  action?: () => void
  showIconBack?: boolean
  children?: ReactNode
}

const ICON_SIZE = 20
const CLOSE_ICON_SIZE = 23

const handleKeyPress = (e: React.KeyboardEvent<{}>, onClick: () => void) => {
  if (e.key == 'Enter') {
    onClick()
  }
}

const Header = (props: HeaderProps): JSX.Element => {
  const { title, onClose, action, children, showIconBack } = props
  const className = classNames('flex items-center pointer', {
    ml3: children,
  })
  return (
    <div className="flex flex-row pa4 items-center bb bt b--muted-4">
      <div
        className={className}
        role="button"
        tabIndex={0}
        onClick={onClose}
        onKeyPress={e => handleKeyPress(e, onClose)}
      >
        {showIconBack && isMobile ? (
          <IconCaretLeft size={ICON_SIZE} />
        ) : (
          <IconClose size={CLOSE_ICON_SIZE} />
        )}
      </div>
      <span className="t-heading-6 w-100 mh5 pv3 flex justify-center">
        {title}
      </span>
      {children}
      {action && (
        <div
          className="flex items-center pointer"
          role="button"
          tabIndex={0}
          onClick={action}
          onKeyPress={e => handleKeyPress(e, action)}
        >
          <IconPlusLines size={ICON_SIZE} />
        </div>
      )}
    </div>
  )
}

export default Header

```


[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 wishlist/react/components/Loading.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { Spinner } from 'vtex.styleguide'

const SPINNER_SIZE = 20

const Loading = () => {
  return (
    <div className="flex justify-center pt4">
      <span className="dib c-muted-1">
        <Spinner color="currentColor" size={SPINNER_SIZE} />
      </span>
    </div>
  )
}

export default Loading

```


[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 wishlist/react/components/ListItem.tsx from the wishlist VTEX app.

[Assistant]
Here's the implementation:

```tsx
import classNames from 'classnames'
import React, { Component, ReactNode } from 'react'
import { InjectedIntlProps, injectIntl, defineMessages } from 'react-intl'
import {
  ActionMenu,
  Checkbox,
  IconOptionsDots,
  IconVisibilityOff,
  IconVisibilityOn,
} from 'vtex.styleguide'
import DialogMessage from './Dialog/DialogMessage'
import withSettings from '../withSettings'
import { compose } from 'react-apollo'

interface ListItemProps extends InjectedIntlProps, SettingsProps {
  id: number
  list: List
  isDefault: boolean
  isSelected?: boolean
  showMenuOptions?: boolean
  hideAction?: boolean
  hideBorders?: boolean
  onClick?: (id: number) => void
  onSelected?: (id: number, isSelected?: boolean) => void
  onDeleted?: (listId: string) => Promise<void>
  onUpdated?: (index: number) => void
}

interface ListItemState {
  showDeleteDialog?: boolean
}

const ICON_SIZE = 20
const messages = defineMessages({
  optionConfiguration: {
    defaultMessage: '',
    id: 'store/wishlist-option-configuration',
  },
  optionDelete: {
    defaultMessage: '',
    id: 'store/wishlist-option-delete',
  },
  messageDeleteConfirmation: {
    defaultMessage: '',
    id: 'store/wishlist-delete-confirmation-message',
  },
  defaultListName: {
    id: 'store/wishlist-default-list-name',
    defaultMessage: '',
  },
})

class ListItem extends Component<ListItemProps, {}> {
  public state: ListItemState = {}

  private options = [
    {
      onClick: () =>
        this.props.onUpdated && this.props.onUpdated(this.props.id),
      label: this.props.intl.formatMessage(messages.optionConfiguration),
    },
    {
      onClick: () => this.setState({ showDeleteDialog: true }),
      label: this.props.intl.formatMessage(messages.optionDelete),
    },
  ]

  private isComponentMounted: boolean = false

  public componentDidMount() {
    this.isComponentMounted = true
  }

  public componentWillUnmount() {
    this.isComponentMounted = false
  }

  public render(): ReactNode {
    const {
      id,
      list: { name, isPublic, id: listId },
      isSelected,
      isDefault,
      showMenuOptions,
      hideAction,
      hideBorders,
      intl: { formatMessage },
      onClick,
      onDeleted,
      onSelected,
      settings: { appSettings },
    } = this.props
    const { showDeleteDialog } = this.state
    const defaultListName =
      (appSettings && appSettings.defaultListName) ||
      formatMessage(messages.defaultListName)

    const className = classNames('w-100 flex flex-row items-center pv4', {
      'bg-action-secondary': isDefault && !hideBorders,
      'bb b--muted-4': !hideBorders,
      'c-emphasis': hideBorders && isSelected,
      'c-muted-2': !isSelected || !hideBorders,
      pl4: showMenuOptions,
      ph4: !showMenuOptions,
    })
    const nameClassName = classNames('w-100 mh4 mv1', {
      'flex justify-center pv2': isDefault && !hideBorders,
    })

    return (
      <div className={className}>
        <div
          tabIndex={0}
          role="button"
          className="w-100 flex pointer"
          onClick={() => onClick && onClick(id)}
          onKeyPress={this.handleKeyPress}
        >
          {(!isDefault || hideBorders) && (
            <div className="flex items-center ml2">
              {isPublic ? <IconVisibilityOn /> : <IconVisibilityOff />}
            </div>
          )}
          <span className={nameClassName}>
            {isDefault ? defaultListName : name}
          </span>
        </div>
        {!hideAction &&
          (showMenuOptions
            ? !isDefault && (
                <ActionMenu
                  options={this.options}
                  hideCaretIcon
                  buttonProps={{
                    variation: 'tertiary',
                    icon: <IconOptionsDots size={ICON_SIZE} />,
                    size: 'small',
                  }}
                />
              )
            : !isDefault && (
                <div className="flex items-center c-action-primary">
                  <Checkbox
                    checked={isSelected}
                    onChange={() => onSelected && onSelected(id, isSelected)}
                  />
                </div>
              ))}
        {showDeleteDialog && (
          <DialogMessage
            message={formatMessage(messages.messageDeleteConfirmation, {
              listName: name,
            })}
            onClose={() => this.setState({ showDeleteDialog: false })}
            onSuccess={() =>
              onDeleted &&
              onDeleted(listId || '')
                .then(
                  () =>
                    this.isComponentMounted &&
                    this.setState({ showDeleteDialog: false })
                )
                .catch(
                  () =>
                    this.isComponentMounted &&
                    this.setState({ showDeleteDialog: false })
                )
            }
          />
        )}
      </div>
    )
  }

  private handleKeyPress = (e: React.KeyboardEvent<{}>) => {
    const { onClick, id } = this.props
    if (e.key == 'Enter') {
      onClick && onClick(id)
    }
  }
}

export default compose(
  injectIntl,
  withSettings
)(ListItem)

```