Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Price range filter on PLP #112

Merged
merged 6 commits into from
Jun 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 56 additions & 43 deletions @generated/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2372,23 +2372,34 @@ export type StoreCurrency = {
symbol: Scalars['String']
}

/** Search facet information. */
export type StoreFacet = {
export type StoreFacet = StoreFacetBoolean | StoreFacetRange

/** Search facet boolean information. */
export type StoreFacetBoolean = {
/** Facet key. */
key: Scalars['String']
/** Facet label. */
label: Scalars['String']
/** Array with information on each facet value. */
values: Array<StoreFacetValueBoolean>
}

/** Search facet range information. */
export type StoreFacetRange = {
/** Facet key. */
key: Scalars['String']
/** Facet label. */
label: Scalars['String']
/** Facet type. Possible values are `BOOLEAN` and `RANGE`. */
type: StoreFacetType
max: StoreFacetValueRange
/** Array with information on each facet value. */
values: Array<StoreFacetValue>
min: StoreFacetValueRange
}

/** Search facet type. */
export type StoreFacetType = 'BOOLEAN' | 'RANGE'

/** Information of a specific facet value. */
export type StoreFacetValue = {
export type StoreFacetValueBoolean = {
/** Facet value label. */
label: Scalars['String']
/** Number of items with this facet. */
Expand All @@ -2399,6 +2410,11 @@ export type StoreFacetValue = {
value: Scalars['String']
}

export type StoreFacetValueRange = {
absolute: Scalars['Float']
selected: Scalars['Float']
}

/** Image. */
export type StoreImage = {
/** Alias for the image. */
Expand Down Expand Up @@ -2669,10 +2685,10 @@ export type ProductSummary_ProductFragment = {
}
}

export type Filter_FacetsFragment = {
export type Filter_Facets_StoreFacetBoolean_Fragment = {
__typename: 'StoreFacetBoolean'
key: string
label: string
type: StoreFacetType
values: Array<{
label: string
value: string
Expand All @@ -2681,6 +2697,18 @@ export type Filter_FacetsFragment = {
}>
}

export type Filter_Facets_StoreFacetRange_Fragment = {
__typename: 'StoreFacetRange'
key: string
label: string
min: { selected: number; absolute: number }
max: { selected: number; absolute: number }
}

export type Filter_FacetsFragment =
| Filter_Facets_StoreFacetBoolean_Fragment
| Filter_Facets_StoreFacetRange_Fragment

export type ProductDetailsFragment_ProductFragment = {
sku: string
name: string
Expand Down Expand Up @@ -2720,42 +2748,27 @@ export type ProductGalleryQueryQueryVariables = Exact<{

export type ProductGalleryQueryQuery = {
search: {
products: {
pageInfo: { totalCount: number }
edges: Array<{
node: {
slug: string
sku: string
name: string
gtin: string
id: string
brand: { name: string; brandName: string }
isVariantOf: { productGroupID: string; name: string }
image: Array<{ url: string; alternateName: string }>
offers: {
lowPrice: number
offers: Array<{
availability: string
price: number
listPrice: number
quantity: number
seller: { identifier: string }
}>
}
products: { pageInfo: { totalCount: number } }
facets: Array<
| {
__typename: 'StoreFacetBoolean'
key: string
label: string
values: Array<{
label: string
value: string
selected: boolean
quantity: number
}>
}
}>
}
facets: Array<{
key: string
label: string
type: StoreFacetType
values: Array<{
label: string
value: string
selected: boolean
quantity: number
}>
}>
| {
__typename: 'StoreFacetRange'
key: string
label: string
min: { selected: number; absolute: number }
max: { selected: number; absolute: number }
}
>
}
}

Expand Down
2 changes: 1 addition & 1 deletion @generated/graphql/persisted.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"ProductGalleryQuery": "query ProductGalleryQuery($first: Int!, $after: String!, $sort: StoreSort!, $term: String!, $selectedFacets: [IStoreSelectedFacet!]!) {\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n id: productID\n slug\n sku\n brand {\n brandName: name\n name\n }\n name\n gtin\n isVariantOf {\n productGroupID\n name\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n quantity\n seller {\n identifier\n }\n }\n }\n }\n }\n }\n facets {\n key\n label\n type\n values {\n label\n value\n selected\n quantity\n }\n }\n }\n}\n",
"ProductGalleryQuery": "query ProductGalleryQuery($first: Int!, $after: String!, $sort: StoreSort!, $term: String!, $selectedFacets: [IStoreSelectedFacet!]!) {\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ... on StoreFacetRange {\n key\n label\n min {\n selected\n absolute\n }\n max {\n selected\n absolute\n }\n __typename\n }\n ... on StoreFacetBoolean {\n key\n label\n values {\n label\n value\n selected\n quantity\n }\n __typename\n }\n }\n }\n}\n",
"CollectionPageQuery": "query CollectionPageQuery {\n site {\n siteMetadata {\n titleTemplate\n title\n description\n siteUrl\n }\n }\n}\n",
"ServerCollectionPageQuery": "query ServerCollectionPageQuery($slug: String!) {\n collection(slug: $slug) {\n seo {\n title\n description\n }\n breadcrumbList {\n itemListElement {\n item\n name\n position\n }\n }\n meta {\n selectedFacets {\n key\n value\n }\n }\n }\n}\n",
"ProductPageQuery": "query ProductPageQuery {\n site {\n siteMetadata {\n title\n description\n titleTemplate\n siteUrl\n }\n }\n}\n",
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- PriceRange component to PLP and StoryBook ([#112](https://github.com/vtex-sites/gatsby.store/pull/112))
- Applies new local tokens to `EmptyState` ([#113](https://github.com/vtex-sites/gatsby.store/pull/113))
- Applies new local tokens to `Dropdown` ([#111](https://github.com/vtex-sites/gatsby.store/pull/111))
- Creates and styles `Dropdown` component ([#111](https://github.com/vtex-sites/gatsby.store/pull/111))
Expand Down
1 change: 1 addition & 0 deletions codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ generates:
skipTypename: true
allowEnumStringTypes: false
namingConvention: 'change-case-all#pascalCase'
exportFragmentSpreadSubTypes: true
plugins:
- typescript
- typescript-operations
Expand Down
4 changes: 3 additions & 1 deletion cypress/integration/plp.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ describe('Search page Filters and Sorting options', () => {
// Apply filters
cy.getById('open-filter-button')
.click()
.getById('mobile-store-filter-accordion-button')
.get(
`[data-testid=mobile-store-filter-accordion-item][data-type=StoreFacetBoolean]>[data-testid=mobile-store-filter-accordion-button]`
)
.first()
.click()
.getById('mobile-store-filter-accordion-panel-checkbox')
Expand Down
3 changes: 1 addition & 2 deletions gatsby-ssr.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { CartProvider, SessionProvider } from '@faststore/sdk'
import { validateSession } from 'src/sdk/session/validate'
import UIProvider from 'src/sdk/ui/Provider'
import type { ReactNode } from 'react'
import type { GatsbySSR } from 'gatsby'

import ThirdPartyScripts from './src/components/ThirdPartyScripts'
Expand Down Expand Up @@ -54,7 +53,7 @@ type StyleComponent = {
}
}

const isStyleComponent = (node: ReactNode): node is StyleComponent =>
const isStyleComponent = (node: any): node is StyleComponent =>
typeof node === 'object' && node != null && (node as any).type === 'style'

/**
Expand Down
17 changes: 8 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@
"dependencies": {
"@builder.io/partytown": "^0.6.1",
"@envelop/core": "^1.2.0",
"@envelop/graphql-jit": "^1.1.1",
"@envelop/parser-cache": "^2.2.0",
"@envelop/validation-cache": "^2.2.0",
"@faststore/api": "^1.9.7",
"@faststore/graphql-utils": "^1.9.4",
"@faststore/sdk": "^1.9.4",
"@faststore/ui": "^1.9.4",
"@faststore/api": "^1.9.11",
"@faststore/graphql-utils": "^1.9.9",
"@faststore/sdk": "^1.9.11",
"@faststore/ui": "^1.9.11",
"gatsby": "^4.16.0",
"gatsby-plugin-gatsby-cloud": "^4.16.0",
"gatsby-plugin-manifest": "^4.16.0",
Expand All @@ -55,10 +54,10 @@
"devDependencies": {
"@babel/core": "^7.17.8",
"@cypress/code-coverage": "^3.9.10",
"@faststore/lighthouse": "^1.9.4",
"@graphql-codegen/cli": "^2.2.1",
"@graphql-codegen/typescript": "^2.2.4",
"@graphql-codegen/typescript-operations": "^2.1.8",
"@faststore/lighthouse": "^1.9.10",
"@graphql-codegen/cli": "^2.6.2",
"@graphql-codegen/typescript": "^2.5.1",
"@graphql-codegen/typescript-operations": "^2.4.2",
"@lhci/cli": "^0.9.0",
"@netlify/plugin-gatsby": "^2.0.0-beta",
"@storybook/addon-a11y": "^6.5.9",
Expand Down
99 changes: 68 additions & 31 deletions src/components/search/Filter/Facets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@ import type {
IStoreSelectedFacet,
Filter_FacetsFragment,
} from '@generated/graphql'
import PriceRange from 'src/components/ui/PriceRange'

type OnFacetChange = (
item: IStoreSelectedFacet,
type: 'BOOLEAN' | 'RANGE'
) => void

interface FacetsProps {
testId: string
facets: Filter_FacetsFragment[]
indicesExpanded: Set<number>
onFacetChange: (item: IStoreSelectedFacet) => void
onFacetChange: OnFacetChange
onAccordionChange: (index: number) => void
}

const formatRange = (min: number, max: number) =>
`${min.toFixed(2)}-to-${max.toFixed(2)}`

function Facets({
testId,
facets,
Expand All @@ -26,37 +35,65 @@ function Facets({
<div className="filter" data-store-filter data-testid={testId}>
<h2 className="text__title-mini-alt">Filters</h2>
<Accordion expandedIndices={indicesExpanded} onChange={onAccordionChange}>
{facets.map(({ label, values, key }, index) => (
<AccordionItem
key={`${label}-${index}`}
prefixId={testId}
testId={`${testId}-accordion`}
isExpanded={indicesExpanded.has(index)}
buttonLabel={label}
>
<UIList>
{values.map((item) => {
const id = `${testId}-${label}-${item.label}`
{facets.map((facet, index) => {
const isExpanded = indicesExpanded.has(index)
const { __typename: type, label } = facet

return (
<AccordionItem
key={`${label}-${index}`}
prefixId={testId}
testId={`${testId}-accordion`}
isExpanded={isExpanded}
buttonLabel={label}
data-type={type}
>
{type === 'StoreFacetBoolean' && isExpanded && (
<UIList>
{facet.values.map((item) => {
const id = `${testId}-${facet.label}-${item.label}`

return (
<li key={id} className="filter__item">
<Checkbox
id={id}
checked={item.selected}
onChange={() => onFacetChange({ key, value: item.value })}
data-testid={`${testId}-accordion-panel-checkbox`}
data-value={item.value}
data-quantity={item.quantity}
/>
<UILabel htmlFor={id} className="text__title-mini-alt">
{item.label} <Badge>{item.quantity}</Badge>
</UILabel>
</li>
)
})}
</UIList>
</AccordionItem>
))}
return (
<li key={id} className="filter__item">
<Checkbox
id={id}
checked={item.selected}
onChange={() =>
onFacetChange(
{ key: facet.key, value: item.value },
'BOOLEAN'
)
}
data-testid={`${testId}-accordion-panel-checkbox`}
data-value={item.value}
data-quantity={item.quantity}
/>
<UILabel htmlFor={id} className="text__title-mini-alt">
{item.label} <Badge>{item.quantity}</Badge>
</UILabel>
</li>
)
})}
</UIList>
)}
{type === 'StoreFacetRange' && isExpanded && (
<PriceRange
min={facet.min}
max={facet.max}
onEnd={(v) =>
onFacetChange(
{
key: facet.key,
value: formatRange(v.min, v.max),
},
'RANGE'
)
}
/>
)}
</AccordionItem>
)
})}
</Accordion>
</div>
)
Expand Down
Loading