Skip to content

Commit

Permalink
feat: Give more visibility to the smart filter (#1942)
Browse files Browse the repository at this point in the history
* feat: Give OnlySmart filter more visibility

* chore: Add TODO

* style: Fix styles reviewed in the PR

* feat: Show different sections depending on the dispositive
  • Loading branch information
kevinszuchet authored and cyaiox committed Jul 27, 2023
1 parent 1e1a2fb commit cf30ad6
Show file tree
Hide file tree
Showing 16 changed files with 230 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const CurrentAccountSidebar = ({ section, onBrowse }: Props) => {
<AssetFilters
defaultCollapsed={{
[AssetFilter.Status]: true,
[AssetFilter.OnlySmart]: true,
[AssetFilter.Rarity]: true,
[AssetFilter.Price]: true,
[AssetFilter.Collection]: true,
Expand Down
8 changes: 8 additions & 0 deletions webapp/src/components/AssetFilters/AssetFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import PriceFilter from './PriceFilter'
import EstateSizeFilter from './EstateSizeFilter'
import CreatorsFilter from './CreatorsFilter'
import { RarityFilter } from './RarityFilter'
import { OnlySmartFilter } from './OnlySmartFilter'
import { NetworkFilter } from './NetworkFilter'
import { Props } from './AssetFilters.types'
import { CollectionFilter } from './CollectionFilter'
Expand Down Expand Up @@ -262,6 +263,13 @@ export const AssetFilters = ({

return (
<Menu className="filters-sidebar">
{shouldRenderFilter(AssetFilter.OnlySmart) ? (
<OnlySmartFilter
isOnlySmart={isOnlySmart}
onChange={handleOnlySmartChange}
defaultCollapsed={!!defaultCollapsed?.[AssetFilter.OnlySmart]}
/>
) : null}
{shouldRenderFilter(AssetFilter.Rarity) ? (
<RarityFilter
onChange={handleRarityChange}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { NFTCategory } from '@dcl/schemas'
import { useTabletAndBelowMediaQuery } from 'decentraland-ui/dist/components/Media'
import { renderWithProviders } from '../../../utils/test'
import { MoreFilters, MoreFiltersProps } from './MoreFilters'

jest.mock('decentraland-ui/dist/components/Media', () => ({
useTabletAndBelowMediaQuery: jest.fn()
}))

function renderMoreFilters(props: Partial<MoreFiltersProps> = {}) {
return renderWithProviders(
<MoreFilters
onOnlySmartChange={jest.fn()}
onSaleChange={jest.fn()}
{...props}
/>
)
}

describe('MoreFilters', () => {
let useTabletAndBelowMediaQueryMock: jest.MockedFunction<typeof useTabletAndBelowMediaQuery>

beforeEach(() => {
useTabletAndBelowMediaQueryMock = useTabletAndBelowMediaQuery as jest.MockedFunction<
typeof useTabletAndBelowMediaQuery
>
})

describe('when the isOnSale filter is visible', () => {
it('should render the more filters section', () => {
const { container } = renderMoreFilters({ isOnSale: true })
expect(container).not.toBeEmptyDOMElement()
})
})

describe('when the wearables category is selected and the dispositive is tablet or mobile', () => {
beforeEach(() => {
useTabletAndBelowMediaQueryMock.mockReturnValue(true)
})

it('should render the more filters section', () => {
const { container } = renderMoreFilters({
category: NFTCategory.WEARABLE
})
expect(container).not.toBeEmptyDOMElement()
})
})

describe('when the isOnSale filter is not visible', () => {
describe('and the selected category is not wearables', () => {
it('should not render the more filters section', () => {
const { container } = renderMoreFilters({
category: NFTCategory.PARCEL,
isOnSale: undefined
})
expect(container).toBeEmptyDOMElement()
})
})

describe('and the selected category is wearables but the dispositive is not mobile nor tablet', () => {
beforeEach(() => {
useTabletAndBelowMediaQueryMock.mockReturnValue(false)
})

it('should not render the more filters section', () => {
const { container } = renderMoreFilters({
category: NFTCategory.WEARABLE,
isOnSale: undefined
})
expect(container).toBeEmptyDOMElement()
})
})
})
})
38 changes: 14 additions & 24 deletions webapp/src/components/AssetFilters/MoreFilters/MoreFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { useCallback, useMemo } from 'react'
import {
Box,
CheckboxProps,
Checkbox,
useTabletAndBelowMediaQuery
} from 'decentraland-ui'
import { Box, CheckboxProps, Checkbox } from 'decentraland-ui'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import { useTabletAndBelowMediaQuery } from 'decentraland-ui/dist/components/Media'
import { NFTCategory } from '@dcl/schemas'
import { OnlySmartFilterContent } from '../OnlySmartFilter'
import './MoreFilters.css'

export type MoreFiltersProps = {
Expand All @@ -28,13 +25,7 @@ export const MoreFilters = ({
}: MoreFiltersProps) => {
const isWearableCategory = category === NFTCategory.WEARABLE
const isMobileOrTablet = useTabletAndBelowMediaQuery()

const handleOnlySmartChange = useCallback(
(_, props: CheckboxProps) => {
onOnlySmartChange(!!props.checked)
},
[onOnlySmartChange]
)
const showOnlySmartFilter = isWearableCategory && isMobileOrTablet

const handleOnSaleChange = useCallback(
(_, props: CheckboxProps) => {
Expand All @@ -48,11 +39,11 @@ export const MoreFilters = ({
values.push(
isOnSale ? t('nft_filters.for_sale') : t('nft_filters.not_on_sale')
)
if (isOnlySmart) {
values.push(t('nft_filters.only_smart'))
if (isOnlySmart && showOnlySmartFilter) {
values.push(t('nft_filters.only_smart.selected'))
}
return values.join(', ')
}, [isOnSale, isOnlySmart])
}, [isOnSale, isOnlySmart, showOnlySmartFilter])

const header = useMemo(
() =>
Expand All @@ -69,7 +60,7 @@ export const MoreFilters = ({
[filterText, isMobileOrTablet]
)

return (
return isOnSale !== undefined || showOnlySmartFilter ? (
<Box
header={header}
className="filters-sidebar-box"
Expand All @@ -85,15 +76,14 @@ export const MoreFilters = ({
onChange={handleOnSaleChange}
/>
) : null}
{isWearableCategory && (
<Checkbox
label="Only smart"
toggle
checked={!!isOnlySmart}
onChange={handleOnlySmartChange}
{showOnlySmartFilter && (
<OnlySmartFilterContent
data-testid="only-smart-filter"
isOnlySmart={isOnlySmart}
onChange={onOnlySmartChange}
/>
)}
</div>
</Box>
)
) : null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.onlySmartFilterSection {
display: flex;
justify-content: space-between;
align-items: center;
}

.onlySmartFilterSection :global(.SmartBadge:not(.clickable)) {
cursor: initial;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useTabletAndBelowMediaQuery } from 'decentraland-ui/dist/components/Media'
import { renderWithProviders } from '../../../utils/test'
import { OnlySmartFilter, OnlySmartFilterProps } from './OnlySmartFilter'

jest.mock('decentraland-ui/dist/components/Media', () => ({
useTabletAndBelowMediaQuery: jest.fn()
}))

function renderOnlySmartFilter(props: Partial<OnlySmartFilterProps> = {}) {
return renderWithProviders(
<OnlySmartFilter onChange={jest.fn()} {...props} />
)
}

describe('OnlySmartFilter', () => {
let useTabletAndBelowMediaQueryMock: jest.MockedFunction<typeof useTabletAndBelowMediaQuery>

beforeEach(() => {
useTabletAndBelowMediaQueryMock = useTabletAndBelowMediaQuery as jest.MockedFunction<
typeof useTabletAndBelowMediaQuery
>
})

describe('when the dispositive is mobile or tablet', () => {
beforeEach(() => {
useTabletAndBelowMediaQueryMock.mockReturnValue(true)
})

it('should not render the only smart filter section', () => {
const { container } = renderOnlySmartFilter()
expect(container).toBeEmptyDOMElement()
})
})

describe('when the dispositive is not mobile nor tablet', () => {
beforeEach(() => {
useTabletAndBelowMediaQueryMock.mockReturnValue(false)
})

it('should render the only smart filter section', () => {
const { container } = renderOnlySmartFilter()
expect(container).not.toBeEmptyDOMElement()
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useCallback } from 'react'
import { Box, Checkbox, CheckboxProps } from 'decentraland-ui'
import { useTabletAndBelowMediaQuery } from 'decentraland-ui/dist/components/Media'
import SmartBadge from '../../AssetPage/SmartBadge'
import styles from './OnlySmartFilter.module.css'

export type OnlySmartFilterProps = {
isOnlySmart?: boolean
onChange: (value: boolean) => void
defaultCollapsed?: boolean
'data-testid'?: string
}

export const OnlySmartFilterContent = (
props: Pick<OnlySmartFilterProps, 'isOnlySmart' | 'onChange' | 'data-testid'>
) => {
const { isOnlySmart, onChange } = props

const handleChange = useCallback(
(_, props: CheckboxProps) => {
onChange(!!props.checked)
},
[onChange]
)

return (
<div
className={styles.onlySmartFilterSection}
data-testid={props['data-testid']}
>
<SmartBadge clickable={false} />
<Checkbox toggle checked={!!isOnlySmart} onChange={handleChange} />
</div>
)
}

export const OnlySmartFilter = ({
isOnlySmart,
onChange,
defaultCollapsed = false
}: OnlySmartFilterProps) => {
const isMobileOrTablet = useTabletAndBelowMediaQuery()

return isMobileOrTablet ? null : (
<Box className="filters-sidebar-box" defaultCollapsed={defaultCollapsed}>
<OnlySmartFilterContent isOnlySmart={isOnlySmart} onChange={onChange} />
</Box>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './OnlySmartFilter'
5 changes: 4 additions & 1 deletion webapp/src/components/AssetFilters/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ export const enum AssetFilter {
Network,
BodyShape,
OnSale,
OnlySmart,
More
}

const WearablesFilters = [
AssetFilter.OnlySmart,
AssetFilter.Rarity,
AssetFilter.Status,
AssetFilter.Price,
Expand All @@ -33,7 +35,8 @@ const EmotesFilters = [
filter =>
filter !== AssetFilter.BodyShape &&
filter !== AssetFilter.Network &&
filter !== AssetFilter.More
filter !== AssetFilter.More &&
filter !== AssetFilter.OnlySmart
),
AssetFilter.PlayMode
]
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/components/AssetPage/SmartBadge/SmartBadge.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
top: 2px;
width: 14px;
height: 14px;
}
}
3 changes: 2 additions & 1 deletion webapp/src/components/AssetPage/SmartBadge/SmartBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useMemo } from 'react'
import classNames from 'classnames'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import { SmartIcon } from 'decentraland-ui'
import { Section } from '../../../modules/vendor/decentraland'
Expand All @@ -20,7 +21,7 @@ const SmartBadge = ({ assetType, clickable = true }: Props) => {

return (
<IconBadge
className="SmartBadge"
className={classNames('SmartBadge', { clickable })}
text={t('wearable.smart_badge')}
href={clickable ? href : undefined}
>
Expand Down
13 changes: 9 additions & 4 deletions webapp/src/components/AssetPage/SmartBadge/SmartBadge.types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { AssetType } from '../../../modules/asset/types'

export type Props = {
assetType: AssetType
clickable?: boolean
}
export type Props =
| {
assetType?: AssetType
clickable?: false
}
| {
assetType: AssetType
clickable: true
}

export type OwnProps = Pick<Props, 'assetType'>
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export const SelectedFilters = ({
) : null}
{onlySmart ? (
<Pill
label={t('nft_filters.only_smart')}
label={t('nft_filters.only_smart.selected')}
id="onlySmart"
onDelete={handleDeleteOnlySmart}
/>
Expand Down
5 changes: 4 additions & 1 deletion webapp/src/modules/translation/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,10 @@
"all_items": "All rarities",
"count_items": "{count} {count, plural, one {rarity} other {rarities}}"
},
"only_smart": {
"title": "Smart Wearables",
"selected": "Only Smart"
},
"periods": {
"title": "Rental Period",
"all_items": "All periods",
Expand Down Expand Up @@ -451,7 +455,6 @@
},
"more_filters": "More filters",
"location": "Location",
"only_smart": "Only smart",
"not_on_sale": "Includes not on sale",
"for_sale": "For sale",
"category": "Category",
Expand Down
5 changes: 4 additions & 1 deletion webapp/src/modules/translation/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,10 @@
},
"more_filters": "Más filtros",
"location": "Ubicación",
"only_smart": "Solo vestimentas interactivas",
"only_smart": {
"title": "Vestimentas interactivas",
"selected": "Solo vestimentas interactivas"
},
"not_on_sale": "Incluye no a la venta",
"for_sale": "A la venta",
"category": "Categoría",
Expand Down
Loading

0 comments on commit cf30ad6

Please sign in to comment.