Skip to content

Commit

Permalink
feat: add distance to plaza and adjacent to road filter (decentraland…
Browse files Browse the repository at this point in the history
  • Loading branch information
Melisa Anabella Rossi authored Mar 1, 2023
1 parent f794d37 commit d279ae5
Show file tree
Hide file tree
Showing 26 changed files with 1,144 additions and 882 deletions.
1,472 changes: 621 additions & 851 deletions webapp/package-lock.json

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"decentraland-crypto-fetch": "^1.0.3",
"decentraland-dapps": "^13.35.0",
"decentraland-transactions": "^1.42.0",
"decentraland-ui": "^3.79.2",
"decentraland-ui": "^3.80.1",
"dotenv": "^10.0.0",
"ethers": "^5.6.8",
"graphql": "^14.7.0",
Expand All @@ -37,9 +37,10 @@
"typesafe-actions": "^5.1.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/react-hooks": "^7.0.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.4",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^14.4.3",
"@typechain/ethers-v5": "^10.0.0",
"@types/history": "^4.7.3",
"@types/jest": "^24.9.1",
Expand Down
8 changes: 8 additions & 0 deletions webapp/src/components/AssetFilters/AssetFilters.container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import { Dispatch } from 'redux'
import { RootState } from '../../modules/reducer'
import { getCategoryFromSection } from '../../modules/routing/search'
import {
getAdjacentToRoad,
getAssetType,
getContracts,
getCreators,
getEmotePlayMode,
getMaxDistanceToPlaza,
getMaxEstateSize,
getMaxPrice,
getMinDistanceToPlaza,
getMinEstateSize,
getMinPrice,
getNetwork,
Expand All @@ -22,6 +25,7 @@ import {
import {
getIsCreatorsFilterEnabled,
getIsEstateSizeFilterEnabled,
getIsLocationFilterEnabled,
getIsPriceFilterEnabled
} from '../../modules/features/selectors'
import { LANDFilters } from '../Vendor/decentraland/types'
Expand Down Expand Up @@ -81,6 +85,10 @@ const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => {
section,
isPriceFilterEnabled: getIsPriceFilterEnabled(state),
isEstateSizeFilterEnabled: getIsEstateSizeFilterEnabled(state),
isLocationFilterEnabled: getIsLocationFilterEnabled(state),
minDistanceToPlaza: 'minDistanceToPlaza' in values ? values.minDistanceToPlaza : getMinDistanceToPlaza(state),
maxDistanceToPlaza: 'maxDistanceToPlaza' in values ? values.maxDistanceToPlaza : getMaxDistanceToPlaza(state),
adjacentToRoad: 'adjacentToRoad' in values ? values.adjacentToRoad : getAdjacentToRoad(state),
isCreatorFiltersEnabled: getIsCreatorsFilterEnabled(state)
}
}
Expand Down
31 changes: 31 additions & 0 deletions webapp/src/components/AssetFilters/AssetFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { LandStatusFilter } from './LandStatusFilter'
import { BodyShapeFilter } from './BodyShapeFilter'
import { MoreFilters } from './MoreFilters'
import { EmotePlayModeFilter } from './EmotePlayModeFilter'
import { LocationFilter } from './LocationFilter'
import { AssetFilter, filtersBySection } from './utils'
import './AssetFilters.css'

Expand All @@ -49,6 +50,10 @@ export const AssetFilters = ({
isPriceFilterEnabled,
view,
isEstateSizeFilterEnabled,
isLocationFilterEnabled,
minDistanceToPlaza,
maxDistanceToPlaza,
adjacentToRoad,
isCreatorFiltersEnabled,
values
}: Props): JSX.Element | null => {
Expand Down Expand Up @@ -114,6 +119,23 @@ export const AssetFilters = ({
[onBrowse]
)

const handleAdjacentToRoadChange = useCallback(
(value?: boolean) => {
onBrowse({ adjacentToRoad: value })
},
[onBrowse]
)

const handleDistanceToPlazaChange = useCallback(
(distanceToPlazaRange?: [string, string]) => {
if (distanceToPlazaRange) {
const [minDistanceToPlaza, maxDistanceToPlaza] = distanceToPlazaRange
onBrowse({ minDistanceToPlaza, maxDistanceToPlaza })
}
},
[onBrowse]
)

function handleCollectionChange(value: string | undefined) {
const newValue = value ? [value] : []
onBrowse({ contracts: newValue })
Expand Down Expand Up @@ -178,6 +200,15 @@ export const AssetFilters = ({
}
/>
) : null}
{isLocationFilterEnabled && (
<LocationFilter
minDistanceToPlaza={minDistanceToPlaza}
maxDistanceToPlaza={maxDistanceToPlaza}
adjacentToRoad={adjacentToRoad}
onAdjacentToRoadChange={handleAdjacentToRoadChange}
onDistanceToPlazaChange={handleDistanceToPlazaChange}
/>
)}
</div>
)
}
Expand Down
12 changes: 10 additions & 2 deletions webapp/src/components/AssetFilters/AssetFilters.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ export type Props = {
values?: BrowseOptions
vendor?: VendorName
view?: View
onBrowse: (options: BrowseOptions) => void
onFilterChange?: (options: BrowseOptions) => void
defaultCollapsed?: Record<AssetFilter, boolean>
isPriceFilterEnabled: boolean
isEstateSizeFilterEnabled: boolean
isLocationFilterEnabled: boolean
minDistanceToPlaza?: string
maxDistanceToPlaza?: string
adjacentToRoad?: boolean
onBrowse: (options: BrowseOptions) => void
onFilterChange?: (options: BrowseOptions) => void
isCreatorFiltersEnabled: boolean
}

Expand All @@ -64,6 +68,10 @@ export type MapStateProps = Pick<
| 'view'
| 'isPriceFilterEnabled'
| 'isEstateSizeFilterEnabled'
| 'isLocationFilterEnabled'
| 'minDistanceToPlaza'
| 'maxDistanceToPlaza'
| 'adjacentToRoad'
| 'isCreatorFiltersEnabled'
>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.checkboxFilter {
margin-bottom: 25px;
}

.slider {
margin-top: 20px;
}

.slider p {
color: var(--secondary-text);
}

.locationContainer {
display: flex;
flex-direction: column;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { render, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import { LocationFilter, LocationFilterProps } from './LocationFilter'

function renderLocationFilter(props: Partial<LocationFilterProps> = {}) {
return render(
<LocationFilter
adjacentToRoad={false}
onAdjacentToRoadChange={jest.fn()}
onDistanceToPlazaChange={jest.fn()}
{...props}
/>
)
}
describe('LocationFilter', () => {
test('should render adjacent to road toggle', () => {
const { getByRole } = renderLocationFilter()
expect(
getByRole('checkbox', { name: t('nft_filters.adjacent_to_road') })
).toBeInTheDocument()
})

test('should render near a plaza toggle', () => {
const { getByRole } = renderLocationFilter()
expect(
getByRole('checkbox', { name: t('nft_filters.distance_to_plaza.title') })
).toBeInTheDocument()
})

test('should call onAdjacentToRoadChange callback when toggle changes', async () => {
const onAdjacentToRoadChangeMock = jest.fn()
const { getByTestId } = renderLocationFilter({
onAdjacentToRoadChange: onAdjacentToRoadChangeMock
})
await userEvent.click(getByTestId('adjacent-to-road-toggle'))
expect(onAdjacentToRoadChangeMock).toHaveBeenCalledWith(true)
})

test('should call oDistanceToPlazaChange callback when toggle changes', async () => {
const onDistanceToPlazaChangeMock = jest.fn()
const { getByTestId } = renderLocationFilter({
onDistanceToPlazaChange: onDistanceToPlazaChangeMock
})
await userEvent.click(getByTestId('near-to-plaza-toggle'))
expect(onDistanceToPlazaChangeMock).toHaveBeenCalledWith(['2', '10'])
})

test('should show distance slider when minDistanceToPlaza and maxDistanceToPlaza are defined', () => {
const { getByRole } = renderLocationFilter({
minDistanceToPlaza: '3',
maxDistanceToPlaza: '5'
})
expect(getByRole('slider', { name: 'min value' })).toBeInTheDocument()
expect(getByRole('slider', { name: 'max value' })).toBeInTheDocument()
})
})
143 changes: 143 additions & 0 deletions webapp/src/components/AssetFilters/LocationFilter/LocationFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { useCallback, useMemo } from 'react'
import {
Box,
CheckboxProps,
Checkbox,
useTabletAndBelowMediaQuery,
SliderField
} from 'decentraland-ui'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import styles from './LocationFilter.module.css'
import classNames from 'classnames'

const DISTANCE_TO_PLAZA_MIN = 2;
const DISTANCE_TO_PLAZA_MAX = 10;

export type LocationFilterProps = {
adjacentToRoad?: boolean
minDistanceToPlaza?: string
maxDistanceToPlaza?: string
defaultCollapsed?: boolean
onAdjacentToRoadChange: (value?: boolean) => void
onDistanceToPlazaChange: (value: [string, string]) => void
}

export const LocationFilter = ({
adjacentToRoad,
minDistanceToPlaza,
maxDistanceToPlaza,
defaultCollapsed = false,
onAdjacentToRoadChange,
onDistanceToPlazaChange
}: LocationFilterProps): JSX.Element => {
const isMobileOrTablet = useTabletAndBelowMediaQuery()

const handleAdjacentToRoadChange = useCallback(
(_evt, props: CheckboxProps) => {
onAdjacentToRoadChange(!!props.checked || undefined)
},
[onAdjacentToRoadChange]
)

const handleDistanceToPlazaChange = useCallback(
(_evt, data) => {
onDistanceToPlazaChange(data)
},
[onDistanceToPlazaChange]
)

const handleToggleDistanceFilter = useCallback(
(_evt, props: CheckboxProps) => {
if (!!props.checked) {
onDistanceToPlazaChange([DISTANCE_TO_PLAZA_MIN.toString(), DISTANCE_TO_PLAZA_MAX.toString()])
} else {
onDistanceToPlazaChange(['', ''])
}
},
[onDistanceToPlazaChange]
)

const locationSelectionText = useMemo(() => {
if (!adjacentToRoad && !minDistanceToPlaza && !maxDistanceToPlaza) {
return t('nft_filters.all_locations')
}

let locationTexts = []
if (adjacentToRoad) {
locationTexts.push(t('nft_filters.adjacent_to_road'))
}

if (minDistanceToPlaza || maxDistanceToPlaza) {
locationTexts.push(
t('nft_filters.distance_to_plaza.title')
)
}

return locationTexts.join(', ')
}, [minDistanceToPlaza, maxDistanceToPlaza, adjacentToRoad])

const header = useMemo(
() =>
isMobileOrTablet ? (
<div className="mobile-box-header">
<span className="box-filter-name">{t('nft_filters.location')}</span>
<span className="box-filter-value">{locationSelectionText}</span>
</div>
) : (
t('nft_filters.location')
),
[isMobileOrTablet, locationSelectionText]
)

const distanceFilterIsOn = !!(minDistanceToPlaza || maxDistanceToPlaza)

return (
<Box
header={header}
className={classNames("filters-sidebar-box", styles.locationContainer)}
collapsible
defaultCollapsed={defaultCollapsed || isMobileOrTablet}
>
<Checkbox
data-testid="adjacent-to-road-toggle"
id="adjacent-to-road-toggle"
label={t('nft_filters.adjacent_to_road')}
toggle
checked={adjacentToRoad}
onChange={handleAdjacentToRoadChange}
className={styles.checkboxFilter}
/>
<Checkbox
data-testid="near-to-plaza-toggle"
id="near-to-plaza-toggle"
label={t('nft_filters.distance_to_plaza.title')}
toggle
checked={distanceFilterIsOn}
onChange={handleToggleDistanceFilter}
/>
{distanceFilterIsOn && (
<SliderField
data-testid="dkstance-to-plaza-slider"
min={DISTANCE_TO_PLAZA_MIN}
max={DISTANCE_TO_PLAZA_MAX}
onChange={handleDistanceToPlazaChange}
step={1}
valueFrom={
minDistanceToPlaza
? Number.parseFloat(minDistanceToPlaza)
: undefined
}
valueTo={
maxDistanceToPlaza
? Number.parseFloat(maxDistanceToPlaza)
: undefined
}
range
header=""
label={t('nft_filters.distance_to_plaza.subtitle')}
className={styles.slider}
/>
)}
</Box>
)
}
1 change: 1 addition & 0 deletions webapp/src/components/AssetFilters/LocationFilter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './LocationFilter'
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,20 @@
background: rgba(115, 110, 125, 1);
}

i:global(.icon).deleteBtn {
.pill .deleteBtn {
margin-left: 8px;
margin-right: 0;
cursor: pointer;
height: auto;
background: transparent;
border: 0;
outline: 0;
color: white;
padding: 0;
margin-right: 0;
}

.pill .deleteBtn i {
margin-right: 0;
}

Loading

0 comments on commit d279ae5

Please sign in to comment.