• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

decentraland / marketplace / 6468133945

10 Oct 2023 10:36AM UTC coverage: 40.778% (+0.7%) from 40.103%
6468133945

Pull #2030

github

juanmahidalgo
test: add Test for SearchBarDropdown
Pull Request #2030: Feat: new search bar component

2260 of 6951 branches covered (0.0%)

Branch coverage included in aggregate %.

215 of 215 new or added lines in 20 files covered. (100.0%)

4280 of 9087 relevant lines covered (47.1%)

18.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

26.85
/webapp/src/components/AssetFilters/AssetFilters.tsx
1
import { useCallback, useMemo } from 'react'
2
import {
3
  EmotePlayMode,
4
  GenderFilterOption,
5
  Network,
6
  Rarity,
7
  WearableGender
8
} from '@dcl/schemas'
9
import { getSectionFromCategory } from '../../modules/routing/search'
10
import { isLandSection } from '../../modules/ui/utils'
11
import { AssetStatusFilter } from '../../utils/filters'
12
import { View } from '../../modules/ui/types'
13
import { Sections, SortBy, BrowseOptions } from '../../modules/routing/types'
14
import { LANDFilters } from '../Vendor/decentraland/types'
15
import { Menu } from '../Menu'
16
import PriceFilter from './PriceFilter'
17
import EstateSizeFilter from './EstateSizeFilter'
18
import CreatorsFilter from './CreatorsFilter'
19
import { RarityFilter } from './RarityFilter'
20
import { OnlySmartFilter } from './OnlySmartFilter'
21
import { NetworkFilter } from './NetworkFilter'
22
import { Props } from './AssetFilters.types'
23
import { CollectionFilter } from './CollectionFilter'
24
import { LandStatusFilter } from './LandStatusFilter'
25
import { BodyShapeFilter } from './BodyShapeFilter'
26
import { RentalPeriodFilter } from './RentalPeriodFilter'
27
import { MoreFilters } from './MoreFilters'
28
import { EmoteAttributesFilter } from './EmoteAttributesFilter'
29
import { LocationFilter } from './LocationFilter'
30
import {
31
  AssetFilter,
32
  filtersBySection,
33
  trackBarChartComponentChange
34
} from './utils'
35
import { StatusFilter } from './StatusFilter'
36
import './AssetFilters.css'
37

38
export const AssetFilters = ({
2✔
39
  minPrice,
40
  maxPrice,
41
  minEstateSize,
42
  maxEstateSize,
43
  collection,
44
  creators,
45
  rarities,
46
  status,
47
  network,
48
  category,
49
  bodyShapes,
50
  isOnlySmart,
51
  isOnSale,
52
  emotePlayMode,
53
  section,
54
  landStatus,
55
  defaultCollapsed,
56
  onBrowse,
57
  view,
58
  minDistanceToPlaza,
59
  maxDistanceToPlaza,
60
  adjacentToRoad,
61
  values,
62
  rentalDays,
63
  emoteHasSound,
64
  emoteHasGeometry,
65
  isPriceFilterEnabled,
66
  isEstateSizeFilterEnabled,
67
  isLocationFilterEnabled,
68
  isCreatorFiltersEnabled,
69
  isRentalPeriodFilterEnabled
70
}: Props): JSX.Element | null => {
71
  const isInLandSection = isLandSection(section)
5✔
72

73
  const handleBrowseParamChange = useCallback(
5✔
74
    (options: BrowseOptions) => onBrowse(options),
×
75
    [onBrowse]
76
  )
77

78
  const handleRangeFilterChange = useCallback(
5✔
79
    (
80
      filterNames: [string, string],
81
      value: [string, string],
82
      source,
83
      prevValues: [string, string]
84
    ) => {
85
      const [filterMinName, filterMaxName] = filterNames
×
86
      const [minValue, maxValue] = value
×
87
      onBrowse({ [filterMinName]: minValue, [filterMaxName]: maxValue })
×
88
      trackBarChartComponentChange(filterNames, value, source, prevValues)
×
89
    },
90
    [onBrowse]
91
  )
92

93
  const handleRarityChange = useCallback(
5✔
94
    (value: Rarity[]) => {
95
      onBrowse({ rarities: value })
×
96
    },
97
    [onBrowse]
98
  )
99

100
  const handleNetworkChange = useCallback(
5✔
101
    (value: Network) => {
102
      onBrowse({ network: value })
×
103
    },
104
    [onBrowse]
105
  )
106

107
  const handleBodyShapeChange = useCallback(
5✔
108
    (value: (WearableGender | GenderFilterOption)[]) => {
109
      onBrowse({ wearableGenders: value })
×
110
    },
111
    [onBrowse]
112
  )
113

114
  const handleOnlySmartChange = useCallback(
5✔
115
    (value: boolean) => {
116
      onBrowse({ onlySmart: value })
×
117
    },
118
    [onBrowse]
119
  )
120

121
  const handleOnSaleChange = useCallback(
5✔
122
    (value: boolean) => {
123
      // when toggling off the on sale filter, we need to reset the sortBy to avoid invalid combinations with the on sale sort options
124
      onBrowse(
×
125
        value
×
126
          ? { onlyOnSale: value }
127
          : { onlyOnSale: value, sortBy: SortBy.NEWEST }
128
      )
129
    },
130
    [onBrowse]
131
  )
132

133
  const handleEmoteAttributesChange = useCallback(
5✔
134
    (value: {
135
      emotePlayMode?: EmotePlayMode[]
136
      emoteHasSound?: boolean
137
      emoteHasGeometry?: boolean
138
    }) => {
139
      onBrowse({
×
140
        emotePlayMode: value.emotePlayMode,
141
        emoteHasSound: value.emoteHasSound,
142
        emoteHasGeometry: value.emoteHasGeometry
143
      })
144
    },
145
    [onBrowse]
146
  )
147

148
  const handleRentalDaysChange = useCallback(
5✔
149
    (value: number[]) => {
150
      onBrowse({ rentalDays: value })
×
151
    },
152
    [onBrowse]
153
  )
154

155
  const handleAdjacentToRoadChange = useCallback(
5✔
156
    (value?: boolean) => {
157
      onBrowse({ adjacentToRoad: value })
×
158
    },
159
    [onBrowse]
160
  )
161

162
  const handleDistanceToPlazaChange = useCallback(
5✔
163
    (distanceToPlazaRange?: [string, string]) => {
164
      if (distanceToPlazaRange) {
×
165
        const [minDistanceToPlaza, maxDistanceToPlaza] = distanceToPlazaRange
×
166
        onBrowse({ minDistanceToPlaza, maxDistanceToPlaza })
×
167
      }
168
    },
169
    [onBrowse]
170
  )
171

172
  function handleCollectionChange(value: string | undefined) {
173
    const newValue = value ? [value] : []
×
174
    onBrowse({ contracts: newValue })
×
175
  }
176

177
  function handleCreatorsChange(value: string[] | undefined) {
178
    onBrowse({ creators: value })
×
179
  }
180

181
  function handleLandStatusChange(value: LANDFilters) {
182
    switch (value) {
×
183
      case LANDFilters.ALL_LAND:
184
        onBrowse({ onlyOnSale: undefined, onlyOnRent: undefined })
×
185
        break
×
186
      case LANDFilters.ONLY_FOR_RENT:
187
        onBrowse({ onlyOnSale: undefined, onlyOnRent: true })
×
188
        break
×
189
      case LANDFilters.ONLY_FOR_SALE:
190
        onBrowse({ onlyOnSale: true, onlyOnRent: undefined })
×
191
        break
×
192
    }
193
  }
194

195
  const locationFilters = useMemo(
5✔
196
    () => ({
5✔
197
      adjacentToRoad,
198
      minDistanceToPlaza,
199
      maxDistanceToPlaza
200
    }),
201
    [adjacentToRoad, maxDistanceToPlaza, minDistanceToPlaza]
202
  )
203

204
  const shouldRenderFilter = useCallback(
5✔
205
    (filter: AssetFilter) => {
206
      // /lands page won't have any category, we fallback to the section, that will be Section.LAND
207
      const parentSection = category
×
208
        ? getSectionFromCategory(category)
209
        : section
210
      return filtersBySection[parentSection!]?.includes(filter)
×
211
    },
212
    [category, section]
213
  )
214

215
  if (isInLandSection) {
5!
216
    return (
5✔
217
      <div className="filters-sidebar">
218
        <LandStatusFilter
219
          landStatus={landStatus}
220
          onChange={handleLandStatusChange}
221
        />
222
        {isPriceFilterEnabled ? (
5✔
223
          <PriceFilter
224
            onChange={(value, source) =>
225
              handleRangeFilterChange(['minPrice', 'maxPrice'], value, source, [
×
226
                minPrice,
227
                maxPrice
228
              ])
229
            }
230
            minPrice={minPrice}
231
            maxPrice={maxPrice}
232
            values={values}
233
          />
234
        ) : null}
235

236
        {isEstateSizeFilterEnabled &&
14✔
237
        section !== Sections.decentraland.PARCELS ? (
238
          <EstateSizeFilter
239
            landStatus={landStatus}
240
            values={values}
241
            min={minEstateSize}
242
            max={maxEstateSize}
243
            minPrice={minPrice}
244
            maxPrice={maxPrice}
245
            onChange={(values, source) =>
246
              handleRangeFilterChange(
×
247
                ['minEstateSize', 'maxEstateSize'],
248
                values,
249
                source,
250
                [minEstateSize, maxEstateSize]
251
              )
252
            }
253
            {...locationFilters}
254
          />
255
        ) : null}
256
        {isRentalPeriodFilterEnabled &&
10!
257
          landStatus === LANDFilters.ONLY_FOR_RENT && (
258
            <RentalPeriodFilter
259
              rentalDays={rentalDays}
260
              onChange={handleRentalDaysChange}
261
            />
262
          )}
263
        {isLocationFilterEnabled && (
10✔
264
          <LocationFilter
265
            {...locationFilters}
266
            onAdjacentToRoadChange={handleAdjacentToRoadChange}
267
            onDistanceToPlazaChange={handleDistanceToPlazaChange}
268
          />
269
        )}
270
      </div>
271
    )
272
  }
273

274
  return (
×
275
    <Menu className="filters-sidebar">
276
      {shouldRenderFilter(AssetFilter.PlayMode) && (
×
277
        <EmoteAttributesFilter
278
          onChange={handleEmoteAttributesChange}
279
          emotePlayMode={emotePlayMode}
280
          defaultCollapsed={!!defaultCollapsed?.[AssetFilter.PlayMode]}
281
          emoteHasSound={emoteHasSound}
282
          emoteHasGeometry={emoteHasGeometry}
283
        />
284
      )}
285
      {shouldRenderFilter(AssetFilter.OnlySmart) ? (
×
286
        <OnlySmartFilter
287
          isOnlySmart={isOnlySmart}
288
          onChange={handleOnlySmartChange}
289
        />
290
      ) : null}
291
      {shouldRenderFilter(AssetFilter.Rarity) ? (
×
292
        <RarityFilter
293
          onChange={handleRarityChange}
294
          rarities={rarities}
295
          defaultCollapsed={!!defaultCollapsed?.[AssetFilter.Network]}
296
        />
297
      ) : null}
298
      {shouldRenderFilter(AssetFilter.Status) && view === View.MARKET ? (
×
299
        <StatusFilter
300
          onChange={handleBrowseParamChange}
301
          status={status}
302
          defaultCollapsed={!!defaultCollapsed?.[AssetFilter.Status]}
303
        />
304
      ) : null}
305
      {isPriceFilterEnabled &&
×
306
      shouldRenderFilter(AssetFilter.Price) &&
307
      (isOnSale || (!!status && status !== AssetStatusFilter.NOT_FOR_SALE)) &&
308
      view !== View.ACCOUNT ? (
309
        <PriceFilter
310
          onChange={(value, source) =>
311
            handleRangeFilterChange(['minPrice', 'maxPrice'], value, source, [
×
312
              minPrice,
313
              maxPrice
314
            ])
315
          }
316
          minPrice={minPrice}
317
          maxPrice={maxPrice}
318
          defaultCollapsed={!!defaultCollapsed?.[AssetFilter.Price]}
319
          values={values}
320
        />
321
      ) : null}
322
      {isCreatorFiltersEnabled &&
×
323
      shouldRenderFilter(AssetFilter.Creators) &&
324
      (!network || (network && network === Network.MATIC)) ? (
325
        <CreatorsFilter
326
          creators={creators}
327
          onChange={handleCreatorsChange}
328
          defaultCollapsed={!!defaultCollapsed?.[AssetFilter.Creators]}
329
        />
330
      ) : null}
331
      {shouldRenderFilter(AssetFilter.Collection) ? (
×
332
        <CollectionFilter
333
          onChange={handleCollectionChange}
334
          collection={collection}
335
          onlyOnSale={isOnSale}
336
          defaultCollapsed={!!defaultCollapsed?.[AssetFilter.Collection]}
337
        />
338
      ) : null}
339
      {shouldRenderFilter(AssetFilter.Network) &&
×
340
        status !== AssetStatusFilter.ONLY_MINTING && (
341
          <NetworkFilter
342
            onChange={handleNetworkChange}
343
            network={network}
344
            defaultCollapsed={!!defaultCollapsed?.[AssetFilter.Network]}
345
          />
346
        )}
347
      {shouldRenderFilter(AssetFilter.BodyShape) && (
×
348
        <BodyShapeFilter
349
          onChange={handleBodyShapeChange}
350
          bodyShapes={bodyShapes}
351
          defaultCollapsed={!!defaultCollapsed?.[AssetFilter.BodyShape]}
352
        />
353
      )}
354
      {shouldRenderFilter(AssetFilter.More) && (
×
355
        <MoreFilters
356
          category={category}
357
          isOnSale={isOnSale}
358
          isOnlySmart={isOnlySmart}
359
          onSaleChange={handleOnSaleChange}
360
          onOnlySmartChange={handleOnlySmartChange}
361
          defaultCollapsed={!!defaultCollapsed?.[AssetFilter.More]}
362
        />
363
      )}
364
    </Menu>
365
  )
366
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc