[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/utils/adsUtils.ts from the search-result VTEX app.

[Assistant]
Here's the implementation:

```typescript
// Use types from @vtex/ads-react where available
import type { useAds } from '@vtex/ads-react'

type SponsoredResult = ReturnType<typeof useAds>

export const mergeWithSponsoredProducts = (
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  productSearchResult: any,
  sponsoredSearchResult: SponsoredResult,
  from?: number
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
): unknown => {
  const isFirstPage = from === 0

  if (!isFirstPage) return productSearchResult

  const originalProducts =
    productSearchResult?.data?.productSearch?.products ?? []

  const sponsoredProducts = sponsoredSearchResult.ads
    .map(p => p.product ?? p)
    .filter(Boolean) as any[]

  if (!sponsoredProducts?.length) return productSearchResult

  const sponsoredIds = new Set(
    sponsoredProducts.map(p => p.id).filter(Boolean) as string[]
  )

  const filteredOriginalProducts = originalProducts.filter(
    (p: { id: string }) => !sponsoredIds.has(p.id)
  )

  const combinedProducts = [...sponsoredProducts, ...filteredOriginalProducts]

  return {
    ...productSearchResult,
    data: {
      ...(productSearchResult?.data ?? {}),
      productSearch: {
        ...(productSearchResult?.data?.productSearch ?? {}),
        products: combinedProducts,
      },
    },
  }
}

```


[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/utils/compatibilityLayer.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
// eslint-disable-next-line no-restricted-imports
import { groupBy, pathOr, zipObj } from 'ramda'

import { PATH_SEPARATOR, MAP_VALUES_SEP } from '../constants'
import { shippingOptions, SHIPPING_KEY } from './getFilters'

const shippingFacetDefault = {
  name: SHIPPING_KEY,
  type: 'DELIVERY',
  hidden: false,
  quantity: 0,
  facets: Object.keys(shippingOptions).map(option => ({
    id: null,
    quantity: 0,
    name: option,
    key: SHIPPING_KEY,
    selected: false,
    map: SHIPPING_KEY,
    value: option,
  })),
}

export const getMainSearches = (query, map) => {
  const querySegments = (query && query.split(PATH_SEPARATOR)) || []
  const mapSegments = (map && map.split(MAP_VALUES_SEP)) || []
  const zip = zipObj(mapSegments, querySegments)

  return {
    ft: zip.ft,
    productClusterIds: zip.productClusterIds,
    seller: zip.seller,
  }
}

export const buildSelectedFacetsAndFullText = (query, map, priceRange) => {
  if (!map || !query) {
    return []
  }

  const queryValues = query.split('/')
  const mapValues = decodeURIComponent(map).split(',')

  let fullText

  const selectedFacets =
    queryValues.length >= mapValues.length
      ? mapValues.map((mapValue, i) => {
          if (mapValue === 'ft') {
            try {
              fullText = decodeURI(queryValues[i])
            } catch {
              fullText = queryValues[i]
            }
          }

          return {
            key: mapValues[i],
            value: queryValues[i],
          }
        })
      : []

  if (priceRange) {
    selectedFacets.push({
      key: 'priceRange',
      value: priceRange,
    })
  }

  return [selectedFacets, fullText]
}

const addMap = facet => {
  facet.map = facet.key

  if (facet.children) {
    facet.children.forEach(facetChild => addMap(facetChild))
  }
}

export const detachFiltersByType = facets => {
  facets.forEach(facet => facet.facets.forEach(value => addMap(value)))

  const byType = groupBy(filter => filter.type)
  const groupedFilters = byType(facets)
  const brands = pathOr([], ['BRAND', 0, 'facets'], groupedFilters)
  const brandsQuantity =
    groupedFilters &&
    groupedFilters.BRAND &&
    groupedFilters.BRAND[0] &&
    groupedFilters.BRAND[0].quantity != null
      ? groupedFilters.BRAND[0].quantity
      : 0

  const specificationFilters = (groupedFilters.NUMBER || []).concat(
    groupedFilters.TEXT || []
  )

  const deliveries = getFormattedDeliveries(groupedFilters.DELIVERY || [])

  const categoriesTrees = pathOr(
    [],
    ['CATEGORYTREE', 0, 'facets'],
    groupedFilters
  )

  const priceRanges = pathOr(
    [],
    ['PRICERANGE', 0, 'facets'],
    groupedFilters
  ).map(priceRange => {
    return {
      ...priceRange,
      slug: `de-${priceRange.range.from}-a-${priceRange.range.to}`,
    }
  })

  return {
    brands,
    brandsQuantity,
    specificationFilters,
    categoriesTrees,
    priceRanges,
    deliveries,
  }
}

export const buildQueryArgsFromSelectedFacets = selectedFacets => {
  return selectedFacets.reduce(
    (queryArgs, facet, index) => {
      queryArgs.query += `${index > 0 ? '/' : ''}${facet.value}`
      queryArgs.map += `${index > 0 ? ',' : ''}${facet.key}`

      return queryArgs
    },
    { query: '', map: '' }
  )
}

const getFormattedDeliveries = deliveries => {
  const shippingFacet = deliveries.find(d => d.name === SHIPPING_KEY)

  if (!shippingFacet) {
    return [...deliveries, shippingFacetDefault]
  }

  const facetsNotIncluded = shippingFacetDefault.facets.filter(facet =>
    shippingFacet.facets.every(f => f.value !== facet.value)
  )

  shippingFacet.facets = [...shippingFacet.facets, ...facetsNotIncluded]

  return deliveries.map(facet =>
    facet.name === SHIPPING_KEY ? shippingFacet : facet
  )
}

```


[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/utils/removeTreePath.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
export const removeTreePath = props => {
  if (!props) {
    return {}
  }

  const { treePath, ...rest } = props

  return rest
}

```


[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/SearchResultCustomQueryWrapper.tsx from the search-result VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Fragment, useMemo } from 'react'
import type { RuntimeWithRoute } from 'vtex.render-runtime'
import { canUseDOM, useRuntime, Helmet } from 'vtex.render-runtime'
import queryString from 'query-string'

import useDataPixel from '../hooks/useDataPixel'
import { usePageView } from '../hooks/usePageView'
import type { SearchQuery } from '../utils/searchMetadata'
import {
  getCategoryMetadata,
  getDepartmentMetadata,
  getPageEventName,
  getSearchMetadata,
  getTitleTag,
} from '../utils/searchMetadata'

interface GetHelmetLinkParams {
  canonicalLink: string | undefined
  page: number
  rel: 'canonical' | 'prev' | 'next'
}

interface GetSearchCanonicalParams {
  canonicalLink: string
  page?: number
}

interface IsNotLastPageParams {
  products: any
  to: number | undefined
  recordsFiltered: number | undefined
}

function getHelmetLink({ canonicalLink, page, rel }: GetHelmetLinkParams) {
  if (!canonicalLink) {
    return null
  }

  let pageAfterTransformation = 0

  if (rel === 'canonical') {
    pageAfterTransformation = page
  } else if (rel === 'next') {
    pageAfterTransformation = page + 1
  } else if (rel === 'prev') {
    pageAfterTransformation = page - 1
  }

  const canonicalWithParams = getSearchCanonical({
    canonicalLink,
    page: pageAfterTransformation,
  })

  if (!canonicalWithParams) {
    return null
  }

  return {
    rel,
    href: canonicalWithParams,
  }
}

function getSearchCanonical({ canonicalLink, page }: GetSearchCanonicalParams) {
  if (page !== null && page !== undefined && page < 1) {
    return null
  }

  const query = {
    page: page !== undefined && page !== null && page > 1 ? page : undefined,
  }

  const canonicalWithPage = queryString.stringifyUrl({
    url: canonicalLink,
    query,
  })

  return canonicalWithPage
}

function useCanonicalLink() {
  const { route, rootPath = '' } = useRuntime() as RuntimeWithRoute
  const { canonicalPath } = route

  const canonicalHost = window.__hostname__ ?? window.location?.hostname

  if (!canonicalHost || !canonicalPath) {
    return undefined
  }

  const canonicalLink = `https://${canonicalHost}${rootPath}${canonicalPath}`

  return canonicalLink
}

function isNotLastPage({ products, to, recordsFiltered }: IsNotLastPageParams) {
  if (
    !products ||
    to === undefined ||
    to === null ||
    recordsFiltered === undefined ||
    recordsFiltered === null
  ) {
    return null
  }

  return to + 1 < recordsFiltered
}

const getSearchIdentifier = (
  searchQuery: SearchQuery,
  orderBy?: string,
  page?: string
) => {
  const { variables } = searchQuery

  if (!variables) {
    return
  }

  const { query, map } = variables

  return query + map + (orderBy ?? '') + (page ?? '')
}

const SearchResultCustomQueryWrapper = (props: any) => {
  const { localSearchQueryData, children } = props

  const {
    maxItemsPerPage,
    searchQuery,
    searchQuery: {
      data: {
        searchMetadata: { titleTag = '' } = {},
        productSearch: {
          products: currProducts = undefined,
          recordsFiltered: currRecords = undefined,
        } = {},
        facets: { recordsFiltered: legacyRecords, queryArgs },
        products: legacyProducts,
      },
      variables: { fullText },
    },
    orderBy,
    facetsLoading,
  } = localSearchQueryData

  const loading = searchQuery ? searchQuery.loading : undefined
  const products = currProducts ?? legacyProducts
  const recordsFiltered = currRecords ?? legacyRecords

  const {
    getSettings,
    account,
    query: { page: pageFromQuery = '1' },
    route: { title: pageTitle },
  } = useRuntime() as RuntimeWithRoute

  const settings = getSettings('vtex.store') || {}
  const {
    titleTag: defaultStoreTitle,
    storeName,
    enablePageNumberTitle = false,
    removeStoreNameTitle = false,
  } = settings

  const title = getTitleTag({
    titleTag,
    storeTitle: storeName || defaultStoreTitle,
    term: fullText,
    pageTitle,
    pageNumber: enablePageNumberTitle ? Number(pageFromQuery) : 0,
    removeStoreNameTitle,
  })

  const pixelEvents = useMemo(() => {
    if (!canUseDOM || !currProducts || !queryArgs || facetsLoading) {
      return null
    }

    const event = getPageEventName(
      currProducts,
      localSearchQueryData.searchQuery.variables
    )

    const pageInfoEvent = {
      event: 'pageInfo',
      eventType: event,
      accountName: account,
      pageUrl: window.location.href,
      orderBy,
      page: pageFromQuery,
      category: searchQuery?.data
        ? getCategoryMetadata(searchQuery.data)
        : null,
      department: searchQuery?.data
        ? getDepartmentMetadata(searchQuery.data)
        : null,
      search: searchQuery?.data ? getSearchMetadata(searchQuery.data) : null,
    }

    return [
      pageInfoEvent,
      {
        event,
        products: currProducts,
      },
    ]
  }, [
    currProducts,
    queryArgs,
    facetsLoading,
    localSearchQueryData.searchQuery.variables,
    account,
    orderBy,
    pageFromQuery,
    searchQuery.data,
  ])

  const pixelCacheKey = getSearchIdentifier(searchQuery, orderBy, pageFromQuery)

  usePageView({ title, cacheKey: pixelCacheKey })
  useDataPixel(pixelEvents, pixelCacheKey, loading)

  const canonicalLink = useCanonicalLink()
  const pageNumber = Number(pageFromQuery)

  /*
    fetch more doesn't update "page" until rerender
    so corresponding "to" isn't updated either
    get "to" base on current "page" from query string
  */
  const currTo = pageNumber * maxItemsPerPage - 1

  return (
    <Fragment>
      <Helmet
        link={[
          getHelmetLink({
            canonicalLink,
            page: pageNumber,
            rel: 'canonical',
          }),
          getHelmetLink({
            canonicalLink,
            page: pageNumber,
            rel: 'prev',
          }),
          isNotLastPage({
            products,
            to: currTo,
            recordsFiltered,
          })
            ? getHelmetLink({
                canonicalLink,
                page: pageNumber,
                rel: 'next',
              })
            : null,
        ].filter(Boolean)}
      />

      {children}
    </Fragment>
  )
}

export default SearchResultCustomQueryWrapper

```


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

[Assistant]
Here's the implementation:

```javascript
import React, { useContext, useState, useEffect, useMemo } from 'react'
import { Checkbox } from 'vtex.styleguide'
import { useCssHandles, applyModifiers } from 'vtex.css-handles'
import classNames from 'classnames'
import { useSearchPage } from 'vtex.search-page-context/SearchPageContext'
import { usePixel } from 'vtex.pixel-manager'
import { useIntl } from 'react-intl'

import { pushFilterManipulationPixelEvent } from '../utils/filterManipulationPixelEvents'
import SettingsContext from './SettingsContext'
import ShippingActionButton from './ShippingActionButton'
import useShippingActions from '../hooks/useShippingActions'

const CSS_HANDLES = ['filterItem', 'productCount', 'filterItemTitle']

// These are used to prevent creating a <Checkbox /> with id equal
// to any of these words.
const reservedVariableNames = [
  'global',
  'window',
  'document',
  'self',
  'screen',
  'parent',
]

const FacetItem = ({
  navigateToFacet,
  facetTitle,
  facet,
  className,
  preventRouteChange,
  showTitle = false,
  onOpenPostalCodeModal,
  onOpenPickupModal,
}) => {
  const intl = useIntl()
  const { push } = usePixel()

  const { actionLabel, actionType, shouldDisable } = useShippingActions(facet)

  const showActionButton = !!actionType

  const { showFacetQuantity } = useContext(SettingsContext)

  const [selected, setSelected] = useState(facet.selected)
  const handles = useCssHandles(CSS_HANDLES)
  const classes = classNames(
    applyModifiers(handles.filterItem, facet.value),
    { [`${handles.filterItem}--selected`]: facet.selected },
    className,
    'lh-copy w-100'
  )

  const { searchQuery } = useSearchPage()
  const sampling = searchQuery?.facets?.sampling

  const checkBoxId = reservedVariableNames.includes(facet.value)
    ? `filterItem--${facet.key}-${facet.value}`
    : `${facet.key}-${facet.value}`

  // This effect fixes the issue described in this PR
  // https://github.com/vtex-apps/search-result/pull/422
  useEffect(() => {
    if (facet.selected !== selected) {
      setSelected(facet.selected)
    }
    // however, having `selected` as a dependency causes it
    // to always reset back to `facet.selected`. So, we remove it,
    // so only changes in facet.selected affect the state.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [facet.selected])

  const facetLabel = useMemo(() => {
    let labelElement = facet.name

    if (showFacetQuantity && !sampling) {
      labelElement = (
        <>
          {labelElement}{' '}
          <span
            data-testid={`facet-quantity-${facet.value}-${facet.quantity}`}
            className={handles.productCount}
          >
            ({facet.quantity})
          </span>
        </>
      )
    }

    if (showActionButton) {
      labelElement = (
        <div>
          <div>{labelElement}</div>
          <ShippingActionButton
            label={intl.formatMessage({ id: actionLabel ?? 'none' })}
            openDrawer={
              actionType === 'DELIVERY'
                ? onOpenPostalCodeModal
                : onOpenPickupModal
            }
          />
        </div>
      )
    }

    if (showTitle) {
      labelElement = (
        <>
          <span className={handles.filterItemTitle}>{facetTitle}</span>:{' '}
          {labelElement}
        </>
      )
    }

    return labelElement
  }, [
    showFacetQuantity,
    sampling,
    facet.name,
    facet.value,
    facet.quantity,
    handles.productCount,
    handles.filterItemTitle,
    showActionButton,
    actionLabel,
    showTitle,
    facetTitle,
    intl,
    actionType,
    onOpenPostalCodeModal,
    onOpenPickupModal,
  ])

  return (
    <div
      className={classes}
      style={{ hyphens: 'auto', wordBreak: 'break-word', alignSelf: 'center' }}
      alt={facet.name}
      title={facet.name}
    >
      <Checkbox
        id={checkBoxId}
        checked={selected}
        label={facetLabel}
        name={facet.name}
        onChange={() => {
          pushFilterManipulationPixelEvent({
            name: facetTitle,
            value: facet.name,
            products: searchQuery?.products ?? [],
            push,
          })

          setSelected(!selected)
          navigateToFacet({ ...facet, title: facetTitle }, preventRouteChange)
        }}
        value={facet.name}
        disabled={shouldDisable}
      />
    </div>
  )
}

export default FacetItem

```


[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/Collapsible.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import classNames from 'classnames'
import React, { useState, useRef } from 'react'
import { FormattedMessage } from 'react-intl'
import { NoSSR } from 'vtex.render-runtime'

import AnimatedDiv from './AnimatedDiv'

const Collapsible = ({
  render,
  maxItems,
  threshold,
  items,
  openLabel = 'store/filter.more-items',
  closedLabel = 'store/filter.less-items',
  linkClassName,
}) => {
  const [open, setOpen] = useState(false)
  const shouldCollapse = items.length >= maxItems + threshold

  const containerRef = useRef(null)

  const overflowQuantity = items.length - maxItems

  return (
    <>
      {items.slice(0, shouldCollapse ? maxItems : items.length).map(render)}
      {shouldCollapse && items.length > maxItems && (
        <>
          <NoSSR>
            <AnimatedDiv open={open} containerRef={containerRef}>
              <div className="dib w-100" aria-hidden={!open} ref={containerRef}>
                {items.slice(maxItems).map(render)}
              </div>
            </AnimatedDiv>
          </NoSSR>
          <button
            className={classNames(
              linkClassName,
              'flex items-center mt2 pv2 ph0 bg-base bn self-start tl c-muted-3 pointer'
            )}
            onClick={() => setOpen(o => !o)}
          >
            <span className="c-link">
              <FormattedMessage
                id={!open ? openLabel : closedLabel}
                values={{ quantity: overflowQuantity }}
              />
            </span>
          </button>
        </>
      )}
    </>
  )
}

export default Collapsible

```


[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/FacetCheckboxListItem.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import classNames from 'classnames'
import React, { useMemo } from 'react'
import { applyModifiers } from 'vtex.css-handles'
import { Checkbox } from 'vtex.styleguide'
import { usePixel } from 'vtex.pixel-manager'
import { useIntl } from 'react-intl'

import styles from '../searchResult.css'
import { pushFilterManipulationPixelEvent } from '../utils/filterManipulationPixelEvents'
import useShippingActions from '../hooks/useShippingActions'
import ShippingActionButton from './ShippingActionButton'

const FacetCheckboxListItem = ({
  facet,
  showFacetQuantity,
  sampling,
  facetTitle,
  searchQuery,
  onFilterCheck,
}) => {
  const intl = useIntl()

  const { push } = usePixel()

  const { actionLabel, actionType, openDrawer, shouldDisable } =
    useShippingActions(facet)

  const showActionButton = !!actionType

  const { name, value: slugifiedName } = facet

  const facetLabel = useMemo(() => {
    let labelElement = facet.name

    if (showFacetQuantity && !sampling) {
      labelElement = `${labelElement} (${facet.quantity})`
    }

    if (showActionButton) {
      labelElement = (
        <div className="flex flex-column">
          <span>{labelElement}</span>
          <ShippingActionButton
            label={intl.formatMessage({ id: actionLabel })}
            openDrawer={openDrawer}
          />
        </div>
      )
    }

    return labelElement
  }, [
    showFacetQuantity,
    sampling,
    facet.name,
    facet.quantity,
    showActionButton,
    actionLabel,
    openDrawer,
    intl,
  ])

  return (
    <div
      className={classNames(
        applyModifiers(styles.filterAccordionItemBox, slugifiedName),
        'pr4 pt3 items-center flex bb b--muted-5'
      )}
      style={{ hyphens: 'auto', wordBreak: 'break-word' }}
    >
      <Checkbox
        disabled={shouldDisable}
        className="mb0"
        checked={facet.selected}
        id={name}
        label={facetLabel}
        name={name}
        onChange={() => {
          pushFilterManipulationPixelEvent({
            name: facetTitle,
            value: name,
            products: searchQuery?.products ?? [],
            push,
          })

          onFilterCheck({ ...facet, title: facetTitle })
        }}
        value={name}
      />
    </div>
  )
}

export default FacetCheckboxListItem

```


[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/Popup.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { IconCaret } from 'vtex.store-icons'

import searchResult from '../searchResult.css'

const { Provider, Consumer } = React.createContext()

export class PopupProvider extends Component {
  static propTypes = {
    children: PropTypes.node,
  }

  state = {
    openedItem: null,
  }

  handleClose = id => e => {
    e.preventDefault()

    if (id === this.state.openedItem) {
      this.setState({
        openedItem: null,
      })

      document.body.classList.remove(searchResult.filterPopupOpen)
    }
  }

  handleClick = id => e => {
    e.preventDefault()
    if (id === this.state.openedItem) {
      this.setState({
        openedItem: null,
      })

      document.body.classList.remove(searchResult.filterPopupOpen)
    } else {
      this.setState({
        openedItem: id,
      })

      document.body.classList.add(searchResult.filterPopupOpen)
    }
  }

  checkOpen = id => id === this.state.openedItem

  render() {
    return (
      <Provider
        value={{
          onToggle: this.handleClick,
          isOpen: this.checkOpen,
          onClose: this.handleClose,
        }}
      >
        {this.props.children}
      </Provider>
    )
  }
}

export default class Popup extends Component {
  static propTypes = {
    title: PropTypes.string.isRequired,
    id: PropTypes.string.isRequired,
    children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
    renderFooter: PropTypes.func,
    icon: PropTypes.node,
  }

  static defaultProps = {
    renderFooter: () => null,
    icon: <IconCaret orientation="down" size={16} />,
  }

  contentRef = React.createRef()

  render() {
    const { children, renderFooter, title, id, icon } = this.props

    return (
      <Consumer>
        {contextProps => {
          const { isOpen, onToggle } = contextProps
          const open = isOpen(id)

          const className = classNames(
            `${searchResult.filterPopup} relative justify-center flex`,
            {
              [searchResult.filterPopupOpen]: open,
            }
          )

          const contentClassName = classNames(
            `${searchResult.filterPopupContentContainer} h-auto bg-base fixed dn w-100 left-0 bottom-0 z-1 ph3 overflow-y-auto flex-column`,
            {
              [`${searchResult.filterPopupContentContainerOpen} flex`]: open,
            }
          )

          const contentTop = this.contentRef.current
            ? this.contentRef.current.getBoundingClientRect().bottom + 1
            : 0

          const renderProps = {
            onClose: contextProps.onClose(id),
            onToggle: contextProps.onToggle(id),
          }

          const childrenFn =
            typeof children === 'function' ? children : () => children

          return (
            <div className={className} ref={this.contentRef}>
              <button
                className={classNames(
                  `${searchResult.filterPopupButton} ph3 pv5 mv0 mh3 pointer flex justify-center items-center`,
                  {
                    'bb b--muted-1': open,
                    bn: !open,
                  }
                )}
                onClick={onToggle(id)}
              >
                <span
                  className={`${searchResult.filterPopupTitle} c-on-base t-action--small ml-auto`}
                >
                  {title}
                </span>
                <span
                  className={`${searchResult.filterPopupArrowIcon} ml-auto pl3 pt2`}
                >
                  {icon}
                </span>
              </button>
              <div className={contentClassName} style={{ top: contentTop }}>
                <div className={searchResult.filterPopupContent}>
                  {childrenFn(renderProps)}
                </div>
                <div className={searchResult.filterPopupFooter}>
                  {renderFooter(renderProps)}
                </div>
              </div>
            </div>
          )
        }}
      </Consumer>
    )
  }
}

```


[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/FilterNavigatorContext.tsx from the search-result VTEX app.

[Assistant]
Here's the implementation:

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

const FilterNavigatorContext = createContext({})

export default FilterNavigatorContext

export const useFilterNavigator = () => useContext(FilterNavigatorContext)

```


[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/CategoryItem.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import classNames from 'classnames'
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'

const CSS_HANDLES = ['categoryItemChildren']

const CategoryItem = ({
  label,
  onClick,
  className,
  href,
  categoryFiltersMode,
}) => {
  const handles = useCssHandles(CSS_HANDLES)

  if (categoryFiltersMode === 'href') {
    return (
      <a
        tabIndex={0}
        className={classNames(
          handles.categoryItemChildren,
          'ph5 ph3-ns pv5 pv1-ns lh-copy pointer hover-bg-muted-5 c-muted-1 db no-underline',
          className
        )}
        onClick={e => {
          e.preventDefault()
          onClick()
        }}
        href={href && href.toLowerCase()}
        title={label}
        onKeyDown={e => e.key === 'Enter' && onClick(e)}
        data-testid={`categoryItem-${label}`}
      >
        {label}
      </a>
    )
  }

  return (
    <div
      tabIndex={0}
      role="link"
      className={classNames(
        handles.categoryItemChildren,
        'ph5 ph3-ns pv5 pv1-ns lh-copy pointer hover-bg-muted-5 c-muted-1',
        className
      )}
      onClick={onClick}
      onKeyDown={e => e.key === 'Enter' && onClick(e)}
      data-testid={`categoryItem-${label}`}
    >
      {label}
    </div>
  )
}

export default CategoryItem

```


[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/AccordionFilterPriceRange.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'

import AccordionFilterItem from './AccordionFilterItem'
import PriceRange from './PriceRange'

const AccordionFilterPriceRange = ({
  className,
  title,
  facets,
  show,
  open,
  onOpen,
  priceRange,
  navigationType,
  initiallyCollapsed,
  priceRangeLayout,
  onClearFilter,
  showClearByFilter,
  onChangePriceRange,
}) => {
  const priceRangeRegex = /^(.*) TO (.*)$/
  const isPriceRangeSelected = priceRange && priceRangeRegex.test(priceRange)

  return (
    <AccordionFilterItem
      title={title}
      open={open}
      show={show}
      onOpen={onOpen}
      navigationType={navigationType}
      initiallyCollapsed={initiallyCollapsed}
      showClearByFilter={showClearByFilter}
      quantity={isPriceRangeSelected ? 1 : undefined}
      onClearFilter={onClearFilter}
    >
      <div className={className}>
        <PriceRange
          title={title}
          facets={facets}
          priceRange={priceRange}
          priceRangeLayout={priceRangeLayout}
          onChangePriceRange={onChangePriceRange}
        />
      </div>
    </AccordionFilterItem>
  )
}

export default AccordionFilterPriceRange

```


[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/GalleryLayoutItem.tsx from the search-result VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ComponentType } from 'react'
import React, { memo, useCallback, useMemo } from 'react'
import { usePixel } from 'vtex.pixel-manager'
import ProductSummary from 'vtex.product-summary/ProductSummaryCustom'
import { useSearchPage } from 'vtex.search-page-context/SearchPageContext'

import type { Product } from '../Gallery'
import type { PreferredSKU } from '../GalleryLayout'

interface GalleryLayoutItemProps {
  GalleryItemComponent: ComponentType<any>
  item: Product
  displayMode: string
  summary: unknown
  position: number
  listName: string
  /** Logic to enable which SKU will be the selected item */
  preferredSKU?: PreferredSKU
}

const GalleryLayoutItem: React.FC<GalleryLayoutItemProps> = ({
  GalleryItemComponent,
  item,
  displayMode,
  summary,
  position,
  listName,
  preferredSKU,
}) => {
  const { push } = usePixel()
  const { searchQuery } = useSearchPage()

  const product = useMemo(
    () => ProductSummary.mapCatalogProductToProductSummary(item, preferredSKU),
    [item, preferredSKU]
  )

  const handleClick = useCallback(() => {
    push({
      event: 'productClick',
      product,
      query: searchQuery?.variables?.query,
      map: searchQuery?.variables?.map,
      position,
      list: listName,
    })
  }, [
    product,
    push,
    searchQuery?.variables?.map,
    searchQuery?.variables?.query,
    position,
    listName,
  ])

  return (
    <GalleryItemComponent
      {...summary}
      product={product}
      displayMode={displayMode}
      actionOnClick={handleClick}
      listName={listName}
      position={position}
    />
  )
}

export default memo(GalleryLayoutItem)

```


[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/ShowMoreFilterButton.tsx from the search-result VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'
import { FormattedMessage } from 'react-intl'

interface ShowMoreFilterButton {
  toggleTruncate: () => void
  truncated: boolean
  quantity: number
}

const CSS_HANDLES = ['seeMoreButton'] as const

const ShowMoreFilterButton: React.FC<ShowMoreFilterButton> = ({
  truncated,
  toggleTruncate,
  quantity,
}) => {
  const handles = useCssHandles(CSS_HANDLES)

  return (
    <button
      onClick={toggleTruncate}
      className={`${handles.seeMoreButton} mt2 pv2 bn pointer c-link`}
      key={truncated ? 'store/filter.more-items' : 'store/filter.less-items'}
    >
      <FormattedMessage
        id={truncated ? 'store/filter.more-items' : 'store/filter.less-items'}
        values={{
          quantity,
        }}
      />
    </button>
  )
}

export default ShowMoreFilterButton

```


[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/withResizeDetector.tsx from the search-result VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ComponentType } from 'react'
import React from 'react'
import ReactResizeDetector from 'react-resize-detector'

interface WrappedComponentProps {
  width: number
}

function withResizeDetector<P extends WrappedComponentProps>(
  WrappedComponent: ComponentType<P>
): ComponentType<P> {
  // eslint-disable-next-line react/display-name
  return props => (
    <ReactResizeDetector handleWidth>
      {(width: number) => <WrappedComponent {...props} width={width} />}
    </ReactResizeDetector>
  )
}

export default withResizeDetector

```


[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/SearchQuery.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { equals } from 'ramda'
import { useQuery } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'
import facetsQuery from 'vtex.store-resources/QueryFacetsV2'
import productSearchQuery from 'vtex.store-resources/QueryProductSearchV3'
import searchMetadataQuery from 'vtex.store-resources/QuerySearchMetadataV2'

import { FACETS_RENDER_THRESHOLD } from '../constants/filterConstants'
import useRedirect from '../hooks/useRedirect'
import useSession from '../hooks/useSession'
import {
  buildQueryArgsFromSelectedFacets,
  buildSelectedFacetsAndFullText,
  detachFiltersByType,
} from '../utils/compatibilityLayer'
import getCookie from '../utils/getCookie'

const DEFAULT_PAGE = 1

const DEFAULT_QUERY_VALUES = {
  facetsBehavior: 'Static',
  installmentCriteria: 'MAX_WITHOUT_INTEREST',
  skusFilter: 'ALL_AVAILABLE',
  simulationBehavior: 'default',
  orderBy: 'OrderByScoreDESC',
}

// Has to match the value in the query middleware,
// in the vtex.render-server app.
// TODO: make these values sync somehow
const INITIAL_ITEMS_LIMIT = 18

const includeFacets = (map, query) =>
  !!(map && map.length > 0 && query && query.length > 0)

const useCombinedRefetch = (productRefetch, facetsRefetch) => {
  return useCallback(
    async refetchVariables => {
      let productVariables
      let facetsVariables

      if (refetchVariables) {
        const { query, map, priceRange, facetQuery, facetMap } =
          refetchVariables

        productVariables = {
          ...refetchVariables,
          withFacets: false,
        }

        facetsVariables = {
          query: refetchVariables.facetQuery,
          map: refetchVariables.facetMap,
          hideUnavailableItems: refetchVariables.hideUnavailableItems,
          behavior: refetchVariables.facetsBehavior,
        }

        /* Some custom components may call the refetch function following the old protocol.
          This code guarantees that the refetchVariables  will follow the new search-protocol */
        if (query && map) {
          const [selectedFacets, fullText] = buildSelectedFacetsAndFullText(
            query,
            map,
            priceRange
          )

          productVariables = { ...productVariables, selectedFacets, fullText }
        }

        if (facetQuery && facetMap) {
          const [facetSelectedFacets, facetFullText] =
            buildSelectedFacetsAndFullText(facetQuery, facetMap, priceRange)

          facetsVariables = {
            ...facetsVariables,
            selectedFacets: facetSelectedFacets,
            fullText: facetFullText,
          }
        }
      }

      const [searchRefetchResult, facetsRefetchResult] = await Promise.all([
        productRefetch && productRefetch(productVariables),
        facetsRefetch && facetsRefetch(facetsVariables),
      ])

      return {
        ...searchRefetchResult,
        data: {
          ...(searchRefetchResult.data || {}),
          facets: facetsRefetchResult.data && facetsRefetchResult.data.facets,
        },
        errors: searchRefetchResult.errors || facetsRefetchResult.errors,
      }
    },
    [productRefetch, facetsRefetch]
  )
}

const isCurrentDifferent = (ref, currentVal) => ref.current !== currentVal

const useShouldResetPage = (query, map, orderBy, priceRange) => {
  const queryRef = useRef(query)
  const mapRef = useRef(map)
  const orderByRef = useRef(orderBy)
  const priceRangeRef = useRef(priceRange)

  return (
    isCurrentDifferent(queryRef, query) ||
    isCurrentDifferent(mapRef, map) ||
    isCurrentDifferent(orderByRef, orderBy) ||
    isCurrentDifferent(priceRangeRef, priceRange)
  )
}

const useCorrectPage = (page, shouldReset) => {
  const pageRef = useRef(page)

  if (shouldReset) {
    pageRef.current = DEFAULT_PAGE
  }

  return pageRef.current
}

const useCorrectSearchStateVariables = (
  fuzzy,
  operator,
  searchState,
  fullText
) => {
  const fullTextRef = useRef(fullText)
  const shouldReset = isCurrentDifferent(fullTextRef, fullText)

  const result = {
    fuzzy: shouldReset ? undefined : fuzzy,
    operator: shouldReset ? undefined : operator,
    searchState: shouldReset ? undefined : searchState,
  }

  fullTextRef.current = fullText

  return result
}

const useQueries = (variables, facetsArgs, price) => {
  const { getSettings, query: runtimeQuery } = useRuntime()

  const settings = getSettings('vtex.store')

  const isLazyFacetsFetchEnabled = settings?.enableFiltersFetchOptimization

  const productSearchResult = useQuery(productSearchQuery, {
    variables: {
      ...variables,
      variant: getCookie('sp-variant'),
    },
  })

  const {
    refetch: searchRefetch,
    loading: searchLoading,
    fetchMore,
  } = productSearchResult

  const { data: { searchMetadata } = {} } = useQuery(searchMetadataQuery, {
    variables: {
      query: variables.query,
      fullText: variables.fullText,
      selectedFacets: variables.selectedFacets,
    },
  })

  const getInitialAttributes = () => {
    if (runtimeQuery && runtimeQuery.initialMap) return runtimeQuery.initialMap

    if (price && facetsArgs.facetMap.indexOf('price') === -1) {
      return `${facetsArgs.facetMap},price`
    }

    return facetsArgs.facetMap
  }

  const {
    data: { facets } = {},
    loading: facetsLoading,
    refetch: facetsRefetch,
    fetchMore: facetsFetchMore,
  } = useQuery(facetsQuery, {
    variables: {
      query: facetsArgs.facetQuery,
      map: facetsArgs.facetMap,
      from: isLazyFacetsFetchEnabled ? 0 : undefined,
      to: isLazyFacetsFetchEnabled ? FACETS_RENDER_THRESHOLD : undefined,
      fullText: variables.fullText,
      selectedFacets: variables.selectedFacets,
      hideUnavailableItems: variables.hideUnavailableItems,
      behavior: variables.facetsBehavior || DEFAULT_QUERY_VALUES.facetsBehavior,
      categoryTreeBehavior: variables.categoryTreeBehavior,
      operator: variables.operator,
      fuzzy: variables.fuzzy,
      searchState: variables.searchState || undefined,
      initialAttributes: getInitialAttributes(),
      variant: getCookie('sp-variant'),
    },
    skip: !facetsArgs.withFacets,
  })

  const refetch = useCombinedRefetch(searchRefetch, facetsRefetch)

  const detachedFilters =
    facets && facets.facets
      ? detachFiltersByType(facets.facets)
      : {
          brands: [],
          brandsQuantity: 0,
          specificationFilters: [],
          categoriesTrees: [],
          priceRanges: [],
          deliveries: [],
        }

  const selectedFacetsOutput =
    facets && facets.queryArgs && facets.queryArgs.selectedFacets

  const queryArgs =
    selectedFacetsOutput &&
    buildQueryArgsFromSelectedFacets(selectedFacetsOutput)

  const redirect =
    productSearchResult &&
    productSearchResult.data &&
    productSearchResult.data.productSearch &&
    productSearchResult.data.productSearch.redirect

  return {
    loading: searchLoading || redirect,
    facetsLoading,
    fetchMore,
    data: {
      productSearch:
        productSearchResult.data && productSearchResult.data.productSearch,
      facets: {
        ...detachedFilters,
        queryArgs,
        breadcrumb: facets && facets.breadcrumb,
        facetsFetchMore,
        sampling: facets?.sampling,
      },
      searchMetadata,
    },
    productSearchResult,
    refetch,
  }
}

const SearchQuery = ({
  maxItemsPerPage,
  query,
  map,
  orderBy,
  priceRange,
  hideUnavailableItems,
  facetsBehavior,
  categoryTreeBehavior,
  pageQuery,
  skusFilter,
  simulationBehavior,
  installmentCriteria,
  excludedPaymentSystems,
  includedPaymentSystems,
  children,
  operator: operatorQuery,
  fuzzy: fuzzyQuery,
  searchState: searchStateQuery,
  lazyItemsQuery: lazyItemsQueryProp,
  __unstableProductOriginVtex,
  sponsoredProductsBehavior,
}) => {
  const [selectedFacets, fullText] = buildSelectedFacetsAndFullText(
    query,
    map,
    priceRange
  )

  const [facetsFromSession, setFacetsFromSession] = useState([])

  const { getSettings } = useRuntime()
  const lazyItemsQuerySetting = getSettings('vtex.store')?.enableLazySearchQuery

  const shouldLimitItems =
    maxItemsPerPage > INITIAL_ITEMS_LIMIT &&
    // Prioritize the `lazyItemsQuery` prop if set, otherwise
    // uses the value from the `enableLazySearchQuery` setting
    (typeof lazyItemsQueryProp === 'boolean'
      ? lazyItemsQueryProp
      : lazyItemsQuerySetting)

  const itemsLimit = shouldLimitItems ? INITIAL_ITEMS_LIMIT : maxItemsPerPage

  /* This is the page of the first query since the component was rendered.
  We want this behaviour so we can show the correct items even if the pageQuery
  changes. It should change only on a new render or if the query or orderby
  change, hence the useCorrectPage that updates its value */
  const shouldReset = useShouldResetPage(query, map, orderBy, priceRange)
  const page = useCorrectPage(
    pageQuery ? parseInt(pageQuery, 10) : DEFAULT_PAGE,
    shouldReset
  )

  const { fuzzy, operator, searchState } = useCorrectSearchStateVariables(
    fuzzyQuery,
    operatorQuery,
    searchStateQuery,
    fullText
  )

  const { setRedirect } = useRedirect()

  const from = (page - 1) * maxItemsPerPage
  const to = from + itemsLimit - 1

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const facetsArgs = {
    facetQuery: query,
    facetMap: map,
    withFacets: includeFacets(map, query),
  }

  const { getSession } = useSession()

  useEffect(() => {
    async function getShippingFromSession() {
      const result = await getSession()

      if (result && !equals(result, facetsFromSession)) {
        setFacetsFromSession(result)
      }
    }

    getShippingFromSession()
  }, [facetsFromSession, getSession, selectedFacets])

  const variables = useMemo(() => {
    return {
      map,
      query,
      orderBy: orderBy || DEFAULT_QUERY_VALUES.orderBy,
      from,
      to,
      selectedFacets: selectedFacets?.concat(facetsFromSession),
      fullText,
      operator,
      fuzzy,
      searchState,
      excludedPaymentSystems,
      includedPaymentSystems,
      productOriginVtex: !!__unstableProductOriginVtex,
      hideUnavailableItems: !!hideUnavailableItems,
      facetsBehavior: facetsBehavior || DEFAULT_QUERY_VALUES.facetsBehavior,
      categoryTreeBehavior,
      withFacets: false,
      skusFilter: skusFilter || DEFAULT_QUERY_VALUES.skusFilter,
      simulationBehavior:
        simulationBehavior || DEFAULT_QUERY_VALUES.simulationBehavior,
      installmentCriteria:
        installmentCriteria || DEFAULT_QUERY_VALUES.installmentCriteria,
    }
  }, [
    map,
    query,
    orderBy,
    from,
    to,
    hideUnavailableItems,
    facetsBehavior,
    categoryTreeBehavior,
    skusFilter,
    simulationBehavior,
    installmentCriteria,
    selectedFacets,
    facetsFromSession,
    fullText,
    operator,
    fuzzy,
    searchState,
    __unstableProductOriginVtex,
    excludedPaymentSystems,
    includedPaymentSystems,
  ])

  const {
    data,
    loading,
    refetch,
    productSearchResult,
    facetsLoading,
    fetchMore,
  } = useQueries(variables, facetsArgs, priceRange, sponsoredProductsBehavior)

  const redirectUrl = data && data.productSearch && data.productSearch.redirect

  useEffect(() => {
    setRedirect(redirectUrl)
  }, [redirectUrl, setRedirect])

  const isFetchingMore = useRef(false)
  const [lazyItemsRemaining, setLazyItemsRemaining] = useState(
    shouldLimitItems ? maxItemsPerPage - itemsLimit : 0
  )

  useEffect(() => {
    setLazyItemsRemaining(shouldLimitItems ? maxItemsPerPage - itemsLimit : 0)
  }, [map, query, from, shouldLimitItems, maxItemsPerPage, itemsLimit])

  useEffect(() => {
    if (!shouldLimitItems) {
      return
    }

    const fetchRemainingItems = () => {
      if (lazyItemsRemaining === 0 || isFetchingMore.current) {
        return
      }

      isFetchingMore.current = true

      fetchMore({
        variables: {
          from: from + INITIAL_ITEMS_LIMIT,
          to: from + maxItemsPerPage - 1,
        },
        updateQuery: (prev, { fetchMoreResult }) => {
          isFetchingMore.current = false
          setLazyItemsRemaining(0)

          return {
            ...prev,
            productSearch: {
              ...prev.productSearch,
              products: [
                ...prev.productSearch.products,
                ...fetchMoreResult.productSearch.products,
              ],
            },
          }
        },
      })
    }

    const timeout = setTimeout(() => {
      fetchRemainingItems()
    }, 500)

    return () => {
      clearTimeout(timeout)
    }
  }, [
    data,
    fetchMore,
    from,
    shouldLimitItems,
    maxItemsPerPage,
    lazyItemsRemaining,
    itemsLimit,
  ])

  const extraParams = useMemo(() => {
    return {
      ...variables,
      ...facetsArgs,
      maxItemsPerPage,
      lazyItemsRemaining,
      page,
      facetsLoading,
    }
  }, [
    variables,
    facetsArgs,
    maxItemsPerPage,
    lazyItemsRemaining,
    page,
    facetsLoading,
  ])

  const searchInfo = useMemo(
    () => ({
      ...(productSearchResult || {}),
      variables,
      data,
      loading,
      refetch,
    }),
    [data, loading, productSearchResult, refetch, variables]
  )

  return children(searchInfo, extraParams)
}

export default SearchQuery

```


[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/FooterButton.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import PropTypes from 'prop-types'
import { useCssHandles } from 'vtex.css-handles'

const CSS_HANDLES = ['footerButton']

export default function FooterButton({ children, tag, ...props }) {
  const handles = useCssHandles(CSS_HANDLES)
  const Tag = tag || 'button'

  return (
    <Tag
      {...props}
      className={`${handles.footerButton} no-underline pointer t-action--small bg-base--inverted br1 c-on-base--inverted ba b--muted-5 pv2 ph7 hover-bg-muted-5 hover-c-action-primary`}
    >
      {children}
    </Tag>
  )
}

FooterButton.propTypes = {
  children: PropTypes.node,
  tag: PropTypes.any,
}

```


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

[User] Show me the implementation of search-result/react/components/SideBar.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import { injectIntl } from 'react-intl'
import OutsideClickHandler from 'react-outside-click-handler'
import classNames from 'classnames'
import Animation from 'vtex.store-components/Animation'

import searchResult from '../searchResult.css'

const OPEN_SIDEBAR_CLASS = 'overflow-hidden'

/* SideBar component */
class Sidebar extends Component {
  updateComponent() {
    if (this.props.isOpen) {
      document.body.classList.add(OPEN_SIDEBAR_CLASS)
    } else {
      document.body.classList.remove(OPEN_SIDEBAR_CLASS)
    }
  }

  componentDidMount() {
    this.updateComponent()
  }

  componentDidUpdate() {
    this.updateComponent()
  }

  componentWillUnmount() {
    document.body.classList.remove(OPEN_SIDEBAR_CLASS)
  }

  render() {
    const { isOpen, onOutsideClick, filtersDrawerDirectionMobile } = this.props

    if (typeof document === 'undefined') {
      return null
    }

    const scrimClasses = classNames(
      `${searchResult.scrim} fixed dim bg-base--inverted top-0 w-100 vh-100 o-40 left-0`,
      {
        dn: !isOpen,
      }
    )

    const sidebarClasses = classNames(
      `${searchResult.sidebar} w-auto-ns h-100 fixed top-0 bg-base shadow-2 flex flex-column`,
      this.props.fullWidth ? 'w-100' : 'w-80',
      filtersDrawerDirectionMobile === 'drawerLeft' ? 'right-0' : 'left-0'
    )

    return ReactDOM.createPortal(
      <OutsideClickHandler onOutsideClick={onOutsideClick}>
        <div
          style={{ willChange: 'opacity' }}
          className={scrimClasses}
          onClick={onOutsideClick}
        />
        <Animation
          className={sidebarClasses}
          isActive={isOpen}
          type={filtersDrawerDirectionMobile}
        >
          {this.props.children}
        </Animation>
      </OutsideClickHandler>,
      document.body
    )
  }
}

Sidebar.propTypes = {
  /* Internationalization */
  intl: PropTypes.any,
  /* Set the sideBar visibility */
  isOpen: PropTypes.bool,
  /* Sidebar content */
  children: PropTypes.node,
  /* Function to be called when click in the close sidebar button or outside the sidebar */
  onOutsideClick: PropTypes.func,
  /* The SideBar will occupy the entire length of the window */
  fullWidth: PropTypes.bool,
  filtersDrawerDirectionMobile: PropTypes.oneOf(['drawerRight', 'drawerLeft']),
}
export default injectIntl(Sidebar)

```


[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/SearchResultContainer.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import InfiniteScroll from 'react-infinite-scroll-component'
import { Container } from 'vtex.store-components'
import { useCssHandles } from 'vtex.css-handles'

import { PopupProvider } from './Popup'
import SearchResult from './SearchResult'
import { searchResultContainerPropTypes } from '../constants/propTypes'
import { useFetchMore } from '../hooks/useFetchMore'
import { PAGINATION_TYPE } from '../constants/paginationType'

const CSS_HANDLES = ['searchResultContainer']
/**
 * Search Result Container Component.
 */
const SearchResultContainer = props => {
  const {
    maxItemsPerPage = 10,
    searchQuery: {
      fetchMore,
      data: {
        facets: {
          brands = [],
          specificationFilters = [],
          priceRanges = [],
          categoriesTrees,
          breadcrumb: facetsBreadcrumb = [],
        } = {},
        productSearch: {
          products = [],
          recordsFiltered,
          breadcrumb: productBreadcrumb = [],
        } = {},
      } = {},
      loading,
      variables: { query, map, orderBy, priceRange },
    },
    pagination,
    page,
    children,
    lazyItemsRemaining,
  } = props

  const queryData = {
    query,
    map,
    orderBy,
    priceRange,
  }

  const {
    handleFetchMoreNext,
    handleFetchMorePrevious,
    loading: fetchMoreLoading,
    from,
    to,
    infiniteScrollError,
  } = useFetchMore({
    page,
    recordsFiltered,
    maxItemsPerPage,
    fetchMore,
    products,
    queryData,
  })

  const handles = useCssHandles(CSS_HANDLES)

  const breadcrumb = productBreadcrumb || facetsBreadcrumb

  const resultComponent = children || (
    <SearchResult
      {...props}
      breadcrumbsProps={{ breadcrumb }}
      onFetchMore={handleFetchMoreNext}
      fetchMoreLoading={fetchMoreLoading}
      onFetchPrevious={handleFetchMorePrevious}
      pagination={pagination}
      query={query}
      loading={loading}
      recordsFiltered={recordsFiltered}
      products={products}
      brands={brands}
      specificationFilters={specificationFilters}
      priceRanges={priceRanges}
      tree={categoriesTrees}
      to={to}
      from={from}
      infiniteScrollError={infiniteScrollError}
      lazyItemsRemaining={lazyItemsRemaining}
    />
  )

  const infiniteScrollComponent = (
    <InfiniteScroll
      style={{ overflow: 'none' }}
      dataLength={products.length}
      next={handleFetchMoreNext}
      hasMore={to + 1 < recordsFiltered}
      useWindow={false}
    >
      {resultComponent}
    </InfiniteScroll>
  )

  const isInfiniteScroll = pagination !== PAGINATION_TYPE.SHOW_MORE

  return (
    <Container className={`${handles.searchResultContainer} pt3-m pt5-l`}>
      <PopupProvider>
        <div id="search-result-anchor" />
        {isInfiniteScroll ? infiniteScrollComponent : resultComponent}
      </PopupProvider>
    </Container>
  )
}

SearchResultContainer.propTypes = searchResultContainerPropTypes

export default SearchResultContainer

```


[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/LayoutModeSwitcher.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import PropTypes from 'prop-types'
import { Button } from 'vtex.styleguide'
import { IconGrid, IconInlineGrid, IconSingleGrid } from 'vtex.store-icons'
import { useCssHandles } from 'vtex.css-handles'

const CSS_HANDLES = ['layoutSwitcher']

export const LAYOUT_MODE = [
  {
    /** This is the single product view on mobile */
    value: 'normal',
    label: 'layoutModeSwitcher.normal',
  },
  {
    /** This is the grid product view on mobile */
    value: 'small',
    label: 'layoutModeSwitcher.small',
  },
  {
    value: 'inline',
    label: 'layoutModeSwitcher.inline',
  },
]

const LayoutIcon = ({ mode }) => {
  switch (mode) {
    case 'small':
      return <IconSingleGrid size={20} />

    case 'inline':
      return <IconInlineGrid size={20} />

    case 'normal':
      return <IconGrid size={20} />

    default: {
      // eslint-disable-next-line no-undef
      if (process.env.NODE_ENV === 'development') {
        console.warn(`Unsupported icon ${mode} in LayoutIcon`)
      }

      return null
    }
  }
}

const LayoutModeSwitcher = ({ activeMode, onChange }) => {
  const handles = useCssHandles(CSS_HANDLES)

  return (
    <div
      className={`${handles.layoutSwitcher} h-100 flex justify-center items-center pl4`}
    >
      <Button
        variation="tertiary"
        size="small"
        onClick={e => onChange(e, activeMode)}
      >
        <span className="c-on-base">
          <LayoutIcon mode={activeMode} />
        </span>
      </Button>
    </div>
  )
}

LayoutModeSwitcher.propTypes = {
  /** Current active mode */
  activeMode: PropTypes.string,
  /** On change callback */
  onChange: PropTypes.func.isRequired,
}

export default LayoutModeSwitcher

```


[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/SelectionListOrderBy.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React, { useState, useCallback, useRef } from 'react'
import PropTypes from 'prop-types'
import { useIntl } from 'react-intl'
import classNames from 'classnames'
// eslint-disable-next-line no-restricted-imports
import { find, propEq } from 'ramda'
import { formatIOMessage } from 'vtex.native-types'
import { IconCaret } from 'vtex.store-icons'
import { useDevice } from 'vtex.device-detector'
import { useCssHandles } from 'vtex.css-handles'

import SelectionListItem from './SelectionListItem'
import useOutsideClick from '../hooks/useOutsideClick'
import styles from '../searchResult.css'

const CSS_HANDLES = [
  'orderByButton',
  'orderByOptionsContainer',
  'orderByDropdown',
  'orderByText',
  'filterPopupTitle',
  'filterPopupArrowIcon',
]

const SelectionListOrderBy = ({
  message = 'store/ordenation.sort-by',
  orderBy,
  options,
  showOrderTitle,
}) => {
  const intl = useIntl()
  const [showDropdown, setShowDropdown] = useState(false)
  const handles = useCssHandles(CSS_HANDLES)

  const orderByRef = useRef(null)

  const handleDropdownBtClick = useCallback(
    () => setShowDropdown(!showDropdown),
    [showDropdown]
  )

  const handleOutsideClick = useCallback(() => setShowDropdown(false), [])

  useOutsideClick(orderByRef, handleOutsideClick, showDropdown)

  const { isMobile } = useDevice()

  const renderOptions = orderByOption => {
    return options.map(option => {
      return (
        <SelectionListItem
          key={option.value}
          onItemClick={handleOutsideClick}
          option={option}
          selected={option.value === orderByOption}
        />
      )
    })
  }

  const sortByMessage = formatIOMessage({ id: message, intl })

  const getOptionTitle = useCallback(
    option => {
      const selectedOption = find(propEq('value', option), options)

      return selectedOption ? selectedOption.label : ''
    },
    [options]
  )

  const btClass = classNames(
    handles.orderByButton,
    'ph3 pv5 mv0 pointer flex items-center justify-end bg-base c-on-base t-action--small bt br bl bb-0 br2 br--top bw1 w-100 outline-0',
    {
      'b--muted-4': showDropdown && isMobile,
      'b--transparent pl1': !showDropdown,
    }
  )

  const contentClass = classNames(
    styles.orderByOptionsContainer,
    'z-3 absolute bg-base shadow-5 w-100 f5 b--muted-4 br2 ba bw1 br--bottom top-0 right-0-ns',
    {
      db: showDropdown,
      dn: !showDropdown,
    }
  )

  const dropdownSort = classNames(
    handles.orderByDropdown,
    'relative pt1 justify-end w-100 w-auto-ns ml-auto'
  )

  return (
    <div className={dropdownSort} ref={orderByRef}>
      <button onClick={handleDropdownBtClick} className={btClass}>
        <span
          className={classNames(
            handles.filterPopupTitle,
            'c-on-base t-action--small ml-auto-ns'
          )}
        >
          <span
            className={classNames(handles.orderByText, 'c-muted-2', {
              'dn dib-ns': !orderBy.length,
            })}
          >
            {sortByMessage}
          </span>{' '}
          {showOrderTitle ? getOptionTitle(orderBy) : null}
        </span>
        <span className={`${handles.filterPopupArrowIcon} ph5 pt1`}>
          <IconCaret orientation={showDropdown ? 'up' : 'down'} size={10} />
        </span>
      </button>

      <div className={contentClass}>{renderOptions(orderBy)}</div>
    </div>
  )
}

SelectionListOrderBy.propTypes = {
  /** Current Ordernation  */
  orderBy: PropTypes.string,
  /** Sort Options */
  options: PropTypes.arrayOf(
    PropTypes.shape({
      /** Label to Option */
      label: PropTypes.string,
      /** Value to value */
      value: PropTypes.string,
    })
  ),
  /** Message to be displayed */
  message: PropTypes.string,
  /** Show or hide order title */
  showOrderTitle: PropTypes.bool,
}

export default SelectionListOrderBy

```


[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/FilterSidebar.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import classNames from 'classnames'
import produce from 'immer'
import React, { useState, useEffect, useMemo, Fragment, useRef } from 'react'
import { FormattedMessage } from 'react-intl'
import { ExtensionPoint } from 'vtex.render-runtime'
import { Button } from 'vtex.styleguide'
import { IconFilter } from 'vtex.store-icons'
import { useCssHandles } from 'vtex.css-handles'
import { useSearchPage } from 'vtex.search-page-context/SearchPageContext'
import { usePixel } from 'vtex.pixel-manager'

import FilterNavigatorContext, {
  useFilterNavigator,
} from './FilterNavigatorContext'
import AccordionFilterContainer from './AccordionFilterContainer'
import Sidebar from './SideBar'
import { MAP_CATEGORY_CHAR } from '../constants'
import { buildNewQueryMap } from '../hooks/useFacetNavigation'
import styles from '../searchResult.css'
import { getMainSearches } from '../utils/compatibilityLayer'
import {
  isCategoryDepartmentCollectionOrFT,
  filterCategoryDepartmentCollectionAndFT,
} from '../utils/queryAndMapUtils'
import { pushFilterManipulationPixelEvent } from '../utils/filterManipulationPixelEvents'
import isShippingOptionsComponent from '../utils/isShippingOptionsComponent'

const CSS_HANDLES = [
  'filterPopupButton',
  'filterPopupTitle',
  'filterButtonsBox',
  'filterPopupArrowIcon',
  'filterClearButtonWrapper',
  'filterApplyButtonWrapper',
  'filterTotalProducts',
  'filterQuantityBadge',
]

const FilterSidebar = ({
  selectedFilters,
  filters,
  tree,
  priceRange,
  preventRouteChange,
  navigateToFacet,
  appliedFiltersOverview,
  totalProductsOnMobile,
  fullWidth,
  navigationType,
  initiallyCollapsed,
  truncateFilters,
  truncatedFacetsFetched,
  setTruncatedFacetsFetched,
  categoryFiltersMode,
  loading,
  updateOnFilterSelectionOnMobile,
  showClearByFilter,
  priceRangeLayout,
  filtersDrawerDirectionMobile,
  showQuantityBadgeOnMobile,
  onOpenPostalCodeModal,
  onOpenPickupModal,
}) => {
  const { searchQuery } = useSearchPage()
  const filterContext = useFilterNavigator()
  const [open, setOpen] = useState(false)
  const handles = useCssHandles(CSS_HANDLES)
  const shouldClear = useRef(false)
  const recordsFiltered =
    searchQuery &&
    searchQuery.data &&
    searchQuery.data.productSearch &&
    searchQuery.data.productSearch.recordsFiltered

  const [filterOperations, setFilterOperations] = useState([])
  const [categoryTreeOperations, setCategoryTreeOperations] = useState([])
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  const currentTree = useCategoryTree(tree, categoryTreeOperations)

  const [ignoreGlobalShipping, setIgnoreGlobalShipping] = useState(false)

  /* Sometimes there are categories included in selectedFilters
   * This useMemo extracts only filters that users can
   * enable and disable directly */
  const selectedFacetsLength = React.useMemo(() => {
    const filterKeys = filters.map(filter => filter.key)

    const selectedFacets =
      selectedFilters.filter(
        facet => filterKeys.includes(facet.key) && facet.selected
      ) ?? []

    return selectedFacets.length
  }, [filters, selectedFilters])

  const isFilterSelected = (slectableFilters, filter) => {
    return slectableFilters.find(
      filterOperation => filter.value === filterOperation.value
    )
  }

  const handleFilterCheck = (filter, isRadio = false) => {
    if (updateOnFilterSelectionOnMobile && preventRouteChange) {
      navigateToFacet(filter, preventRouteChange)

      return
    }

    if (isRadio) {
      setFilterOperations(
        filterOperations
          .filter(facet => facet.key !== filter.key)
          .concat(filter)
      )

      return
    }

    if (!isFilterSelected(filterOperations, filter)) {
      setFilterOperations(filterOperations.concat(filter))
    } else {
      setFilterOperations(
        filterOperations.filter(facet => facet.value !== filter.value)
      )
    }
  }

  const handleClose = e => {
    if (isShippingOptionsComponent(e)) {
      return
    }

    setOpen(false)
  }

  const handleOpen = () => {
    setOpen(true)
  }

  const handleApply = (newPriceRange = undefined) => {
    setOpen(false)

    if (
      updateOnFilterSelectionOnMobile &&
      preventRouteChange &&
      !newPriceRange
    ) {
      return
    }

    navigateToFacet(filterOperations, preventRouteChange, false, newPriceRange)
    setFilterOperations([])
  }

  const { push } = usePixel()

  const handleClearFilters = key => {
    pushFilterManipulationPixelEvent({
      name: 'CleanFilters',
      value: true,
      products: searchQuery?.products ?? [],
      push,
    })

    if (!key) {
      setOpen(false)
    }

    const isClearByFacet = !!key

    shouldClear.current =
      !updateOnFilterSelectionOnMobile || !preventRouteChange
    // Gets the previously selected facets that should be cleared
    const selectedFacets = selectedFilters.filter(
      facet =>
        !isCategoryDepartmentCollectionOrFT(facet.key) &&
        facet.selected &&
        (!key || (key && key === facet.key))
    )

    if (selectedFacets.length === 0 && key) {
      setFilterOperations(filterOperations.filter(filter => filter.key !== key))

      return
    }

    // Should not clear categories, departments and clusterIds
    const selectedRest = filterOperations.filter(facet =>
      isCategoryDepartmentCollectionOrFT(facet.key)
    )

    const facetsToRemove = [...selectedFacets, ...selectedRest]

    if (updateOnFilterSelectionOnMobile && preventRouteChange) {
      navigateToFacet(facetsToRemove, preventRouteChange)

      return
    }

    if (!key) {
      setFilterOperations([])
    } else {
      setFilterOperations(facetsToRemove)
    }

    navigateToFacet(facetsToRemove, preventRouteChange, !isClearByFacet)
  }

  const handleUpdateCategories = maybeCategories => {
    const categories = Array.isArray(maybeCategories)
      ? maybeCategories
      : [maybeCategories]

    /* There is no need to compare with CATEGORY and DEPARTMENT since
     they are seen as a normal facet in the new VTEX search */
    const categoriesSelected = filterOperations.filter(
      op => op.map === MAP_CATEGORY_CHAR
    )

    const newCategories = [...categoriesSelected, ...categories]

    if (updateOnFilterSelectionOnMobile && preventRouteChange) {
      navigateToFacet(newCategories, preventRouteChange)

      return
    }

    // Just save the newest operation here to be recorded at the category tree hook and update the tree
    setCategoryTreeOperations(categories)

    // Save all filters along with the new categories, appended to the old ones
    setFilterOperations(selectableFilters => {
      return selectableFilters
        .filter(operations => operations.map !== MAP_CATEGORY_CHAR)
        .concat(newCategories)
    })
  }

  const context = useMemo(() => {
    const { query, map } = filterContext
    const fullTextAndCollection = getMainSearches(query, map)

    /* This removes the previously selected stuff from the context when you click on 'clear'.
    It is important to notice that it keeps categories, departments and clusterIds since they
    are important to show the correct facets. */
    if (shouldClear.current) {
      shouldClear.current = false

      return filterCategoryDepartmentCollectionAndFT(filterContext)
    }

    /* The spread on selectedFilters was necessary because buildNewQueryMap
     changes the object but we do not want that on mobile */
    return {
      ...filterContext,
      ...buildNewQueryMap(
        fullTextAndCollection,
        filterOperations,
        [...selectedFilters],
        ignoreGlobalShipping,
        should => setIgnoreGlobalShipping(should)
      ),
    }
  }, [filterOperations, filterContext, selectedFilters, ignoreGlobalShipping])

  return (
    <Fragment>
      <button
        className={classNames(
          `${styles.filterPopupButton} ${
            showQuantityBadgeOnMobile ? 'relative' : ''
          }  ph3 pv5 mv0 mv0 pointer flex justify-center items-center`,
          {
            'bb b--muted-1': open,
            bn: !open,
          }
        )}
        onClick={handleOpen}
      >
        <span
          className={`${handles.filterPopupTitle} c-on-base t-action--small ml-auto`}
        >
          <FormattedMessage id="store/search-result.filter-action.title" />
        </span>
        <span className={`${handles.filterPopupArrowIcon} ml-auto pl3 pt2`}>
          <IconFilter size={16} viewBox="0 0 17 17" />

          {showQuantityBadgeOnMobile && selectedFacetsLength > 0 && (
            <span
              className={`${styles.filterQuantityBadgeDefault} ${handles.filterQuantityBadge} absolute t-mini bg-muted-2 c-on-muted-2 br4 w1 h1 pa1 flex justify-center items-center lh-solid`}
            >
              {selectedFacetsLength}
            </span>
          )}
        </span>
      </button>

      <Sidebar
        onOutsideClick={handleClose}
        isOpen={open}
        fullWidth={fullWidth}
        filtersDrawerDirectionMobile={filtersDrawerDirectionMobile}
      >
        <FilterNavigatorContext.Provider value={context}>
          <AccordionFilterContainer
            filters={filters}
            tree={currentTree}
            onFilterCheck={handleFilterCheck}
            onCategorySelect={handleUpdateCategories}
            priceRange={priceRange}
            onChangePriceRange={handleApply}
            appliedFiltersOverview={appliedFiltersOverview}
            navigationType={navigationType}
            initiallyCollapsed={initiallyCollapsed}
            truncateFilters={truncateFilters}
            truncatedFacetsFetched={truncatedFacetsFetched}
            setTruncatedFacetsFetched={setTruncatedFacetsFetched}
            categoryFiltersMode={categoryFiltersMode}
            loading={loading}
            onClearFilter={handleClearFilters}
            showClearByFilter={showClearByFilter}
            updateOnFilterSelectionOnMobile={updateOnFilterSelectionOnMobile}
            priceRangeLayout={priceRangeLayout}
            onOpenPostalCodeModal={onOpenPostalCodeModal}
            onOpenPickupModal={onOpenPickupModal}
          />
          <ExtensionPoint id="sidebar-close-button" onClose={handleClose} />
        </FilterNavigatorContext.Provider>
        <div
          className={`${styles.filterButtonsBox} bt b--muted-5 bottom-0 fixed w-100 items-center flex z-1 bg-base flex-wrap`}
        >
          <div
            className={`${handles.filterClearButtonWrapper} bottom-0 fl w-50 pl4 pr2`}
          >
            <Button
              block
              variation="tertiary"
              size="regular"
              onClick={() => handleClearFilters()}
            >
              <FormattedMessage id="store/search-result.filter-button.clear" />
            </Button>
          </div>
          <div
            className={`${handles.filterApplyButtonWrapper} bottom-0 fr w-50 pr4 pl2`}
          >
            <Button
              block
              variation="secondary"
              size="regular"
              onClick={() => handleApply()}
            >
              <FormattedMessage id="store/search-result.filter-button.apply" />
            </Button>
          </div>
          {totalProductsOnMobile === 'show' && recordsFiltered && (
            <div
              className={`${handles.filterTotalProducts} w-100 flex flex-grow-1 items-center justify-center pre t-small`}
            >
              <FormattedMessage
                id="store/search.total-products-2"
                values={{
                  recordsFiltered,
                  // eslint-disable-next-line react/display-name
                  span: chunks => <span>{chunks}</span>,
                }}
              />
            </div>
          )}
        </div>
      </Sidebar>
    </Fragment>
  )
}

const updateTree = categories =>
  produce(draft => {
    if (!categories.length) {
      return
    }

    let currentLevel = draft

    while (
      !(
        currentLevel.find(category => category.value === categories[0].value) ||
        currentLevel.every(category => !category.selected)
      )
    ) {
      currentLevel = currentLevel.find(category => category.selected).children
    }

    categories.forEach(category => {
      const selectedIndex = currentLevel.findIndex(
        cat => cat.value === category.value
      )

      currentLevel[selectedIndex].selected =
        !currentLevel[selectedIndex].selected
      currentLevel = currentLevel[selectedIndex].children
    })
  })

// in order for us to avoid sending a request to the facets
// API and refetch all filters on every category change (like
// we are doing on desktop), we'll keep a local copy of the category
// tree structure, and locally modify it with the information we
// have.
//
// the component responsible for displaying the category tree
// in a user-friendly manner should reflect to the changes
// we make in the tree, the same as it would with a tree fetched
// from the API.
const useCategoryTree = (initialTree, categoryTreeOperations) => {
  const [tree, setTree] = useState(initialTree)

  useEffect(() => {
    setTree(initialTree)
  }, [initialTree])

  useEffect(() => {
    setTree(updateTree(categoryTreeOperations))
  }, [categoryTreeOperations, initialTree])

  return tree
}

export default FilterSidebar

```


[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/SettingsContext.ts from the search-result VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { createContext } from 'react'

interface SettingsContext {
  trackingId?: string
  [key: string]: unknown
}

const SettingsContext = createContext<SettingsContext>({})

export default SettingsContext

```


[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/GalleryLayoutRow.tsx from the search-result VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ComponentType } from 'react'
import React, { memo } from 'react'
import { useCssHandles, applyModifiers } from 'vtex.css-handles'
import classNames from 'classnames'
import { useSearchPage } from 'vtex.search-page-context/SearchPageContext'

import { useRenderOnView } from '../hooks/useRenderOnView'
import GalleryItem from './GalleryLayoutItem'
import type { Product } from '../Gallery'
import type { PreferredSKU } from '../GalleryLayout'

const CSS_HANDLES = ['galleryItem'] as const

interface GalleryLayoutRowProps {
  currentLayoutName: string
  displayMode: string
  GalleryItemComponent: ComponentType
  itemsPerRow: number
  lazyRender: boolean
  products: Product[]
  summary: unknown
  rowIndex: number
  listName: string
  /** Logic to enable which SKU will be the selected item */
  preferredSKU?: PreferredSKU
}

const GalleryLayoutRow: React.FC<GalleryLayoutRowProps> = ({
  GalleryItemComponent,
  displayMode,
  itemsPerRow,
  lazyRender,
  products,
  summary,
  currentLayoutName,
  rowIndex,
  listName,
  preferredSKU,
}) => {
  const { searchQuery } = useSearchPage()
  const handles = useCssHandles(CSS_HANDLES)

  const style = {
    flexBasis: `${100 / itemsPerRow}%`,
    maxWidth: `${100 / itemsPerRow}%`,
  }

  const { hasBeenViewed, dummyElement } = useRenderOnView({
    lazyRender,
    offset: 900,
  })

  if (!hasBeenViewed) {
    return dummyElement
  }

  const { searchId, redirect } = searchQuery?.data?.productSearch || {}

  return (
    <>
      {products.map((product, index) => {
        const absoluteProductIndex = rowIndex * itemsPerRow + index + 1
        const shouldAddAFAttr = searchId && !redirect && product.productId

        return (
          <div
            data-af-element={shouldAddAFAttr ? 'search-result' : undefined}
            data-af-onclick={shouldAddAFAttr ? true : undefined}
            data-af-search-id={shouldAddAFAttr ? searchId : undefined}
            data-af-product-position={
              shouldAddAFAttr ? absoluteProductIndex : undefined
            }
            data-af-product-id={shouldAddAFAttr ? product.productId : undefined}
            data-af-product-specification={
              shouldAddAFAttr ? product.specification : undefined
            }
            key={product.cacheId}
            style={style}
            className={classNames(
              applyModifiers(handles.galleryItem, [
                displayMode,
                currentLayoutName,
              ]),
              'pa4'
            )}
          >
            <GalleryItem
              GalleryItemComponent={GalleryItemComponent}
              item={product}
              summary={summary}
              displayMode={displayMode}
              position={absoluteProductIndex}
              listName={listName}
              preferredSKU={preferredSKU}
            />
          </div>
        )
      })}
    </>
  )
}

export default memo(GalleryLayoutRow)

```


[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/ShippingActionButton.tsx from the search-result VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'

const CSS_HANDLES = ['shippingActionButton']

interface Props {
  label: string
  openDrawer: () => void
}

const ShippingActionButton = ({ label, openDrawer }: Props) => {
  const handles = useCssHandles(CSS_HANDLES)

  return (
    <button
      onClick={() => openDrawer()}
      className={handles.shippingActionButton}
    >
      {label}
    </button>
  )
}

export default ShippingActionButton

```


[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/GalleryRow.tsx from the search-result VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { memo } from 'react'
import type { ComponentType } from 'react'
import { useCssHandles, applyModifiers } from 'vtex.css-handles'
import { useSearchPage } from 'vtex.search-page-context/SearchPageContext'
import classNames from 'classnames'

import { useRenderOnView } from '../hooks/useRenderOnView'
import GalleryItem from './GalleryItem'
import type { Product } from '../Gallery'
import type { MobileLayoutMode } from '../GalleryLegacy'
import type { PreferredSKU } from '../GalleryLayout'

const CSS_HANDLES = ['galleryItem'] as const

interface GalleryRowProps {
  products: Product[]
  /**
   * Apparently this is a dead prop and should as I could not find any scenario where the runtime passes it down or
   * even a case where product summary uses it. Therefore I will leave it as :any
   */
  summary: any
  displayMode: MobileLayoutMode
  itemsPerRow: number
  lazyRender?: boolean
  rowIndex?: number
  listName: string
  customSummaryInterval?: number
  CustomSummary?: ComponentType
  /** Logic to enable which SKU will be the selected item */
  preferredSKU?: PreferredSKU
}

function GalleryRow({
  products,
  summary,
  displayMode,
  itemsPerRow,
  lazyRender,
  rowIndex = 0,
  listName,
  customSummaryInterval,
  CustomSummary,
  preferredSKU,
}: GalleryRowProps) {
  const { searchQuery } = useSearchPage()
  const handles = useCssHandles(CSS_HANDLES)

  const style = {
    flexBasis: `${100 / itemsPerRow}%`,
    maxWidth: `${100 / itemsPerRow}%`,
  }

  const { hasBeenViewed, dummyElement } = useRenderOnView({
    lazyRender,
    offset: 900,
  })

  if (!hasBeenViewed) {
    return dummyElement
  }

  const { searchId, redirect } = searchQuery?.data?.productSearch || {}

  return (
    <>
      {products.map((product, index) => {
        const absoluteProductIndex = rowIndex * itemsPerRow + index + 1

        const shouldRenderCustom = !!(
          CustomSummary &&
          customSummaryInterval &&
          absoluteProductIndex % customSummaryInterval === 0
        )

        const shouldAddAFAttr = searchId && !redirect && product.productId

        return (
          <div
            data-af-element={shouldAddAFAttr ? 'search-result' : undefined}
            data-af-onclick={shouldAddAFAttr ? true : undefined}
            data-af-search-id={shouldAddAFAttr ? searchId : undefined}
            data-af-product-position={
              shouldAddAFAttr ? absoluteProductIndex : undefined
            }
            data-af-product-id={shouldAddAFAttr ? product.productId : undefined}
            data-af-product-specification={
              shouldAddAFAttr ? product.specification : undefined
            }
            key={product.cacheId}
            style={style}
            className={classNames(
              applyModifiers(handles.galleryItem, [
                displayMode,
                shouldRenderCustom ? 'custom' : '',
              ]),
              'pa4'
            )}
          >
            <GalleryItem
              item={product}
              summary={summary}
              displayMode={displayMode}
              position={absoluteProductIndex}
              listName={listName}
              CustomSummary={shouldRenderCustom ? CustomSummary : undefined}
              preferredSKU={preferredSKU}
            />
          </div>
        )
      })}
    </>
  )
}

export default memo(GalleryRow)

```


[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/AnimatedDiv.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { useSpring, animated } from 'react-spring/web.cjs'

import useMeasure from '../hooks/useMeasure'

const AnimatedDiv = ({ open, containerRef, children }) => {
  const { height } = useMeasure(containerRef)
  const styles = useSpring({ height: open ? height : 0 })

  return (
    <animated.div style={{ overflow: 'hidden', ...styles }}>
      {children}
    </animated.div>
  )
}

export default AnimatedDiv

```


[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/ContextProviders.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'

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

const ContextProviders = ({ children, queryVariables, settings }) => {
  return (
    <QueryContext.Provider value={queryVariables}>
      <SettingsContext.Provider value={settings}>
        {children}
      </SettingsContext.Provider>
    </QueryContext.Provider>
  )
}

export default ContextProviders

```


[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/SelectedFilters.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import PropTypes from 'prop-types'
import React, { useContext } from 'react'
import { useIntl } from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'

import FacetItem from './FacetItem'
import FilterOptionTemplate from './FilterOptionTemplate'
import { facetOptionShape } from '../constants/propTypes'
import SettingsContext from './SettingsContext'

const CSS_HANDLES = ['selectedFilterItem']

/**
 * Search Filter Component.
 */
const SelectedFilters = ({
  map,
  filters = [],
  preventRouteChange = false,
  navigateToFacet,
  onOpenPostalCodeModal,
  onOpenPickupModal,
  showShippingFacet,
}) => {
  const intl = useIntl()
  const handles = useCssHandles(CSS_HANDLES)
  const { showFacetTitle } = useContext(SettingsContext)

  const visibleFilters = filters
    .filter(filter => !filter.hidden)
    .filter(filter => showShippingFacet || filter.key !== 'shipping')

  if (!visibleFilters.length) {
    return null
  }

  const title = intl.formatMessage({ id: 'store/search.selected-filters' })

  return (
    <FilterOptionTemplate
      id="selectedFilters"
      title={title}
      filters={visibleFilters}
      collapsable={false}
      selected
      isSelectedFiltersSection
      onOpenPostalCodeModal={onOpenPostalCodeModal}
      onOpenPickupModal={onOpenPickupModal}
    >
      {facet => {
        return (
          <FacetItem
            map={map}
            key={facet.name}
            showTitle={showFacetTitle}
            facetTitle={facet.title}
            facet={facet}
            className={handles.selectedFilterItem}
            preventRouteChange={preventRouteChange}
            navigateToFacet={navigateToFacet}
            onOpenPostalCodeModal={onOpenPostalCodeModal}
            onOpenPickupModal={onOpenPickupModal}
          />
        )
      }}
    </FilterOptionTemplate>
  )
}

SelectedFilters.propTypes = {
  filterTitle: PropTypes.string,
  /** Legacy search map */
  map: PropTypes.string,
  /** Selected filters. */
  filters: PropTypes.arrayOf(facetOptionShape).isRequired,
  /** Prevent route changes */
  preventRouteChange: PropTypes.bool,
  navigateToFacet: PropTypes.func,
  onOpenPostalCodeModal: PropTypes.func,
  onOpenPickupModal: PropTypes.func,
  showShippingFacet: PropTypes.bool,
}

export default SelectedFilters

```


[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/CategoryFilter.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import classNames from 'classnames'
import React from 'react'
import { IconClose } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import { useFilterNavigator } from './FilterNavigatorContext'
import Collapsible from './Collapsible'
import CategoryItem from './CategoryItem'

const CSS_HANDLES = [
  'categoryGroup',
  'categoryParent',
  'categoryItemName',
  'selectedCategoryIcon',
  'categoryItemChildrenContainer',
  'selectedCategory',
  'selectedCategoryName',
  'selectedCategoryIcon',
]

const getSelectedCategories = rootCategory => {
  let node = rootCategory.children
  const selectedCategories = [rootCategory]

  while (node) {
    const category = node.find(categ => categ.selected)

    if (!category) {
      break
    }

    selectedCategories.push(category)
    node = category.children
  }

  return selectedCategories
}

const CategoryFilter = ({
  category,
  shallow = false,
  onCategorySelect,
  preventRouteChange,
  maxItemsCategory,
  categoryFiltersMode,
}) => {
  const { map } = useFilterNavigator()
  const handles = useCssHandles(CSS_HANDLES)

  const selectedCategories = getSelectedCategories(category)

  const handleUnselectCategories = index => {
    const categoriesToRemove = selectedCategories.slice(index)

    onCategorySelect(categoriesToRemove, preventRouteChange)
  }

  const lastSelectedCategory = selectedCategories[selectedCategories.length - 1]

  const canDisableRoot = map.split(',').indexOf('c') === -1

  const handleRootCategoryClick = () => {
    if (!canDisableRoot) {
      return
    }

    if (shallow) {
      onCategorySelect(category)
    } else {
      // deselect root category
      handleUnselectCategories(0)
    }
  }

  return (
    <div className={classNames(handles.categoryGroup, 'mt4')}>
      <div
        role="button"
        tabIndex={canDisableRoot ? 0 : -1}
        className={classNames(
          handles.categoryParent,
          'flex items-center pointer'
        )}
        onClick={handleRootCategoryClick}
        onKeyDown={e => e.key === 'Enter' && handleRootCategoryClick()}
        data-testid={`root-category-${category.value}`}
      >
        <div className="flex-grow-1 dim">
          <span
            className={classNames(handles.categoryItemName, 'f5 c-on-base')}
          >
            {category.name}
          </span>
        </div>
        {!shallow && canDisableRoot && (
          <span
            className={classNames(
              handles.selectedCategoryIcon,
              'flex items-center c-muted-3'
            )}
          >
            <IconClose size={14} />
          </span>
        )}
      </div>
      <div
        className={classNames(
          handles.categoryItemChildrenContainer,
          'pl5 pl0-ns'
        )}
      >
        {selectedCategories.slice(1).map((subCategory, index) => (
          <span
            key={subCategory.id}
            role="button"
            tabIndex={0}
            className={classNames(
              handles.selectedCategory,
              'mt5 mt4-ns flex items-center justify-between pointer f5 f6-ns'
            )}
            onClick={() => handleUnselectCategories(index + 1)}
            onKeyDown={e =>
              e.key === 'Enter' && handleUnselectCategories(index + 1)
            }
            data-testid={`selected-category-${subCategory.value}`}
          >
            <span className={handles.selectedCategoryName}>
              {subCategory.name}
            </span>
            <span
              className={classNames(
                handles.selectedCategoryIcon,
                'flex items-center c-muted-3'
              )}
            >
              <IconClose size={14} />
            </span>
          </span>
        ))}
        {lastSelectedCategory.children &&
          lastSelectedCategory.children.length > 0 && (
            <div
              className={classNames({
                'mt5 mt4-ns bl b--muted-4': !shallow,
                mt2: shallow,
              })}
            >
              <Collapsible
                items={lastSelectedCategory.children}
                maxItems={maxItemsCategory}
                threshold={2}
                linkClassName="ml3"
                openLabel="store/filter.more-categories"
                render={(childCategory, index) => (
                  <CategoryItem
                    key={childCategory.id}
                    href={childCategory.href}
                    categoryFiltersMode={categoryFiltersMode}
                    className={classNames({
                      mt2: index === 0 && !shallow,
                    })}
                    onClick={() =>
                      onCategorySelect(
                        shallow ? [category, childCategory] : childCategory,
                        preventRouteChange
                      )
                    }
                    label={childCategory.name}
                  />
                )}
              />
            </div>
          )}
      </div>
    </div>
  )
}

export default CategoryFilter

```


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

[Assistant]
Here's the implementation:

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

import SearchFilter from './SearchFilter'
import PriceRange from './PriceRange'

const LAZY_RENDER_THRESHOLD = 3

const AvailableFilters = ({ filters = [], ...props }) => {
  const [lastOpenFilter, setLastOpenFilter] = useState()

  return filters.map((filter, i) => (
    <Filter
      filter={filter}
      {...props}
      lastOpenFilter={lastOpenFilter}
      setLastOpenFilter={setLastOpenFilter}
      key={filter.title}
      lazyRender={i >= LAZY_RENDER_THRESHOLD}
    />
  ))
}

const Filter = ({
  filter,
  priceRange,
  preventRouteChange,
  initiallyCollapsed,
  navigateToFacet,
  lazyRender,
  truncateFilters = false,
  openFiltersMode = 'many',
  lastOpenFilter,
  setLastOpenFilter,
  truncatedFacetsFetched,
  setTruncatedFacetsFetched,
  closeOnOutsideClick,
  appliedFiltersOverview,
  showClearByFilter,
  priceRangeLayout,
  scrollToTop,
  onOpenPostalCodeModal,
  onOpenPickupModal,
}) => {
  const { type, title, facets, quantity, oneSelectedCollapse = false } = filter

  switch (type) {
    case 'PriceRanges':
      return (
        <PriceRange
          key={title}
          title={title}
          facets={facets}
          priceRange={priceRange}
          preventRouteChange={preventRouteChange}
          priceRangeLayout={priceRangeLayout}
          scrollToTop={scrollToTop}
          showClearByFilter={showClearByFilter}
          onChangePriceRange={newPriceRange =>
            navigateToFacet([], preventRouteChange, false, newPriceRange)
          }
        />
      )

    default:
      return (
        <SearchFilter
          key={title}
          title={title}
          facets={facets}
          quantity={quantity}
          oneSelectedCollapse={oneSelectedCollapse}
          preventRouteChange={preventRouteChange}
          initiallyCollapsed={initiallyCollapsed}
          navigateToFacet={navigateToFacet}
          lazyRender={lazyRender}
          truncateFilters={truncateFilters}
          lastOpenFilter={lastOpenFilter}
          setLastOpenFilter={setLastOpenFilter}
          openFiltersMode={openFiltersMode}
          truncatedFacetsFetched={truncatedFacetsFetched}
          setTruncatedFacetsFetched={setTruncatedFacetsFetched}
          closeOnOutsideClick={closeOnOutsideClick}
          appliedFiltersOverview={appliedFiltersOverview}
          showClearByFilter={showClearByFilter}
          onOpenPostalCodeModal={onOpenPostalCodeModal}
          onOpenPickupModal={onOpenPickupModal}
        />
      )
  }
}

AvailableFilters.propTypes = {
  /** Filters to be displayed */
  filters: PropTypes.arrayOf(
    PropTypes.shape({
      title: PropTypes.string,
      type: PropTypes.string,
      oneSelectedCollapse: PropTypes.bool,
    })
  ),
  /** Price range query parameter */
  priceRange: PropTypes.string,
  /** Prevent route changes */
  preventRouteChange: PropTypes.bool,
  /** If filters start collapsed */
  initiallyCollapsed: PropTypes.bool,
  /** If filters start truncated */
  truncateFilters: PropTypes.bool,
}

export default AvailableFilters

```


[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/SidebarCloseButton.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'
import { IconClose } from 'vtex.store-icons'

const CSS_HANDLES = ['closeIconButton']

const SidebarCloseButton = ({ size = 30, type = 'line', onClose }) => {
  const handles = useCssHandles(CSS_HANDLES)

  return (
    <button
      className={`${handles.closeIconButton} pa5 bg-transparent c-on-base bn absolute top-0 right-0 pointer`}
      onClick={onClose}
    >
      <IconClose size={size} type={type} />
    </button>
  )
}

export default SidebarCloseButton

```


[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/SelectionListItem.js from the search-result VTEX app.

[Assistant]
Here's the implementation:

```javascript
import React from 'react'
import { useRuntime } from 'vtex.render-runtime'
import classNames from 'classnames'
import { useCssHandles, applyModifiers } from 'vtex.css-handles'

const CSS_HANDLES = ['orderByOptionItem']

const SelectionListItem = ({ option, onItemClick, selected }) => {
  const { setQuery } = useRuntime()
  const handles = useCssHandles(CSS_HANDLES)

  const handleOptionClick = () => {
    onItemClick()
    setQuery({ order: option.value, page: undefined })
  }

  const highlight = selected ? 'bg-light-gray' : 'hover-bg-muted-5 bg-base'

  return (
    <button
      className={classNames(
        highlight,
        applyModifiers(handles.orderByOptionItem, selected ? 'selected' : ''),
        ' c-on-base f5 ml-auto db no-underline pointer tl bn pv4 ph5 w-100 right-0-ns'
      )}
      key={option.value}
      onClick={handleOptionClick}
    >
      {option.label}
    </button>
  )
}

export default SelectionListItem

```