1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-06-20 11:23:24 +02:00

layout component & site metadata refactor

This commit is contained in:
Matthias Kretschmann 2020-06-30 11:59:20 +02:00
parent 38ccfc635e
commit 9cc38304c8
Signed by: m
GPG Key ID: 606EEEF3C479A91F
36 changed files with 356 additions and 149 deletions

22
content/site.json Normal file
View File

@ -0,0 +1,22 @@
{
"site": {
"siteTitle": "Ocean Market",
"siteTagline": "A marketplace to find and publish open data sets in the Ocean Network.",
"siteUrl": "https://market.oceanprotocol.now.sh/",
"copyright": "All Rights Reserved. Powered by [Ocean Protocol](https://oceanprotocol.com)",
"menu": [
{
"name": "Explore",
"link": "/explore"
},
{
"name": "Publish",
"link": "/publish"
},
{
"name": "Transactions",
"link": "/transactions"
}
]
}
}

9
package-lock.json generated
View File

@ -8385,6 +8385,15 @@
"popper.js": "^1.14.1"
}
},
"@types/react-helmet": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.0.0.tgz",
"integrity": "sha512-NBMPAxgjpaMooXa51cU1BTgrX6T+hQbMiLm77JhBbfOzPQea3RB5rNpPOD5xGWHIVpGXHd59cltEzIq0qglGcQ==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/react-jsonschema-form": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@types/react-jsonschema-form/-/react-jsonschema-form-1.7.3.tgz",

View File

@ -71,6 +71,7 @@
"@types/numeral": "^0.0.28",
"@types/react": "^16.9.41",
"@types/react-datepicker": "^3.0.2",
"@types/react-helmet": "^6.0.0",
"@types/react-jsonschema-form": "^1.7.3",
"@types/react-paginate": "^6.2.1",
"@types/shortid": "0.0.29",

View File

@ -1,21 +0,0 @@
module.exports = {
siteTitle: 'Ocean Market',
siteTagline: `A marketplace to find and publish open data sets in the Ocean Network.`,
siteUrl: 'https://market.oceanprotocol.now.sh/',
copyright:
'All Rights Reserved. Powered by [Ocean Protocol](https://oceanprotocol.com)',
menu: [
{
name: 'Explore',
link: '/explore'
},
{
name: 'Publish',
link: '/publish'
},
{
name: 'Transactions',
link: '/transactions'
}
]
}

View File

@ -1,3 +1,8 @@
declare module '*.module.css' {
const classes: { [key: string]: string }
export default classes
}
declare module '*.svg' {
import * as React from 'react'
export const ReactComponent: React.FunctionComponent<React.SVGProps<
@ -7,5 +12,4 @@ declare module '*.svg' {
export default src
}
declare type Nullable<T> = T | null
declare module '*.md'
declare module '*.gif'

View File

@ -0,0 +1,9 @@
import React from 'react'
import testRender from '../../tests/unit/testRender'
import Layout from './Layout'
describe('Layout', () => {
testRender(
<Layout location={{ href: 'https://demo.com' } as Location}>Hello</Layout>
)
})

View File

@ -1,31 +1,40 @@
import React, { ReactNode } from 'react'
import Head from 'next/head'
import { NextSeo } from 'next-seo'
import React, { ReactNode, ReactElement } from 'react'
import { Helmet } from 'react-helmet'
import Header from './organisms/Header'
import Footer from './organisms/Footer'
import PageHeader from './molecules/PageHeader'
import styles from './Layout.module.css'
import Header from './components/organisms/Header'
import Footer from './components/organisms/Footer'
import PageHeader from './components/molecules/PageHeader'
import Seo from './atoms/Seo'
export interface LayoutProps {
children: ReactNode
title?: string
description?: string
noPageHeader?: boolean
location?: Location
}
export default function Layout({
children,
title,
description,
noPageHeader
}: {
children: ReactNode
title?: string
description?: string
noPageHeader?: boolean
}) {
noPageHeader,
location
}: LayoutProps): ReactElement {
return (
<div className={styles.app}>
<Head>
<Helmet>
<link rel="icon" href="/icons/icon-96x96.png" />
<link rel="apple-touch-icon" href="icons/icon-256x256.png" />
<meta name="theme-color" content="#ca2935" />
</Head>
</Helmet>
<NextSeo title={title} description={description} />
<Seo
title={title}
description={description}
uri={location.href}
location={location}
/>
<Header />
<main className={styles.main}>

View File

@ -16,8 +16,7 @@
background: var(--brand-grey-light);
box-shadow: 0 9px 18px 0 rgba(0, 0, 0, 0.1);
user-select: none;
margin-left: calc(var(--spacer) / 4);
margin-right: calc(var(--spacer) / 4);
text-align: center;
}
.button:first-child {
@ -57,21 +56,28 @@
background: var(--brand-gradient);
}
.link {
.ghost,
.ghost:hover,
.ghost:focus,
.ghost:active {
color: var(--brand-grey);
background: var(--brand-white);
}
.text,
.text:hover,
.text:focus,
.text:active {
border: 0;
border-radius: 0;
outline: 0;
padding: 0;
display: inline-block;
background: 0;
padding: 0;
color: var(--brand-pink);
box-shadow: none;
cursor: pointer;
}
.link:hover {
background: none;
color: var(--brand-pink);
box-shadow: none;
}
/* Size Modifiers */
.small {

View File

@ -1,14 +1,57 @@
import React from 'react'
import { action } from '@storybook/addon-actions'
import Button from './Button'
import { Center } from '../../../.storybook/helpers'
export default {
title: 'Atoms/Button',
decorators: [(storyFn: any) => <Center>{storyFn()}</Center>]
title: 'Atoms/Button'
}
export const Normal = () => <Button>Hello Button</Button>
export const Default = () => (
<>
<Button onClick={action('clicked')}>Hello Button</Button>
<br />
<br />
<Button size="small" onClick={action('clicked')}>
Hello Button
</Button>
</>
)
export const Primary = () => <Button primary>Hello Button</Button>
export const Primary = () => (
<>
<Button style="primary" onClick={action('clicked')}>
Hello Button
</Button>
<br />
<br />
<Button style="primary" size="small" onClick={action('clicked')}>
Hello Button
</Button>
</>
)
export const Link = () => <Button link>Hello Button</Button>
export const Ghost = () => (
<>
<Button style="ghost" onClick={action('clicked')}>
Hello Button
</Button>
<br />
<br />
<Button style="ghost" size="small" onClick={action('clicked')}>
Hello Button
</Button>
</>
)
export const Text = () => (
<>
<Button style="text" onClick={action('clicked')}>
Hello Button
</Button>
<br />
<br />
<Button style="text" size="small" onClick={action('clicked')}>
Hello Button
</Button>
</>
)

View File

@ -1,44 +1,53 @@
import React, { ReactElement } from 'react'
import Link from 'next/link'
import React, { ReactNode, FormEvent } from 'react'
import { Link } from 'gatsby'
import classNames from 'classnames/bind'
import styles from './Button.module.css'
declare type ButtonProps = {
children: string | ReactElement
const cx = classNames.bind(styles)
interface ButtonProps {
children: ReactNode
className?: string
primary?: boolean
link?: boolean
href?: string
size?: string
onClick?: any
onClick?: (e: FormEvent) => void
disabled?: boolean
to?: string
name?: string
size?: 'small'
style?: 'primary' | 'ghost' | 'text'
type?: 'submit'
download?: boolean
}
const Button = ({
primary,
link,
export default function Button({
href,
size,
children,
className,
to,
size,
style,
...props
}: ButtonProps) => {
const classes = primary
? `${styles.button} ${styles.primary}`
: link
? `${styles.button} ${styles.link}`
: styles.button
}: ButtonProps) {
const styleClasses = cx({
button: true,
primary: style === 'primary',
ghost: style === 'ghost',
text: style === 'text',
small: size === 'small',
[className]: className
})
return href ? (
<Link href={href}>
<a className={`${classes} ${className}`} {...props}>
{children}
</a>
return to ? (
<Link to={to} className={styleClasses} {...props}>
{children}
</Link>
) : href ? (
<a href={href} className={styleClasses} {...props}>
{children}
</a>
) : (
<button className={`${classes} ${className}`} {...props}>
<button className={styleClasses} {...props}>
{children}
</button>
)
}
export default Button

View File

@ -0,0 +1,56 @@
import React from 'react'
import { Helmet } from 'react-helmet'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
export default function Seo({
title,
description,
uri,
location
}: {
title?: string
description?: string
uri: string
location: Location
}) {
const { siteTitle, siteTagline, siteUrl, siteImage } = useSiteMetadata()
// Remove trailing slash from all URLs
const canonical = `${siteUrl}${uri}`.replace(/\/$/, '')
return (
<Helmet
defaultTitle={`${siteTitle}${siteTagline}`}
titleTemplate="%s — Ocean Protocol"
title={title}
>
<html lang="en" />
{typeof window !== 'undefined' &&
window.location &&
window.location.hostname !== 'oceanprotocol.com' && (
<meta name="robots" content="noindex,nofollow" />
)}
<link rel="canonical" href={canonical} />
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={location.href} />
<meta
name="image"
content={`${siteUrl}${siteImage.childImageSharp.original.src}`}
/>
<meta
property="og:image"
content={`${siteUrl}${siteImage.childImageSharp.original.src}`}
/>
<meta property="og:site_name" content={siteTitle} />
<meta name="twitter:creator" content="@oceanprotocol" />
<meta name="twitter:card" content="summary_large_image" />
</Helmet>
)
}

View File

@ -1,11 +1,6 @@
import React from 'react'
import Time from '../Time'
import Link from 'next/link'
import { Link } from 'gatsby'
export default function DdoLinkCell({ id, name }: { id: any; name: any }) {
return (
<Link href="/asset/[did]" as={`/asset/${id}`} passHref>
<a>{name}</a>
</Link>
)
return <Link to={`/asset/${id}`}>{name}</Link>
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import Link from 'next/link'
import { Link } from 'gatsby'
import shortid from 'shortid'
import slugify from 'slugify'
import styles from './Tags.module.css'
@ -20,10 +20,8 @@ const Tag = ({ tag, noLinks }: { tag: string; noLinks?: boolean }) => {
return noLinks ? (
<span className={styles.tag}>{cleanTag}</span>
) : (
<Link href={`/search?tags=${tag}`}>
<a className={styles.tag} title={cleanTag}>
{cleanTag}
</a>
<Link to={`/search?tags=${tag}`} className={styles.tag} title={cleanTag}>
{cleanTag}
</Link>
)
}

View File

@ -1,6 +1,6 @@
import React from 'react'
import { DDO } from '@oceanprotocol/squid'
import Link from 'next/link'
import { Link } from 'gatsby'
import Dotdotdot from 'react-dotdotdot'
import {
AdditionalInformationMarket,
@ -40,7 +40,7 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({ ddo }: AssetTeaserProps) => {
return (
<article className={styles.teaser}>
<Link href="/asset/[did]" as={`/asset/${ddo.id}`}>
<Link to={`/asset/${ddo.id}`}>
<a className={styles.link}>
<h1 className={styles.title}>{name}</h1>
{access === 'Compute' && (

View File

@ -6,7 +6,7 @@ import styles from './JobDetailsDialog.module.css'
import MetaItem from '../templates/AssetDetails/MetaItem'
import Time from '../atoms/Time'
import shortid from 'shortid'
import Link from 'next/link'
import { Link } from 'gatsby'
export default function JobDetailsDialog({
computeItem,
@ -46,8 +46,8 @@ export default function JobDetailsDialog({
<MetaItem
title="Results"
content={resultsUrls.map((url: string) => (
<Link href={url} key={shortid.generate()} passHref>
<a>{url}</a>
<Link to={url} key={shortid.generate()}>
{url}
</Link>
))}
/>
@ -55,24 +55,12 @@ export default function JobDetailsDialog({
{algorithmLogUrl && (
<MetaItem
title="Algorithm Log"
content={
<Link href={algorithmLogUrl} key={shortid.generate()} passHref>
<a>{algorithmLogUrl}</a>
</Link>
}
content={<Link to={algorithmLogUrl}>{algorithmLogUrl}</Link>}
/>
)}
<MetaItem
title="Data Set"
content={
<Link
href="/asset/[did]"
as={`/asset/${computeItem.ddo.id}`}
passHref
>
<a>{name}</a>
</Link>
}
content={<Link to={`/asset/${computeItem.ddo.id}`}>{name}</Link>}
/>
</div>
</BaseDialog>

View File

@ -1,8 +1,8 @@
import React from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { menu } from '../../../site.config'
import { Link } from 'gatsby'
import { useLocation } from '@reach/router'
import styles from './Menu.module.css'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
declare type MenuItem = {
name: string
@ -10,20 +10,23 @@ declare type MenuItem = {
}
function MenuLink({ item }: { item: MenuItem }) {
const router = useRouter()
const location = useLocation()
const classes =
router && router.pathname === item.link
location && location.pathname === item.link
? `${styles.link} ${styles.active}`
: styles.link
return (
<Link key={item.name} href={item.link}>
<a className={classes}>{item.name}</a>
<Link key={item.name} to={item.link} className={classes}>
{item.name}
</Link>
)
}
export default function Menu() {
const { menu } = useSiteMetadata()
return (
<nav className={styles.menu}>
{menu.map((item: MenuItem) => (

View File

@ -1,6 +1,5 @@
import AssetTeaser from '../molecules/AssetTeaser'
import React from 'react'
import { useRouter } from 'next/router'
import { QueryResult } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import shortid from 'shortid'
import Pagination from '../molecules/Pagination'
@ -12,8 +11,6 @@ declare type AssetListProps = {
}
const AssetList: React.FC<AssetListProps> = ({ queryResult }) => {
const router = useRouter()
// Construct the urls on the pagination links. This is only for UX,
// since the links are no <Link> they will not work by itself.
function hrefBuilder(pageIndex: number) {

View File

@ -10,9 +10,8 @@ import Price from '../atoms/Price'
import { fromWei } from 'web3-utils'
import DateCell from '../atoms/Table/DateCell'
import DdoLinkCell from '../atoms/Table/DdoLinkCell'
import { DDO, MetaDataMain } from '@oceanprotocol/squid'
import { MetaDataMain } from '@oceanprotocol/squid'
import { findServiceByType } from '../../utils'
import { config } from '../../config/ocean'
const consumedColumns = [
{

View File

@ -1,19 +1,19 @@
import React from 'react'
import Link from 'next/link'
import { Link } from 'gatsby'
import Menu from '../molecules/Menu'
import styles from './Header.module.css'
import { title } from '../../../site.config'
import Logo from '@oceanprotocol/art/logo/logo.svg'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
export default function Header() {
const { siteTitle } = useSiteMetadata()
return (
<header className={styles.header}>
<div className={styles.content}>
<Link href="/">
<a className={styles.logoUnit}>
<Logo />
<h1 className={styles.title}>{title}</h1>
</a>
<Link to="/" className={styles.logoUnit}>
<Logo />
<h1 className={styles.title}>{siteTitle}</h1>
</Link>
<Menu />

View File

@ -1,14 +1,12 @@
import React, { useEffect, useState } from 'react'
import Loader from '../atoms/Loader'
import { DDO, MetaDataMain } from '@oceanprotocol/squid'
import { MetaDataMain } from '@oceanprotocol/squid'
import {
useOcean,
OceanConnectionStatus,
useSearch
} from '@oceanprotocol/react'
import Table from '../atoms/Table'
import Time from '../atoms/Time'
import Link from 'next/link'
import Price from '../atoms/Price'
import { fromWei } from 'web3-utils'
import { findServiceByType } from '../../utils'

View File

@ -1,5 +1,5 @@
import React from 'react'
import Layout from '../../Layout'
import Layout from '../../components/Layout'
import AssetList from '../organisms/AssetList'
import { QueryResult } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'

View File

@ -1,14 +1,14 @@
import React from 'react'
import Link from 'next/link'
import { Link } from 'gatsby'
import shortid from 'shortid'
import Layout from '../../Layout'
import Layout from '../../components/Layout'
import Button from '../atoms/Button'
import SearchBar from '../molecules/SearchBar'
import { title, description } from '../../../site.config'
import Explore from '../../images/explore.svg'
import Publish from '../../images/publish.svg'
import DataPool from '../../images/datapool.svg'
import styles from './Home.module.css'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
const actions = [
{
@ -36,25 +36,29 @@ const actions = [
}
]
const HomePage = () => {
export default function HomePage() {
const { siteTitle, siteDescription } = useSiteMetadata()
return (
<Layout noPageHeader>
<header className={styles.header}>
<h1>{title}</h1>
<h2>{description}</h2>
<h1>{siteTitle}</h1>
<h2>{siteDescription}</h2>
<SearchBar large />
</header>
<div className={styles.actions}>
{actions.map((action) => (
<Link key={shortid.generate()} href={action.link}>
<Link key={shortid.generate()} to={action.link}>
<a
className={action.comingSoon ? styles.comingSoon : styles.action}
>
{action.icon}
<h3 className={styles.actionTitle}>{action.title}</h3>
<p>{action.text}</p>
<Button primary={action.primary}>{action.action}</Button>
<Button style={action.primary ? 'primary' : null}>
{action.action}
</Button>
</a>
</Link>
))}
@ -62,4 +66,3 @@ const HomePage = () => {
</Layout>
)
}
export default HomePage

View File

@ -1,5 +1,5 @@
import React from 'react'
import Layout from '../../Layout'
import Layout from '../../components/Layout'
import PublishForm from '../molecules/PublishForm/PublishForm'
import styles from './Publish.module.css'
import Web3Feedback from '../molecules/Web3Feedback'

View File

@ -1,5 +1,5 @@
import React from 'react'
import Layout from '../../Layout'
import Layout from '../../components/Layout'
import styles from './Transactions.module.css'
import Web3Feedback from '../molecules/Web3Feedback'
import ConsumedList from '../organisms/ConsumedList'

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react'
import { DDO } from '@oceanprotocol/squid'
import Link from 'next/link'
import Layout from '../../../Layout'
import { Link } from 'gatsby'
import Layout from '../../../components/Layout'
import { MetaDataMarket } from '../../../@types/MetaData'
import Time from '../../atoms/Time'
import Markdown from '../../atoms/Markdown'
@ -67,7 +67,7 @@ const AssetDetailsPageMeta = ({
</p>
{categories && (
<p>
<Link href={`/search?categories=["${categories[0]}"]`}>
<Link to={`/search?categories=["${categories[0]}"]`}>
<a>{categories[0]}</a>
</Link>
</p>

View File

@ -1,6 +1,6 @@
import React from 'react'
import { QueryResult } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import Layout from '../../Layout'
import Layout from '../../components/Layout'
import PageHeader from '../molecules/PageHeader'
import SearchBar from '../molecules/SearchBar'
import AssetList from '../organisms/AssetList'

11
src/global/Styles.tsx Normal file
View File

@ -0,0 +1,11 @@
import React, { ReactElement, ReactNode } from 'react'
import '../global/styles.css'
export default function Styles({
children
}: {
children: ReactNode
}): ReactElement {
return <>{children}</>
}

View File

@ -0,0 +1,8 @@
import React, { ReactElement } from 'react'
import Styles from '../global/Styles'
const wrapPageElement = ({ element }: { element: ReactElement }) => (
<Styles>{element}</Styles>
)
export default wrapPageElement

View File

@ -0,0 +1,48 @@
import { useStaticQuery, graphql } from 'gatsby'
const query = graphql`
query {
allFile(filter: { relativePath: { eq: "site.json" } }) {
edges {
node {
childContentJson {
site {
siteTitle
siteTagline
siteDescription
siteUrl
siteIcon
siteImage {
childImageSharp {
original {
src
}
}
}
analyticsId
company {
name
address {
location
street
city
zip
country
}
}
social {
name
url
}
}
}
}
}
}
}
`
export function useSiteMetadata() {
const data = useStaticQuery(query)
return data.allFile.edges[0].node.childContentJson.site
}

12
tests/unit/testRender.ts Normal file
View File

@ -0,0 +1,12 @@
import { render } from '@testing-library/react'
import { ReactElement } from 'react'
const testRender = (component: ReactElement): void => {
it('renders without crashing', () => {
const { container } = render(component)
expect(container.firstChild).toBeInTheDocument()
})
}
export default testRender