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>
|
||||
|
||||
<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>
|
||||
.loader {
|
||||
display: block;
|
||||
|
@ -21,5 +21,5 @@
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"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'
|
||||
|
||||
interface AssetProps {
|
||||
location: Location
|
||||
match: {
|
||||
params: {
|
||||
did: string
|
||||
|
@ -4,6 +4,7 @@ import Channel from './Channel'
|
||||
import { User } from '../../context'
|
||||
import { createMemoryHistory } from 'history'
|
||||
import { userMockConnected } from '../../../__mocks__/user-mock'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
|
||||
describe('Channel', () => {
|
||||
it('renders without crashing', () => {
|
||||
@ -11,12 +12,14 @@ describe('Channel', () => {
|
||||
|
||||
const { container } = render(
|
||||
<User.Provider value={userMockConnected}>
|
||||
<MemoryRouter>
|
||||
<Channel
|
||||
match={{
|
||||
params: { channel: 'ai-for-good' }
|
||||
}}
|
||||
history={history}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</User.Provider>
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
|
@ -1,18 +1,25 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import Route from './Route'
|
||||
import { BrowserRouter as Router } from 'react-router-dom'
|
||||
|
||||
describe('Route', () => {
|
||||
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()
|
||||
})
|
||||
|
||||
it('renders title & description', () => {
|
||||
const { container } = render(
|
||||
<Router>
|
||||
<Route title="Hello Title" description="Hello Description">
|
||||
Hello
|
||||
</Route>
|
||||
</Router>
|
||||
)
|
||||
expect(container.querySelector('.title')).toHaveTextContent(
|
||||
'Hello Title'
|
||||
|
@ -1,36 +1,43 @@
|
||||
import React from 'react'
|
||||
import Helmet from 'react-helmet'
|
||||
import Content from '../atoms/Content'
|
||||
import styles from './Route.module.scss'
|
||||
import meta from '../../data/meta.json'
|
||||
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 = ({
|
||||
title,
|
||||
description,
|
||||
image,
|
||||
shareImage,
|
||||
wide,
|
||||
children,
|
||||
className
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
image?: any
|
||||
children: any
|
||||
wide?: boolean
|
||||
className?: string
|
||||
}) => (
|
||||
}: RouteProps) => {
|
||||
// Strip HTML from passed title
|
||||
const titleSanitized = title.replace(/(<([^>]+)>)/gi, '')
|
||||
|
||||
return (
|
||||
<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>
|
||||
<Seo
|
||||
title={titleSanitized}
|
||||
description={description}
|
||||
shareImage={shareImage}
|
||||
/>
|
||||
|
||||
<article>
|
||||
<header className={styles.header}>
|
||||
<Content wide={wide}>
|
||||
<h1 className={styles.title}>{title}</h1>
|
||||
<h1 className={styles.title}>{titleSanitized}</h1>
|
||||
|
||||
{image && image}
|
||||
|
||||
@ -46,6 +53,7 @@ const Route = ({
|
||||
{children}
|
||||
</article>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export default Route
|
||||
|
@ -2,6 +2,7 @@
|
||||
"title": "Commons",
|
||||
"description": "A marketplace to find and publish open data sets in the Ocean Network.",
|
||||
"company": "Ocean Protocol Foundation Ltd.",
|
||||
"url": "https://commons.oceanprotocol.com",
|
||||
"social": [
|
||||
{
|
||||
"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 { render } from '@testing-library/react'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
import About from './About'
|
||||
|
||||
describe('About', () => {
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(<About />)
|
||||
const { container } = render(
|
||||
<MemoryRouter>
|
||||
<About />
|
||||
</MemoryRouter>
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { BrowserRouter as Router } from 'react-router-dom'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
import { render } from '@testing-library/react'
|
||||
import Channels from './Channels'
|
||||
import { User } from '../context'
|
||||
@ -9,9 +9,9 @@ describe('Channels', () => {
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(
|
||||
<User.Provider value={userMockConnected}>
|
||||
<Router>
|
||||
<MemoryRouter>
|
||||
<Channels />
|
||||
</Router>
|
||||
</MemoryRouter>
|
||||
</User.Provider>
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import { render, fireEvent } from '@testing-library/react'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
import Faucet from './Faucet'
|
||||
import { User } from '../context'
|
||||
import { userMockConnected } from '../../__mocks__/user-mock'
|
||||
@ -7,7 +8,9 @@ import { userMockConnected } from '../../__mocks__/user-mock'
|
||||
const setup = () => {
|
||||
const utils = render(
|
||||
<User.Provider value={userMockConnected}>
|
||||
<MemoryRouter>
|
||||
<Faucet />
|
||||
</MemoryRouter>
|
||||
</User.Provider>
|
||||
)
|
||||
const button = utils.getByText('Request Ether')
|
||||
@ -21,7 +24,7 @@ const setup = () => {
|
||||
|
||||
describe('Faucet', () => {
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(<Faucet />)
|
||||
const { container } = setup()
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
})
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
import { User } from '../context'
|
||||
import History from './History'
|
||||
|
||||
describe('History', () => {
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(<History />)
|
||||
const { container } = render(
|
||||
<MemoryRouter>
|
||||
<History />
|
||||
</MemoryRouter>
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -27,7 +32,9 @@ describe('History', () => {
|
||||
|
||||
const { container } = render(
|
||||
<User.Provider value={context}>
|
||||
<MemoryRouter>
|
||||
<History />
|
||||
</MemoryRouter>
|
||||
</User.Provider>
|
||||
)
|
||||
expect(container.querySelector('.message')).toBeInTheDocument()
|
||||
|
@ -1,10 +1,15 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import NotFound from './NotFound'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
|
||||
describe('NotFound', () => {
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(<NotFound />)
|
||||
const { container } = render(
|
||||
<MemoryRouter>
|
||||
<NotFound />
|
||||
</MemoryRouter>
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
import { render, fireEvent } from '@testing-library/react'
|
||||
import Publish from '.'
|
||||
import { User } from '../../context'
|
||||
@ -8,7 +9,9 @@ describe('Publish', () => {
|
||||
it('renders without crashing', () => {
|
||||
const { container, getByText } = render(
|
||||
<User.Provider value={userMockConnected}>
|
||||
<MemoryRouter>
|
||||
<Publish />
|
||||
</MemoryRouter>
|
||||
</User.Provider>
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
@ -18,7 +21,9 @@ describe('Publish', () => {
|
||||
it('next button works', () => {
|
||||
const { getByText, getByLabelText, getByTestId } = render(
|
||||
<User.Provider value={userMockConnected}>
|
||||
<MemoryRouter>
|
||||
<Publish />
|
||||
</MemoryRouter>
|
||||
</User.Provider>
|
||||
)
|
||||
|
||||
|
@ -1,10 +1,15 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import Styleguide from './Styleguide'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
|
||||
describe('Styleguide', () => {
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(<Styleguide />)
|
||||
const { container } = render(
|
||||
<MemoryRouter>
|
||||
<Styleguide />
|
||||
</MemoryRouter>
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user