mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Tests for @shared components (#1737)
* asset teaser test, story, useUserPreferences Jest mock * fixtures & mocks * more tests * cleanup * more tests * more tests * more tests * more tests * typing fix * package updates * reorg * more tests * more tests * more tests * more tests * reorg, more tests * reorg fixes * more tests * rebase fix * subgraph query typing fix * restore some storybook stories * more tests * more tests * package lock fix * graphql override
This commit is contained in:
parent
a042387804
commit
56f5e77c49
71
.jest/__fixtures__/assetAquarius.ts
Normal file
71
.jest/__fixtures__/assetAquarius.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { Asset } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
|
export const assetAquarius: Asset = {
|
||||||
|
'@context': ['https://w3id.org/did/v1'],
|
||||||
|
id: 'did:op:6654b0793765b269696cec8d2f0d077d9bbcdd3c4f033d941ab9684e8ad06630',
|
||||||
|
nftAddress: '0xbA5BA7B09e2FA1eb0258f647503F81D2Af5cb07d',
|
||||||
|
version: '4.1.0',
|
||||||
|
chainId: 5,
|
||||||
|
metadata: {
|
||||||
|
created: '2022-09-29T11:30:26Z',
|
||||||
|
updated: '2022-09-29T11:30:26Z',
|
||||||
|
type: 'dataset',
|
||||||
|
name: 'Testitest',
|
||||||
|
description: 'This is a test.',
|
||||||
|
tags: [],
|
||||||
|
author: 'Test User',
|
||||||
|
license: 'https://market.oceanprotocol.com/terms',
|
||||||
|
additionalInformation: {
|
||||||
|
termsAndConditions: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
services: [
|
||||||
|
{
|
||||||
|
id: 'dbc42f4c62d2452f8731fd023eacfae74e9c7a42fbd12ce84310f13342e4aab1',
|
||||||
|
type: 'access',
|
||||||
|
files:
|
||||||
|
'0x04022ef1afafe340f41b261ef721b8dd55dee094975cc70330803d760beef38871948ce572ff1c533d56cda2665749ed2eb8283e243ec5ee19011f510b6b263b2da0af537e3f1fdff7ddd90fa26c7a4761a6d26928bc1348a302634012aac7998e92c84456ab73e9a847120c44ebda15781787e8c382391b2eaefc8b8d36998f3998d1c4647f4f7bb28f4278093c1d231f66e78f81452049443b9e540aeb42ebbdc1b748c024eb10218532814736e241efa1c2a687685b4e2ea7a877685aa0ea325d1a8cf765d1b423b32d81ec3c3e22fc9c15c6b9b71f2862edaec4e4cf7c3a638ffc0ecb88ede3cabb511d4780543a53c001a95f42de1877796e13c997b57bc671507e92198934b4ea7c2e6554993388421253e8c2f10458dec872a7ebfa71b6e77ed359222c93261ba252028c5da06ccf8defcd529885b2125816325a47e23728b513',
|
||||||
|
datatokenAddress: '0x067e1E6ec580F3F0f6781679A4A5AB07A6464b08',
|
||||||
|
serviceEndpoint: 'https://v4.provider.goerli.oceanprotocol.com',
|
||||||
|
timeout: 604800
|
||||||
|
}
|
||||||
|
],
|
||||||
|
event: {
|
||||||
|
tx: '0x3e07a75c1cc5d4146222a93ab4319144e60ecca3ebfb8b15f1ff339d6f479dc9',
|
||||||
|
block: 7680195,
|
||||||
|
from: '0x903322C7E45A60d7c8C3EA236c5beA9Af86310c7',
|
||||||
|
contract: '0xbA5BA7B09e2FA1eb0258f647503F81D2Af5cb07d',
|
||||||
|
datetime: '2022-09-29T11:31:12'
|
||||||
|
},
|
||||||
|
nft: {
|
||||||
|
address: '0xbA5BA7B09e2FA1eb0258f647503F81D2Af5cb07d',
|
||||||
|
name: 'Ocean Data NFT',
|
||||||
|
symbol: 'OCEAN-NFT',
|
||||||
|
state: 0,
|
||||||
|
tokenURI:
|
||||||
|
'data:application/json;base64,eyJuYW1lIjoiT2NlYW4gRGF0YSBORlQiLCJzeW1ib2wiOiJPQ0VBTi1ORlQiLCJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYW4gYXNzZXQgaW4gdGhlIE9jZWFuIFByb3RvY29sIHY0IGVjb3N5c3RlbS5cblxuVmlldyBvbiBPY2VhbiBNYXJrZXQ6IGh0dHBzOi8vbWFya2V0Lm9jZWFucHJvdG9jb2wuY29tL2Fzc2V0L2RpZDpvcDo2NjU0YjA3OTM3NjViMjY5Njk2Y2VjOGQyZjBkMDc3ZDliYmNkZDNjNGYwMzNkOTQxYWI5Njg0ZThhZDA2NjMwIiwiZXh0ZXJuYWxfdXJsIjoiaHR0cHM6Ly9tYXJrZXQub2NlYW5wcm90b2NvbC5jb20vYXNzZXQvZGlkOm9wOjY2NTRiMDc5Mzc2NWIyNjk2OTZjZWM4ZDJmMGQwNzdkOWJiY2RkM2M0ZjAzM2Q5NDFhYjk2ODRlOGFkMDY2MzAiLCJiYWNrZ3JvdW5kX2NvbG9yIjoiMTQxNDE0IiwiaW1hZ2VfZGF0YSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbCwlM0Nzdmcgdmlld0JveD0nMCAwIDk5IDk5JyBmaWxsPSd1bmRlZmluZWQnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyclM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5Mjc3JyBkPSdNMCw5OUwwLDI5QzksMjUgMTksMjIgMjksMjFDMzgsMTkgNDksMTkgNjEsMjFDNzIsMjIgODUsMjUgOTksMjlMOTksOTlaJy8lM0UlM0NwYXRoIGZpbGw9JyUyM2ZmNDA5MmJiJyBkPSdNMCw5OUwwLDU1QzgsNDkgMTcsNDQgMjgsNDNDMzgsNDEgNTAsNDIgNjMsNDNDNzUsNDMgODcsNDIgOTksNDJMOTksOTlaJyUzRSUzQy9wYXRoJTNFJTNDcGF0aCBmaWxsPSclMjNmZjQwOTJmZicgZD0nTTAsOTlMMCw2OEMxMSw2NiAyMiw2NSAzMiw2N0M0MSw2OCA1MCw3MyA2MSw3NkM3MSw3OCA4NSw3OCA5OSw3OUw5OSw5OVonJTNFJTNDL3BhdGglM0UlM0Mvc3ZnJTNFIn0=',
|
||||||
|
owner: '0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0',
|
||||||
|
created: '2022-09-29T11:31:12'
|
||||||
|
},
|
||||||
|
datatokens: [
|
||||||
|
{
|
||||||
|
address: '0x067e1E6ec580F3F0f6781679A4A5AB07A6464b08',
|
||||||
|
name: 'Stupendous Orca Token',
|
||||||
|
symbol: 'STUORC-59',
|
||||||
|
serviceId:
|
||||||
|
'dbc42f4c62d2452f8731fd023eacfae74e9c7a42fbd12ce84310f13342e4aab1'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
stats: {
|
||||||
|
orders: 22
|
||||||
|
// price: {
|
||||||
|
// value: 3231343254,
|
||||||
|
// tokenAddress: '0xCfDdA22C9837aE76E0faA845354f33C62E03653a',
|
||||||
|
// tokenSymbol: 'OCEAN'
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
purgatory: {
|
||||||
|
state: false,
|
||||||
|
reason: ''
|
||||||
|
}
|
||||||
|
}
|
26
.jest/__fixtures__/assetWithAccessDetails.ts
Normal file
26
.jest/__fixtures__/assetWithAccessDetails.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { assetAquarius } from './assetAquarius'
|
||||||
|
|
||||||
|
export const asset: AssetExtended = {
|
||||||
|
...assetAquarius,
|
||||||
|
accessDetails: {
|
||||||
|
publisherMarketOrderFee: '0',
|
||||||
|
type: 'fixed',
|
||||||
|
addressOrId:
|
||||||
|
'0x00e3b740e4d8bf6e97010ecb5b14d1b7efc0913bfa291fcf5adb8eb9e6c29e93',
|
||||||
|
price: '3231343254',
|
||||||
|
isPurchasable: true,
|
||||||
|
isOwned: false,
|
||||||
|
validOrderTx: null,
|
||||||
|
baseToken: {
|
||||||
|
address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a',
|
||||||
|
name: 'Ocean Token',
|
||||||
|
symbol: 'OCEAN',
|
||||||
|
decimals: 18
|
||||||
|
},
|
||||||
|
datatoken: {
|
||||||
|
address: '0x067e1e6ec580f3f0f6781679a4a5ab07a6464b08',
|
||||||
|
name: 'Stupendous Orca Token',
|
||||||
|
symbol: 'STUORC-59'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1538
.jest/__fixtures__/assetsWithAccessDetails.ts
Normal file
1538
.jest/__fixtures__/assetsWithAccessDetails.ts
Normal file
File diff suppressed because it is too large
Load Diff
64
.jest/__fixtures__/prices.ts
Normal file
64
.jest/__fixtures__/prices.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
export default {
|
||||||
|
prices: {
|
||||||
|
h2o: {
|
||||||
|
btc: 0.00009013,
|
||||||
|
cad: 2.38,
|
||||||
|
cny: 12.36,
|
||||||
|
eth: 0.00132713,
|
||||||
|
eur: 1.78,
|
||||||
|
gbp: 1.55,
|
||||||
|
hkd: 13.53,
|
||||||
|
inr: 141.78,
|
||||||
|
jpy: 253.21,
|
||||||
|
link: 0.24050954,
|
||||||
|
rub: 109.81,
|
||||||
|
sgd: 2.47,
|
||||||
|
usd: 1.72
|
||||||
|
},
|
||||||
|
'matic-network': {
|
||||||
|
btc: 0.0000414,
|
||||||
|
cad: 1.093,
|
||||||
|
cny: 5.67,
|
||||||
|
eth: 0.00061102,
|
||||||
|
eur: 0.816959,
|
||||||
|
gbp: 0.715112,
|
||||||
|
hkd: 6.21,
|
||||||
|
inr: 65.06,
|
||||||
|
jpy: 116.23,
|
||||||
|
link: 0.11072143,
|
||||||
|
rub: 50.4,
|
||||||
|
sgd: 1.14,
|
||||||
|
usd: 0.790922
|
||||||
|
},
|
||||||
|
ethereum: {
|
||||||
|
btc: 0.06775775,
|
||||||
|
cad: 1789.03,
|
||||||
|
cny: 9288.09,
|
||||||
|
eth: 1,
|
||||||
|
eur: 1337.1,
|
||||||
|
gbp: 1170.41,
|
||||||
|
hkd: 10161.71,
|
||||||
|
inr: 106490,
|
||||||
|
jpy: 190239,
|
||||||
|
link: 181.216,
|
||||||
|
rub: 82491,
|
||||||
|
sgd: 1859.58,
|
||||||
|
usd: 1294.49
|
||||||
|
},
|
||||||
|
'ocean-protocol': {
|
||||||
|
btc: 0.00000809,
|
||||||
|
cad: 0.213554,
|
||||||
|
cny: 1.11,
|
||||||
|
eth: 0.00011937,
|
||||||
|
eur: 0.159608,
|
||||||
|
gbp: 0.13971,
|
||||||
|
hkd: 1.21,
|
||||||
|
inr: 12.71,
|
||||||
|
jpy: 22.71,
|
||||||
|
link: 0.02163146,
|
||||||
|
rub: 9.85,
|
||||||
|
sgd: 0.221976,
|
||||||
|
usd: 0.154521
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
.jest/__fixtures__/table.ts
Normal file
41
.jest/__fixtures__/table.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
export const columns = [
|
||||||
|
{
|
||||||
|
name: 'Name',
|
||||||
|
selector: (row: any) => row.name,
|
||||||
|
maxWidth: '45rem',
|
||||||
|
grow: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Symbol',
|
||||||
|
selector: (row: any) => row.symbol,
|
||||||
|
maxWidth: '10rem'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Price',
|
||||||
|
selector: (row: any) => row.price,
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const data = [
|
||||||
|
{
|
||||||
|
name: 'Title asset',
|
||||||
|
symbol: 'DATA-70',
|
||||||
|
price: '1.011'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Title asset Title asset Title asset Title asset Title asset',
|
||||||
|
symbol: 'DATA-71',
|
||||||
|
price: '1.011'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Title asset',
|
||||||
|
symbol: 'DATA-72',
|
||||||
|
price: '1.011'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset',
|
||||||
|
symbol: 'DATA-71',
|
||||||
|
price: '1.011'
|
||||||
|
}
|
||||||
|
]
|
10
.jest/__fixtures__/userPreferences.ts
Normal file
10
.jest/__fixtures__/userPreferences.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export default {
|
||||||
|
debug: true,
|
||||||
|
currency: 'EUR',
|
||||||
|
locale: 'en-US',
|
||||||
|
chainIds: [5, 1, 137, 56, 1285, 246],
|
||||||
|
bookmarks: [],
|
||||||
|
privacyPolicySlug: '/privacy/en',
|
||||||
|
showPPC: true,
|
||||||
|
infiniteApproval: false
|
||||||
|
}
|
33
.jest/__fixtures__/web3.ts
Normal file
33
.jest/__fixtures__/web3.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
export default {
|
||||||
|
accountEns: 'jellymcjellyfish.eth',
|
||||||
|
accountEnsAvatar:
|
||||||
|
'https://metadata.ens.domains/mainnet/avatar/jellymcjellyfish.eth',
|
||||||
|
accountId: '0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0',
|
||||||
|
approvedBaseTokens: [
|
||||||
|
{
|
||||||
|
address: '0xcfdda22c9837ae76e0faa845354f33c62e03653a',
|
||||||
|
symbol: 'OCEAN',
|
||||||
|
name: 'Ocean Token',
|
||||||
|
decimals: 18
|
||||||
|
}
|
||||||
|
],
|
||||||
|
balance: { eth: '0', ocean: '1000' },
|
||||||
|
block: 7751969,
|
||||||
|
chainId: 5,
|
||||||
|
connect: jest.fn(),
|
||||||
|
isSupportedOceanNetwork: true,
|
||||||
|
isTestnet: true,
|
||||||
|
logout: jest.fn(),
|
||||||
|
networkData: { name: 'Görli', title: 'Ethereum Testnet Görli', chain: 'ETH' },
|
||||||
|
networkDisplayName: 'ETH Görli',
|
||||||
|
networkId: 5,
|
||||||
|
web3: { currentProvider: {} },
|
||||||
|
web3Loading: false,
|
||||||
|
web3Modal: { show: false, eventController: {}, connect: jest.fn() },
|
||||||
|
web3Provider: {},
|
||||||
|
web3ProviderInfo: {
|
||||||
|
id: 'injected',
|
||||||
|
name: 'MetaMask',
|
||||||
|
logo: ''
|
||||||
|
}
|
||||||
|
}
|
3
.jest/__mocks__/@utils/accessDetailsAndPricing.ts
Normal file
3
.jest/__mocks__/@utils/accessDetailsAndPricing.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { assets } from '../../__fixtures__/assetsWithAccessDetails'
|
||||||
|
|
||||||
|
export const getAccessDetailsForAssets = jest.fn().mockResolvedValue(assets)
|
1
.jest/__mocks__/is-url-superb/index.ts
Normal file
1
.jest/__mocks__/is-url-superb/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default true
|
@ -1,7 +1,23 @@
|
|||||||
import '@testing-library/jest-dom/extend-expect'
|
import '@testing-library/jest-dom/extend-expect'
|
||||||
import './__mocks__/matchMedia'
|
import './__mocks__/matchMedia'
|
||||||
import marketMetadataMock from './__mocks__/MarketMetadata'
|
|
||||||
|
import marketMetadata from './__fixtures__/marketMetadata'
|
||||||
|
import userPreferences from './__fixtures__/userPreferences'
|
||||||
|
import web3 from './__fixtures__/web3'
|
||||||
|
import { asset } from './__fixtures__/assetWithAccessDetails'
|
||||||
|
|
||||||
jest.mock('../../src/@context/MarketMetadata', () => ({
|
jest.mock('../../src/@context/MarketMetadata', () => ({
|
||||||
useMarketMetadata: () => marketMetadataMock
|
useMarketMetadata: () => marketMetadata
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.mock('../../src/@context/UserPreferences', () => ({
|
||||||
|
useUserPreferences: () => userPreferences
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.mock('../../src/@context/Web3', () => ({
|
||||||
|
useWeb3: () => web3
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.mock('../../../@context/Asset', () => ({
|
||||||
|
useAsset: () => ({ asset })
|
||||||
}))
|
}))
|
||||||
|
7605
package-lock.json
generated
7605
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
29
package.json
29
package.json
@ -17,7 +17,7 @@
|
|||||||
"format": "prettier --ignore-path .gitignore './**/*.{css,yml,js,ts,tsx,json}' --write",
|
"format": "prettier --ignore-path .gitignore './**/*.{css,yml,js,ts,tsx,json}' --write",
|
||||||
"type-check": "tsc --noEmit",
|
"type-check": "tsc --noEmit",
|
||||||
"deploy:s3": "bash scripts/deploy-s3.sh",
|
"deploy:s3": "bash scripts/deploy-s3.sh",
|
||||||
"postinstall": "husky install && rm -r node_modules/apollo-language-server/node_modules/graphql",
|
"postinstall": "husky install",
|
||||||
"codegen:apollo": "apollo client:codegen --endpoint=https://v4.subgraph.goerli.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph --target typescript --tsFileExtension=d.ts --outputFlat src/@types/subgraph/",
|
"codegen:apollo": "apollo client:codegen --endpoint=https://v4.subgraph.goerli.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph --target typescript --tsFileExtension=d.ts --outputFlat src/@types/subgraph/",
|
||||||
"storybook": "cross-env NODE_ENV=test start-storybook -p 6006 --quiet",
|
"storybook": "cross-env NODE_ENV=test start-storybook -p 6006 --quiet",
|
||||||
"storybook:build": "cross-env NODE_ENV=test build-storybook"
|
"storybook:build": "cross-env NODE_ENV=test build-storybook"
|
||||||
@ -43,8 +43,6 @@
|
|||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"is-url-superb": "^6.1.0",
|
"is-url-superb": "^6.1.0",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
|
||||||
"lodash.omit": "^4.5.0",
|
|
||||||
"match-sorter": "^6.3.1",
|
"match-sorter": "^6.3.1",
|
||||||
"myetherwallet-blockies": "^0.1.1",
|
"myetherwallet-blockies": "^0.1.1",
|
||||||
"next": "12.3.1",
|
"next": "12.3.1",
|
||||||
@ -72,8 +70,8 @@
|
|||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/addon-essentials": "^6.5.12",
|
"@storybook/addon-essentials": "^6.5.13",
|
||||||
"@storybook/builder-webpack5": "^6.5.12",
|
"@storybook/builder-webpack5": "^6.5.13",
|
||||||
"@storybook/manager-webpack5": "^6.5.13",
|
"@storybook/manager-webpack5": "^6.5.13",
|
||||||
"@storybook/react": "^6.5.13",
|
"@storybook/react": "^6.5.13",
|
||||||
"@svgr/webpack": "^6.5.1",
|
"@svgr/webpack": "^6.5.1",
|
||||||
@ -81,26 +79,24 @@
|
|||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@types/js-cookie": "^3.0.2",
|
"@types/js-cookie": "^3.0.2",
|
||||||
"@types/loadable__component": "^5.13.4",
|
"@types/loadable__component": "^5.13.4",
|
||||||
"@types/lodash.debounce": "^4.0.7",
|
"@types/node": "^18.8.5",
|
||||||
"@types/lodash.omit": "^4.5.7",
|
|
||||||
"@types/node": "^18.7.18",
|
|
||||||
"@types/react": "^18.0.21",
|
"@types/react": "^18.0.21",
|
||||||
"@types/react-dom": "^18.0.5",
|
"@types/react-dom": "^18.0.5",
|
||||||
"@types/react-modal": "^3.13.1",
|
"@types/react-modal": "^3.13.1",
|
||||||
"@types/react-paginate": "^7.1.1",
|
"@types/react-paginate": "^7.1.1",
|
||||||
"@types/remove-markdown": "^0.3.1",
|
"@types/remove-markdown": "^0.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.38.1",
|
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
||||||
"@typescript-eslint/parser": "^5.38.1",
|
"@typescript-eslint/parser": "^5.42.0",
|
||||||
"apollo": "^2.34.0",
|
"apollo": "^2.34.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.23.1",
|
"eslint": "^8.25.0",
|
||||||
"eslint-config-oceanprotocol": "^2.0.4",
|
"eslint-config-oceanprotocol": "^2.0.4",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-jest-dom": "^4.0.2",
|
"eslint-plugin-jest-dom": "^4.0.2",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.31.8",
|
"eslint-plugin-react": "^7.31.10",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-testing-library": "^5.7.0",
|
"eslint-plugin-testing-library": "^5.7.2",
|
||||||
"https-browserify": "^1.0.0",
|
"https-browserify": "^1.0.0",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.1",
|
||||||
"jest": "^29.1.2",
|
"jest": "^29.1.2",
|
||||||
@ -111,14 +107,17 @@
|
|||||||
"serve": "^14.0.1",
|
"serve": "^14.0.1",
|
||||||
"stream-http": "^3.2.0",
|
"stream-http": "^3.2.0",
|
||||||
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
||||||
"typescript": "^4.8.3"
|
"typescript": "^4.8.4"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"graphql": "15.8.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/oceanprotocol/market"
|
"url": "https://github.com/oceanprotocol/market"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": "16"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
|
@ -323,7 +323,7 @@ export async function getAccessDetailsForAssets(
|
|||||||
},
|
},
|
||||||
queryContext
|
queryContext
|
||||||
)
|
)
|
||||||
tokenQueryResult.data?.tokens.forEach((token) => {
|
tokenQueryResult?.data?.tokens?.forEach((token) => {
|
||||||
const currentAsset = assetsExtended.find(
|
const currentAsset = assetsExtended.find(
|
||||||
(asset) =>
|
(asset) =>
|
||||||
asset.services[0].datatokenAddress.toLowerCase() === token.id
|
asset.services[0].datatokenAddress.toLowerCase() === token.id
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
|
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
|
||||||
import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
|
||||||
import axios, { CancelToken, AxiosResponse } from 'axios'
|
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||||
import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData'
|
import { OrdersData_orders as OrdersData } from '../@types/subgraph/OrdersData'
|
||||||
import { metadataCacheUri } from '../../app.config'
|
import { metadataCacheUri } from '../../app.config'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { getAccessDetailsForAssets } from './accessDetailsAndPricing'
|
import { getAccessDetailsForAssets } from './accessDetailsAndPricing'
|
||||||
import { PublisherTrustedAlgorithm, Asset } from '@oceanprotocol/lib'
|
import { PublisherTrustedAlgorithm, Asset } from '@oceanprotocol/lib'
|
||||||
import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
|
||||||
import { getServiceByName } from './ddo'
|
import { getServiceByName } from './ddo'
|
||||||
|
|
||||||
export async function transformAssetToAssetSelection(
|
export async function transformAssetToAssetSelection(
|
||||||
|
@ -21,10 +21,10 @@ import {
|
|||||||
} from './aquarius'
|
} from './aquarius'
|
||||||
import { fetchDataForMultipleChains } from './subgraph'
|
import { fetchDataForMultipleChains } from './subgraph'
|
||||||
import { getServiceById, getServiceByName } from './ddo'
|
import { getServiceById, getServiceByName } from './ddo'
|
||||||
import { SortTermOptions } from 'src/@types/aquarius/SearchQuery'
|
import { SortTermOptions } from '../@types/aquarius/SearchQuery'
|
||||||
import { AssetSelectionAsset } from '@shared/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
|
||||||
import { transformAssetToAssetSelection } from './assetConvertor'
|
import { transformAssetToAssetSelection } from './assetConvertor'
|
||||||
import { ComputeEditForm } from 'src/components/Asset/Edit/_types'
|
import { ComputeEditForm } from '../components/Asset/Edit/_types'
|
||||||
import { getFileDidInfo } from './provider'
|
import { getFileDidInfo } from './provider'
|
||||||
|
|
||||||
const getComputeOrders = gql`
|
const getComputeOrders = gql`
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
} from '@hooks/useNetworkMetadata'
|
} from '@hooks/useNetworkMetadata'
|
||||||
import { getAssetsFromNftList } from './aquarius'
|
import { getAssetsFromNftList } from './aquarius'
|
||||||
import { chainIdsSupported } from 'app.config'
|
import { chainIdsSupported } from 'app.config'
|
||||||
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
|
import { Asset } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
const AllLocked = gql`
|
const AllLocked = gql`
|
||||||
query AllLocked {
|
query AllLocked {
|
||||||
@ -42,7 +42,7 @@ const NftOwnAllocation = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
const OceanLocked = gql`
|
const OceanLocked = gql`
|
||||||
query OceanLocked($address: String) {
|
query OceanLocked($address: ID!) {
|
||||||
veOCEAN(id: $address) {
|
veOCEAN(id: $address) {
|
||||||
id
|
id
|
||||||
lockedAmount
|
lockedAmount
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
.accountList {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: calc(var(--spacer) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 25rem) {
|
|
||||||
.accountList {
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
|
|
||||||
gap: var(--spacer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
color: var(--color-secondary);
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loaderWrap {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
29
src/components/@shared/AddToken/index.stories.tsx
Normal file
29
src/components/@shared/AddToken/index.stories.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import AddToken, { AddTokenProps } from '@shared/AddToken'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/AddToken',
|
||||||
|
component: AddToken
|
||||||
|
} as ComponentMeta<typeof AddToken>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof AddToken> = (args: AddTokenProps) => {
|
||||||
|
return <AddToken {...args} />
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: AddTokenProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
address: '0xd8992Ed72C445c35Cb4A2be468568Ed1079357c8',
|
||||||
|
symbol: 'OCEAN'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Minimal: Props = Template.bind({})
|
||||||
|
Minimal.args = {
|
||||||
|
address: '0xd8992Ed72C445c35Cb4A2be468568Ed1079357c8',
|
||||||
|
symbol: 'OCEAN',
|
||||||
|
minimal: true
|
||||||
|
}
|
24
src/components/@shared/AddToken/index.test.tsx
Normal file
24
src/components/@shared/AddToken/index.test.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import AddToken from './index'
|
||||||
|
|
||||||
|
jest.mock('../../../@utils/web3', () => ({ addTokenToWallet: jest.fn() }))
|
||||||
|
|
||||||
|
describe('@shared/AddToken', () => {
|
||||||
|
const propsBase = {
|
||||||
|
address: '0xd8992Ed72C445c35Cb4A2be468568Ed1079357c8',
|
||||||
|
symbol: 'OCEAN'
|
||||||
|
}
|
||||||
|
testRender(<AddToken {...propsBase} />)
|
||||||
|
|
||||||
|
it('renders with custom text', () => {
|
||||||
|
render(<AddToken {...propsBase} text="Hello Text" />)
|
||||||
|
expect(screen.getByText('Hello Text')).toBeInTheDocument()
|
||||||
|
fireEvent.click(screen.getByRole('button'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders minimal', () => {
|
||||||
|
render(<AddToken {...propsBase} minimal />)
|
||||||
|
})
|
||||||
|
})
|
@ -8,19 +8,21 @@ import styles from './index.module.css'
|
|||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
export interface AddTokenProps {
|
||||||
|
address: string
|
||||||
|
symbol: string
|
||||||
|
text?: string
|
||||||
|
className?: string
|
||||||
|
minimal?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export default function AddToken({
|
export default function AddToken({
|
||||||
address,
|
address,
|
||||||
symbol,
|
symbol,
|
||||||
text,
|
text,
|
||||||
className,
|
className,
|
||||||
minimal
|
minimal
|
||||||
}: {
|
}: AddTokenProps): ReactElement {
|
||||||
address: string
|
|
||||||
symbol: string
|
|
||||||
text?: string
|
|
||||||
className?: string
|
|
||||||
minimal?: boolean
|
|
||||||
}): ReactElement {
|
|
||||||
const { web3Provider } = useWeb3()
|
const { web3Provider } = useWeb3()
|
||||||
|
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
|
65
src/components/@shared/AnnouncementBanner/index.stories.tsx
Normal file
65
src/components/@shared/AnnouncementBanner/index.stories.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import AddAnnouncementBanner, {
|
||||||
|
AnnouncementBannerProps
|
||||||
|
} from '@shared/AnnouncementBanner'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/AnnouncementBanner',
|
||||||
|
component: AddAnnouncementBanner
|
||||||
|
} as ComponentMeta<typeof AddAnnouncementBanner>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof AddAnnouncementBanner> = (
|
||||||
|
args: AnnouncementBannerProps
|
||||||
|
) => <AddAnnouncementBanner {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: AnnouncementBannerProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce malesuada ipsum ac enim auctor placerat.',
|
||||||
|
action: {
|
||||||
|
name: 'see more',
|
||||||
|
handleAction: () => {
|
||||||
|
alert('Link clicked!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Success: Props = Template.bind({})
|
||||||
|
Success.args = {
|
||||||
|
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce malesuada ipsum ac enim auctor placerat.',
|
||||||
|
state: 'success',
|
||||||
|
action: {
|
||||||
|
name: 'see more',
|
||||||
|
handleAction: () => {
|
||||||
|
alert('Link clicked!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Warning: Props = Template.bind({})
|
||||||
|
Warning.args = {
|
||||||
|
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce malesuada ipsum ac enim auctor placerat.',
|
||||||
|
state: 'warning',
|
||||||
|
action: {
|
||||||
|
name: 'see more',
|
||||||
|
handleAction: () => {
|
||||||
|
alert('Link clicked!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Error: Props = Template.bind({})
|
||||||
|
Error.args = {
|
||||||
|
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce malesuada ipsum ac enim auctor placerat.',
|
||||||
|
state: 'error',
|
||||||
|
action: {
|
||||||
|
name: 'see more',
|
||||||
|
handleAction: () => {
|
||||||
|
alert('Link clicked!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/components/@shared/AnnouncementBanner/index.test.tsx
Normal file
13
src/components/@shared/AnnouncementBanner/index.test.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import AnnouncementBanner from './index'
|
||||||
|
|
||||||
|
describe('@shared/AnnouncementBanner', () => {
|
||||||
|
testRender(
|
||||||
|
<AnnouncementBanner
|
||||||
|
text="# Hello World!"
|
||||||
|
action={{ name: 'hello', handleAction: jest.fn() }}
|
||||||
|
state="success"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
@ -12,17 +12,19 @@ export interface AnnouncementAction {
|
|||||||
handleAction: () => void
|
handleAction: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AnnouncementBannerProps {
|
||||||
|
text: string
|
||||||
|
action?: AnnouncementAction
|
||||||
|
state?: 'success' | 'warning' | 'error'
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
export default function AnnouncementBanner({
|
export default function AnnouncementBanner({
|
||||||
text,
|
text,
|
||||||
action,
|
action,
|
||||||
state,
|
state,
|
||||||
className
|
className
|
||||||
}: {
|
}: AnnouncementBannerProps): ReactElement {
|
||||||
text: string
|
|
||||||
action?: AnnouncementAction
|
|
||||||
state?: 'success' | 'warning' | 'error'
|
|
||||||
className?: string
|
|
||||||
}): ReactElement {
|
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
banner: true,
|
banner: true,
|
||||||
error: state === 'error',
|
error: state === 'error',
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
.display {
|
|
||||||
composes: selection from '@shared/FormFields/AssetSelection/index.module.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.display [class*='loaderWrap'] {
|
|
||||||
margin: calc(var(--spacer) / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll {
|
|
||||||
composes: scroll from '@shared/FormFields/AssetSelection/index.module.css';
|
|
||||||
margin-top: 0;
|
|
||||||
border-top: none;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
composes: row from '@shared/FormFields/AssetSelection/index.module.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:first-child {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:hover {
|
|
||||||
background-color: var(--background-content);
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
composes: title from '@shared/FormFields/AssetSelection/index.module.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.hover:hover {
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.price {
|
|
||||||
composes: price from '@shared/FormFields/AssetSelection/index.module.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.price [class*='symbol'] {
|
|
||||||
font-size: calc(var(--font-size-small) / 1.2) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.did {
|
|
||||||
composes: did from '@shared/FormFields/AssetSelection/index.module.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
composes: empty from '@shared/FormFields/AssetSelection/index.module.css';
|
|
||||||
}
|
|
35
src/components/@shared/AssetList/index.stories.tsx
Normal file
35
src/components/@shared/AssetList/index.stories.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import MarketMetadataProvider from '@context/MarketMetadata'
|
||||||
|
import { UserPreferencesProvider } from '@context/UserPreferences'
|
||||||
|
import AssetList, { AssetListProps } from '.'
|
||||||
|
import { assets } from '../../../../.jest/__fixtures__/assetsWithAccessDetails'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/AssetList',
|
||||||
|
component: AssetList
|
||||||
|
} as ComponentMeta<typeof AssetList>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof AssetList> = (args: AssetListProps) => {
|
||||||
|
return (
|
||||||
|
<MarketMetadataProvider>
|
||||||
|
<UserPreferencesProvider>
|
||||||
|
<AssetList {...args} />
|
||||||
|
</UserPreferencesProvider>
|
||||||
|
</MarketMetadataProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: { args: AssetListProps } = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
assets,
|
||||||
|
showPagination: true,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Empty: { args: AssetListProps } = Template.bind({})
|
||||||
|
Empty.args = {
|
||||||
|
assets: [],
|
||||||
|
showPagination: false
|
||||||
|
}
|
32
src/components/@shared/AssetList/index.test.tsx
Normal file
32
src/components/@shared/AssetList/index.test.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { render, screen, fireEvent } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import AssetList from './index'
|
||||||
|
import { assetAquarius } from '../../../../.jest/__fixtures__/assetAquarius'
|
||||||
|
|
||||||
|
describe('@shared/AssetList', () => {
|
||||||
|
it('renders without crashing', async () => {
|
||||||
|
const onPageChange = jest.fn()
|
||||||
|
|
||||||
|
render(
|
||||||
|
<AssetList
|
||||||
|
assets={[assetAquarius]}
|
||||||
|
showPagination
|
||||||
|
page={1}
|
||||||
|
totalPages={10}
|
||||||
|
onPageChange={onPageChange}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
await screen.findAllByText('OCEAN')
|
||||||
|
fireEvent.click(screen.getByLabelText('Page 2'))
|
||||||
|
expect(onPageChange).toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders empty', async () => {
|
||||||
|
render(<AssetList assets={[]} showPagination={false} isLoading={false} />)
|
||||||
|
await screen.findByText('No results found')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders loading', async () => {
|
||||||
|
render(<AssetList assets={[]} showPagination={false} isLoading />)
|
||||||
|
})
|
||||||
|
})
|
@ -2,15 +2,11 @@ import AssetTeaser from '@shared/AssetTeaser'
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import Pagination from '@shared/Pagination'
|
import Pagination from '@shared/Pagination'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import Loader from '@shared/atoms/Loader'
|
import Loader from '@shared/atoms/Loader'
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
|
||||||
import { useIsMounted } from '@hooks/useIsMounted'
|
import { useIsMounted } from '@hooks/useIsMounted'
|
||||||
import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing'
|
import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
function LoaderArea() {
|
function LoaderArea() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.loaderWrap}>
|
<div className={styles.loaderWrap}>
|
||||||
@ -19,7 +15,7 @@ function LoaderArea() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
declare type AssetListProps = {
|
export declare type AssetListProps = {
|
||||||
assets: AssetExtended[]
|
assets: AssetExtended[]
|
||||||
showPagination: boolean
|
showPagination: boolean
|
||||||
page?: number
|
page?: number
|
||||||
@ -40,14 +36,14 @@ export default function AssetList({
|
|||||||
className,
|
className,
|
||||||
noPublisher
|
noPublisher
|
||||||
}: AssetListProps): ReactElement {
|
}: AssetListProps): ReactElement {
|
||||||
const { chainIds } = useUserPreferences()
|
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const [assetsWithPrices, setAssetsWithPrices] = useState<AssetExtended[]>()
|
const [assetsWithPrices, setAssetsWithPrices] =
|
||||||
|
useState<AssetExtended[]>(assets)
|
||||||
const [loading, setLoading] = useState<boolean>(isLoading)
|
const [loading, setLoading] = useState<boolean>(isLoading)
|
||||||
const isMounted = useIsMounted()
|
const isMounted = useIsMounted()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!assets) return
|
if (!assets || !assets.length) return
|
||||||
|
|
||||||
setAssetsWithPrices(assets as AssetExtended[])
|
setAssetsWithPrices(assets as AssetExtended[])
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@ -67,16 +63,9 @@ export default function AssetList({
|
|||||||
onPageChange(selected + 1)
|
onPageChange(selected + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styleClasses = cx({
|
const styleClasses = `${styles.assetList} ${className || ''}`
|
||||||
assetList: true,
|
|
||||||
[className]: className
|
|
||||||
})
|
|
||||||
|
|
||||||
return chainIds.length === 0 ? (
|
return assetsWithPrices && !loading ? (
|
||||||
<div className={styleClasses}>
|
|
||||||
<div className={styles.empty}>No network selected</div>
|
|
||||||
</div>
|
|
||||||
) : assetsWithPrices && !loading ? (
|
|
||||||
<>
|
<>
|
||||||
<div className={styleClasses}>
|
<div className={styleClasses}>
|
||||||
{assetsWithPrices.length > 0 ? (
|
{assetsWithPrices.length > 0 ? (
|
||||||
|
24
src/components/@shared/AssetListTitle/index.test.tsx
Normal file
24
src/components/@shared/AssetListTitle/index.test.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import AssetListTitle from '.'
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
|
||||||
|
jest.mock('../../../@utils/aquarius', () => ({
|
||||||
|
getAssetsNames: () => Promise.resolve('Test')
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('AssetListTitle', () => {
|
||||||
|
testRender(
|
||||||
|
<AssetListTitle asset={{ metadata: { name: 'Hello world' } } as any} />
|
||||||
|
)
|
||||||
|
|
||||||
|
it('renders with passed title', () => {
|
||||||
|
render(<AssetListTitle title="Hello Title" />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders with passed DID', () => {
|
||||||
|
render(
|
||||||
|
<AssetListTitle did="did:op:764b81877039fa2651b919fc91c399799acb837f270e6d17bfb7973fbe6e9408" />
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
@ -1,7 +1,7 @@
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { getAssetsNames } from '@utils/aquarius'
|
import { getAssetsNames } from '@utils/aquarius'
|
||||||
import styles from './AssetListTitle.module.css'
|
import styles from './index.module.css'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { Asset } from '@oceanprotocol/lib'
|
import { Asset } from '@oceanprotocol/lib'
|
||||||
import { useMarketMetadata } from '@context/MarketMetadata'
|
import { useMarketMetadata } from '@context/MarketMetadata'
|
8
src/components/@shared/AssetTeaser/index.test.tsx
Normal file
8
src/components/@shared/AssetTeaser/index.test.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import AssetTeaser from './index'
|
||||||
|
import { asset } from '../../../../.jest/__fixtures__/assetWithAccessDetails'
|
||||||
|
|
||||||
|
describe('@shared/AssetTeaser', () => {
|
||||||
|
testRender(<AssetTeaser asset={asset} />)
|
||||||
|
})
|
@ -11,7 +11,7 @@ import { getServiceByName } from '@utils/ddo'
|
|||||||
import { formatPrice } from '@shared/Price/PriceUnit'
|
import { formatPrice } from '@shared/Price/PriceUnit'
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
|
|
||||||
declare type AssetTeaserProps = {
|
export declare type AssetTeaserProps = {
|
||||||
asset: AssetExtended
|
asset: AssetExtended
|
||||||
noPublisher?: boolean
|
noPublisher?: boolean
|
||||||
}
|
}
|
||||||
|
7
src/components/@shared/DebugOutput/index.test.tsx
Normal file
7
src/components/@shared/DebugOutput/index.test.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import DebugOutput from './index'
|
||||||
|
|
||||||
|
describe('@shared/DebugOutput', () => {
|
||||||
|
testRender(<DebugOutput title="Debug" output="Hello Output" />)
|
||||||
|
})
|
21
src/components/@shared/ExplorerLink/index.test.tsx
Normal file
21
src/components/@shared/ExplorerLink/index.test.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import ExplorerLink from './index'
|
||||||
|
|
||||||
|
describe('@shared/ExplorerLink', () => {
|
||||||
|
testRender(
|
||||||
|
<ExplorerLink networkId={1} path="/tx">
|
||||||
|
Hello Link
|
||||||
|
</ExplorerLink>
|
||||||
|
)
|
||||||
|
|
||||||
|
it('renders without networkId', () => {
|
||||||
|
render(
|
||||||
|
<ExplorerLink networkId={null} path="/tx">
|
||||||
|
Hello Link
|
||||||
|
</ExplorerLink>
|
||||||
|
)
|
||||||
|
expect(screen.getByRole('link')).toHaveTextContent('Hello Link')
|
||||||
|
})
|
||||||
|
})
|
@ -1,12 +1,9 @@
|
|||||||
import React, { ReactElement, ReactNode, useEffect, useState } from 'react'
|
import React, { ReactElement, ReactNode, useEffect, useState } from 'react'
|
||||||
import External from '@images/external.svg'
|
import External from '@images/external.svg'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import { Config } from '@oceanprotocol/lib'
|
import { Config } from '@oceanprotocol/lib'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { getOceanConfig } from '@utils/ocean'
|
import { getOceanConfig } from '@utils/ocean'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
export default function ExplorerLink({
|
export default function ExplorerLink({
|
||||||
networkId,
|
networkId,
|
||||||
path,
|
path,
|
||||||
@ -20,10 +17,6 @@ export default function ExplorerLink({
|
|||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [url, setUrl] = useState<string>()
|
const [url, setUrl] = useState<string>()
|
||||||
const [oceanConfig, setOceanConfig] = useState<Config>()
|
const [oceanConfig, setOceanConfig] = useState<Config>()
|
||||||
const styleClasses = cx({
|
|
||||||
link: true,
|
|
||||||
[className]: className
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!networkId) return
|
if (!networkId) return
|
||||||
@ -39,7 +32,7 @@ export default function ExplorerLink({
|
|||||||
title={`View on ${oceanConfig?.explorerUri}`}
|
title={`View on ${oceanConfig?.explorerUri}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className={styleClasses}
|
className={`${styles.link} ${className || ''}`}
|
||||||
>
|
>
|
||||||
{children} <External />
|
{children} <External />
|
||||||
</a>
|
</a>
|
||||||
|
37
src/components/@shared/FileIcon/index.test.tsx
Normal file
37
src/components/@shared/FileIcon/index.test.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import { FileInfo } from '@oceanprotocol/lib'
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import FileIcon from './index'
|
||||||
|
|
||||||
|
describe('@shared/FileIcon', () => {
|
||||||
|
const file: FileInfo = {
|
||||||
|
type: 'url',
|
||||||
|
contentType: 'text/plain',
|
||||||
|
contentLength: '123'
|
||||||
|
}
|
||||||
|
|
||||||
|
testRender(<FileIcon file={file} />)
|
||||||
|
|
||||||
|
it('renders small', () => {
|
||||||
|
render(<FileIcon file={file} small />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders loading', () => {
|
||||||
|
render(<FileIcon file={file} isLoading />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders empty', () => {
|
||||||
|
const file: FileInfo = { type: 'url' }
|
||||||
|
render(<FileIcon file={file} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders with 0 contentLength', () => {
|
||||||
|
const file: FileInfo = {
|
||||||
|
type: 'url',
|
||||||
|
contentType: 'text/plain',
|
||||||
|
contentLength: '0'
|
||||||
|
}
|
||||||
|
render(<FileIcon file={file} />)
|
||||||
|
})
|
||||||
|
})
|
@ -1,17 +0,0 @@
|
|||||||
export function prettySize(
|
|
||||||
bytes: number,
|
|
||||||
separator = ' ',
|
|
||||||
postFix = ''
|
|
||||||
): string {
|
|
||||||
if (bytes) {
|
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
|
|
||||||
const i = Math.min(
|
|
||||||
Math.floor(Math.log(bytes) / Math.log(1024)),
|
|
||||||
sizes.length - 1
|
|
||||||
)
|
|
||||||
return `${(bytes / 1024 ** i).toFixed(i ? 1 : 0)}${separator}${
|
|
||||||
sizes[i]
|
|
||||||
}${postFix}`
|
|
||||||
}
|
|
||||||
return 'n/a'
|
|
||||||
}
|
|
21
src/components/@shared/FormInput/Error.test.tsx
Normal file
21
src/components/@shared/FormInput/Error.test.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import Error from './Error'
|
||||||
|
|
||||||
|
describe('@shared/FormInput/Error', () => {
|
||||||
|
const propsBase = {
|
||||||
|
value: '',
|
||||||
|
touched: false,
|
||||||
|
initialTouched: false
|
||||||
|
}
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(<Error meta={{ ...propsBase, error: 'Hello Error' }} />)
|
||||||
|
expect(screen.getByText('Hello Error')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders nothing without error passed', () => {
|
||||||
|
render(<Error meta={{ ...propsBase }} />)
|
||||||
|
expect(screen.queryByText('Hello Error')).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
@ -1,9 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Help.module.css'
|
import styles from './Help.module.css'
|
||||||
import Markdown from '@shared/Markdown'
|
import Markdown from '@shared/Markdown'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
const FormHelp = ({
|
const FormHelp = ({
|
||||||
children,
|
children,
|
||||||
@ -12,12 +9,9 @@ const FormHelp = ({
|
|||||||
children: string
|
children: string
|
||||||
className?: string
|
className?: string
|
||||||
}): ReactElement => {
|
}): ReactElement => {
|
||||||
const styleClasses = cx({
|
return (
|
||||||
help: true,
|
<Markdown className={`${styles.help} ${className || ''}`} text={children} />
|
||||||
[className]: className
|
)
|
||||||
})
|
|
||||||
|
|
||||||
return <Markdown className={styleClasses} text={children} />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FormHelp
|
export default FormHelp
|
||||||
|
@ -58,11 +58,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.radio {
|
.radio {
|
||||||
composes: radio from '@shared/FormInput/InputRadio.module.css';
|
composes: radio from '@shared/FormInput/InputElement/Radio/index.module.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
composes: checkbox from '@shared/FormInput/InputRadio.module.css';
|
composes: checkbox from '@shared/FormInput/InputElement/Radio/index.module.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
@ -0,0 +1,52 @@
|
|||||||
|
import AssetSelection, { AssetSelectionAsset } from './'
|
||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
describe('@shared/FormInput/InputElement/AssetSelection', () => {
|
||||||
|
const assets: AssetSelectionAsset[] = [
|
||||||
|
{
|
||||||
|
did: 'did:op:xxx',
|
||||||
|
name: 'Asset',
|
||||||
|
price: '10',
|
||||||
|
checked: false,
|
||||||
|
symbol: 'OCEAN'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
did: 'did:op:yyy',
|
||||||
|
name: 'Asset',
|
||||||
|
price: '10',
|
||||||
|
checked: true,
|
||||||
|
symbol: 'OCEAN'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
did: 'did:op:zzz',
|
||||||
|
name: 'Asset',
|
||||||
|
price: '0',
|
||||||
|
checked: false,
|
||||||
|
symbol: 'OCEAN'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(<AssetSelection assets={assets} />)
|
||||||
|
const searchInput = screen.getByPlaceholderText(
|
||||||
|
'Search by title, datatoken, or DID...'
|
||||||
|
)
|
||||||
|
fireEvent.change(searchInput, { target: { value: 'Assets' } })
|
||||||
|
fireEvent.change(searchInput, { target: { value: '' } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders empty assetSelection', () => {
|
||||||
|
render(<AssetSelection assets={[]} />)
|
||||||
|
expect(screen.getByText('No assets found.')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders disabled assetSelection', () => {
|
||||||
|
render(<AssetSelection assets={[]} disabled />)
|
||||||
|
expect(screen.getByText('No assets found.')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders assetSelectionMultiple', () => {
|
||||||
|
render(<AssetSelection assets={assets} multiple />)
|
||||||
|
})
|
||||||
|
})
|
@ -1,15 +1,12 @@
|
|||||||
import React, { ChangeEvent, useState } from 'react'
|
import React, { ChangeEvent, useState } from 'react'
|
||||||
import Dotdotdot from 'react-dotdotdot'
|
import Dotdotdot from 'react-dotdotdot'
|
||||||
import slugify from 'slugify'
|
import slugify from 'slugify'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import PriceUnit from '@shared/Price/PriceUnit'
|
import PriceUnit from '@shared/Price/PriceUnit'
|
||||||
import External from '@images/external.svg'
|
import External from '@images/external.svg'
|
||||||
import InputElement from '@shared/FormInput/InputElement'
|
import InputElement from '@shared/FormInput/InputElement'
|
||||||
import Loader from '@shared/atoms/Loader'
|
import Loader from '@shared/atoms/Loader'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
export interface AssetSelectionAsset {
|
export interface AssetSelectionAsset {
|
||||||
did: string
|
did: string
|
||||||
name: string
|
name: string
|
||||||
@ -34,18 +31,19 @@ export default function AssetSelection({
|
|||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const [searchValue, setSearchValue] = useState('')
|
const [searchValue, setSearchValue] = useState('')
|
||||||
|
|
||||||
const styleClassesInput = cx({
|
const styleClassesWrapper = `${styles.selection} ${
|
||||||
input: true,
|
disabled ? styles.disabled : ''
|
||||||
[styles.checkbox]: multiple,
|
}`
|
||||||
[styles.radio]: !multiple
|
const styleClassesInput = `${styles.input} ${
|
||||||
})
|
multiple ? styles.checkbox : styles.radio
|
||||||
|
}`
|
||||||
|
|
||||||
function handleSearchInput(e: ChangeEvent<HTMLInputElement>) {
|
function handleSearchInput(e: ChangeEvent<HTMLInputElement>) {
|
||||||
setSearchValue(e.target.value)
|
setSearchValue(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.selection} ${disabled ? styles.disabled : ''}`}>
|
<div className={styleClassesWrapper}>
|
||||||
<InputElement
|
<InputElement
|
||||||
type="search"
|
type="search"
|
||||||
name="search"
|
name="search"
|
@ -0,0 +1,38 @@
|
|||||||
|
import BoxSelection, { BoxSelectionOption } from './'
|
||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
describe('@shared/FormInput/InputElement/BoxSelection', () => {
|
||||||
|
const handleChange = jest.fn()
|
||||||
|
const options: BoxSelectionOption[] = [
|
||||||
|
{
|
||||||
|
name: 'option1',
|
||||||
|
value: 'option1',
|
||||||
|
title: 'Option 1',
|
||||||
|
checked: true,
|
||||||
|
text: 'Option 1 Text',
|
||||||
|
icon: <div>Icon</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'option2',
|
||||||
|
title: 'Option 2 Text',
|
||||||
|
checked: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(
|
||||||
|
<BoxSelection name="box" options={options} handleChange={handleChange} />
|
||||||
|
)
|
||||||
|
fireEvent.click(screen.getByText('Option 2 Text'))
|
||||||
|
expect(handleChange).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders disabled', () => {
|
||||||
|
render(<BoxSelection name="box" options={options} disabled />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders loader without options', () => {
|
||||||
|
render(<BoxSelection name="box" options={null} />)
|
||||||
|
})
|
||||||
|
})
|
@ -1,10 +1,7 @@
|
|||||||
import React, { ChangeEvent } from 'react'
|
import React, { ChangeEvent } from 'react'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import Loader from '@shared/atoms/Loader'
|
import Loader from '@shared/atoms/Loader'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
export interface BoxSelectionOption {
|
export interface BoxSelectionOption {
|
||||||
name: string
|
name: string
|
||||||
value?: string
|
value?: string
|
||||||
@ -26,15 +23,10 @@ export default function BoxSelection({
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
handleChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
handleChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const styleClassesWrapper = cx({
|
const styleClassesWrapper = `${styles.boxSelectionsWrapper} ${
|
||||||
boxSelectionsWrapper: true,
|
disabled ? styles.disabled : ''
|
||||||
[styles.disabled]: disabled
|
}`
|
||||||
})
|
const styleClassesInput = `${styles.input} ${styles.radio}`
|
||||||
|
|
||||||
const styleClassesInput = cx({
|
|
||||||
input: true,
|
|
||||||
radio: true
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styleClassesWrapper}>
|
<div className={styleClassesWrapper}>
|
||||||
@ -51,7 +43,7 @@ export default function BoxSelection({
|
|||||||
type="radio"
|
type="radio"
|
||||||
className={styleClassesInput}
|
className={styleClassesInput}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={option.value ? option.value : option.name}
|
value={option.value || option.name}
|
||||||
name={name}
|
name={name}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
@ -0,0 +1,59 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import Datatoken from './index'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
|
||||||
|
jest.mock('formik')
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
name: 'Datatoken'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockMeta = {
|
||||||
|
touched: false,
|
||||||
|
error: '',
|
||||||
|
initialError: '',
|
||||||
|
initialTouched: false,
|
||||||
|
initialValue: '',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockField = {
|
||||||
|
value: {
|
||||||
|
name: '',
|
||||||
|
symbol: ''
|
||||||
|
},
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'NFT'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockHelpers = {
|
||||||
|
setValue: jest.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('@shared/FormInput/InputElement/Datatoken', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([mockField, mockMeta, mockHelpers])
|
||||||
|
render(<Datatoken {...props} />)
|
||||||
|
fireEvent.click(screen.getByRole('button'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does nothing when data already present', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
|
{
|
||||||
|
value: {
|
||||||
|
name: 'Hello Name'
|
||||||
|
},
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'NFT'
|
||||||
|
},
|
||||||
|
mockMeta,
|
||||||
|
mockHelpers
|
||||||
|
])
|
||||||
|
render(<Datatoken {...props} />)
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,125 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import FilesInput from './index'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
import { getFileUrlInfo } from '@utils/provider'
|
||||||
|
|
||||||
|
jest.mock('formik')
|
||||||
|
jest.mock('@utils/provider')
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
name: 'File'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockMeta = {
|
||||||
|
touched: false,
|
||||||
|
error: '',
|
||||||
|
initialError: '',
|
||||||
|
initialTouched: false,
|
||||||
|
initialValue: '',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockField = {
|
||||||
|
value: 'https://hello.com',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'url'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockHelpers = {
|
||||||
|
setValue: jest.fn(),
|
||||||
|
setTouched: jest.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockForm = {
|
||||||
|
values: {
|
||||||
|
services: [{ providerUrl: 'https://provider.url' }]
|
||||||
|
},
|
||||||
|
errors: {},
|
||||||
|
touched: {},
|
||||||
|
isSubmitting: false,
|
||||||
|
isValidating: false,
|
||||||
|
submitCount: 0,
|
||||||
|
setFieldError: jest.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('@shared/FormInput/InputElement/FilesInput', () => {
|
||||||
|
it('renders without crashing', async () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([mockField, mockMeta, mockHelpers])
|
||||||
|
;(getFileUrlInfo as jest.Mock).mockReturnValue([
|
||||||
|
{
|
||||||
|
valid: true,
|
||||||
|
url: 'https://hello.com',
|
||||||
|
contentType: 'text/html',
|
||||||
|
contentLength: 100
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
render(<FilesInput form={mockForm} {...props} />)
|
||||||
|
expect(screen.getByText('Validate')).toBeInTheDocument()
|
||||||
|
fireEvent.click(screen.getByText('Validate'))
|
||||||
|
|
||||||
|
// can't really re-mock our helpers.setValue() behavior switching
|
||||||
|
// to Info component, so we just wait for Validate button to be back again.
|
||||||
|
await screen.findByText('Validate')
|
||||||
|
expect(mockHelpers.setValue).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders fileinfo when file is valid', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
|
{
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
valid: true,
|
||||||
|
url: 'https://hello.com',
|
||||||
|
contentType: 'text/html',
|
||||||
|
contentLength: 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mockMeta,
|
||||||
|
mockHelpers
|
||||||
|
])
|
||||||
|
render(<FilesInput {...props} />)
|
||||||
|
expect(screen.getByText('https://hello.com')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders fileinfo without contentType', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
|
{
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
valid: true,
|
||||||
|
url: 'https://hello.com',
|
||||||
|
contentLength: 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mockMeta,
|
||||||
|
mockHelpers
|
||||||
|
])
|
||||||
|
render(<FilesInput {...props} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders fileinfo placeholder when hideUrl is passed', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
|
{
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
valid: true,
|
||||||
|
url: 'https://hello.com',
|
||||||
|
type: 'hidden'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mockMeta,
|
||||||
|
mockHelpers
|
||||||
|
])
|
||||||
|
render(<FilesInput {...props} />)
|
||||||
|
expect(
|
||||||
|
screen.getByText('https://oceanprotocol/placeholder')
|
||||||
|
).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
@ -1,17 +1,15 @@
|
|||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { useField, useFormikContext } from 'formik'
|
import { useField } from 'formik'
|
||||||
import FileInfo from './Info'
|
import FileInfo from './Info'
|
||||||
import UrlInput from '../URLInput'
|
import UrlInput from '../URLInput'
|
||||||
import { InputProps } from '@shared/FormInput'
|
import { InputProps } from '@shared/FormInput'
|
||||||
import { getFileUrlInfo } from '@utils/provider'
|
import { getFileUrlInfo } from '@utils/provider'
|
||||||
import { FormPublishData } from 'src/components/Publish/_types'
|
|
||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||||
import { useAsset } from '@context/Asset'
|
import { useAsset } from '@context/Asset'
|
||||||
|
|
||||||
export default function FilesInput(props: InputProps): ReactElement {
|
export default function FilesInput(props: InputProps): ReactElement {
|
||||||
const [field, meta, helpers] = useField(props.name)
|
const [field, meta, helpers] = useField(props.name)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const { values, setFieldError } = useFormikContext<FormPublishData>()
|
|
||||||
const { asset } = useAsset()
|
const { asset } = useAsset()
|
||||||
|
|
||||||
async function handleValidation(e: React.SyntheticEvent, url: string) {
|
async function handleValidation(e: React.SyntheticEvent, url: string) {
|
||||||
@ -19,8 +17,8 @@ export default function FilesInput(props: InputProps): ReactElement {
|
|||||||
e?.preventDefault()
|
e?.preventDefault()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const providerUrl = values?.services
|
const providerUrl = props.form?.values?.services
|
||||||
? values?.services[0].providerUrl.url
|
? props.form?.values?.services[0].providerUrl.url
|
||||||
: asset.services[0].serviceEndpoint
|
: asset.services[0].serviceEndpoint
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const checkedFile = await getFileUrlInfo(url, providerUrl)
|
const checkedFile = await getFileUrlInfo(url, providerUrl)
|
||||||
@ -35,7 +33,7 @@ export default function FilesInput(props: InputProps): ReactElement {
|
|||||||
// if all good, add file to formik state
|
// if all good, add file to formik state
|
||||||
helpers.setValue([{ url, ...checkedFile[0] }])
|
helpers.setValue([{ url, ...checkedFile[0] }])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFieldError(`${field.name}[0].url`, error.message)
|
props.form.setFieldError(`${field.name}[0].url`, error.message)
|
||||||
LoggerInstance.error(error.message)
|
LoggerInstance.error(error.message)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
@ -0,0 +1,16 @@
|
|||||||
|
export function prettySize(
|
||||||
|
bytes: number,
|
||||||
|
separator = ' ',
|
||||||
|
postFix = ''
|
||||||
|
): string {
|
||||||
|
if (!bytes) return 'n/a'
|
||||||
|
|
||||||
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
|
||||||
|
const i = Math.min(
|
||||||
|
Math.floor(Math.log(bytes) / Math.log(1024)),
|
||||||
|
sizes.length - 1
|
||||||
|
)
|
||||||
|
return `${(bytes / 1024 ** i).toFixed(i ? 1 : 0)}${separator}${
|
||||||
|
sizes[i]
|
||||||
|
}${postFix}`
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import Nft from './index'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
|
||||||
|
jest.mock('formik')
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
name: 'NFT'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockMeta = {
|
||||||
|
touched: false,
|
||||||
|
error: '',
|
||||||
|
initialError: '',
|
||||||
|
initialTouched: false,
|
||||||
|
initialValue: '',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockField = {
|
||||||
|
value: {
|
||||||
|
name: '',
|
||||||
|
symbol: '',
|
||||||
|
description: '',
|
||||||
|
external_url: '',
|
||||||
|
background_color: '',
|
||||||
|
image_data: ''
|
||||||
|
},
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'NFT'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockHelpers = {
|
||||||
|
setValue: jest.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('@shared/FormInput/InputElement/Nft', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([mockField, mockMeta, mockHelpers])
|
||||||
|
render(<Nft {...props} />)
|
||||||
|
fireEvent.click(screen.getByRole('button'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does nothing when data already present', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
|
{
|
||||||
|
value: {
|
||||||
|
name: 'Hello Name'
|
||||||
|
},
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'NFT'
|
||||||
|
},
|
||||||
|
mockMeta,
|
||||||
|
mockHelpers
|
||||||
|
])
|
||||||
|
render(<Nft {...props} />)
|
||||||
|
})
|
||||||
|
})
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
.radio,
|
.radio,
|
||||||
.checkbox {
|
.checkbox {
|
||||||
composes: input from './InputElement.module.css';
|
composes: input from '../index.module.css';
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 18px;
|
width: 18px;
|
@ -1,7 +1,7 @@
|
|||||||
import React, { InputHTMLAttributes, ReactElement } from 'react'
|
import React, { InputHTMLAttributes, ReactElement } from 'react'
|
||||||
import slugify from 'slugify'
|
import slugify from 'slugify'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import styles from './InputRadio.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
@ -2,11 +2,11 @@ import React, { ReactElement, useEffect, useState } from 'react'
|
|||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import { OnChangeValue } from 'react-select'
|
import { OnChangeValue } from 'react-select'
|
||||||
import { useField } from 'formik'
|
import { useField } from 'formik'
|
||||||
import { InputProps } from '.'
|
import { InputProps } from '../..'
|
||||||
import { getTagsList } from '@utils/aquarius'
|
import { getTagsList } from '@utils/aquarius'
|
||||||
import { chainIds } from 'app.config'
|
import { chainIds } from '../../../../../../app.config'
|
||||||
import { useCancelToken } from '@hooks/useCancelToken'
|
import { useCancelToken } from '@hooks/useCancelToken'
|
||||||
import styles from './TagsAutoComplete.module.css'
|
import styles from './index.module.css'
|
||||||
import { matchSorter } from 'match-sorter'
|
import { matchSorter } from 'match-sorter'
|
||||||
|
|
||||||
interface AutoCompleteOption {
|
interface AutoCompleteOption {
|
@ -1,5 +1,5 @@
|
|||||||
.input {
|
.input {
|
||||||
composes: input from '@shared/FormInput/InputElement.module.css';
|
composes: input from '@shared/FormInput/InputElement/index.module.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
.hasError {
|
.hasError {
|
@ -0,0 +1,57 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import URLInput, { URLInputProps } from './index'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
|
||||||
|
jest.mock('formik')
|
||||||
|
|
||||||
|
const props: URLInputProps = {
|
||||||
|
submitText: 'Submit',
|
||||||
|
handleButtonClick: jest.fn(),
|
||||||
|
isLoading: false,
|
||||||
|
name: 'Hello Name'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockMeta = {
|
||||||
|
touched: false,
|
||||||
|
error: '',
|
||||||
|
initialError: '',
|
||||||
|
initialTouched: false,
|
||||||
|
initialValue: '',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('@shared/FormInput/InputElement/URLInput', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const mockField = {
|
||||||
|
value: '',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'url'
|
||||||
|
}
|
||||||
|
;(useField as jest.Mock).mockReturnValue([mockField, mockMeta])
|
||||||
|
|
||||||
|
render(<URLInput {...props} />)
|
||||||
|
expect(screen.getByRole('button')).toBeDisabled()
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByRole('textbox'), {
|
||||||
|
target: { value: 'https://google.com' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders button enabled with value', () => {
|
||||||
|
const mockField = {
|
||||||
|
value: 'https://google.com',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'url'
|
||||||
|
}
|
||||||
|
;(useField as jest.Mock).mockReturnValue([mockField, mockMeta])
|
||||||
|
|
||||||
|
render(<URLInput {...props} />)
|
||||||
|
expect(screen.getByRole('button')).toBeEnabled()
|
||||||
|
fireEvent.click(screen.getByRole('button'))
|
||||||
|
})
|
||||||
|
})
|
@ -7,6 +7,14 @@ import InputGroup from '@shared/FormInput/InputGroup'
|
|||||||
import InputElement from '@shared/FormInput/InputElement'
|
import InputElement from '@shared/FormInput/InputElement'
|
||||||
import isUrl from 'is-url-superb'
|
import isUrl from 'is-url-superb'
|
||||||
|
|
||||||
|
export interface URLInputProps {
|
||||||
|
submitText: string
|
||||||
|
handleButtonClick(e: React.SyntheticEvent, data: string): void
|
||||||
|
isLoading: boolean
|
||||||
|
name: string
|
||||||
|
checkUrl?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export default function URLInput({
|
export default function URLInput({
|
||||||
submitText,
|
submitText,
|
||||||
handleButtonClick,
|
handleButtonClick,
|
||||||
@ -14,13 +22,7 @@ export default function URLInput({
|
|||||||
name,
|
name,
|
||||||
checkUrl,
|
checkUrl,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: URLInputProps): ReactElement {
|
||||||
submitText: string
|
|
||||||
handleButtonClick(e: React.SyntheticEvent, data: string): void
|
|
||||||
isLoading: boolean
|
|
||||||
name: string
|
|
||||||
checkUrl?: boolean
|
|
||||||
}): ReactElement {
|
|
||||||
const [field, meta] = useField(name)
|
const [field, meta] = useField(name)
|
||||||
const [isButtonDisabled, setIsButtonDisabled] = useState(true)
|
const [isButtonDisabled, setIsButtonDisabled] = useState(true)
|
||||||
|
|
@ -1,17 +1,15 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './InputElement.module.css'
|
import styles from './index.module.css'
|
||||||
import { InputProps } from '.'
|
import { InputProps } from '..'
|
||||||
import FilesInput from '../FormFields/FilesInput'
|
import FilesInput from './FilesInput'
|
||||||
import CustomProvider from '../FormFields/Provider'
|
import CustomProvider from './Provider'
|
||||||
import BoxSelection, { BoxSelectionOption } from '../FormFields/BoxSelection'
|
import BoxSelection, { BoxSelectionOption } from './BoxSelection'
|
||||||
import Datatoken from '../FormFields/Datatoken'
|
import Datatoken from './Datatoken'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import AssetSelection, {
|
import AssetSelection, { AssetSelectionAsset } from './AssetSelection'
|
||||||
AssetSelectionAsset
|
import Nft from './Nft'
|
||||||
} from '../FormFields/AssetSelection'
|
import InputRadio from './Radio'
|
||||||
import Nft from '../FormFields/Nft'
|
import ContainerInput from '@shared/FormInput/InputElement/ContainerInput'
|
||||||
import InputRadio from './InputRadio'
|
|
||||||
import ContainerInput from '@shared/FormFields/ContainerInput'
|
|
||||||
import TagsAutoComplete from './TagsAutoComplete'
|
import TagsAutoComplete from './TagsAutoComplete'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
@ -87,12 +85,14 @@ export default function InputElement({
|
|||||||
|
|
||||||
case 'radio':
|
case 'radio':
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
return <InputRadio options={options} inputSize={size} {...props} />
|
return (
|
||||||
|
<InputRadio options={options as string[]} inputSize={size} {...props} />
|
||||||
|
)
|
||||||
|
|
||||||
case 'assetSelection':
|
case 'assetSelection':
|
||||||
return (
|
return (
|
||||||
<AssetSelection
|
<AssetSelection
|
||||||
assets={options as unknown as AssetSelectionAsset[]}
|
assets={options as AssetSelectionAsset[]}
|
||||||
{...field}
|
{...field}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -101,14 +101,14 @@ export default function InputElement({
|
|||||||
case 'assetSelectionMultiple':
|
case 'assetSelectionMultiple':
|
||||||
return (
|
return (
|
||||||
<AssetSelection
|
<AssetSelection
|
||||||
assets={options as unknown as AssetSelectionAsset[]}
|
assets={options as AssetSelectionAsset[]}
|
||||||
multiple
|
multiple
|
||||||
{...field}
|
{...field}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case 'files':
|
case 'files':
|
||||||
return <FilesInput {...field} {...props} />
|
return <FilesInput {...field} form={form} {...props} />
|
||||||
case 'container':
|
case 'container':
|
||||||
return <ContainerInput {...field} {...props} />
|
return <ContainerInput {...field} {...props} />
|
||||||
case 'providerUrl':
|
case 'providerUrl':
|
||||||
@ -120,7 +120,7 @@ export default function InputElement({
|
|||||||
case 'boxSelection':
|
case 'boxSelection':
|
||||||
return (
|
return (
|
||||||
<BoxSelection
|
<BoxSelection
|
||||||
options={options as unknown as BoxSelectionOption[]}
|
options={options as BoxSelectionOption[]}
|
||||||
{...field}
|
{...field}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
@ -1,3 +0,0 @@
|
|||||||
.row {
|
|
||||||
margin-bottom: var(--spacer);
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import React, { ReactElement, ReactNode } from 'react'
|
|
||||||
import styles from './Row.module.css'
|
|
||||||
|
|
||||||
const Row = ({ children }: { children: ReactNode }): ReactElement => (
|
|
||||||
<div className={styles.row}>{children}</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default Row
|
|
105
src/components/@shared/FormInput/index.test.tsx
Normal file
105
src/components/@shared/FormInput/index.test.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { BoxSelectionOption } from '@shared/FormInput/InputElement/BoxSelection'
|
||||||
|
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
|
||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import FormInput from './index'
|
||||||
|
|
||||||
|
describe('@shared/FormInput', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(
|
||||||
|
<FormInput
|
||||||
|
type="text"
|
||||||
|
name="Hello Name"
|
||||||
|
label="Hello Label"
|
||||||
|
placeholder="Hello Placeholder"
|
||||||
|
required
|
||||||
|
help="Hello Help"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(screen.getByText('Hello Label')).toBeInTheDocument()
|
||||||
|
expect(screen.getByPlaceholderText('Hello Placeholder')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders prominent help', () => {
|
||||||
|
render(<FormInput type="text" help="Hello Help" prominentHelp />)
|
||||||
|
expect(screen.getByText('Hello Help')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders disclaimer', () => {
|
||||||
|
render(<FormInput type="text" disclaimer="Hello Disclaimer" />)
|
||||||
|
expect(screen.getByText('Hello Disclaimer')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders with prefix & postfix', () => {
|
||||||
|
render(
|
||||||
|
<FormInput type="text" prefix="Hello Prefix" postfix="Hello Postfix" />
|
||||||
|
)
|
||||||
|
expect(screen.getByText('Hello Prefix')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('Hello Postfix')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders textarea', () => {
|
||||||
|
render(<FormInput type="textarea" />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders radio', () => {
|
||||||
|
render(<FormInput type="radio" options={['option1', 'option2']} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders checkbox', () => {
|
||||||
|
render(<FormInput type="checkbox" options={['option1', 'option2']} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders select', () => {
|
||||||
|
render(<FormInput type="select" options={['option1', 'option2']} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders assetSelection', () => {
|
||||||
|
const assets: AssetSelectionAsset[] = [
|
||||||
|
{
|
||||||
|
did: 'did:op:xxx',
|
||||||
|
name: 'Asset',
|
||||||
|
price: '10',
|
||||||
|
checked: false,
|
||||||
|
symbol: 'OCEAN'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
did: 'did:op:yyy',
|
||||||
|
name: 'Asset',
|
||||||
|
price: '10',
|
||||||
|
checked: true,
|
||||||
|
symbol: 'OCEAN'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
render(<FormInput type="assetSelection" options={assets} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders assetSelectionMultiple', () => {
|
||||||
|
render(<FormInput type="assetSelectionMultiple" />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders boxSelection', () => {
|
||||||
|
const options: BoxSelectionOption[] = [
|
||||||
|
{
|
||||||
|
name: 'option1',
|
||||||
|
title: 'Option 1',
|
||||||
|
checked: true,
|
||||||
|
text: 'Option 1 Text',
|
||||||
|
icon: <div>Icon</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'option2',
|
||||||
|
title: 'Option 2',
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
render(
|
||||||
|
<FormInput
|
||||||
|
type="boxSelection"
|
||||||
|
options={options}
|
||||||
|
onChange={() => jest.fn()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
@ -16,6 +16,8 @@ import Disclaimer from './Disclaimer'
|
|||||||
import Tooltip from '@shared/atoms/Tooltip'
|
import Tooltip from '@shared/atoms/Tooltip'
|
||||||
import Markdown from '@shared/Markdown'
|
import Markdown from '@shared/Markdown'
|
||||||
import FormHelp from './Help'
|
import FormHelp from './Help'
|
||||||
|
import { AssetSelectionAsset } from '@shared/FormInput/InputElement/AssetSelection'
|
||||||
|
import { BoxSelectionOption } from '@shared/FormInput/InputElement/BoxSelection'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
@ -28,7 +30,7 @@ export interface InputProps {
|
|||||||
prominentHelp?: boolean
|
prominentHelp?: boolean
|
||||||
tag?: string
|
tag?: string
|
||||||
type?: string
|
type?: string
|
||||||
options?: string[]
|
options?: string[] | AssetSelectionAsset[] | BoxSelectionOption[]
|
||||||
sortOptions?: boolean
|
sortOptions?: boolean
|
||||||
additionalComponent?: ReactElement
|
additionalComponent?: ReactElement
|
||||||
value?: string | number
|
value?: string | number
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
import * as React from 'react'
|
|
||||||
import { FormikProps, connect } from 'formik'
|
|
||||||
import debounce from 'lodash.debounce'
|
|
||||||
import omit from 'lodash.omit'
|
|
||||||
import isEqual from 'react-fast-compare'
|
|
||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
|
||||||
|
|
||||||
export interface PersistProps {
|
|
||||||
name: string
|
|
||||||
ignoreFields?: string[]
|
|
||||||
debounce?: number
|
|
||||||
isSessionStorage?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: refactor into functional component
|
|
||||||
class PersistImpl extends React.Component<
|
|
||||||
PersistProps & { formik: FormikProps<any> },
|
|
||||||
any
|
|
||||||
> {
|
|
||||||
static defaultProps = {
|
|
||||||
debounce: 300
|
|
||||||
}
|
|
||||||
|
|
||||||
saveForm = debounce((data: FormikProps<any>) => {
|
|
||||||
const dataToSave = this.omitIgnoredFields(data)
|
|
||||||
LoggerInstance.log('data to save', dataToSave)
|
|
||||||
if (this.props.isSessionStorage) {
|
|
||||||
window.sessionStorage.setItem(this.props.name, JSON.stringify(dataToSave))
|
|
||||||
} else {
|
|
||||||
window.localStorage.setItem(this.props.name, JSON.stringify(dataToSave))
|
|
||||||
}
|
|
||||||
}, this.props.debounce)
|
|
||||||
|
|
||||||
omitIgnoredFields = (data: FormikProps<any>) => {
|
|
||||||
const { ignoreFields } = this.props
|
|
||||||
LoggerInstance.log('omitted fields', ignoreFields)
|
|
||||||
const { values, touched, errors } = data
|
|
||||||
|
|
||||||
LoggerInstance.log('values', values, omit(values, ignoreFields))
|
|
||||||
return ignoreFields
|
|
||||||
? omit(
|
|
||||||
{
|
|
||||||
...data,
|
|
||||||
values: omit(values, ignoreFields),
|
|
||||||
touched: omit(touched, ignoreFields),
|
|
||||||
errors: omit(errors, ignoreFields)
|
|
||||||
},
|
|
||||||
ignoreFields
|
|
||||||
)
|
|
||||||
: data
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: PersistProps & { formik: FormikProps<any> }) {
|
|
||||||
if (!isEqual(prevProps.formik, this.props.formik)) {
|
|
||||||
this.saveForm(this.props.formik)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const maybeState = this.props.isSessionStorage
|
|
||||||
? window.sessionStorage.getItem(this.props.name)
|
|
||||||
: window.localStorage.getItem(this.props.name)
|
|
||||||
if (maybeState && maybeState !== null) {
|
|
||||||
this.props.formik.setFormikState(JSON.parse(maybeState))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): null {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Persist = connect<PersistProps, any>(PersistImpl)
|
|
@ -19,5 +19,5 @@ export function NetworkIcon({ name }: { name: string }): ReactElement {
|
|||||||
? EnergywebIcon
|
? EnergywebIcon
|
||||||
: EthIcon // ETH icon as fallback
|
: EthIcon // ETH icon as fallback
|
||||||
|
|
||||||
return IconMapped ? <IconMapped className={styles.icon} /> : null
|
return <IconMapped className={styles.icon} />
|
||||||
}
|
}
|
||||||
|
33
src/components/@shared/NetworkName/index.test.tsx
Normal file
33
src/components/@shared/NetworkName/index.test.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import NetworkName from './index'
|
||||||
|
|
||||||
|
describe('@shared/NetworkName', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(<NetworkName networkId={1} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders minimal', () => {
|
||||||
|
render(<NetworkName networkId={1} minimal />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders Polygon', () => {
|
||||||
|
render(<NetworkName networkId={137} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders BSC', () => {
|
||||||
|
render(<NetworkName networkId={56} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders Energy Web', () => {
|
||||||
|
render(<NetworkName networkId={246} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders Moonriver', () => {
|
||||||
|
render(<NetworkName networkId={1285} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders icon fallback', () => {
|
||||||
|
render(<NetworkName networkId={99999} />)
|
||||||
|
})
|
||||||
|
})
|
20
src/components/@shared/Page/index.test.tsx
Normal file
20
src/components/@shared/Page/index.test.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import Page from './index'
|
||||||
|
|
||||||
|
describe('@shared/Page', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(
|
||||||
|
<Page uri="/hello" title="Hello Title" description="Hello Description">
|
||||||
|
Hello Children
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
expect(screen.getByText('Hello Children')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('Hello Title')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('Hello Description')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders without title', () => {
|
||||||
|
render(<Page uri="/hello">Hello Children</Page>)
|
||||||
|
})
|
||||||
|
})
|
28
src/components/@shared/Pagination/index.test.tsx
Normal file
28
src/components/@shared/Pagination/index.test.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import Pagination from './index'
|
||||||
|
import { MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS } from '@utils/aquarius'
|
||||||
|
|
||||||
|
describe('@shared/Pagination', () => {
|
||||||
|
testRender(
|
||||||
|
<Pagination
|
||||||
|
totalPages={MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS + 1}
|
||||||
|
currentPage={2}
|
||||||
|
rowsPerPage={10}
|
||||||
|
rowCount={30}
|
||||||
|
onChangePage={() => jest.fn()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
it('renders without currentPage prop', () => {
|
||||||
|
render(
|
||||||
|
<Pagination
|
||||||
|
totalPages={10}
|
||||||
|
rowsPerPage={10}
|
||||||
|
rowCount={30}
|
||||||
|
onChangePage={() => jest.fn()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
51
src/components/@shared/Price/index.test.tsx
Normal file
51
src/components/@shared/Price/index.test.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import Price from './index'
|
||||||
|
import { asset } from '../../../../.jest/__fixtures__/assetWithAccessDetails'
|
||||||
|
import prices from '../../../../.jest/__fixtures__/prices'
|
||||||
|
|
||||||
|
jest.mock('../../../@context/Prices', () => ({
|
||||||
|
usePrices: () => prices,
|
||||||
|
getCoingeckoTokenId: () => 'ocean-protocol'
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('@shared/Price', () => {
|
||||||
|
it('renders fixed price', () => {
|
||||||
|
render(
|
||||||
|
<Price
|
||||||
|
accessDetails={{ ...asset.accessDetails, type: 'fixed', price: '10' }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(screen.getByText('10')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders free price', () => {
|
||||||
|
render(<Price accessDetails={{ ...asset.accessDetails, type: 'free' }} />)
|
||||||
|
expect(screen.getByText('Free')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders null price', () => {
|
||||||
|
render(<Price accessDetails={{ ...asset.accessDetails, price: null }} />)
|
||||||
|
expect(screen.getByText('-')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders conversion', async () => {
|
||||||
|
render(
|
||||||
|
<Price
|
||||||
|
accessDetails={{ ...asset.accessDetails, price: '10' }}
|
||||||
|
conversion
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(await screen.findByText('≈')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders no conversion when no price defined', async () => {
|
||||||
|
render(
|
||||||
|
<Price
|
||||||
|
accessDetails={{ ...asset.accessDetails, price: null }}
|
||||||
|
conversion
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(screen.queryByText('≈')).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
@ -7,7 +7,7 @@ const account = '0x0000000000000000000000000000000000000000'
|
|||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
describe('Publisher', () => {
|
describe('@shared/Publisher', () => {
|
||||||
test('should return correct markup by default', async () => {
|
test('should return correct markup by default', async () => {
|
||||||
;(axios as any).get.mockImplementationOnce(() =>
|
;(axios as any).get.mockImplementationOnce(() =>
|
||||||
Promise.resolve({ data: { name: 'jellymcjellyfish.eth' } })
|
Promise.resolve({ data: { name: 'jellymcjellyfish.eth' } })
|
||||||
|
12
src/components/@shared/SuccessConfetti/index.test.tsx
Normal file
12
src/components/@shared/SuccessConfetti/index.test.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import testRender from '../../../../.jest/testRender'
|
||||||
|
import SuccessConfetti from './index'
|
||||||
|
|
||||||
|
describe('@shared/SuccessConfetti', () => {
|
||||||
|
testRender(<SuccessConfetti success="Nice Success!" />)
|
||||||
|
|
||||||
|
it('renders without success', () => {
|
||||||
|
render(<SuccessConfetti success={null} />)
|
||||||
|
})
|
||||||
|
})
|
18
src/components/@shared/WalletNetworkSwitcher/index.test.tsx
Normal file
18
src/components/@shared/WalletNetworkSwitcher/index.test.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { render, fireEvent, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import WalletNetworkSwitcher from './'
|
||||||
|
|
||||||
|
jest.mock('../../../@utils/web3', () => ({
|
||||||
|
addCustomNetwork: () => jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('@shared/WalletNetworkSwitcher', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(<WalletNetworkSwitcher />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('switching networks can be invoked', () => {
|
||||||
|
render(<WalletNetworkSwitcher />)
|
||||||
|
fireEvent.click(screen.getByRole('button'))
|
||||||
|
})
|
||||||
|
})
|
@ -13,6 +13,7 @@ export default function WalletNetworkSwitcher(): ReactElement {
|
|||||||
const { networkId, web3Provider } = useWeb3()
|
const { networkId, web3Provider } = useWeb3()
|
||||||
const { asset } = useAsset()
|
const { asset } = useAsset()
|
||||||
const { networksList } = useNetworkMetadata()
|
const { networksList } = useNetworkMetadata()
|
||||||
|
|
||||||
const ddoNetworkData = getNetworkDataById(networksList, asset.chainId)
|
const ddoNetworkData = getNetworkDataById(networksList, asset.chainId)
|
||||||
const walletNetworkData = getNetworkDataById(networksList, networkId)
|
const walletNetworkData = getNetworkDataById(networksList, networkId)
|
||||||
|
|
||||||
|
52
src/components/@shared/Web3Feedback/index.test.tsx
Normal file
52
src/components/@shared/Web3Feedback/index.test.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import Web3Feedback from './'
|
||||||
|
import { useGraphSyncStatus } from '../../../@hooks/useGraphSyncStatus'
|
||||||
|
|
||||||
|
jest.mock('../../../@hooks/useGraphSyncStatus')
|
||||||
|
|
||||||
|
describe('@shared/Web3Feedback', () => {
|
||||||
|
;(useGraphSyncStatus as jest.Mock).mockImplementation(() => ({
|
||||||
|
isGraphSynced: true,
|
||||||
|
blockGraph: '333333',
|
||||||
|
blockHead: '333333'
|
||||||
|
}))
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(<Web3Feedback networkId={1} accountId="0xxxx" />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders isAssetNetwork === false', async () => {
|
||||||
|
render(
|
||||||
|
<Web3Feedback networkId={1} accountId="0xxxx" isAssetNetwork={false} />
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
await screen.findByText('Not connected to asset network')
|
||||||
|
).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders isGraphSynced === false', async () => {
|
||||||
|
;(useGraphSyncStatus as jest.Mock).mockImplementation(() => ({
|
||||||
|
isGraphSynced: false
|
||||||
|
}))
|
||||||
|
render(
|
||||||
|
<Web3Feedback networkId={1} accountId="0xxxx" isAssetNetwork={true} />
|
||||||
|
)
|
||||||
|
expect(await screen.findByText('Data out of sync')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders no account', async () => {
|
||||||
|
render(<Web3Feedback networkId={1} accountId={undefined} />)
|
||||||
|
expect(await screen.findByText('No account connected')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('do nothing if nothing to show', async () => {
|
||||||
|
;(useGraphSyncStatus as jest.Mock).mockImplementation(() => ({
|
||||||
|
isGraphSynced: true
|
||||||
|
}))
|
||||||
|
render(
|
||||||
|
<Web3Feedback networkId={1} accountId="0xxxx" isAssetNetwork={true} />
|
||||||
|
)
|
||||||
|
expect(screen.queryByRole('heading')).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
@ -13,12 +13,13 @@ export declare type Web3Error = {
|
|||||||
|
|
||||||
export default function Web3Feedback({
|
export default function Web3Feedback({
|
||||||
networkId,
|
networkId,
|
||||||
|
accountId,
|
||||||
isAssetNetwork
|
isAssetNetwork
|
||||||
}: {
|
}: {
|
||||||
networkId: number
|
networkId: number
|
||||||
|
accountId: string
|
||||||
isAssetNetwork?: boolean
|
isAssetNetwork?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
|
||||||
const { isGraphSynced, blockGraph, blockHead } = useGraphSyncStatus(networkId)
|
const { isGraphSynced, blockGraph, blockHead } = useGraphSyncStatus(networkId)
|
||||||
const [state, setState] = useState<string>()
|
const [state, setState] = useState<string>()
|
||||||
const [title, setTitle] = useState<string>()
|
const [title, setTitle] = useState<string>()
|
||||||
|
@ -28,7 +28,7 @@ Full.args = {
|
|||||||
state: 'info',
|
state: 'info',
|
||||||
action: {
|
action: {
|
||||||
name: 'Action',
|
name: 'Action',
|
||||||
handleAction: () => null as any
|
handleAction: () => null
|
||||||
},
|
},
|
||||||
badge: 'Hello',
|
badge: 'Hello',
|
||||||
onDismiss: () => {
|
onDismiss: () => {
|
||||||
|
@ -1,7 +1,34 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import testRender from '../../../../../.jest/testRender'
|
import testRender from '../../../../../.jest/testRender'
|
||||||
import Alert from '@shared/atoms/Alert'
|
import Alert from '@shared/atoms/Alert'
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
|
||||||
describe('Alert', () => {
|
describe('Alert', () => {
|
||||||
testRender(<Alert text="Alert text" state="info" />)
|
testRender(
|
||||||
|
<Alert
|
||||||
|
title="Alert Title"
|
||||||
|
text="Alert text"
|
||||||
|
state="info"
|
||||||
|
badge="Hello"
|
||||||
|
action={{
|
||||||
|
name: 'Hello action',
|
||||||
|
style: 'text',
|
||||||
|
handleAction: () => null
|
||||||
|
}}
|
||||||
|
onDismiss={() => null}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
it('renders without action style', () => {
|
||||||
|
render(
|
||||||
|
<Alert
|
||||||
|
text="Alert text"
|
||||||
|
state="info"
|
||||||
|
action={{
|
||||||
|
name: 'Hello action',
|
||||||
|
handleAction: () => null
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -2,6 +2,15 @@ import React from 'react'
|
|||||||
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
import { ListItem } from '@shared/atoms/Lists'
|
import { ListItem } from '@shared/atoms/Lists'
|
||||||
|
|
||||||
|
export const items = [
|
||||||
|
'List item short',
|
||||||
|
'List item long ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie',
|
||||||
|
'List item long ipsum dolor sit amet, consectetur adipiscing elit',
|
||||||
|
'List item short',
|
||||||
|
'List item long ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie',
|
||||||
|
'List item long ipsum dolor sit amet, consectetur adipiscing elit'
|
||||||
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Component/@shared/atoms/Lists',
|
title: 'Component/@shared/atoms/Lists',
|
||||||
component: ListItem
|
component: ListItem
|
||||||
@ -11,15 +20,6 @@ const Template: ComponentStory<typeof ListItem> = (args) => (
|
|||||||
<ListItem {...args} />
|
<ListItem {...args} />
|
||||||
)
|
)
|
||||||
|
|
||||||
const items = [
|
|
||||||
'List item short',
|
|
||||||
'List item long ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie',
|
|
||||||
'List item long ipsum dolor sit amet, consectetur adipiscing elit',
|
|
||||||
'List item short',
|
|
||||||
'List item long ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie',
|
|
||||||
'List item long ipsum dolor sit amet, consectetur adipiscing elit'
|
|
||||||
]
|
|
||||||
|
|
||||||
export const Unordered = Template.bind({})
|
export const Unordered = Template.bind({})
|
||||||
Unordered.decorators = [
|
Unordered.decorators = [
|
||||||
() => (
|
() => (
|
||||||
|
28
src/components/@shared/atoms/Lists/index.test.tsx
Normal file
28
src/components/@shared/atoms/Lists/index.test.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import { ListItem } from './index'
|
||||||
|
import { items } from './index.stories'
|
||||||
|
|
||||||
|
describe('Lists', () => {
|
||||||
|
it('renders unordered', () => {
|
||||||
|
render(
|
||||||
|
<ul>
|
||||||
|
{items.map((item, key) => (
|
||||||
|
<ListItem key={key}>{item}</ListItem>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders ordered', () => {
|
||||||
|
render(
|
||||||
|
<ol>
|
||||||
|
{items.map((item, key) => (
|
||||||
|
<ListItem ol key={key}>
|
||||||
|
{item}
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
@ -1,8 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import testRender from '../../../../../.jest/testRender'
|
import testRender from '../../../../../.jest/testRender'
|
||||||
import Loader from '@shared/atoms/Loader'
|
import Loader, { LoaderProps } from '@shared/atoms/Loader'
|
||||||
import { Default } from './index.stories'
|
import { Default, WithMessage } from './index.stories'
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
|
||||||
describe('Loader', () => {
|
describe('Loader', () => {
|
||||||
testRender(<Loader {...Default.args} />)
|
testRender(<Loader {...Default.args} />)
|
||||||
|
|
||||||
|
it('renders without wordmark', () => {
|
||||||
|
render(<Loader {...WithMessage.args} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders white', () => {
|
||||||
|
const props: LoaderProps = { white: true }
|
||||||
|
render(<Loader {...props} />)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import testRender from '../../../../../.jest/testRender'
|
import testRender from '../../../../../.jest/testRender'
|
||||||
import Logo from '@shared/atoms/Logo'
|
import Logo from '@shared/atoms/Logo'
|
||||||
import { Default } from './index.stories'
|
import { Default, WithoutWordmark } from './index.stories'
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
|
||||||
describe('Logo', () => {
|
describe('Logo', () => {
|
||||||
testRender(<Logo {...Default.args} />)
|
testRender(<Logo {...Default.args} />)
|
||||||
|
|
||||||
|
it('renders without wordmark', () => {
|
||||||
|
render(<Logo {...WithoutWordmark.args} />)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
23
src/components/@shared/atoms/Modal/index.test.tsx
Normal file
23
src/components/@shared/atoms/Modal/index.test.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import Modal from './'
|
||||||
|
import ReactModal from 'react-modal'
|
||||||
|
|
||||||
|
describe('Modal', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
ReactModal.setAppElement(document.createElement('div'))
|
||||||
|
|
||||||
|
const { rerender } = render(
|
||||||
|
<Modal title="Hello" isOpen onToggleModal={() => null}>
|
||||||
|
Hello
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
expect(document.querySelector('.ReactModalPortal')).toBeInTheDocument()
|
||||||
|
|
||||||
|
rerender(
|
||||||
|
<Modal title="Hello" isOpen={false} onToggleModal={() => null}>
|
||||||
|
Hello
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
@ -1,6 +1,26 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
import Table, { TableOceanProps } from '@shared/atoms/Table'
|
import Table, { TableOceanProps } from '@shared/atoms/Table'
|
||||||
|
import { columns, data } from '../../../../../.jest/__fixtures__/table'
|
||||||
|
|
||||||
|
export const args: TableOceanProps<any> = { columns, data }
|
||||||
|
|
||||||
|
export const argsWithPagination: TableOceanProps<any> = {
|
||||||
|
columns,
|
||||||
|
data: data.flatMap((i) => [i, i, i])
|
||||||
|
}
|
||||||
|
|
||||||
|
export const argsLoading: TableOceanProps<any> = {
|
||||||
|
isLoading: true,
|
||||||
|
columns: [],
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
|
||||||
|
export const argsEmpty: TableOceanProps<any> = {
|
||||||
|
emptyMessage: 'I am empty',
|
||||||
|
columns: [],
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Component/@shared/atoms/Table',
|
title: 'Component/@shared/atoms/Table',
|
||||||
@ -13,70 +33,14 @@ interface Props {
|
|||||||
args: TableOceanProps<any>
|
args: TableOceanProps<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'Name',
|
|
||||||
selector: (row: any) => row.name,
|
|
||||||
maxWidth: '45rem',
|
|
||||||
grow: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Symbol',
|
|
||||||
selector: (row: any) => row.symbol,
|
|
||||||
maxWidth: '10rem'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Price',
|
|
||||||
selector: (row: any) => row.price,
|
|
||||||
right: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
{
|
|
||||||
name: 'Title asset',
|
|
||||||
symbol: 'DATA-70',
|
|
||||||
price: '1.011'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Title asset Title asset Title asset Title asset Title asset',
|
|
||||||
symbol: 'DATA-71',
|
|
||||||
price: '1.011'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Title asset',
|
|
||||||
symbol: 'DATA-72',
|
|
||||||
price: '1.011'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset',
|
|
||||||
symbol: 'DATA-71',
|
|
||||||
price: '1.011'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
export const WithData: Props = Template.bind({})
|
export const WithData: Props = Template.bind({})
|
||||||
WithData.args = {
|
WithData.args = args
|
||||||
columns,
|
|
||||||
data
|
|
||||||
}
|
|
||||||
|
|
||||||
export const WithPagination: Props = Template.bind({})
|
export const WithPagination: Props = Template.bind({})
|
||||||
WithPagination.args = {
|
WithPagination.args = argsWithPagination
|
||||||
columns,
|
|
||||||
data: data.flatMap((i) => [i, i, i])
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Loading: Props = Template.bind({})
|
export const Loading: Props = Template.bind({})
|
||||||
Loading.args = {
|
Loading.args = argsLoading
|
||||||
isLoading: true,
|
|
||||||
columns: [],
|
|
||||||
data: []
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Empty: Props = Template.bind({})
|
export const Empty: Props = Template.bind({})
|
||||||
Empty.args = {
|
Empty.args = argsEmpty
|
||||||
emptyMessage: 'I am empty',
|
|
||||||
columns: [],
|
|
||||||
data: []
|
|
||||||
}
|
|
||||||
|
34
src/components/@shared/atoms/Table/index.test.tsx
Normal file
34
src/components/@shared/atoms/Table/index.test.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import Table, { TableOceanProps } from '.'
|
||||||
|
import {
|
||||||
|
args,
|
||||||
|
argsWithPagination,
|
||||||
|
argsLoading,
|
||||||
|
argsEmpty
|
||||||
|
} from './index.stories'
|
||||||
|
|
||||||
|
describe('Table', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(<Table {...args} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders WithPagination', () => {
|
||||||
|
render(<Table {...argsWithPagination} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders Loading', () => {
|
||||||
|
render(<Table {...argsLoading} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders Empty', () => {
|
||||||
|
render(<Table {...argsEmpty} />)
|
||||||
|
expect(screen.getByText('I am empty')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders Empty without message', () => {
|
||||||
|
const args: TableOceanProps<any> = { ...argsEmpty, emptyMessage: undefined }
|
||||||
|
render(<Table {...args} />)
|
||||||
|
expect(screen.getByText('No results found')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user