Commit 3e5c0bdd authored by Charles Bachmeier's avatar Charles Bachmeier Committed by GitHub

feat: [DetailsV2] build containers for data page (#6399)

* add page break and placeholder files

* container layout

* description tabs

* add table tabs

* mobile wrap

* remove todo

* adaptive padding

* add extensible tabbed component

* add new file

* add snapshot test for empty data page

* update snapshot

* add new snapshot

* undo unrelated snapshot change

* draft: details v2 containers

* Revert "draft: details v2 containers"

This reverts commit 7c6580c974a7c749490c909778757a51a13b8e07.

* uniform padding and single tab styling

* update snapshot

* update default tab behaviour

* max width

* convert to map with keys

---------
Co-authored-by: default avatarCharles Bachmeier <charlie@genie.xyz>
Co-authored-by: default avatarJack Short <john.short.tj@gmail.com>
parent b161022f
export const ActivityTableContent = () => {
return <div>Activity Content</div>
}
import { render } from 'test-utils/render'
import { DataPage } from './DataPage'
it('placeholder containers load', () => {
const { asFragment } = render(<DataPage />)
expect(asFragment()).toMatchSnapshot()
})
import Column from 'components/Column'
import Row from 'components/Row'
import styled from 'styled-components/macro'
import { BREAKPOINTS } from 'theme'
import { DataPageDescription } from './DataPageDescription'
import { DataPageHeader } from './DataPageHeader'
import { DataPageTable } from './DataPageTable'
import { DataPageTraits } from './DataPageTraits'
const DataPageContainer = styled(Column)`
padding: 24px 64px;
height: 100vh;
width: 100%;
gap: 36px;
max-width: ${({ theme }) => theme.maxWidth};
margin: 0 auto;
@media screen and (max-width: ${BREAKPOINTS.sm}px) {
padding: 24px 48px;
}
@media screen and (max-width: ${BREAKPOINTS.xs}px) {
padding: 24px 20px;
}
`
const ContentContainer = styled(Row)`
gap: 24px;
padding-bottom: 45px;
@media screen and (max-width: ${BREAKPOINTS.lg}px) {
flex-wrap: wrap;
}
`
const LeftColumn = styled(Column)`
gap: 24px;
width: 100%;
`
export const DataPage = () => {
return (
<DataPageContainer>
<DataPageHeader />
<ContentContainer>
<LeftColumn>
<DataPageTraits />
<DataPageDescription />
</LeftColumn>
<DataPageTable />
</ContentContainer>
</DataPageContainer>
)
}
import { Trans } from '@lingui/macro'
import { Tab, TabbedComponent } from './TabbedComponent'
const DescriptionContent = () => {
return <div>Description Content</div>
}
const DetailsContent = () => {
return <div>Details Content</div>
}
enum DescriptionTabsKeys {
Description = 'description',
Details = 'details',
}
const DescriptionTabs: Map<string, Tab> = new Map([
[
DescriptionTabsKeys.Description,
{
title: <Trans>Description</Trans>,
key: DescriptionTabsKeys.Description,
content: <DescriptionContent />,
},
],
[
DescriptionTabsKeys.Details,
{
title: <Trans>Details</Trans>,
key: DescriptionTabsKeys.Details,
content: <DetailsContent />,
},
],
])
export const DataPageDescription = () => {
return <TabbedComponent tabs={DescriptionTabs} style={{ height: '288px' }} />
}
import styled from 'styled-components/macro'
import { containerStyles } from './shared'
const HeaderContainer = styled.div`
height: 96px;
flex-shrink: 0;
${containerStyles}
padding-left: 0px;
`
export const DataPageHeader = () => {
return <HeaderContainer>Header</HeaderContainer>
}
import { Trans } from '@lingui/macro'
import { ActivityTableContent } from './ActivityTableContent'
import { ListingsTableContent } from './ListingsTableContent'
import { OffersTableContent } from './OffersTableContent'
import { Tab, TabbedComponent } from './TabbedComponent'
enum TableTabsKeys {
Activity = 'activity',
Offers = 'offers',
Listings = 'listings',
}
const TableTabs: Map<string, Tab> = new Map([
[
TableTabsKeys.Activity,
{
title: <Trans>Activity</Trans>,
key: TableTabsKeys.Activity,
content: <ActivityTableContent />,
},
],
[
TableTabsKeys.Offers,
{
title: <Trans>Offers</Trans>,
key: TableTabsKeys.Offers,
content: <OffersTableContent />,
},
],
[
TableTabsKeys.Listings,
{
title: <Trans>Listings</Trans>,
key: TableTabsKeys.Listings,
content: <ListingsTableContent />,
},
],
])
export const DataPageTable = () => {
return <TabbedComponent tabs={TableTabs} style={{ height: '604px' }} />
}
import { Trans } from '@lingui/macro'
import { Tab, TabbedComponent } from './TabbedComponent'
const TraitsContent = () => {
return <div>Traits Content</div>
}
enum TraitTabsKeys {
Traits = 'traits',
}
const TraitTabs: Map<string, Tab> = new Map([
[
TraitTabsKeys.Traits,
{
title: <Trans>Traits</Trans>,
key: TraitTabsKeys.Traits,
content: <TraitsContent />,
},
],
])
export const DataPageTraits = () => {
return <TabbedComponent tabs={TraitTabs} style={{ height: '528px' }} />
}
export const ListingsTableContent = () => {
return <div>Listings Content</div>
}
import { CollectionInfoForAsset, GenieAsset } from 'nft/types' import { CollectionInfoForAsset, GenieAsset } from 'nft/types'
import styled from 'styled-components/macro'
import { DataPage } from './DataPage'
interface NftDetailsProps { interface NftDetailsProps {
asset: GenieAsset asset: GenieAsset
collection: CollectionInfoForAsset collection: CollectionInfoForAsset
} }
const LandingPage = styled.div`
height: 100vh;
`
export const NftDetails = ({ asset, collection }: NftDetailsProps) => { export const NftDetails = ({ asset, collection }: NftDetailsProps) => {
return ( return (
<div> <>
Details page for {asset.name} from {collection.collectionName} <LandingPage>
</div> Details page for {asset.name} from {collection.collectionName}
</LandingPage>
<DataPage />
</>
) )
} }
export const OffersTableContent = () => {
return <div>Offers Content</div>
}
import Row from 'components/Row'
import { useState } from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { containerStyles } from './shared'
const TabbedComponentContainer = styled.div`
height: 288px;
${containerStyles}
`
const TabsRow = styled(Row)`
gap: 32px;
margin-bottom: 12px;
width: 100;
`
const Tab = styled(ThemedText.SubHeader)<{ isActive: boolean; numTabs: number }>`
color: ${({ theme, isActive }) => (isActive ? theme.textPrimary : theme.textTertiary)};
line-height: 24px;
cursor: ${({ numTabs }) => (numTabs > 1 ? 'pointer' : 'default')};
&:hover {
opacity: ${({ numTabs, theme }) => numTabs > 1 && theme.opacity.hover}};
}
`
export interface Tab {
title: React.ReactNode
key: string
content: JSX.Element
}
interface TabbedComponentProps {
tabs: Map<string, Tab>
defaultTabKey?: string
style?: React.CSSProperties
}
export const TabbedComponent = ({ tabs, defaultTabKey, style }: TabbedComponentProps) => {
const firstKey = tabs.keys().next().value
const [activeKey, setActiveKey] = useState(defaultTabKey ?? firstKey)
const activeContent = tabs.get(activeKey)?.content
const tabArray = Array.from(tabs.values())
return (
<TabbedComponentContainer style={style}>
<TabsRow>
{tabArray.map((tab) => (
<Tab
isActive={activeKey === tab.key}
numTabs={tabArray.length}
onClick={() => setActiveKey(tab.key)}
key={tab.key}
>
{tab.title}
</Tab>
))}
</TabsRow>
{activeContent}
</TabbedComponentContainer>
)
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`placeholder containers load 1`] = `
<DocumentFragment>
.c3 {
box-sizing: border-box;
margin: 0;
min-width: 0;
}
.c4 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
}
.c9 {
color: #0D111C;
}
.c0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
}
.c7 {
height: 288px;
background: #FFFFFF;
border-radius: 16px;
padding: 16px 20px;
width: 100%;
-webkit-align-self: flex-start;
-ms-flex-item-align: start;
align-self: flex-start;
}
.c8 {
gap: 32px;
margin-bottom: 12px;
width: 100;
}
.c10 {
color: #0D111C;
line-height: 24px;
cursor: default;
}
.c11 {
color: #0D111C;
line-height: 24px;
cursor: pointer;
}
.c11:hover {
opacity: 0.6;
}
.c12 {
color: #98A1C0;
line-height: 24px;
cursor: pointer;
}
.c12:hover {
opacity: 0.6;
}
.c2 {
height: 96px;
-webkit-flex-shrink: 0;
-ms-flex-negative: 0;
flex-shrink: 0;
background: #FFFFFF;
border-radius: 16px;
padding: 16px 20px;
width: 100%;
-webkit-align-self: flex-start;
-ms-flex-item-align: start;
align-self: flex-start;
padding-left: 0px;
}
.c1 {
padding: 24px 64px;
height: 100vh;
width: 100%;
gap: 36px;
max-width: 1200px;
margin: 0 auto;
}
.c5 {
gap: 24px;
padding-bottom: 45px;
}
.c6 {
gap: 24px;
width: 100%;
}
@media screen and (max-width:640px) {
.c1 {
padding: 24px 48px;
}
}
@media screen and (max-width:396px) {
.c1 {
padding: 24px 20px;
}
}
@media screen and (max-width:1024px) {
.c5 {
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
}
<div
class="c0 c1"
>
<div
class="c2"
>
Header
</div>
<div
class="c3 c4 c5"
>
<div
class="c0 c6"
>
<div
class="c7"
style="height: 528px;"
>
<div
class="c3 c4 c8"
>
<div
class="c9 c10 css-6ms77s"
>
Traits
</div>
</div>
<div>
Traits Content
</div>
</div>
<div
class="c7"
style="height: 288px;"
>
<div
class="c3 c4 c8"
>
<div
class="c9 c11 css-6ms77s"
>
Description
</div>
<div
class="c9 c12 css-6ms77s"
>
Details
</div>
</div>
<div>
Description Content
</div>
</div>
</div>
<div
class="c7"
style="height: 604px;"
>
<div
class="c3 c4 c8"
>
<div
class="c9 c11 css-6ms77s"
>
Activity
</div>
<div
class="c9 c12 css-6ms77s"
>
Offers
</div>
<div
class="c9 c12 css-6ms77s"
>
Listings
</div>
</div>
<div>
Activity Content
</div>
</div>
</div>
</div>
</DocumentFragment>
`;
import { css } from 'styled-components/macro'
export const containerStyles = css`
background: ${({ theme }) => theme.backgroundSurface};
border-radius: 16px;
padding: 16px 20px;
width: 100%;
align-self: flex-start;
`
...@@ -54,7 +54,7 @@ const BannerMainArea = styled.div` ...@@ -54,7 +54,7 @@ const BannerMainArea = styled.div`
width: 100%; width: 100%;
height: 100%; height: 100%;
gap: 36px; gap: 36px;
max-width: 1200px; max-width: ${({ theme }) => theme.maxWidth};
justify-content: space-between; justify-content: space-between;
z-index: 2; z-index: 2;
......
...@@ -24,7 +24,7 @@ const ExploreContainer = styled.div` ...@@ -24,7 +24,7 @@ const ExploreContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
max-width: 1200px; max-width: ${({ theme }) => theme.maxWidth};
padding: 0 16px; padding: 0 16px;
` `
......
...@@ -123,7 +123,7 @@ const FloatingConfirmationBar = styled(Row)<{ issues: boolean }>` ...@@ -123,7 +123,7 @@ const FloatingConfirmationBar = styled(Row)<{ issues: boolean }>`
width: calc(100vw - ${LIST_PAGE_MARGIN * 2}px); width: calc(100vw - ${LIST_PAGE_MARGIN * 2}px);
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
max-width: 1200px; max-width: ${({ theme }) => theme.maxWidth};
z-index: ${Z_INDEX.under_dropdown}; z-index: ${Z_INDEX.under_dropdown};
box-shadow: ${({ theme }) => theme.shallowShadow}; box-shadow: ${({ theme }) => theme.shallowShadow};
......
...@@ -5,7 +5,7 @@ import { useNftAssetDetails } from 'graphql/data/nft/Details' ...@@ -5,7 +5,7 @@ import { useNftAssetDetails } from 'graphql/data/nft/Details'
import { AssetDetails } from 'nft/components/details/AssetDetails' import { AssetDetails } from 'nft/components/details/AssetDetails'
import { AssetDetailsLoading } from 'nft/components/details/AssetDetailsLoading' import { AssetDetailsLoading } from 'nft/components/details/AssetDetailsLoading'
import { AssetPriceDetails } from 'nft/components/details/AssetPriceDetails' import { AssetPriceDetails } from 'nft/components/details/AssetPriceDetails'
import { NftDetails } from 'nft/components/details/NftDetails' import { NftDetails } from 'nft/components/details/detailsV2/NftDetails'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
......
...@@ -17,6 +17,8 @@ export const MEDIA_WIDTHS = { ...@@ -17,6 +17,8 @@ export const MEDIA_WIDTHS = {
deprecated_upToLarge: 1280, deprecated_upToLarge: 1280,
} }
const MAX_CONTENT_WIDTH = '1200px'
const deprecated_mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys( const deprecated_mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(
MEDIA_WIDTHS MEDIA_WIDTHS
).reduce((acc, size) => { ).reduce((acc, size) => {
...@@ -84,6 +86,7 @@ function getSettings(darkMode: boolean) { ...@@ -84,6 +86,7 @@ function getSettings(darkMode: boolean) {
navHeight: 72, navHeight: 72,
mobileBottomBarHeight: 52, mobileBottomBarHeight: 52,
maxWidth: MAX_CONTENT_WIDTH,
// deprecated - please use hardcoded exported values instead of // deprecated - please use hardcoded exported values instead of
// adding to the theme object // adding to the theme object
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment