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/tables redesign #1541

Merged
merged 13 commits into from
Apr 24, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,19 @@
margin-left: unset;
}

.BestBuyingOption :global(.ui.loader.active) {
display: flex;
position: unset;
margin-top: 20px;
}

.BestBuyingOption .emptyContainer {
display: flex;
width: 100%;
justify-content: center;
align-items: center;
height: 150px;
}
.BestBuyingOption .buyWithCardClassName {
background-color: white !important;
color: black !important;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('Best Buying Option', () => {
rarity: Rarity.UNIQUE,
price: '10',
available: 2,
isOnSale: false,
isOnSale: true,
creator: '0xcreator',
beneficiary: null,
createdAt: 1671033414000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import noListings from '../../../images/noListings.png'
import { ManaToFiat } from '../../ManaToFiat'
import { LinkedProfile } from '../../LinkedProfile'
import { BuyNFTButtons } from '../SaleActionBox/BuyNFTButtons'
import { BelowTabs } from '../ListingsTableContainer/ListingsTableContainer.types'
import { BuyOptions, Props } from './BestBuyingOption.types'
import styles from './BestBuyingOption.module.css'

Expand All @@ -43,14 +42,14 @@ const BestBuyingOption = ({ asset, tableRef }: Props) => {
const handleViewOffers = () => {
history.replace({
pathname: location.pathname,
search: `selectedTableTab=${BelowTabs.OWNERS}`
search: `selectedTableTab=owners`
})
tableRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' })
}

useEffect(() => {
if (asset && !isNFT(asset)) {
if (asset.available > 1) {
if (asset.available > 1 && asset.isOnSale) {
setBuyOption(BuyOptions.MINT)
} else {
setIsLoading(true)
Expand Down Expand Up @@ -103,7 +102,7 @@ const BestBuyingOption = ({ asset, tableRef }: Props) => {
return (
<div className={styles.BestBuyingOption}>
{isLoading ? (
<div className={`${styles.containerColumn} ${styles.fullWitdth}`}>
<div className={styles.emptyContainer}>
<Loader active data-testid="loader" />
</div>
) : buyOption === BuyOptions.MINT && asset && !isNFT(asset) ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
padding: 20px !important;
}

.ListingsTable .issuedIdContainer {
.issuedIdContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
Expand All @@ -23,39 +23,56 @@
font-weight: 700;
}

.ListingsTable .row {
.row {
display: flex;
flex-direction: row;
gap: 10px;
justify-content: space-between;
align-items: center;
}

.ListingsTable .badge {
.badgeContainer :global(.dcl.badge) {
color: white;
background-color: var(--background) !important;
background-color: transparent !important;
}

.badgeContainer {
display: flex;
flex-direction: row;
gap: 10px;
}

.ListingsTable .goToNFT {
color: white;
padding-right: 25px;
}

.ListingsTable .emptyTable {
height: 200px;
.emptyTable {
width: 100%;
height: 350px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 20px;
}

.ListingsTable .manaField {
.manaField {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
}

.ListingsTable :global(.ui.header.dcl.mana) {
margin: 0px;
font-size: 15px;
.manaField :global(.ui.header:last-child) {
font-size: 14px;
}

.viewListingContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-right: 10px;
}

@media (max-width: 768px) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { waitFor } from '@testing-library/react'
import {
ChainId,
Item,
Expand All @@ -7,15 +8,14 @@ import {
Order,
Rarity
} from '@dcl/schemas'
import { waitFor, within } from '@testing-library/react'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import { formatDistanceToNow, getDateAndMonthName } from '../../../lib/date'
import { formatWeiMANA } from '../../../lib/mana'
import { OwnersResponse } from '../../../modules/vendor/decentraland'
import * as nftAPI from '../../../modules/vendor/decentraland/nft/api'
import * as orderAPI from '../../../modules/vendor/decentraland/order/api'
import { renderWithProviders } from '../../../utils/tests'
import ListingsTable, { ROWS_PER_PAGE } from './ListingsTable'
import ListingsTable from './ListingsTable'

jest.mock('../../../modules/vendor/decentraland/nft/api')
jest.mock('../../../modules/vendor/decentraland/order/api')
Expand Down Expand Up @@ -139,7 +139,7 @@ describe('Listings Table', () => {
expect(loader).not.toBeInTheDocument()
})

expect(getByTestId('listings-table')).not.toBe(null)
expect(getByTestId('table-content')).not.toBe(null)
})

it('should render the table data correctly', async () => {
Expand All @@ -165,63 +165,4 @@ describe('Listings Table', () => {
expect(getByText(price)).not.toBe(null)
})
})

describe('Pagination', () => {
describe('Should have pagination', () => {
beforeEach(() => {
;(nftAPI.nftAPI.getOwners as jest.Mock).mockResolvedValueOnce({
data: Array(ROWS_PER_PAGE + 1).fill(ownersResponse),
total: 7
})
;(orderAPI.orderAPI.fetchOrders as jest.Mock).mockResolvedValueOnce({
data: Array(ROWS_PER_PAGE + 1).fill(orderResponse),
total: 7
})
})

it('should render the pagination correctly', async () => {
const screen = renderWithProviders(<ListingsTable asset={asset} />)

const { findByTestId, getByRole } = screen

const loader = await findByTestId('loader')

await waitFor(() => {
expect(loader).not.toBeInTheDocument()
})

const navigation = getByRole('navigation')

expect(within(navigation).getByText('1')).toBeInTheDocument()
expect(within(navigation).getByText('2')).toBeInTheDocument()
})
})

describe('Should not have pagination', () => {
beforeEach(() => {
;(nftAPI.nftAPI.getOwners as jest.Mock).mockResolvedValueOnce({
data: Array(ROWS_PER_PAGE - 1).fill(ownersResponse),
total: 5
})
;(orderAPI.orderAPI.fetchOrders as jest.Mock).mockResolvedValueOnce({
data: Array(ROWS_PER_PAGE - 1).fill(orderResponse),
total: 5
})
})

it('should not render pagination as there is no need', async () => {
const screen = renderWithProviders(<ListingsTable asset={asset} />)

const { findByTestId, queryByRole } = screen

const loader = await findByTestId('loader')

await waitFor(() => {
expect(loader).not.toBeInTheDocument()
})

expect(queryByRole('navigation')).not.toBeInTheDocument()
})
})
})
})
116 changes: 18 additions & 98 deletions webapp/src/components/AssetPage/ListingsTable/ListingsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { ListingStatus, Network } from '@dcl/schemas'
import { Table, Loader, Row, Pagination, Icon, Mana } from 'decentraland-ui'
import { OrderFilters, OrderSortBy } from '@dcl/schemas/dist/dapps/order'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import { Order, OrderFilters, OrderSortBy } from '@dcl/schemas/dist/dapps/order'
import { nftAPI, orderAPI } from '../../../modules/vendor/decentraland'
import { locations } from '../../../modules/routing/locations'
import { formatWeiMANA } from '../../../lib/mana'
import { formatDistanceToNow, getDateAndMonthName } from '../../../lib/date'
import { LinkedProfile } from '../../LinkedProfile'
import noListings from '../../../images/noListings.png'
import { TableContent } from '../../Table/TableContent'
import { DataTableType } from '../../Table/TableContent/TableContent.types'
import { formatDataToTable } from './utils'
import { Props } from './ListingsTable.types'
import styles from './ListingsTable.module.css'

Expand All @@ -18,7 +16,7 @@ const INITIAL_PAGE = 1
const ListingsTable = (props: Props) => {
const { asset, sortBy = OrderSortBy.CHEAPEST } = props

const [orders, setOrders] = useState<Order[]>([])
const [orders, setOrders] = useState<DataTableType[]>([])
const [total, setTotal] = useState(0)
const [page, setPage] = useState(INITIAL_PAGE)
const [totalPages, setTotalPages] = useState<number>(0)
Expand Down Expand Up @@ -61,8 +59,8 @@ const ListingsTable = (props: Props) => {
orderAPI
.fetchOrders(params, sortBy)
.then(response => {
setOrders(response.data)
setTotalPages(Math.ceil(response.total / ROWS_PER_PAGE) || 0)
setOrders(formatDataToTable(response.data))
})
.finally(() => setIsLoading(false))
.catch(error => {
Expand All @@ -72,99 +70,21 @@ const ListingsTable = (props: Props) => {
}, [asset, setIsLoading, setOrders, sortBy, page])

return (
<div className={styles.ListingsTable}>
{isLoading ? (
<div className={styles.emptyTable}>
<Loader active data-testid="loader" />
</div>
) : orders.length === 0 ? (
<TableContent
data={orders}
isLoading={isLoading}
setPage={setPage}
totalPages={totalPages}
empty={() => (
<div className={styles.emptyTable}>
<img src={noListings} alt="empty" />
<span>{t('listings_table.there_are_no_listings')}</span>
</div>
) : (
<>
<Table basic="very" data-testid="listings-table">
<Table.Header>
<Table.Row>
<Table.HeaderCell className={styles.headerMargin}>
{t('listings_table.owner')}
</Table.HeaderCell>
<Table.HeaderCell>
{t('listings_table.published_date')}
</Table.HeaderCell>
<Table.HeaderCell>
{t('listings_table.expiration_date')}
</Table.HeaderCell>
<Table.HeaderCell>
{t('listings_table.issue_number')}
</Table.HeaderCell>
<Table.HeaderCell>{t('listings_table.price')}</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body className={isLoading ? 'is-loading' : ''}>
{orders?.map(order => (
<Table.Row key={order.id}>
<Table.Cell>
<LinkedProfile
className={styles.linkedProfileRow}
address={order.owner}
/>
</Table.Cell>
<Table.Cell>
{getDateAndMonthName(order.createdAt)}
</Table.Cell>
<Table.Cell>
{formatDistanceToNow(+order.expiresAt, {
addSuffix: true
})}
</Table.Cell>
<Table.Cell>
<div className={styles.issuedIdContainer}>
<div className={styles.row}>
<span>
<span className={styles.issuedId}>
{order.issuedId}
</span>
/ {total}
</span>
</div>
</div>
</Table.Cell>
<Table.Cell>
<div className={styles.manaField}>
<Mana className="manaField" network={asset?.network}>
{formatWeiMANA(order.price)}
</Mana>
{asset?.contractAddress && order.tokenId && (
<Link
to={locations.nft(
asset.contractAddress,
order.tokenId
)}
>
<Icon name="arrow right" className={styles.goToNFT} />
</Link>
)}
</div>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
{totalPages && totalPages > 1 ? (
<Row center>
<Pagination
activePage={page}
totalPages={totalPages}
onPageChange={(_event, props) => setPage(+props.activePage!)}
firstItem={null}
lastItem={null}
/>
</Row>
) : null}
</>
)}
</div>
total={total}
rowsPerPage={ROWS_PER_PAGE}
activePage={page}
/>
)
}

Expand Down
Loading