mirror of
https://github.com/oceanprotocol/commons.git
synced 2023-03-15 18:03:00 +01:00
SEO component
This commit is contained in:
parent
30249447ce
commit
b8113798b6
@ -12,39 +12,6 @@
|
|||||||
|
|
||||||
<title>Commons</title>
|
<title>Commons</title>
|
||||||
|
|
||||||
<meta
|
|
||||||
content="A marketplace to find and publish open data sets in the Ocean Network."
|
|
||||||
name="description"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
content="https://commons.oceanprotocol.com/share.png"
|
|
||||||
name="image"
|
|
||||||
/>
|
|
||||||
<link href="https://commons.oceanprotocol.com" rel="canonical" />
|
|
||||||
|
|
||||||
<meta content="https://commons.oceanprotocol.com" property="og:url" />
|
|
||||||
<meta content="Commons" property="og:title" />
|
|
||||||
<meta
|
|
||||||
content="A marketplace to find and publish open data sets in the Ocean Network."
|
|
||||||
property="og:description"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
content="https://commons.oceanprotocol.com/share.png"
|
|
||||||
property="og:image"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<meta content="summary_large_image" name="twitter:card" />
|
|
||||||
<meta content="@oceanprotocol" name="twitter:creator" />
|
|
||||||
<meta content="Commons" name="twitter:title" />
|
|
||||||
<meta
|
|
||||||
content="A marketplace to find and publish open data sets in the Ocean Network."
|
|
||||||
name="twitter:description"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
content="https://commons.oceanprotocol.com/share.png"
|
|
||||||
name="twitter:image"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.loader {
|
.loader {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -21,5 +21,5 @@
|
|||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"theme_color": "#141414",
|
"theme_color": "#141414",
|
||||||
"background_color": "#141414"
|
"background_color": "#ffffff"
|
||||||
}
|
}
|
||||||
|
71
client/src/components/atoms/Seo.tsx
Normal file
71
client/src/components/atoms/Seo.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Helmet from 'react-helmet'
|
||||||
|
import { withRouter, RouteComponentProps } from 'react-router-dom'
|
||||||
|
import meta from '../../data/meta.json'
|
||||||
|
import imageDefault from '../../img/share.png'
|
||||||
|
|
||||||
|
const MetaTags = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
url,
|
||||||
|
image
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
url: string
|
||||||
|
image: string
|
||||||
|
}) => (
|
||||||
|
<Helmet defaultTitle={meta.title} titleTemplate={`%s - ${meta.title}`}>
|
||||||
|
<html lang="en" />
|
||||||
|
|
||||||
|
{title && <title>{title}</title>}
|
||||||
|
|
||||||
|
{/* General tags */}
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<meta name="image" content={image} />
|
||||||
|
<link rel="canonical" href={url} />
|
||||||
|
|
||||||
|
{/* OpenGraph tags */}
|
||||||
|
<meta property="og:url" content={url} />
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:image" content={image} />
|
||||||
|
|
||||||
|
{/* Twitter Card tags */}
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:creator" content="@oceanprotocol" />
|
||||||
|
<meta name="twitter:title" content={title} />
|
||||||
|
<meta name="twitter:description" content={description} />
|
||||||
|
<meta name="twitter:image" content={image} />
|
||||||
|
|
||||||
|
{/* Prevent search engine indexing except for live */}
|
||||||
|
{window.location.hostname !== 'commons.oceanprotocol.com' && (
|
||||||
|
<meta name="robots" content="noindex,nofollow" />
|
||||||
|
)}
|
||||||
|
</Helmet>
|
||||||
|
)
|
||||||
|
|
||||||
|
interface SeoProps extends RouteComponentProps {
|
||||||
|
title?: string
|
||||||
|
description?: string
|
||||||
|
shareImage?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Seo = ({ title, description, shareImage, location }: SeoProps) => {
|
||||||
|
title = title || meta.title
|
||||||
|
description = description || meta.description
|
||||||
|
shareImage = shareImage || meta.url + imageDefault
|
||||||
|
|
||||||
|
const url = meta.url + location.pathname + location.search
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MetaTags
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
url={url}
|
||||||
|
image={shareImage}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(Seo)
|
@ -10,7 +10,6 @@ import CategoryImage from '../../atoms/CategoryImage'
|
|||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
|
|
||||||
interface AssetProps {
|
interface AssetProps {
|
||||||
location: Location
|
|
||||||
match: {
|
match: {
|
||||||
params: {
|
params: {
|
||||||
did: string
|
did: string
|
||||||
|
@ -4,6 +4,7 @@ import Channel from './Channel'
|
|||||||
import { User } from '../../context'
|
import { User } from '../../context'
|
||||||
import { createMemoryHistory } from 'history'
|
import { createMemoryHistory } from 'history'
|
||||||
import { userMockConnected } from '../../../__mocks__/user-mock'
|
import { userMockConnected } from '../../../__mocks__/user-mock'
|
||||||
|
import { MemoryRouter } from 'react-router'
|
||||||
|
|
||||||
describe('Channel', () => {
|
describe('Channel', () => {
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
@ -11,12 +12,14 @@ describe('Channel', () => {
|
|||||||
|
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<User.Provider value={userMockConnected}>
|
<User.Provider value={userMockConnected}>
|
||||||
<Channel
|
<MemoryRouter>
|
||||||
match={{
|
<Channel
|
||||||
params: { channel: 'ai-for-good' }
|
match={{
|
||||||
}}
|
params: { channel: 'ai-for-good' }
|
||||||
history={history}
|
}}
|
||||||
/>
|
history={history}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
)
|
)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
import Route from './Route'
|
import Route from './Route'
|
||||||
|
import { BrowserRouter as Router } from 'react-router-dom'
|
||||||
|
|
||||||
describe('Route', () => {
|
describe('Route', () => {
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const { container } = render(<Route title="Hello Title">Hello</Route>)
|
const { container } = render(
|
||||||
|
<Router>
|
||||||
|
<Route title="Hello Title">Hello</Route>
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders title & description', () => {
|
it('renders title & description', () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<Route title="Hello Title" description="Hello Description">
|
<Router>
|
||||||
Hello
|
<Route title="Hello Title" description="Hello Description">
|
||||||
</Route>
|
Hello
|
||||||
|
</Route>
|
||||||
|
</Router>
|
||||||
)
|
)
|
||||||
expect(container.querySelector('.title')).toHaveTextContent(
|
expect(container.querySelector('.title')).toHaveTextContent(
|
||||||
'Hello Title'
|
'Hello Title'
|
||||||
|
@ -1,51 +1,59 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Helmet from 'react-helmet'
|
|
||||||
import Content from '../atoms/Content'
|
import Content from '../atoms/Content'
|
||||||
import styles from './Route.module.scss'
|
import styles from './Route.module.scss'
|
||||||
import meta from '../../data/meta.json'
|
|
||||||
import Markdown from '../atoms/Markdown'
|
import Markdown from '../atoms/Markdown'
|
||||||
|
import Seo from '../atoms/Seo'
|
||||||
|
|
||||||
|
interface RouteProps {
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
image?: any
|
||||||
|
shareImage?: string
|
||||||
|
children: any
|
||||||
|
wide?: boolean
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
const Route = ({
|
const Route = ({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
image,
|
image,
|
||||||
|
shareImage,
|
||||||
wide,
|
wide,
|
||||||
children,
|
children,
|
||||||
className
|
className
|
||||||
}: {
|
}: RouteProps) => {
|
||||||
title: string
|
// Strip HTML from passed title
|
||||||
description?: string
|
const titleSanitized = title.replace(/(<([^>]+)>)/gi, '')
|
||||||
image?: any
|
|
||||||
children: any
|
|
||||||
wide?: boolean
|
|
||||||
className?: string
|
|
||||||
}) => (
|
|
||||||
<div className={className}>
|
|
||||||
<Helmet defaultTitle={meta.title} titleTemplate={`%s - ${meta.title}`}>
|
|
||||||
{/* Strip HTML from passed title */}
|
|
||||||
<title>{title.replace(/(<([^>]+)>)/gi, '')}</title>
|
|
||||||
{description && <meta name="description" content={description} />}
|
|
||||||
</Helmet>
|
|
||||||
|
|
||||||
<article>
|
return (
|
||||||
<header className={styles.header}>
|
<div className={className}>
|
||||||
<Content wide={wide}>
|
<Seo
|
||||||
<h1 className={styles.title}>{title}</h1>
|
title={titleSanitized}
|
||||||
|
description={description}
|
||||||
|
shareImage={shareImage}
|
||||||
|
/>
|
||||||
|
|
||||||
{image && image}
|
<article>
|
||||||
|
<header className={styles.header}>
|
||||||
|
<Content wide={wide}>
|
||||||
|
<h1 className={styles.title}>{titleSanitized}</h1>
|
||||||
|
|
||||||
{description && (
|
{image && image}
|
||||||
<Markdown
|
|
||||||
text={description}
|
|
||||||
className={styles.description}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Content>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{children}
|
{description && (
|
||||||
</article>
|
<Markdown
|
||||||
</div>
|
text={description}
|
||||||
)
|
className={styles.description}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Content>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default Route
|
export default Route
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"title": "Commons",
|
"title": "Commons",
|
||||||
"description": "A marketplace to find and publish open data sets in the Ocean Network.",
|
"description": "A marketplace to find and publish open data sets in the Ocean Network.",
|
||||||
"company": "Ocean Protocol Foundation Ltd.",
|
"company": "Ocean Protocol Foundation Ltd.",
|
||||||
|
"url": "https://commons.oceanprotocol.com",
|
||||||
"social": [
|
"social": [
|
||||||
{
|
{
|
||||||
"title": "Site",
|
"title": "Site",
|
||||||
|
BIN
client/src/img/share.png
Normal file
BIN
client/src/img/share.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 167 KiB |
@ -1,10 +1,15 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
|
import { MemoryRouter } from 'react-router'
|
||||||
import About from './About'
|
import About from './About'
|
||||||
|
|
||||||
describe('About', () => {
|
describe('About', () => {
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const { container } = render(<About />)
|
const { container } = render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<About />
|
||||||
|
</MemoryRouter>
|
||||||
|
)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { BrowserRouter as Router } from 'react-router-dom'
|
import { MemoryRouter } from 'react-router'
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
import Channels from './Channels'
|
import Channels from './Channels'
|
||||||
import { User } from '../context'
|
import { User } from '../context'
|
||||||
@ -9,9 +9,9 @@ describe('Channels', () => {
|
|||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<User.Provider value={userMockConnected}>
|
<User.Provider value={userMockConnected}>
|
||||||
<Router>
|
<MemoryRouter>
|
||||||
<Channels />
|
<Channels />
|
||||||
</Router>
|
</MemoryRouter>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
)
|
)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render, fireEvent } from '@testing-library/react'
|
import { render, fireEvent } from '@testing-library/react'
|
||||||
|
import { MemoryRouter } from 'react-router'
|
||||||
import Faucet from './Faucet'
|
import Faucet from './Faucet'
|
||||||
import { User } from '../context'
|
import { User } from '../context'
|
||||||
import { userMockConnected } from '../../__mocks__/user-mock'
|
import { userMockConnected } from '../../__mocks__/user-mock'
|
||||||
@ -7,7 +8,9 @@ import { userMockConnected } from '../../__mocks__/user-mock'
|
|||||||
const setup = () => {
|
const setup = () => {
|
||||||
const utils = render(
|
const utils = render(
|
||||||
<User.Provider value={userMockConnected}>
|
<User.Provider value={userMockConnected}>
|
||||||
<Faucet />
|
<MemoryRouter>
|
||||||
|
<Faucet />
|
||||||
|
</MemoryRouter>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
)
|
)
|
||||||
const button = utils.getByText('Request Ether')
|
const button = utils.getByText('Request Ether')
|
||||||
@ -21,7 +24,7 @@ const setup = () => {
|
|||||||
|
|
||||||
describe('Faucet', () => {
|
describe('Faucet', () => {
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const { container } = render(<Faucet />)
|
const { container } = setup()
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
|
import { MemoryRouter } from 'react-router'
|
||||||
import { User } from '../context'
|
import { User } from '../context'
|
||||||
import History from './History'
|
import History from './History'
|
||||||
|
|
||||||
describe('History', () => {
|
describe('History', () => {
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const { container } = render(<History />)
|
const { container } = render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<History />
|
||||||
|
</MemoryRouter>
|
||||||
|
)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -27,7 +32,9 @@ describe('History', () => {
|
|||||||
|
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<User.Provider value={context}>
|
<User.Provider value={context}>
|
||||||
<History />
|
<MemoryRouter>
|
||||||
|
<History />
|
||||||
|
</MemoryRouter>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
)
|
)
|
||||||
expect(container.querySelector('.message')).toBeInTheDocument()
|
expect(container.querySelector('.message')).toBeInTheDocument()
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
import NotFound from './NotFound'
|
import NotFound from './NotFound'
|
||||||
|
import { MemoryRouter } from 'react-router'
|
||||||
|
|
||||||
describe('NotFound', () => {
|
describe('NotFound', () => {
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const { container } = render(<NotFound />)
|
const { container } = render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<NotFound />
|
||||||
|
</MemoryRouter>
|
||||||
|
)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { MemoryRouter } from 'react-router'
|
||||||
import { render, fireEvent } from '@testing-library/react'
|
import { render, fireEvent } from '@testing-library/react'
|
||||||
import Publish from '.'
|
import Publish from '.'
|
||||||
import { User } from '../../context'
|
import { User } from '../../context'
|
||||||
@ -8,7 +9,9 @@ describe('Publish', () => {
|
|||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const { container, getByText } = render(
|
const { container, getByText } = render(
|
||||||
<User.Provider value={userMockConnected}>
|
<User.Provider value={userMockConnected}>
|
||||||
<Publish />
|
<MemoryRouter>
|
||||||
|
<Publish />
|
||||||
|
</MemoryRouter>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
)
|
)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
@ -18,7 +21,9 @@ describe('Publish', () => {
|
|||||||
it('next button works', () => {
|
it('next button works', () => {
|
||||||
const { getByText, getByLabelText, getByTestId } = render(
|
const { getByText, getByLabelText, getByTestId } = render(
|
||||||
<User.Provider value={userMockConnected}>
|
<User.Provider value={userMockConnected}>
|
||||||
<Publish />
|
<MemoryRouter>
|
||||||
|
<Publish />
|
||||||
|
</MemoryRouter>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
import Styleguide from './Styleguide'
|
import Styleguide from './Styleguide'
|
||||||
|
import { MemoryRouter } from 'react-router'
|
||||||
|
|
||||||
describe('Styleguide', () => {
|
describe('Styleguide', () => {
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const { container } = render(<Styleguide />)
|
const { container } = render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<Styleguide />
|
||||||
|
</MemoryRouter>
|
||||||
|
)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user