1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

refactor for Gatsby, update all the things

This commit is contained in:
Matthias Kretschmann 2020-06-30 11:28:49 +02:00
parent 41fcb2f4c3
commit 38ccfc635e
Signed by: m
GPG Key ID: 606EEEF3C479A91F
56 changed files with 19744 additions and 7532 deletions

View File

@ -1,6 +1,7 @@
{ {
"parser": "babel-eslint",
"extends": ["eslint:recommended", "prettier"], "extends": ["eslint:recommended", "prettier"],
"env": { "es6": true, "browser": true, "node": true }, "env": { "es6": true, "browser": true, "node": true, "jest": true },
"settings": { "settings": {
"react": { "react": {
"version": "detect" "version": "detect"

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
* @maxieprotocol @kremalicious @pfmescher @unjapones * @mihaisc @kremalicious

4
.gitignore vendored
View File

@ -4,8 +4,10 @@ out
.next .next
.idea .idea
.env .env
.env.build
coverage coverage
dist
public
.cache
storybook-static storybook-static
public/storybook public/storybook
.artifacts .artifacts

View File

@ -1,7 +1,3 @@
module.exports = { module.exports = {
stories: [ stories: ['../src/**/*.stories.tsx', '../tests/**/*.stories.tsx']
'../src/components/**/*.stories.tsx',
'../src/styles/**/*.stories.tsx'
],
addons: []
} }

View File

@ -1,20 +1,11 @@
import React from 'react' import React from 'react'
import { addDecorator } from '@storybook/react' import { addDecorator } from '@storybook/react'
import WebFont from 'webfontloader'
WebFont.load({
google: {
families: ['Montserrat:400,400i,600']
}
})
// Import global css with custom properties once for all stories. // Import global css with custom properties once for all stories.
// Needed because in Next.js we impoprt that file only once too, import '../src/global/styles.css'
// in src/_app.tsx which does not get loaded by Storybook
import '../src/styles/global.css'
// Wrapper for all stories previews // Wrapper for all stories previews
addDecorator(storyFn => ( addDecorator((storyFn) => (
<div <div
style={{ style={{
minHeight: '100vh', minHeight: '100vh',
@ -25,3 +16,19 @@ addDecorator(storyFn => (
{storyFn()} {storyFn()}
</div> </div>
)) ))
// Gatsby's Link overrides:
// Gatsby defines a global called ___loader to prevent its method calls from creating console errors you override it here
global.___loader = {
enqueue: () => {},
hovering: () => {}
}
// Gatsby internal mocking to prevent unnecessary errors in storybook testing environment
global.__PATH_PREFIX__ = ''
global.__BASE_PATH__ = ''
// This is to utilized to override the window.___navigate method Gatsby defines and uses to report what path a Link would be taking us to if it wasn't inside a storybook
window.___navigate = (pathname) => {
action('NavigateTo:')(pathname)
}

View File

@ -1,6 +1,8 @@
// https://www.gatsbyjs.org/docs/visual-testing-with-storybook/
// Make CSS modules work // Make CSS modules work
// https://github.com/storybookjs/storybook/issues/4306#issuecomment-517951264 // https://github.com/storybookjs/storybook/issues/4306#issuecomment-517951264
const setCssModulesRule = rule => { const setCssModulesRule = (rule) => {
const nextRule = rule const nextRule = rule
const cssLoader = rule.use[1] const cssLoader = rule.use[1]
@ -15,8 +17,8 @@ const setCssModulesRule = rule => {
return nextRule return nextRule
} }
module.exports = async ({ config, mode }) => { module.exports = ({ config }) => {
const cssRules = config.module.rules.map(rule => { const cssRules = config.module.rules.map((rule) => {
const isCssRule = rule.test.toString().indexOf('css') !== -1 const isCssRule = rule.test.toString().indexOf('css') !== -1
let nextRule = rule let nextRule = rule
@ -28,28 +30,46 @@ module.exports = async ({ config, mode }) => {
config.module.rules = cssRules config.module.rules = cssRules
// Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]
// use installed babel-loader which is v8.0-beta (which is meant to work with @babel/core@7)
config.module.rules[0].use[0].loader = require.resolve('babel-loader')
// use @babel/preset-react for JSX and env (instead of staged presets)
config.module.rules[0].use[0].options.presets = [
require.resolve('@babel/preset-react'),
require.resolve('@babel/preset-env')
]
config.module.rules[0].use[0].options.plugins = [
// use @babel/plugin-proposal-class-properties for class arrow functions
require.resolve('@babel/plugin-proposal-class-properties'),
// use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
require.resolve('babel-plugin-remove-graphql-queries')
]
// Prefer Gatsby ES6 entrypoint (module) over commonjs (main) entrypoint
config.resolve.mainFields = ['browser', 'module', 'main']
// Handle TypeScript
config.module.rules.push({ config.module.rules.push({
test: /\.(ts|tsx)$/, test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'), loader: require.resolve('babel-loader'),
options: { options: {
presets: [['react-app', { flow: false, typescript: true }]] presets: [['react-app', { flow: false, typescript: true }]],
plugins: [
require.resolve('@babel/plugin-proposal-class-properties'),
// use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
require.resolve('babel-plugin-remove-graphql-queries')
]
} }
}) })
config.resolve.extensions.push('.ts', '.tsx') config.resolve.extensions.push('.ts', '.tsx')
config.node = {
fs: 'empty'
}
// Handle SVGs // Handle SVGs
// Don't use Storybook's default SVG Configuration // Don't use Storybook's default SVG Configuration
config.module.rules = config.module.rules.map(rule => { config.module.rules = config.module.rules.map((rule) => {
if (rule.test.toString().includes('svg')) { if (rule.test.toString().includes('svg')) {
const test = rule.test const test = rule.test.toString().replace('svg|', '').replace(/\//g, '')
.toString()
.replace('svg|', '')
.replace(/\//g, '')
return { ...rule, test: new RegExp(test) } return { ...rule, test: new RegExp(test) }
} else { } else {
return rule return rule
@ -59,7 +79,19 @@ module.exports = async ({ config, mode }) => {
// Use SVG Configuration for SVGR yourself // Use SVG Configuration for SVGR yourself
config.module.rules.push({ config.module.rules.push({
test: /\.svg$/, test: /\.svg$/,
use: ['@svgr/webpack'] use: [
{
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: {
removeViewBox: false
}
}
}
},
'url-loader'
]
}) })
return config return config

View File

@ -4,8 +4,6 @@ node_js: node
cache: cache:
npm: true npm: true
directories:
- .next/cache
before_script: before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter

View File

@ -2,8 +2,6 @@
<h1 align="center">Ocean Marketplace</h1> <h1 align="center">Ocean Marketplace</h1>
>
[![Build Status](https://travis-ci.com/oceanprotocol/market.svg?branch=master)](https://travis-ci.com/oceanprotocol/market) [![Build Status](https://travis-ci.com/oceanprotocol/market.svg?branch=master)](https://travis-ci.com/oceanprotocol/market)
[![Now deployment](https://flat.badgen.net/badge/now/auto-deployment/21c4dd?icon=now)](https://zeit.co/oceanprotocol/market) [![Now deployment](https://flat.badgen.net/badge/now/auto-deployment/21c4dd?icon=now)](https://zeit.co/oceanprotocol/market)
[![Maintainability](https://api.codeclimate.com/v1/badges/d114f94f75e6efd2ee71/maintainability)](https://codeclimate.com/repos/5e3933869a31771fd800011c/maintainability) [![Maintainability](https://api.codeclimate.com/v1/badges/d114f94f75e6efd2ee71/maintainability)](https://codeclimate.com/repos/5e3933869a31771fd800011c/maintainability)
@ -15,6 +13,7 @@
- [🤓 Resources](#-resources) - [🤓 Resources](#-resources)
- [🏄 Get Started](#-get-started) - [🏄 Get Started](#-get-started)
- [Local Spree components with Barge](#local-spree-components-with-barge) - [Local Spree components with Barge](#local-spree-components-with-barge)
- [API](#api)
- [🦑 Environment variables](#-environment-variables) - [🦑 Environment variables](#-environment-variables)
- [🎨 Storybook](#-storybook) - [🎨 Storybook](#-storybook)
- [✨ Code Style](#-code-style) - [✨ Code Style](#-code-style)
@ -27,30 +26,25 @@
## 🤓 Resources ## 🤓 Resources
- [UI Design: Figma Mock Up](https://www.figma.com/file/K38ZsQjzndyp2YFJCLxIN7/dexFreight-Marketplace)
- [Planning: ZenHub Board](https://app.zenhub.com/workspaces/dexfreight-marketplace-5e2f201751116794cf4f2e75/board?repos=236508929)
## 🏄 Get Started ## 🏄 Get Started
The app is a React app built with [Next.js](https://nextjs.org) + TypeScript + CSS modules and will connect to Ocean components in Pacific by default. The app is a React app built with [Gatsby.js](https://www.gatsbyjs.org) + TypeScript + CSS modules and will connect to Ocean components in Pacific by default.
To start local development: To start local development:
```bash ```bash
git clone git@github.com:oceanprotocol/dexfreight.git git clone git@github.com:oceanprotocol/market.git
cd dexfreight cd market
npm install npm install
npm start npm start
``` ```
This will launch the app under [localhost:3000](http://localhost:3000). This will start the development server under
`http://localhost:8000`.
Depending on your configuration, you might have to increase the amount of `inotify` watchers: To explore the generated GraphQL data structure fire up the accompanying GraphiQL IDE under
`http://localhost:8000/__graphql`.
```
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
```
### Local Spree components with Barge ### Local Spree components with Barge
@ -72,33 +66,30 @@ This will take some time on first start, and at the end you need to copy the gen
The script will wait for all contracts to be generated in the `keeper-contracts` Docker container, then will copy the artifacts in place into `node_modules/@oceanprotocol/keeper-contracts/artifacts/`. The script will wait for all contracts to be generated in the `keeper-contracts` Docker container, then will copy the artifacts in place into `node_modules/@oceanprotocol/keeper-contracts/artifacts/`.
Finally, set environment variables to use those local connections in `.env` & `.env.build` in the app: Finally, set environment variables to use those local connections in `.env` in the app:
```bash ```bash
# modify env variables, Spree is enabled by default when using those files # modify env variables, Spree is enabled by default when using those files
cp .env.example .env && cp .env.example .env.build cp .env.example .env
``` ```
### API
Files under `/api` are isolated and not part of Gatsby.
- [API Documentation](api/)
## 🦑 Environment variables ## 🦑 Environment variables
The `./src/config/ocean.ts` file is setup to prioritize environment variables for setting each Ocean component endpoint. By setting environment variables, you can easily switch between Ocean networks the app connects to, without directly modifying `./src/config/ocean.ts`. The `./src/config/ocean.ts` file is setup to prioritize environment variables for setting each Ocean component endpoint. By setting environment variables, you can easily switch between Ocean networks the app connects to, without directly modifying `./src/config/ocean.ts`.
For local development, you can use a `.env` & `.env.build` file: For local development, you can use a `.env` file:
```bash ```bash
# modify env variables, Spree is enabled by default when using those files # modify env variables, Spree is enabled by default when using those files
cp .env.example .env && cp .env.example .env.build cp .env.example .env
``` ```
For a Now deployment, all environment variables defining the Ocean component endpoints need to be added with `now secrets` to `oceanprotocol` org based on the `@` variable names defined in `now.json`, e.g.:
```bash
now switch
now secrets add aquarius_uri https://aquarius.pacific.dexfreight.dev-ocean.com
```
Adding the env vars like that will provide them during both, build & run time.
## 🎨 Storybook ## 🎨 Storybook
[Storybook](https://storybook.js.org) is set up for this project and is used for UI development of components. Stories are created inside `src/components/` alongside each component in the form of `ComponentName.stories.tsx`. [Storybook](https://storybook.js.org) is set up for this project and is used for UI development of components. Stories are created inside `src/components/` alongside each component in the form of `ComponentName.stories.tsx`.
@ -130,9 +121,7 @@ npm run format
Test suite for unit tests is setup with [Jest](https://jestjs.io) as a test runner and: Test suite for unit tests is setup with [Jest](https://jestjs.io) as a test runner and:
- [react-testing-library](https://github.com/kentcdodds/react-testing-library) for all React components - [react-testing-library](https://github.com/kentcdodds/react-testing-library) for all React components
- [node-mocks-http](https://github.com/howardabrams/node-mocks-http) for all `src/pages/api/` routes - [node-mocks-http](https://github.com/howardabrams/node-mocks-http) for all `api/` routes
> Note: fully testing Next.js API routes should be part of integration tests. There are [various problems](https://spectrum.chat/next-js/general/api-routes-unit-testing~aa868f97-3a7d-45fe-97e5-3f0408f0022d) with fully testing them so a proper unit test suite for them should be setup.
To run all linting and unit tests: To run all linting and unit tests:
@ -164,7 +153,7 @@ npm run serve
## ⬆️ Deployment ## ⬆️ Deployment
Every branch or Pull Request is automatically deployed by [Now](https://zeit.co/now) with their GitHub integration. A link to a deployment will appear under each Pull Request. Every branch or Pull Request is automatically deployed by [Vercel](https://vercel.com) with their GitHub integration. A link to a deployment will appear under each Pull Request.
The latest deployment of the `master` branch is automatically aliased to `xxx`. The latest deployment of the `master` branch is automatically aliased to `xxx`.
@ -174,47 +163,47 @@ If needed, app can be deployed manually. Make sure to switch to Ocean Protocol o
```bash ```bash
# first run # first run
now login vercel login
now switch vercel switch
# deploy # deploy
now vercel
# switch alias to new deployment # switch alias to new deployment
now alias vercel alias
``` ```
## 🏗 Ocean Protocol Infrastructure ## 🏗 Ocean Protocol Infrastructure
The following Aquarius & Brizo instances specifically for dexFreight marketplace are deployed in Ocean Protocol's AWS K8: The following Aquarius & Brizo instances specifically for marketplace are deployed in Ocean Protocol's AWS K8:
**Nile (Staging)** **Nile (Staging)**
- K8 namespace: `dexfreight-nile` - K8 namespace: `market-nile`
- `aquarius.nile.dexfreight.dev-ocean.com` - `aquarius.nile.market.dev-ocean.com`
- `brizo.nile.dexfreight.dev-ocean.com` - `brizo.nile.market.dev-ocean.com`
Edit command with `kubectl`, e.g.: Edit command with `kubectl`, e.g.:
```bash ```bash
kubectl edit deployment -n dexfreight-nile aquarius kubectl edit deployment -n market-nile aquarius
``` ```
**Pacific (Production)** **Pacific (Production)**
- K8 namespace: `dexfreight-pacific` - K8 namespace: `market-pacific`
- `aquarius.pacific.dexfreight.dev-ocean.com` - `aquarius.pacific.market.dev-ocean.com`
- `brizo.pacific.dexfreight.dev-ocean.com` - `brizo.pacific.market.dev-ocean.com`
Edit command with `kubectl`, e.g.: Edit command with `kubectl`, e.g.:
```bash ```bash
kubectl edit deployment -n dexfreight-pacific aquarius kubectl edit deployment -n market-pacific aquarius
``` ```
## 🏛 License ## 🏛 License
```text ```text
Copyright 2019 Ocean Protocol Foundation Ltd. Copyright 2020 Ocean Protocol Foundation Ltd.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

46
gatsby-config.js Normal file
View File

@ -0,0 +1,46 @@
require('dotenv').config()
const siteConfig = require('./site.config.js')
module.exports = {
siteMetadata: {
...siteConfig
},
plugins: [
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'content',
path: `${__dirname}/content`
}
},
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'images',
path: `${__dirname}/src/images`
}
},
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'art',
path: `${__dirname}/node_modules/@oceanprotocol/art/`
}
},
'gatsby-transformer-json',
'gatsby-transformer-remark',
{
resolve: 'gatsby-plugin-svgr',
options: {
icon: false,
svgoConfig: {
plugins: [{ removeViewBox: false }]
}
}
},
'gatsby-plugin-react-helmet',
'gatsby-plugin-remove-trailing-slashes',
'gatsby-plugin-webpack-size'
]
}

View File

@ -1,12 +0,0 @@
{
"compilerOptions": {
"jsx": "react",
"allowJs": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"noImplicitAny": true,
"sourceMap": true,
"target": "es5"
}
}

2
next-env.d.ts vendored
View File

@ -1,2 +0,0 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

View File

@ -1,109 +0,0 @@
const webpack = require('webpack')
require('dotenv').config()
// Returns environment variables as an object
const env = Object.keys(process.env).reduce((acc, curr) => {
acc[`process.env.${curr}`] = JSON.stringify(process.env[curr])
return acc
}, {})
const withSvgr = (nextConfig = {}) => ({
webpack(config, options) {
config.module.rules.push({
test: /\.svg$/,
use: [
{
loader: '@svgr/webpack',
options: {
icon: true
}
}
]
})
if (typeof nextConfig.webpack === 'function') {
return nextConfig.webpack(config, options)
}
return config
}
})
// eslint-disable-next-line no-unused-vars
const withFsFix = (nextConfig = {}) => ({
webpack(config, options) {
// Fixes npm packages that depend on `fs` module
// https://github.com/zeit/next.js/issues/7755#issuecomment-508633125
// or https://github.com/zeit/next.js/issues/7755
if (!options.isServer) {
config.node = {
fs: 'empty'
}
}
if (typeof nextConfig.webpack === 'function') {
return nextConfig.webpack(config, options)
}
return config
}
})
const withGlobalConstants = (nextConfig = {}) => ({
webpack(config, options) {
// Allows to create global constants which can be configured at compile
// time (in this case they are the environment variables)
config.plugins.push(new webpack.DefinePlugin(env))
if (typeof nextConfig.webpack === 'function') {
return nextConfig.webpack(config, options)
}
return config
}
})
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true'
})
const withMarkdown = (nextConfig = {}) => ({
webpack(config, options) {
config.module.rules.push({
test: /\.md$/,
loader: 'raw-loader'
})
if (typeof nextConfig.webpack === 'function') {
return nextConfig.webpack(config, options)
}
return config
}
})
module.exports = withBundleAnalyzer(
withSvgr(
withFsFix(
withMarkdown(
withGlobalConstants({
exportPathMap: (defaultPathMap, { dev }) => {
// In dev environment return defaultPathMas as it is
if (dev) {
return defaultPathMap
}
// pages we know about beforehand
const paths = {
'/': { page: '/' },
'/publish': { page: '/publish' },
'/explore': { page: '/explore' }
}
return paths
}
})
)
)
)
)

26605
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,20 @@
{ {
"name": "market", "name": "@oceanprotocol/market",
"description": "Data marketplace for ocean.", "description": "Data marketplace for ocean.",
"version": "0.0.1", "version": "0.0.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"homepage": "https://oceanprotocol.com",
"scripts": { "scripts": {
"start": "next dev", "start": "gatsby develop --host 0.0.0.0",
"export": "next export", "build": "gatsby build",
"build": "npm run storybook:build && next build", "serve": "serve -s public/",
"serve": "next start",
"jest": "NODE_ENV=test jest -c tests/unit/jest.config.js", "jest": "NODE_ENV=test jest -c tests/unit/jest.config.js",
"test": "npm run lint && npm run jest", "test": "npm run lint && npm run jest",
"test:watch": "npm run lint && npm run jest -- --watch", "test:watch": "npm run lint && npm run jest -- --watch",
"lint": "eslint --ignore-path .gitignore --ext .js --ext .ts --ext .tsx .", "lint": "eslint --ignore-path .gitignore --ext .js --ext .ts --ext .tsx .",
"format": "prettier --ignore-path .gitignore **/**/*.{css,yml,js,jsx,ts,tsx,json} --write", "format": "prettier --ignore-path .gitignore './**/*.{css,yml,js,ts,tsx,json}' --write",
"analyze": "ANALYZE=true next build", "type-check": "tsc --noEmit",
"analyze": "npm run build && source-map-explorer 'public/*.js'",
"storybook": "start-storybook -p 4000 -c .storybook", "storybook": "start-storybook -p 4000 -c .storybook",
"storybook:build": "build-storybook -c .storybook -o public/storybook" "storybook:build": "build-storybook -c .storybook -o public/storybook"
}, },
@ -23,70 +24,70 @@
"@oceanprotocol/squid": "^2.2.0", "@oceanprotocol/squid": "^2.2.0",
"@oceanprotocol/typographies": "^0.1.0", "@oceanprotocol/typographies": "^0.1.0",
"@sindresorhus/slugify": "^1.0.0", "@sindresorhus/slugify": "^1.0.0",
"@tippyjs/react": "^4.0.2", "@tippyjs/react": "^4.0.5",
"@types/classnames": "^2.2.10", "@types/classnames": "^2.2.10",
"axios": "^0.19.2", "axios": "^0.19.2",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"date-fns": "^2.11.0", "date-fns": "^2.14.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"filesize": "^6.1.0", "filesize": "^6.1.0",
"is-url-superb": "^3.0.0", "gatsby": "^2.23.12",
"next": "^9.3.2", "gatsby-plugin-react-helmet": "^3.3.6",
"next-seo": "^4.4.0", "gatsby-plugin-remove-trailing-slashes": "^2.3.7",
"next-svgr": "^0.0.2", "gatsby-plugin-svgr": "^2.0.2",
"nprogress": "^0.2.0", "gatsby-plugin-webpack-size": "^1.0.0",
"gatsby-source-filesystem": "^2.3.14",
"gatsby-source-graphql": "^2.5.7",
"gatsby-transformer-json": "^2.4.7",
"gatsby-transformer-remark": "^2.8.20",
"is-url-superb": "^4.0.0",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"react": "^16.13.1", "react": "^16.13.1",
"react-data-table-component": "^6.9.2", "react-data-table-component": "^6.9.3",
"react-datepicker": "^2.14.0", "react-datepicker": "^3.0.0",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-dotdotdot": "^1.3.1", "react-dotdotdot": "^1.3.1",
"react-dropzone": "^11.0.1", "react-dropzone": "^11.0.1",
"react-jsonschema-form": "^1.8.1", "react-jsonschema-form": "^1.8.1",
"react-markdown": "^4.3.1", "react-markdown": "^4.3.1",
"react-paginate": "^6.3.2", "react-paginate": "^6.3.2",
"react-rating": "^2.0.4", "react-rating": "^2.0.5",
"react-responsive-modal": "^5.0.2", "react-responsive-modal": "^5.0.2",
"react-toastify": "^5.5.0", "react-toastify": "^6.0.6",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"slugify": "^1.4.0", "slugify": "^1.4.4",
"use-debounce": "^3.4.0", "use-debounce": "^3.4.2",
"web3connect": "^1.0.0-beta.33" "web3connect": "^1.0.0-beta.33"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.8.7", "@babel/core": "^7.10.3",
"@next/bundle-analyzer": "^9.3.0", "@storybook/addon-storyshots": "^5.3.19",
"@storybook/addon-storyshots": "^5.3.17", "@storybook/react": "^5.3.19",
"@storybook/react": "^5.3.17", "@testing-library/jest-dom": "^5.11.0",
"@testing-library/jest-dom": "^5.1.1", "@testing-library/react": "^10.4.3",
"@testing-library/react": "^10.0.1", "@testing-library/react-hooks": "^3.3.0",
"@testing-library/react-hooks": "^3.2.1", "@types/jest": "^26.0.3",
"@types/jest": "^25.1.4", "@types/node": "^14.0.14",
"@types/node": "^13.9.1", "@types/numeral": "^0.0.28",
"@types/nprogress": "^0.2.0", "@types/react": "^16.9.41",
"@types/numeral": "0.0.26", "@types/react-datepicker": "^3.0.2",
"@types/react": "^16.9.23", "@types/react-jsonschema-form": "^1.7.3",
"@types/react-datepicker": "^2.11.0",
"@types/react-jsonschema-form": "^1.7.0",
"@types/react-paginate": "^6.2.1", "@types/react-paginate": "^6.2.1",
"@types/shortid": "0.0.29", "@types/shortid": "0.0.29",
"@typescript-eslint/eslint-plugin": "^2.23.0", "@typescript-eslint/eslint-plugin": "^3.5.0",
"@typescript-eslint/parser": "^2.23.0", "@typescript-eslint/parser": "^3.5.0",
"babel-loader": "^8.0.6", "babel-loader": "^8.1.0",
"babel-preset-react-app": "^9.1.1", "babel-preset-react-app": "^9.1.2",
"eslint": "^6.8.0", "eslint": "^7.3.1",
"eslint-config-oceanprotocol": "^1.5.0", "eslint-config-oceanprotocol": "^1.5.0",
"eslint-config-prettier": "^6.10.0", "eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.2", "eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-react": "^7.19.0", "eslint-plugin-react": "^7.20.2",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jest": "^25.1.0", "jest": "^26.1.0",
"node-mocks-http": "^1.8.1", "node-mocks-http": "^1.8.1",
"prettier": "^1.19.1", "prettier": "^2.0.5",
"react-test-renderer": "^16.12.0", "typescript": "^3.9.5"
"ts-jest": "^25.2.1",
"typescript": "^3.8.3",
"webfontloader": "^1.6.28"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,23 +1,9 @@
module.exports = { module.exports = {
title: 'Ocean Market', siteTitle: 'Ocean Market',
description: `A marketplace to find and publish open data sets in the Ocean Network.`, siteTagline: `A marketplace to find and publish open data sets in the Ocean Network.`,
url: 'https://market.oceanprotocol.now.sh/', siteUrl: 'https://market.oceanprotocol.now.sh/',
copyright: copyright:
'All Rights Reserved. Powered by [Ocean Protocol](https://oceanprotocol.com)', 'All Rights Reserved. Powered by [Ocean Protocol](https://oceanprotocol.com)',
refundPolicy: [
'Data can be challenged within 2 days after purchase.',
'The marketplace decides if you are eligible for refund.'
],
assetTerms: [
{
name: 'Personal Identifiable Information',
value: 'This offer contains no personal data'
},
{
name: 'Regions where data can be used',
value: 'Worldwide'
}
],
menu: [ menu: [
{ {
name: 'Explore', name: 'Explore',

View File

@ -14,7 +14,7 @@ export default function Dropzone({
multiple?: boolean multiple?: boolean
error?: string error?: string
}) { }) {
const onDrop = useCallback(acceptedFiles => handleOnDrop(acceptedFiles), [ const onDrop = useCallback((acceptedFiles) => handleOnDrop(acceptedFiles), [
handleOnDrop handleOnDrop
]) ])

View File

@ -75,7 +75,7 @@ export default function DateRangeWidget(props: WidgetProps) {
<input <input
id="range" id="range"
type="checkbox" type="checkbox"
onChange={ev => setRange(ev.target.checked)} onChange={(ev) => setRange(ev.target.checked)}
checked={range} checked={range}
/> />
<label className={styles.label} htmlFor="range"> <label className={styles.label} htmlFor="range">

View File

@ -32,9 +32,9 @@ export default function TermsWidget(props: WidgetProps) {
checked={typeof value === 'undefined' ? false : value} checked={typeof value === 'undefined' ? false : value}
disabled={disabled || readonly} disabled={disabled || readonly}
autoFocus={autofocus} autoFocus={autofocus}
onChange={event => onChange(event.target.checked)} onChange={(event) => onChange(event.target.checked)}
onBlur={onBlur && (event => onBlur(id, event.target.checked))} onBlur={onBlur && ((event) => onBlur(id, event.target.checked))}
onFocus={onFocus && (event => onFocus(id, event.target.checked))} onFocus={onFocus && ((event) => onFocus(id, event.target.checked))}
/> />
<span>{label}</span> <span>{label}</span>
</label> </label>

View File

@ -44,7 +44,7 @@ const Tags: React.FC<TagsProps> = ({
return ( return (
<div className={classes}> <div className={classes}>
{tags && {tags &&
tags.map(tag => ( tags.map((tag) => (
<Tag tag={tag} noLinks={noLinks} key={shortid.generate()} /> <Tag tag={tag} noLinks={noLinks} key={shortid.generate()} />
))} ))}
{shouldShowMore && ( {shouldShowMore && (

View File

@ -13,7 +13,7 @@ export const FieldTemplate = ({
rawErrors, rawErrors,
children children
}: FieldTemplateProps) => { }: FieldTemplateProps) => {
const noLabel = id !== noLabelFields.filter(f => id === f)[0] const noLabel = id !== noLabelFields.filter((f) => id === f)[0]
return ( return (
<section <section
key={id} key={id}

View File

@ -39,8 +39,8 @@ export default function Pagination({
// adapt based on media query match // adapt based on media query match
marginPagesDisplayed={smallViewport ? 0 : 1} marginPagesDisplayed={smallViewport ? 0 : 1}
pageRangeDisplayed={smallViewport ? 3 : 6} pageRangeDisplayed={smallViewport ? 3 : 6}
onPageChange={data => onPageChange(data.selected)} onPageChange={(data) => onPageChange(data.selected)}
hrefBuilder={pageIndex => hrefBuilder(pageIndex)} hrefBuilder={(pageIndex) => hrefBuilder(pageIndex)}
disableInitialCallback disableInitialCallback
previousLabel="←" previousLabel="←"
nextLabel="→" nextLabel="→"

View File

@ -51,7 +51,7 @@ export default function SearchBar({
className={large ? `${styles.input} ${styles.large}` : styles.input} className={large ? `${styles.input} ${styles.large}` : styles.input}
placeholder={placeholder || 'What are you looking for?'} placeholder={placeholder || 'What are you looking for?'}
value={value} value={value}
onChange={e => handleChange(e)} onChange={(e) => handleChange(e)}
required required
/> />
<Button onClick={(e: FormEvent<HTMLButtonElement>) => startSearch(e)}> <Button onClick={(e: FormEvent<HTMLButtonElement>) => startSearch(e)}>

View File

@ -42,13 +42,13 @@ export const SearchPriceFilter = () => {
<PriceInput <PriceInput
label="minPrice" label="minPrice"
value={min} value={min}
onChange={ev => setMin(ev.target.value)} onChange={(ev) => setMin(ev.target.value)}
text="Min price" text="Min price"
/> />
<PriceInput <PriceInput
label="maxPrice" label="maxPrice"
value={max} value={max}
onChange={ev => setMax(ev.target.value)} onChange={(ev) => setMax(ev.target.value)}
text="Max price" text="Max price"
/> />
</div> </div>

View File

@ -5,7 +5,7 @@ import asset from '../../../tests/unit/__fixtures__/ddo'
const queryResult = { const queryResult = {
results: [asset, asset, asset, asset, asset, asset].map( results: [asset, asset, asset, asset, asset, asset].map(
asset => new DDO(asset) (asset) => new DDO(asset)
), ),
page: 1, page: 1,
totalPages: 100, totalPages: 100,

View File

@ -40,7 +40,7 @@ const AssetList: React.FC<AssetListProps> = ({ queryResult }) => {
<> <>
<div className={styles.assetList}> <div className={styles.assetList}>
{queryResult.results && {queryResult.results &&
queryResult.results.map(ddo => ( queryResult.results.map((ddo) => (
<AssetTeaser ddo={ddo} key={shortid.generate()} /> <AssetTeaser ddo={ddo} key={shortid.generate()} />
))} ))}
</div> </div>

View File

@ -74,7 +74,7 @@ export default function Compute({
const comType = event.target.value const comType = event.target.value
setComputeType(comType) setComputeType(comType)
const selectedComputeOption = computeOptions.find(x => x.name === comType) const selectedComputeOption = computeOptions.find((x) => x.name === comType)
if (selectedComputeOption !== undefined) if (selectedComputeOption !== undefined)
setComputeContainer(selectedComputeOption.value) setComputeContainer(selectedComputeOption.value)
} }
@ -109,7 +109,7 @@ export default function Compute({
label="Select image to run the algorithm" label="Select image to run the algorithm"
placeholder="" placeholder=""
value={computeType} value={computeType}
options={computeOptions.map(x => x.name)} options={computeOptions.map((x) => x.name)}
onChange={handleSelectChange} onChange={handleSelectChange}
/> />
</div> </div>

View File

@ -57,7 +57,7 @@ export default function ConsumedList() {
if (!consumedItems) return if (!consumedItems) return
const data = consumedItems.map(ddo => { const data = consumedItems.map((ddo) => {
const { attributes } = findServiceByType(ddo, 'metadata') const { attributes } = findServiceByType(ddo, 'metadata')
const { name, price, datePublished } = attributes.main as MetaDataMain const { name, price, datePublished } = attributes.main as MetaDataMain
return { return {

View File

@ -96,7 +96,7 @@ export default function JobsList() {
try { try {
const computeItems = await getComputeItems() const computeItems = await getComputeItems()
if (!computeItems) return if (!computeItems) return
const data = computeItems.map(item => { const data = computeItems.map((item) => {
const { attributes } = findServiceByType(item.ddo, 'metadata') const { attributes } = findServiceByType(item.ddo, 'metadata')
const { name, price } = attributes.main as MetaDataMain const { name, price } = attributes.main as MetaDataMain
return { return {

View File

@ -73,7 +73,7 @@ export default function PublishedList() {
count: publishedItems.totalPages count: publishedItems.totalPages
}) })
const data = publishedItems.results.map(ddo => { const data = publishedItems.results.map((ddo) => {
const { attributes } = findServiceByType(ddo, 'metadata') const { attributes } = findServiceByType(ddo, 'metadata')
const { name, price, datePublished } = attributes.main as MetaDataMain const { name, price, datePublished } = attributes.main as MetaDataMain
return { return {

View File

@ -46,7 +46,7 @@ const HomePage = () => {
</header> </header>
<div className={styles.actions}> <div className={styles.actions}>
{actions.map(action => ( {actions.map((action) => (
<Link key={shortid.generate()} href={action.link}> <Link key={shortid.generate()} href={action.link}>
<a <a
className={action.comingSoon ? styles.comingSoon : styles.action} className={action.comingSoon ? styles.comingSoon : styles.action}

View File

@ -38,7 +38,7 @@ const TransactionsPage: React.FC = () => {
<Layout title={title} description={description}> <Layout title={title} description={description}>
<article className={styles.grid}> <article className={styles.grid}>
<div> <div>
{sections.map(section => { {sections.map((section) => {
const { title, component } = section const { title, component } = section
return <Section key={title} title={title} component={component} /> return <Section key={title} title={title} component={component} />
})} })}

View File

@ -25,7 +25,7 @@ export default function MetaSecondary({
title="Sample Data" title="Sample Data"
content={ content={
<ul> <ul>
{links?.map(link => ( {links?.map((link) => (
<ListItem key={shortid.generate()}> <ListItem key={shortid.generate()}>
<a href={link.url}>{link.name}</a> <a href={link.url}>{link.name}</a>
</ListItem> </ListItem>
@ -51,7 +51,7 @@ export default function MetaSecondary({
title="Refund Policy" title="Refund Policy"
content={ content={
<ul> <ul>
{refundPolicy.map(item => ( {refundPolicy.map((item) => (
<ListItem key={shortid.generate()}>{item}</ListItem> <ListItem key={shortid.generate()}>{item}</ListItem>
))} ))}
</ul> </ul>
@ -59,7 +59,7 @@ export default function MetaSecondary({
/> />
)} )}
{assetTerms.map(item => ( {assetTerms.map((item) => (
<MetaItem <MetaItem
key={shortid.generate()} key={shortid.generate()}
title={item.name} title={item.name}

View File

@ -34,7 +34,7 @@ export default function useCategoriesQueryParam(allCategories: string[]) {
// Update url and the state with the selected categories // Update url and the state with the selected categories
const toggleCategory = (category: string) => { const toggleCategory = (category: string) => {
const newSelectedCategories = selectedCategories.includes(category) const newSelectedCategories = selectedCategories.includes(category)
? selectedCategories.filter(c => c !== category) ? selectedCategories.filter((c) => c !== category)
: [...selectedCategories, category] : [...selectedCategories, category]
setSelectedCategories(newSelectedCategories) setSelectedCategories(newSelectedCategories)

View File

@ -9,7 +9,7 @@ const Explore: NextPage<{ queryResult: string }> = ({ queryResult }) => (
<ExplorePage queryResult={JSON.parse(queryResult)} /> <ExplorePage queryResult={JSON.parse(queryResult)} />
) )
export const getServerSideProps: GetServerSideProps = async context => { export const getServerSideProps: GetServerSideProps = async (context) => {
const searchQuery = { const searchQuery = {
offset: 15, offset: 15,
page: Number(context.query.page) || 1, page: Number(context.query.page) || 1,

View File

@ -36,7 +36,7 @@ export function getSearchQuery(
} as SearchQuery } as SearchQuery
} }
Search.getInitialProps = async context => { Search.getInitialProps = async (context) => {
const { text, tag, page, offset, minPrice, maxPrice } = context.query const { text, tag, page, offset, minPrice, maxPrice } = context.query
const minPriceParsed = priceQueryParamToWei( const minPriceParsed = priceQueryParamToWei(

View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,29 @@
/* eslint-disable no-unused-vars */
const React = require('react')
const gatsby = jest.requireActual('gatsby')
module.exports = {
...gatsby,
graphql: jest.fn(),
Link: jest.fn().mockImplementation(
// these props are invalid for an `a` tag
({
activeClassName,
activeStyle,
getProps,
innerRef,
partiallyActive,
ref,
replace,
to,
...rest
}) =>
React.createElement('a', {
...rest,
href: to
})
),
StaticQuery: jest.fn(),
useStaticQuery: jest.fn()
}

View File

@ -1,6 +1,6 @@
export default Object.defineProperty(window, 'matchMedia', { export default Object.defineProperty(window, 'matchMedia', {
writable: true, writable: true,
value: jest.fn().mockImplementation(query => ({ value: jest.fn().mockImplementation((query) => ({
matches: false, matches: false,
media: query, media: query,
onchange: null, onchange: null,

View File

@ -1,24 +0,0 @@
const Router = require('next/router')
Router.router = {
push: () => null,
prefetch: () => null,
asPath: '/hello',
events: {
on: () => null,
off: () => null
},
useRouter: () => {
return {
push: () => null,
prefetch: () => null,
asPath: '/hello',
events: {
on: () => null,
off: () => null
}
}
}
}
module.exports = Router.router

View File

@ -1 +1,6 @@
module.exports = 'svg' import React from 'react'
export default 'SvgrURL'
const SvgrMock = React.forwardRef((props, ref) => <span ref={ref} {...props} />)
export const ReactComponent = SvgrMock

View File

@ -0,0 +1,4 @@
// this file only exists for Jest
module.exports = {
presets: ['babel-preset-gatsby', '@babel/preset-typescript']
}

View File

@ -1,11 +1,7 @@
module.exports = { module.exports = {
rootDir: '../../', rootDir: '../../',
preset: 'ts-jest/presets/js-with-ts', transform: {
setupFilesAfterEnv: ['<rootDir>/tests/unit/setupTests.ts'], '^.+\\.[jt]sx?$': ['babel-jest', { configFile: './jest/babel.config.js' }]
globals: {
'ts-jest': {
tsConfig: 'jest.tsconfig.json'
}
}, },
moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node', 'md'], moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node', 'md'],
moduleNameMapper: { moduleNameMapper: {
@ -15,12 +11,13 @@ module.exports = {
'\\.svg': '<rootDir>/tests/unit/__mocks__/svgrMock.js', '\\.svg': '<rootDir>/tests/unit/__mocks__/svgrMock.js',
'next/router': '<rootDir>/tests/unit/__mocks__/nextRouter.js' 'next/router': '<rootDir>/tests/unit/__mocks__/nextRouter.js'
}, },
testPathIgnorePatterns: [ testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'],
'<rootDir>/.next', transformIgnorePatterns: ['node_modules/(?!(gatsby)/)'],
'<rootDir>/node_modules', globals: {
'<rootDir>/build', __PATH_PREFIX__: ''
'<rootDir>/coverage' },
], setupFiles: ['<rootDir>/tests/unit/loadershim.js'],
setupFilesAfterEnv: ['<rootDir>/tests/unit/setupTests.ts'],
collectCoverageFrom: [ collectCoverageFrom: [
'<rootDir>/src/**/*.{ts,tsx}', '<rootDir>/src/**/*.{ts,tsx}',
'!<rootDir>/src/@types/**/*', '!<rootDir>/src/@types/**/*',

3
tests/unit/loadershim.js Normal file
View File

@ -0,0 +1,3 @@
global.___loader = {
enqueue: jest.fn()
}

View File

@ -7,7 +7,7 @@ import { DDO } from '@oceanprotocol/squid'
const asset = new DDO(ddo) const asset = new DDO(ddo)
const queryResult = { const queryResult = {
results: [asset, asset, asset, asset, asset, asset].map( results: [asset, asset, asset, asset, asset, asset].map(
asset => new DDO(asset) (asset) => new DDO(asset)
), ),
page: 1, page: 1,
totalPages: 100, totalPages: 100,

View File

@ -1,5 +1,9 @@
import '@testing-library/jest-dom/extend-expect' import '@testing-library/jest-dom/extend-expect'
if (typeof window.IntersectionObserver === 'undefined') {
import('intersection-observer')
}
beforeAll(() => { beforeAll(() => {
require('./__mocks__/matchMedia') require('./__mocks__/matchMedia')
jest.mock('web3') jest.mock('web3')

View File

@ -1,19 +1,18 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"], "module": "commonjs",
"allowJs": true, "lib": ["dom", "es2017"],
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "jsx": "react",
"jsx": "preserve" "esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmit": true,
"sourceMap": true,
"noImplicitAny": true,
"skipLibCheck": true
}, },
"exclude": ["node_modules", ".next"], "exclude": ["node_modules", "public", ".cache", "*.js"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] "include": ["./src/**/*", "./api/**/*", "./tests/**/*"]
} }

View File

@ -1,23 +1,4 @@
{ {
"name": "market", "cleanUrls": true,
"build": { "trailingSlash": false
"env": {
"NODE_URI": "@node_uri",
"AQUARIUS_URI": "@aquarius_uri",
"BRIZO_URI": "@brizo_uri",
"BRIZO_ADDRESS": "@brizo_address",
"SECRET_STORE_URI": "@secret_store_uri",
"FAUCET_URI": "@faucet_uri",
"RATING_URI": "@rating_uri"
}
},
"env": {
"NODE_URI": "@node_uri",
"AQUARIUS_URI": "@aquarius_uri",
"BRIZO_URI": "@brizo_uri",
"BRIZO_ADDRESS": "@brizo_address",
"SECRET_STORE_URI": "@secret_store_uri",
"FAUCET_URI": "@faucet_uri",
"RATING_URI": "@rating_uri"
}
} }