From c30212279505c8a3b36fd27d9c5dd26d3171267f Mon Sep 17 00:00:00 2001 From: mihaisc Date: Tue, 8 Nov 2022 19:17:22 +0200 Subject: [PATCH 1/7] Add most viewed (#1754) * add most viewed * Update src/components/Home/index.tsx Co-authored-by: Jamie Hewitt * add views in teasers * typo * add test * switch to axios * test tweaks * fix * add 30 days label Co-authored-by: Jamie Hewitt Co-authored-by: Matthias Kretschmann --- .jest/__fixtures__/assetAquarius.ts | 2 +- src/@hooks/useCancelToken.ts | 4 +- src/@types/Analytics.d.ts | 4 + src/@types/AssetExtended.d.ts | 1 + src/@utils/aquarius.ts | 6 +- src/@utils/index.ts | 9 +++ src/components/@shared/AssetTeaser/index.tsx | 7 ++ .../@shared/Publisher/index.test.tsx | 11 +-- src/components/Home/MostViews/index.test.tsx | 56 ++++++++++++++ src/components/Home/MostViews/index.tsx | 73 +++++++++++++++++++ src/components/Home/SectionQueryResult.tsx | 22 +++--- src/components/Home/index.module.css | 7 ++ src/components/Home/index.tsx | 4 +- 13 files changed, 184 insertions(+), 22 deletions(-) create mode 100644 src/@types/Analytics.d.ts create mode 100644 src/components/Home/MostViews/index.test.tsx create mode 100644 src/components/Home/MostViews/index.tsx diff --git a/.jest/__fixtures__/assetAquarius.ts b/.jest/__fixtures__/assetAquarius.ts index 1f25cda19..2e892b14f 100644 --- a/.jest/__fixtures__/assetAquarius.ts +++ b/.jest/__fixtures__/assetAquarius.ts @@ -5,7 +5,7 @@ export const assetAquarius: Asset = { id: 'did:op:6654b0793765b269696cec8d2f0d077d9bbcdd3c4f033d941ab9684e8ad06630', nftAddress: '0xbA5BA7B09e2FA1eb0258f647503F81D2Af5cb07d', version: '4.1.0', - chainId: 5, + chainId: 1, metadata: { created: '2022-09-29T11:30:26Z', updated: '2022-09-29T11:30:26Z', diff --git a/src/@hooks/useCancelToken.ts b/src/@hooks/useCancelToken.ts index 35eb3d0a9..4875c2070 100644 --- a/src/@hooks/useCancelToken.ts +++ b/src/@hooks/useCancelToken.ts @@ -1,10 +1,12 @@ import { useRef, useEffect, useCallback } from 'react' import axios, { CancelToken } from 'axios' + export const useCancelToken = (): (() => CancelToken) => { const axiosSource = useRef(null) + const newCancelToken = useCallback(() => { axiosSource.current = axios.CancelToken.source() - return axiosSource.current.token + return axiosSource?.current?.token }, []) useEffect( diff --git a/src/@types/Analytics.d.ts b/src/@types/Analytics.d.ts new file mode 100644 index 000000000..746ccef6d --- /dev/null +++ b/src/@types/Analytics.d.ts @@ -0,0 +1,4 @@ +interface PageViews { + count: number + did: string +} diff --git a/src/@types/AssetExtended.d.ts b/src/@types/AssetExtended.d.ts index 19a472e44..d6ee6ade1 100644 --- a/src/@types/AssetExtended.d.ts +++ b/src/@types/AssetExtended.d.ts @@ -5,5 +5,6 @@ import { Asset } from '@oceanprotocol/lib' declare global { interface AssetExtended extends Asset { accessDetails?: AccessDetails + views?: number } } diff --git a/src/@utils/aquarius.ts b/src/@utils/aquarius.ts index 96985125a..111851aaa 100644 --- a/src/@utils/aquarius.ts +++ b/src/@utils/aquarius.ts @@ -55,13 +55,13 @@ export function generateBaseQuery( ...(baseQueryParams.filters || []), baseQueryParams.chainIds ? getFilterTerm('chainId', baseQueryParams.chainIds) - : [], + : '', getFilterTerm('_index', 'aquarius'), ...(baseQueryParams.ignorePurgatory - ? [] + ? '' : [getFilterTerm('purgatory.state', false)]), ...(baseQueryParams.ignoreState - ? [] + ? '' : [ { bool: { diff --git a/src/@utils/index.ts b/src/@utils/index.ts index a07ba65fe..32249f4c5 100644 --- a/src/@utils/index.ts +++ b/src/@utils/index.ts @@ -1,3 +1,5 @@ +import { Asset } from '@oceanprotocol/lib' + // Boolean value that will be true if we are inside a browser, false otherwise export const isBrowser = typeof window !== 'undefined' @@ -14,3 +16,10 @@ export function removeItemFromArray(arr: Array, value: T): Array { } return arr } + +export function sortAssets(items: Asset[], sorted: string[]) { + items.sort(function (a, b) { + return sorted?.indexOf(a.id) - sorted?.indexOf(b.id) + }) + return items +} diff --git a/src/components/@shared/AssetTeaser/index.tsx b/src/components/@shared/AssetTeaser/index.tsx index f9d61504e..1b8b0d5ee 100644 --- a/src/components/@shared/AssetTeaser/index.tsx +++ b/src/components/@shared/AssetTeaser/index.tsx @@ -89,6 +89,13 @@ export default function AssetTeaser({ : `${orders} ${orders === 1 ? 'sale' : 'sales'}`} ) : null} + {asset.views && asset.views > 0 ? ( + + {asset.views < 0 + ? 'N/A' + : `${asset.views} ${asset.views === 1 ? 'view' : 'views'}`} + + ) : null} diff --git a/src/components/@shared/Publisher/index.test.tsx b/src/components/@shared/Publisher/index.test.tsx index 33ead400c..45641eba7 100644 --- a/src/components/@shared/Publisher/index.test.tsx +++ b/src/components/@shared/Publisher/index.test.tsx @@ -1,15 +1,16 @@ import React from 'react' import { render, screen } from '@testing-library/react' -import * as axios from 'axios' +import axios from 'axios' import Publisher from './' const account = '0x0000000000000000000000000000000000000000' jest.mock('axios') +const axiosMock = axios as jest.Mocked describe('@shared/Publisher', () => { test('should return correct markup by default', async () => { - ;(axios as any).get.mockImplementationOnce(() => + axiosMock.get.mockImplementationOnce(() => Promise.resolve({ data: { name: 'jellymcjellyfish.eth' } }) ) @@ -22,7 +23,7 @@ describe('@shared/Publisher', () => { }) test('should truncate account by default', async () => { - ;(axios as any).get.mockImplementationOnce(() => + axiosMock.get.mockImplementationOnce(() => Promise.resolve({ data: { name: null } }) ) @@ -33,7 +34,7 @@ describe('@shared/Publisher', () => { }) test('should return correct markup in minimal state', async () => { - ;(axios as any).get.mockImplementationOnce(() => + axiosMock.get.mockImplementationOnce(() => Promise.resolve({ data: { name: null } }) ) @@ -44,7 +45,7 @@ describe('@shared/Publisher', () => { }) test('should return markup with empty account', async () => { - ;(axios as any).get.mockImplementationOnce(() => + axiosMock.get.mockImplementationOnce(() => Promise.resolve({ data: { name: null } }) ) diff --git a/src/components/Home/MostViews/index.test.tsx b/src/components/Home/MostViews/index.test.tsx new file mode 100644 index 000000000..8c61a3787 --- /dev/null +++ b/src/components/Home/MostViews/index.test.tsx @@ -0,0 +1,56 @@ +import { render, screen } from '@testing-library/react' +import React from 'react' +import MostViews from '.' +import axios from 'axios' +import { queryMetadata } from '@utils/aquarius' +import { assetAquarius } from '../../../../.jest/__fixtures__/assetAquarius' + +jest.mock('axios') +jest.mock('@utils/aquarius') + +const axiosMock = axios as jest.Mocked +const queryMetadataMock = queryMetadata as jest.Mock + +const queryMetadataBaseReturn: PagedAssets = { + results: [assetAquarius], + page: 1, + totalPages: 1, + totalResults: 1, + aggregations: {} +} + +describe('components/Home/MostViews', () => { + beforeEach(() => { + jest.resetAllMocks() + }) + + it('renders without crashing', async () => { + axiosMock.get.mockImplementation(() => + Promise.resolve({ + data: [{ count: 666, did: assetAquarius.id }] + }) + ) + queryMetadataMock.mockResolvedValue(queryMetadataBaseReturn) + render() + await screen.findByText('666 views') + }) + + it('catches errors', async () => { + queryMetadataMock.mockImplementation(() => { + throw new Error('Hello error') + }) + + // prevent console error from showing up in test log + const originalError = console.error + console.error = jest.fn() + + try { + render() + await screen.findByText('No results found') + } catch (error) { + expect(error).toEqual({ message: 'Hello error' }) + } + + console.error = originalError + }) +}) diff --git a/src/components/Home/MostViews/index.tsx b/src/components/Home/MostViews/index.tsx new file mode 100644 index 000000000..08e711e7e --- /dev/null +++ b/src/components/Home/MostViews/index.tsx @@ -0,0 +1,73 @@ +import React, { ReactElement, useCallback, useEffect, useState } from 'react' +import styles from '../index.module.css' +import { + generateBaseQuery, + getFilterTerm, + queryMetadata +} from '@utils/aquarius' +import { useCancelToken } from '@hooks/useCancelToken' +import Tooltip from '@shared/atoms/Tooltip' +import AssetList from '@shared/AssetList' +import { LoggerInstance } from '@oceanprotocol/lib' +import { sortAssets } from '@utils/index' +import axios, { AxiosResponse } from 'axios' + +export default function MostViews(): ReactElement { + const [loading, setLoading] = useState() + const [mostViewed, setMostViewed] = useState([]) + const newCancelToken = useCancelToken() + + const getMostViewed = useCallback(async () => { + try { + setLoading(true) + const response: AxiosResponse = await axios.get( + 'https://market-analytics.oceanprotocol.com/pages?limit=6', + { cancelToken: newCancelToken() } + ) + const dids = response?.data?.map((x: PageViews) => x.did) + const assetsWithViews: AssetExtended[] = [] + const baseParams = { + esPaginationOptions: { size: 6 }, + filters: [getFilterTerm('_id', dids)] + } as BaseQueryParams + const query = generateBaseQuery(baseParams) + const result = await queryMetadata(query, newCancelToken()) + + if (result?.totalResults > 0) { + const sortedAssets = sortAssets(result.results, dids) + const overflow = sortedAssets.length - 6 + sortedAssets.splice(sortedAssets.length - overflow, overflow) + sortedAssets.forEach((asset) => { + assetsWithViews.push({ + ...asset, + views: response.data.filter((x) => x.did === asset.id)?.[0]?.count + }) + }) + setMostViewed(assetsWithViews) + } + } catch (error) { + LoggerInstance.error(error.message) + } finally { + setLoading(false) + } + }, [newCancelToken]) + + useEffect(() => { + getMostViewed() + }, [getMostViewed]) + + return ( +
+

+ Most Views last 30 days + +

+ + +
+ ) +} diff --git a/src/components/Home/SectionQueryResult.tsx b/src/components/Home/SectionQueryResult.tsx index 7efc796f1..7eed3103f 100644 --- a/src/components/Home/SectionQueryResult.tsx +++ b/src/components/Home/SectionQueryResult.tsx @@ -1,29 +1,27 @@ import { useUserPreferences } from '@context/UserPreferences' import { useCancelToken } from '@hooks/useCancelToken' import { useIsMounted } from '@hooks/useIsMounted' -import { Asset, LoggerInstance } from '@oceanprotocol/lib' +import { LoggerInstance } from '@oceanprotocol/lib' import AssetList from '@shared/AssetList' +import Tooltip from '@shared/atoms/Tooltip' +import Markdown from '@shared/Markdown' import { queryMetadata } from '@utils/aquarius' +import { sortAssets } from '@utils/index' import React, { ReactElement, useState, useEffect } from 'react' import styles from './index.module.css' -function sortElements(items: Asset[], sorted: string[]) { - items.sort(function (a, b) { - return sorted.indexOf(a.nftAddress) - sorted.indexOf(b.nftAddress) - }) - return items -} - export default function SectionQueryResult({ title, query, action, - queryData + queryData, + tooltip }: { title: ReactElement | string query: SearchQuery action?: ReactElement queryData?: string[] + tooltip?: string }): ReactElement { const { chainIds } = useUserPreferences() const [result, setResult] = useState() @@ -52,7 +50,7 @@ export default function SectionQueryResult({ const result = await queryMetadata(query, newCancelToken()) if (!isMounted()) return if (queryData && result?.totalResults > 0) { - const sortedAssets = sortElements(result.results, queryData) + const sortedAssets = sortAssets(result.results, queryData) const overflow = sortedAssets.length - 6 sortedAssets.splice(sortedAssets.length - overflow, overflow) result.results = sortedAssets @@ -69,7 +67,9 @@ export default function SectionQueryResult({ return (
-

{title}

+

+ {title} {tooltip && } />} +

() const [queryMostSales, setQueryMostSales] = useState() + const [queryMostAllocation, setQueryMostAllocation] = useState() useEffect(() => { @@ -66,7 +68,7 @@ export default function HomePage(): ReactElement { /> - + From ee4e5c3b4d866f1262710a42dd715abca6160949 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Nov 2022 09:02:18 +0000 Subject: [PATCH 2/7] Bump loader-utils from 1.4.0 to 1.4.1 (#1780) Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.1. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.1/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 1272 ++------------------------------------------- 1 file changed, 54 insertions(+), 1218 deletions(-) diff --git a/package-lock.json b/package-lock.json index 003dfca3f..27d741552 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7610,9 +7610,9 @@ } }, "node_modules/@storybook/builder-webpack4/node_modules/css-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -7855,9 +7855,9 @@ } }, "node_modules/@storybook/builder-webpack4/node_modules/html-webpack-plugin/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -8454,9 +8454,9 @@ } }, "node_modules/@storybook/builder-webpack4/node_modules/webpack/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -8622,12 +8622,6 @@ "webpack": "^4.0.0" } }, - "node_modules/@storybook/builder-webpack4/node_modules/webpack/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/@storybook/builder-webpack4/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", @@ -9345,9 +9339,9 @@ } }, "node_modules/@storybook/core-common/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -10065,9 +10059,9 @@ } }, "node_modules/@storybook/core-server/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -10759,9 +10753,9 @@ } }, "node_modules/@storybook/manager-webpack4/node_modules/css-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -10976,9 +10970,9 @@ } }, "node_modules/@storybook/manager-webpack4/node_modules/html-webpack-plugin/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -11560,9 +11554,9 @@ } }, "node_modules/@storybook/manager-webpack4/node_modules/webpack/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -12135,305 +12129,6 @@ "integrity": "sha512-6u+36Dj3aDzhfBVUf/mfmc92OEdzQ2kx2jcXGdigfl70E/neV21ZHE6UCz4MDzTRcVqGAM27fk+DLXvyDsn3Jw==", "dev": true }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" - } - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", - "dev": true - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", - "dev": true - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "dev": true - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" - } - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", - "dev": true - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" - } - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "node_modules/@storybook/react/node_modules/@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@storybook/react/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/react/node_modules/cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "dependencies": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "node_modules/@storybook/react/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/@storybook/react/node_modules/enhanced-resolve": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", - "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@storybook/react/node_modules/enhanced-resolve/node_modules/memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/@storybook/react/node_modules/eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@storybook/react/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@storybook/react/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/react/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/@storybook/react/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/react/node_modules/is-extendable/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/react/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/react/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@storybook/react/node_modules/is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", @@ -12443,114 +12138,6 @@ "node": ">=0.10.0" } }, - "node_modules/@storybook/react/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@storybook/react/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/@storybook/react/node_modules/loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/@storybook/react/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@storybook/react/node_modules/loader-utils/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/@storybook/react/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@storybook/react/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/react/node_modules/micromatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/react/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/@storybook/react/node_modules/react-element-to-jsx-string": { "version": "14.3.4", "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz", @@ -12572,218 +12159,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "node_modules/@storybook/react/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/@storybook/react/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/@storybook/react/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@storybook/react/node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/@storybook/react/node_modules/ssri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", - "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", - "dev": true, - "dependencies": { - "figgy-pudding": "^3.5.1" - } - }, - "node_modules/@storybook/react/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/@storybook/react/node_modules/terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", - "dev": true, - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@storybook/react/node_modules/terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", - "dev": true, - "dependencies": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - }, - "engines": { - "node": ">= 6.9.0" - }, - "peerDependencies": { - "webpack": "^4.0.0" - } - }, - "node_modules/@storybook/react/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/react/node_modules/watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - }, - "optionalDependencies": { - "chokidar": "^3.4.1", - "watchpack-chokidar2": "^2.0.1" - } - }, - "node_modules/@storybook/react/node_modules/webpack": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", - "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.5.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.3", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - }, - "webpack-command": { - "optional": true - } - } - }, - "node_modules/@storybook/react/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/@storybook/react/node_modules/webpack/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@storybook/react/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, "node_modules/@storybook/router": { "version": "6.5.13", "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.5.13.tgz", @@ -29490,9 +28865,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -47767,9 +47142,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -47959,9 +47334,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -48384,9 +47759,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -48509,12 +47884,6 @@ "webpack-sources": "^1.4.0", "worker-farm": "^1.7.0" } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true } } }, @@ -49150,9 +48519,9 @@ "dev": true }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -49753,9 +49122,9 @@ "dev": true }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -50329,9 +49698,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -50498,9 +49867,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -50923,9 +50292,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.1.tgz", + "integrity": "sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -51303,378 +50672,12 @@ "integrity": "sha512-6u+36Dj3aDzhfBVUf/mfmc92OEdzQ2kx2jcXGdigfl70E/neV21ZHE6UCz4MDzTRcVqGAM27fk+DLXvyDsn3Jw==", "dev": true }, - "@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" - } - }, - "@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", - "dev": true - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "enhanced-resolve": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", - "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - } - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - }, - "dependencies": { - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - } - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, "react-element-to-jsx-string": { "version": "14.3.4", "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz", @@ -51691,173 +50694,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "ssri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", - "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - } - }, - "terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", - "dev": true, - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", - "dev": true, - "requires": { - "chokidar": "^3.4.1", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.1" - } - }, - "webpack": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", - "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.5.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.3", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" - }, - "dependencies": { - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true - } - } - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true } } }, @@ -65136,9 +63972,9 @@ "dev": true }, "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", "dev": true, "requires": { "big.js": "^5.2.2", From 482af1be0cbc756a82074944b6cf2f9f38233be3 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Wed, 9 Nov 2022 12:22:30 +0000 Subject: [PATCH 3/7] NFT image & faulty metadata fixes (#1779) * safeguard against faults in metadata.tags * properly handle image strings in tokenURI * keep image ratios * render test for faulty tags * tweak fallback in popover --- src/@utils/nft.ts | 9 ++++++--- src/components/@shared/AssetList/index.tsx | 10 +++++----- src/components/@shared/atoms/Tags/index.test.tsx | 5 +++++ src/components/@shared/atoms/Tags/index.tsx | 3 +++ .../Asset/AssetContent/Nft/NftTooltip.module.css | 2 +- .../Asset/AssetContent/Nft/NftTooltip.tsx | 4 +++- .../Asset/AssetContent/Nft/index.module.css | 7 ++++++- src/components/Asset/AssetContent/Nft/index.tsx | 1 + src/components/Asset/RelatedAssets/index.tsx | 16 +++++++++++----- 9 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/@utils/nft.ts b/src/@utils/nft.ts index 2bbc30155..a4343801d 100644 --- a/src/@utils/nft.ts +++ b/src/@utils/nft.ts @@ -82,10 +82,13 @@ export function generateNftCreateData( export function decodeTokenURI(tokenURI: string): NftMetadata { if (!tokenURI) return undefined + try { - const nftMeta = JSON.parse( - Buffer.from(tokenURI.replace(tokenUriPrefix, ''), 'base64').toString() - ) as NftMetadata + const nftMeta = tokenURI.includes('data:application/json') + ? (JSON.parse( + Buffer.from(tokenURI.replace(tokenUriPrefix, ''), 'base64').toString() + ) as NftMetadata) + : ({ image: tokenURI } as NftMetadata) return nftMeta } catch (error) { diff --git a/src/components/@shared/AssetList/index.tsx b/src/components/@shared/AssetList/index.tsx index f58a43c34..7624a61aa 100644 --- a/src/components/@shared/AssetList/index.tsx +++ b/src/components/@shared/AssetList/index.tsx @@ -69,11 +69,13 @@ export default function AssetList({ const styleClasses = `${styles.assetList} ${className || ''}` - return assetsWithPrices && !loading ? ( + return loading ? ( + + ) : ( <>
- {assetsWithPrices.length > 0 ? ( - assetsWithPrices.map((assetWithPrice) => ( + {assetsWithPrices?.length > 0 ? ( + assetsWithPrices?.map((assetWithPrice) => ( )} - ) : ( - ) } diff --git a/src/components/@shared/atoms/Tags/index.test.tsx b/src/components/@shared/atoms/Tags/index.test.tsx index 5867e96ad..71b253315 100644 --- a/src/components/@shared/atoms/Tags/index.test.tsx +++ b/src/components/@shared/atoms/Tags/index.test.tsx @@ -24,4 +24,9 @@ describe('Tags', () => { it('renders WithoutLinks', () => { render() }) + + it('renders with faulty tags', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + render() + }) }) diff --git a/src/components/@shared/atoms/Tags/index.tsx b/src/components/@shared/atoms/Tags/index.tsx index 1f5c70943..2922417c5 100644 --- a/src/components/@shared/atoms/Tags/index.tsx +++ b/src/components/@shared/atoms/Tags/index.tsx @@ -30,6 +30,9 @@ export default function Tags({ className, noLinks }: TagsProps): ReactElement { + // safeguard against faults in the metadata + if (!(items instanceof Array)) return null + max = max || items.length const remainder = items.length - max // filter out empty array items, and restrict to `max` diff --git a/src/components/Asset/AssetContent/Nft/NftTooltip.module.css b/src/components/Asset/AssetContent/Nft/NftTooltip.module.css index 3916afc25..1b7b73c38 100644 --- a/src/components/Asset/AssetContent/Nft/NftTooltip.module.css +++ b/src/components/Asset/AssetContent/Nft/NftTooltip.module.css @@ -7,7 +7,7 @@ .wrapper img { margin: 0; width: 128px; - height: 128px; + height: auto; } .info { diff --git a/src/components/Asset/AssetContent/Nft/NftTooltip.tsx b/src/components/Asset/AssetContent/Nft/NftTooltip.tsx index 3a13868b0..9290bd9fd 100644 --- a/src/components/Asset/AssetContent/Nft/NftTooltip.tsx +++ b/src/components/Asset/AssetContent/Nft/NftTooltip.tsx @@ -14,11 +14,13 @@ const openSeaTestNetworks = [4] export default function NftTooltip({ nft, + nftImage, address, chainId, isBlockscoutExplorer }: { nft: NftMetadata + nftImage: string address: string chainId: number isBlockscoutExplorer: boolean @@ -39,7 +41,7 @@ export default function NftTooltip({ return (
- {nft && {nft?.name}} + {nftImage && {nft?.name}}
{nft &&
{nft.name}
} {address && ( diff --git a/src/components/Asset/AssetContent/Nft/index.module.css b/src/components/Asset/AssetContent/Nft/index.module.css index 4c6e3850c..eb54b9cd1 100644 --- a/src/components/Asset/AssetContent/Nft/index.module.css +++ b/src/components/Asset/AssetContent/Nft/index.module.css @@ -4,14 +4,19 @@ border-right: 1px solid var(--border-color); width: calc(var(--spacer) * 2); height: calc(var(--spacer) * 2); + display: flex; + align-items: center; } -.nftImage img, .nftImage > svg:first-of-type { width: 100%; height: 100%; } +.nftImage img { + height: auto; +} + .nftImage > svg:first-of-type { transform: scale(0.7); } diff --git a/src/components/Asset/AssetContent/Nft/index.tsx b/src/components/Asset/AssetContent/Nft/index.tsx index f741c6eaf..7f063bd75 100644 --- a/src/components/Asset/AssetContent/Nft/index.tsx +++ b/src/components/Asset/AssetContent/Nft/index.tsx @@ -48,6 +48,7 @@ export default function Nft({ content={ Date: Wed, 9 Nov 2022 12:23:26 +0000 Subject: [PATCH 4/7] filter out `className` from `Tippy` (#1783) --- src/components/@shared/atoms/Tooltip/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/@shared/atoms/Tooltip/index.tsx b/src/components/@shared/atoms/Tooltip/index.tsx index 76ee1fe71..7d5e98835 100644 --- a/src/components/@shared/atoms/Tooltip/index.tsx +++ b/src/components/@shared/atoms/Tooltip/index.tsx @@ -17,7 +17,8 @@ const DefaultTrigger = React.forwardRef((props, ref: any) => { }) export default function Tooltip(props: TippyProps): ReactElement { - const { content, children, trigger, disabled, className, placement } = props + const { className, ...restProps } = props + const { content, children, trigger, disabled, placement } = props const [styles, api] = useSpring(() => animation.from) function onMount() { @@ -60,7 +61,7 @@ export default function Tooltip(props: TippyProps): ReactElement { onMount={onMount} onHide={onHide} // animation - {...props} + {...restProps} >
{children || }
From 4b4a926f891dd1672bdd9ebdcdf5fba02430d636 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Wed, 9 Nov 2022 14:14:57 +0000 Subject: [PATCH 5/7] special veOCEAN number treatment (#1782) * special veOCEAN number treatment * path fix * fix duplicate AllLocked declarations * so many duplicate declarations * bold numbers on asset teasers * update test * unify number formatting in more generic `formatNumber()` method --- src/@utils/numbers.ts | 15 ++++++++ src/@utils/veAllocation.ts | 35 ++++++++++--------- .../@shared/AssetTeaser/index.module.css | 2 +- src/components/@shared/AssetTeaser/index.tsx | 34 ++++++++++++------ src/components/@shared/Price/PriceUnit.tsx | 17 +++------ .../Asset/AssetActions/AssetStats/index.tsx | 6 ++-- src/components/Footer/MarketStats/index.tsx | 15 ++++++-- src/components/Home/MostViews/index.test.tsx | 2 +- 8 files changed, 80 insertions(+), 46 deletions(-) diff --git a/src/@utils/numbers.ts b/src/@utils/numbers.ts index e7b4a4327..aa82804b5 100644 --- a/src/@utils/numbers.ts +++ b/src/@utils/numbers.ts @@ -1,5 +1,20 @@ +import { formatCurrency } from '@coingecko/cryptoformat' import { Decimal } from 'decimal.js' +export function formatNumber( + price: number, + locale: string, + decimals?: string +): string { + return formatCurrency(price, '', locale, false, { + // Not exactly clear what `significant figures` are for this library, + // but setting this seems to give us the formatting we want. + // See https://github.com/oceanprotocol/market/issues/70 + significantFigures: 4, + ...(decimals && { decimalPlaces: Number(decimals) }) + }) +} + // Run decimal.js comparison // http://mikemcl.github.io/decimal.js/#cmp export function compareAsBN(balance: string, price: string): boolean { diff --git a/src/@utils/veAllocation.ts b/src/@utils/veAllocation.ts index bba0ea067..98ab7fc66 100644 --- a/src/@utils/veAllocation.ts +++ b/src/@utils/veAllocation.ts @@ -1,7 +1,7 @@ -import { AllLocked } from 'src/@types/subgraph/AllLocked' -import { OwnAllocations } from 'src/@types/subgraph/OwnAllocations' -import { NftOwnAllocation } from 'src/@types/subgraph/NftOwnAllocation' -import { OceanLocked } from 'src/@types/subgraph/OceanLocked' +import { AllLockedQuery } from 'src/@types/subgraph/AllLockedQuery' +import { OwnAllocationsQuery } from 'src/@types/subgraph/OwnAllocationsQuery' +import { NftOwnAllocationQuery } from 'src/@types/subgraph/NftOwnAllocationQuery' +import { OceanLockedQuery } from 'src/@types/subgraph/OceanLockedQuery' import { gql, OperationResult } from 'urql' import { fetchData, getQueryContext } from './subgraph' import axios from 'axios' @@ -12,11 +12,11 @@ import { NetworkType } from '@hooks/useNetworkMetadata' import { getAssetsFromNftList } from './aquarius' -import { chainIdsSupported } from 'app.config' +import { chainIdsSupported } from '../../app.config' import { Asset } from '@oceanprotocol/lib' const AllLocked = gql` - query AllLocked { + query AllLockedQuery { veOCEANs(first: 1000) { lockedAmount } @@ -24,7 +24,7 @@ const AllLocked = gql` ` const OwnAllocations = gql` - query OwnAllocations($address: String) { + query OwnAllocationsQuery($address: String) { veAllocations(where: { allocationUser: $address }) { id nftAddress @@ -33,7 +33,7 @@ const OwnAllocations = gql` } ` const NftOwnAllocation = gql` - query NftOwnAllocation($address: String, $nftAddress: String) { + query NftOwnAllocationQuery($address: String, $nftAddress: String) { veAllocations( where: { allocationUser: $address, nftAddress: $nftAddress } ) { @@ -42,7 +42,7 @@ const NftOwnAllocation = gql` } ` const OceanLocked = gql` - query OceanLocked($address: ID!) { + query OceanLockedQuery($address: ID!) { veOCEAN(id: $address) { id lockedAmount @@ -87,7 +87,7 @@ export async function getNftOwnAllocation( ): Promise { const veNetworkId = getVeChainNetworkId(networkId) const queryContext = getQueryContext(veNetworkId) - const fetchedAllocation: OperationResult = + const fetchedAllocation: OperationResult = await fetchData( NftOwnAllocation, { @@ -115,7 +115,7 @@ export async function getTotalAllocatedAndLocked(): Promise { 0 ) - const fetchedLocked: OperationResult = await fetchData( + const fetchedLocked: OperationResult = await fetchData( AllLocked, null, queryContext @@ -136,11 +136,12 @@ export async function getLocked( const veNetworkIds = getVeChainNetworkIds(networkIds) for (let i = 0; i < veNetworkIds.length; i++) { const queryContext = getQueryContext(veNetworkIds[i]) - const fetchedLocked: OperationResult = await fetchData( - OceanLocked, - { address: userAddress.toLowerCase() }, - queryContext - ) + const fetchedLocked: OperationResult = + await fetchData( + OceanLocked, + { address: userAddress.toLowerCase() }, + queryContext + ) fetchedLocked.data?.veOCEAN?.lockedAmount && (total += Number(fetchedLocked.data?.veOCEAN?.lockedAmount)) @@ -157,7 +158,7 @@ export async function getOwnAllocations( const veNetworkIds = getVeChainNetworkIds(networkIds) for (let i = 0; i < veNetworkIds.length; i++) { const queryContext = getQueryContext(veNetworkIds[i]) - const fetchedAllocations: OperationResult = + const fetchedAllocations: OperationResult = await fetchData( OwnAllocations, { address: userAddress.toLowerCase() }, diff --git a/src/components/@shared/AssetTeaser/index.module.css b/src/components/@shared/AssetTeaser/index.module.css index c88f6f700..336f4f780 100644 --- a/src/components/@shared/AssetTeaser/index.module.css +++ b/src/components/@shared/AssetTeaser/index.module.css @@ -48,7 +48,7 @@ } .footer { - margin-top: calc(var(--spacer) / 12); + margin-top: calc(var(--spacer) / 24); } .typeLabel { diff --git a/src/components/@shared/AssetTeaser/index.tsx b/src/components/@shared/AssetTeaser/index.tsx index 1b8b0d5ee..f91b2c302 100644 --- a/src/components/@shared/AssetTeaser/index.tsx +++ b/src/components/@shared/AssetTeaser/index.tsx @@ -8,8 +8,8 @@ import AssetType from '@shared/AssetType' import NetworkName from '@shared/NetworkName' import styles from './index.module.css' import { getServiceByName } from '@utils/ddo' -import { formatPrice } from '@shared/Price/PriceUnit' import { useUserPreferences } from '@context/UserPreferences' +import { formatNumber } from '@utils/numbers' export declare type AssetTeaserProps = { asset: AssetExtended @@ -77,23 +77,37 @@ export default function AssetTeaser({
{allocated && allocated > 0 ? ( - {allocated < 0 - ? '' - : `${formatPrice(allocated, locale)} veOCEAN`} + {allocated < 0 ? ( + '' + ) : ( + <> + {formatNumber(allocated, locale, '0')}{' '} + veOCEAN + + )} ) : null} {orders && orders > 0 ? ( - {orders < 0 - ? 'N/A' - : `${orders} ${orders === 1 ? 'sale' : 'sales'}`} + {orders < 0 ? ( + 'N/A' + ) : ( + <> + {orders} {orders === 1 ? 'sale' : 'sales'} + + )} ) : null} {asset.views && asset.views > 0 ? ( - {asset.views < 0 - ? 'N/A' - : `${asset.views} ${asset.views === 1 ? 'view' : 'views'}`} + {asset.views < 0 ? ( + 'N/A' + ) : ( + <> + {asset.views}{' '} + {asset.views === 1 ? 'view' : 'views'} + + )} ) : null}
diff --git a/src/components/@shared/Price/PriceUnit.tsx b/src/components/@shared/Price/PriceUnit.tsx index 12a1ea7e4..1ddd742a0 100644 --- a/src/components/@shared/Price/PriceUnit.tsx +++ b/src/components/@shared/Price/PriceUnit.tsx @@ -1,17 +1,8 @@ import React, { ReactElement } from 'react' -import { formatCurrency } from '@coingecko/cryptoformat' import Conversion from './Conversion' import styles from './PriceUnit.module.css' import { useUserPreferences } from '@context/UserPreferences' - -export function formatPrice(price: number, locale: string): string { - return formatCurrency(price, '', locale, false, { - // Not exactly clear what `significant figures` are for this library, - // but setting this seems to give us the formatting we want. - // See https://github.com/oceanprotocol/market/issues/70 - significantFigures: 4 - }) -} +import { formatNumber } from '@utils/numbers' export default function PriceUnit({ price, @@ -19,7 +10,8 @@ export default function PriceUnit({ size = 'small', conversion, symbol, - type + type, + decimals }: { price: number type?: string @@ -27,6 +19,7 @@ export default function PriceUnit({ size?: 'small' | 'mini' | 'large' conversion?: boolean symbol?: string + decimals?: string }): ReactElement { const { locale } = useUserPreferences() @@ -37,7 +30,7 @@ export default function PriceUnit({ ) : ( <>
- {Number.isNaN(price) ? '-' : formatPrice(price, locale)}{' '} + {Number.isNaN(price) ? '-' : formatNumber(price, locale, decimals)}{' '} {symbol}
{conversion && } diff --git a/src/components/Asset/AssetActions/AssetStats/index.tsx b/src/components/Asset/AssetActions/AssetStats/index.tsx index a32dc2d38..4d02d0352 100644 --- a/src/components/Asset/AssetActions/AssetStats/index.tsx +++ b/src/components/Asset/AssetActions/AssetStats/index.tsx @@ -2,7 +2,7 @@ import { useAsset } from '@context/Asset' import { useUserPreferences } from '@context/UserPreferences' import { useWeb3 } from '@context/Web3' import Tooltip from '@shared/atoms/Tooltip' -import { formatPrice } from '@shared/Price/PriceUnit' +import { formatNumber } from '@utils/numbers' import { getNftOwnAllocation } from '@utils/veAllocation' import React, { useEffect, useState } from 'react' import styles from './index.module.css' @@ -33,8 +33,8 @@ export default function AssetStats() { {asset?.stats?.allocated && asset?.stats?.allocated > 0 ? ( - {formatPrice(asset.stats.allocated, locale)} - + {formatNumber(asset.stats.allocated, locale, '0')} + {' '} veOCEAN ) : null} diff --git a/src/components/Footer/MarketStats/index.tsx b/src/components/Footer/MarketStats/index.tsx index 19f4ab32d..99caa2923 100644 --- a/src/components/Footer/MarketStats/index.tsx +++ b/src/components/Footer/MarketStats/index.tsx @@ -124,8 +124,19 @@ export default function MarketStats(): ReactElement { />
- locked.{' '} - {' '} + {' '} + locked.{' '} + {' '} allocated.
diff --git a/src/components/Home/MostViews/index.test.tsx b/src/components/Home/MostViews/index.test.tsx index 8c61a3787..dbdb91cb8 100644 --- a/src/components/Home/MostViews/index.test.tsx +++ b/src/components/Home/MostViews/index.test.tsx @@ -32,7 +32,7 @@ describe('components/Home/MostViews', () => { ) queryMetadataMock.mockResolvedValue(queryMetadataBaseReturn) render() - await screen.findByText('666 views') + await screen.findByText('666') }) it('catches errors', async () => { From 4ff84b0871c90c9ad02ad8b6faef5eb2190094a0 Mon Sep 17 00:00:00 2001 From: Jamie Hewitt Date: Fri, 11 Nov 2022 12:54:21 +0300 Subject: [PATCH 6/7] Handle provider error & showing Retry button (#1770) * adding error handling for button onCLick * Updating butoon to show retry & updating toast message * Logging provider error * Removing unused imports * Using early return when provider fees are present to prevent unneccessary nesting * Adding retry logic for compute * Removing duplicate teast message * Adding tests for rendering Buy button * Additional tests for BuyButton * Refactoring BuyButton tests for download * Fix failing tests * Refactoring compute tests * Refactoring tests Co-authored-by: Matthias Kretschmann --- src/@utils/order.ts | 51 ++++--- .../AssetActions/ButtonBuy/index.test.tsx | 143 ++++++++++++++++++ .../Asset/AssetActions/ButtonBuy/index.tsx | 35 +++-- .../Compute/FormComputeDataset.tsx | 5 +- .../Asset/AssetActions/Compute/index.tsx | 6 +- .../Asset/AssetActions/Download.tsx | 5 +- 6 files changed, 207 insertions(+), 38 deletions(-) create mode 100644 src/components/Asset/AssetActions/ButtonBuy/index.test.tsx diff --git a/src/@utils/order.ts b/src/@utils/order.ts index 9dc189c61..3f9337d77 100644 --- a/src/@utils/order.ts +++ b/src/@utils/order.ts @@ -8,7 +8,8 @@ import { OrderParams, ProviderComputeInitialize, ProviderFees, - ProviderInstance + ProviderInstance, + ProviderInitialize } from '@oceanprotocol/lib' import Web3 from 'web3' import { getOceanConfig } from './ocean' @@ -20,6 +21,26 @@ import { } from '../../app.config' import { toast } from 'react-toastify' +async function initializeProvider( + asset: AssetExtended, + accountId: string, + providerFees?: ProviderFees +): Promise { + if (providerFees) return + try { + const provider = await ProviderInstance.initialize( + asset.id, + asset.services[0].id, + 0, + accountId, + asset.services[0].serviceEndpoint + ) + return provider + } catch (error) { + LoggerInstance.log('[Initialize Provider] Error:', error) + } +} + /** * @param web3 * @param asset @@ -40,15 +61,11 @@ export async function order( const datatoken = new Datatoken(web3) const config = getOceanConfig(asset.chainId) - const initializeData = - !providerFees && - (await ProviderInstance.initialize( - asset.id, - asset.services[0].id, - 0, - accountId, - asset.services[0].serviceEndpoint - )) + const initializeData = await initializeProvider( + asset, + accountId, + providerFees + ) const orderParams = { consumer: computeConsumerAddress || accountId, @@ -130,15 +147,11 @@ export async function reuseOrder( providerFees?: ProviderFees ): Promise { const datatoken = new Datatoken(web3) - const initializeData = - !providerFees && - (await ProviderInstance.initialize( - asset.id, - asset.services[0].id, - 0, - accountId, - asset.services[0].serviceEndpoint - )) + const initializeData = await initializeProvider( + asset, + accountId, + providerFees + ) const tx = await datatoken.reuseOrder( asset.accessDetails.datatoken.address, diff --git a/src/components/Asset/AssetActions/ButtonBuy/index.test.tsx b/src/components/Asset/AssetActions/ButtonBuy/index.test.tsx new file mode 100644 index 000000000..43ef5c433 --- /dev/null +++ b/src/components/Asset/AssetActions/ButtonBuy/index.test.tsx @@ -0,0 +1,143 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import ButtonBuy, { ButtonBuyProps } from './' + +const downloadProps: ButtonBuyProps = { + action: 'download', + disabled: false, + hasPreviousOrder: false, + hasDatatoken: false, + btSymbol: 'btSymbol', + dtSymbol: 'dtSymbol', + dtBalance: '100000000000', + assetTimeout: '1 day', + assetType: 'Dataset', + stepText: 'TEST', + priceType: 'fixed', + isConsumable: true, + isBalanceSufficient: true, + consumableFeedback: 'TEST: consumableFeedback' +} + +const computeProps: ButtonBuyProps = { + action: 'compute', + disabled: false, + hasPreviousOrder: false, + hasDatatoken: true, + btSymbol: 'btSymbol', + dtSymbol: 'dtSymbol', + dtBalance: '100000000000', + assetTimeout: '1 day', + assetType: 'algorithm', + hasPreviousOrderSelectedComputeAsset: false, + hasDatatokenSelectedComputeAsset: true, + dtSymbolSelectedComputeAsset: 'dtSymbol', + dtBalanceSelectedComputeAsset: 'dtBalance', + selectedComputeAssetType: 'selectedComputeAssetType', + stepText: ' ', + isLoading: false, + type: 'submit', + priceType: 'fixed', + algorithmPriceType: 'free', + isBalanceSufficient: true, + isConsumable: true, + consumableFeedback: 'consumableFeedback', + isAlgorithmConsumable: true, + hasProviderFee: false, + retry: false +} + +describe('Asset/AssetActions/ButtonBuy', () => { + // TESTS FOR LOADING + it('Renders Buy button without crashing', () => { + render() + const button = screen.getByText('TEST') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Buy for 1 day') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Download') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Retry') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Download') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Get') + expect(button).toContainHTML(' { + render( + + ) + const button = screen.getByText('Buy') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Buy Compute Job') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Buy Compute Job') + expect(button).toContainHTML(' { + render( + + ) + const button = screen.getByText('Start Compute Job') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Order Compute Job') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Order Compute Job') + expect(button).toContainHTML(' { + render() + const button = screen.getByText('Retry') + expect(button).toContainHTML(' diff --git a/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx b/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx index abe7b3398..18b37b2ba 100644 --- a/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx +++ b/src/components/Asset/AssetActions/Compute/FormComputeDataset.tsx @@ -43,7 +43,8 @@ export default function FormStartCompute({ datasetOrderPriceAndFees, algoOrderPriceAndFees, providerFeeAmount, - validUntil + validUntil, + retry }: { algorithms: AssetSelectionAsset[] ddoListAlgorithms: Asset[] @@ -71,6 +72,7 @@ export default function FormStartCompute({ algoOrderPriceAndFees?: OrderPriceAndFees providerFeeAmount?: string validUntil?: string + retry: boolean }): ReactElement { const { siteContent } = useMarketMetadata() const { accountId, balance } = useWeb3() @@ -294,6 +296,7 @@ export default function FormStartCompute({ selectedAlgorithmAsset?.accessDetails?.isPurchasable } hasProviderFee={providerFeeAmount && providerFeeAmount !== '0'} + retry={retry} /> ) diff --git a/src/components/Asset/AssetActions/Compute/index.tsx b/src/components/Asset/AssetActions/Compute/index.tsx index 13e2533cf..4407473eb 100644 --- a/src/components/Asset/AssetActions/Compute/index.tsx +++ b/src/components/Asset/AssetActions/Compute/index.tsx @@ -97,6 +97,7 @@ export default function Compute({ const [refetchJobs, setRefetchJobs] = useState(false) const [isLoadingJobs, setIsLoadingJobs] = useState(false) const [jobs, setJobs] = useState([]) + const [retry, setRetry] = useState(false) const hasDatatoken = Number(dtBalance) >= 1 const isComputeButtonDisabled = @@ -291,7 +292,8 @@ export default function Compute({ useEffect(() => { const newError = error if (!newError) return - toast.error(newError) + const errorMsg = newError + '. Please retry.' + toast.error(errorMsg) }, [error]) async function startJob(): Promise { @@ -386,6 +388,7 @@ export default function Compute({ initPriceAndFees() } catch (error) { setError(error.message) + setRetry(true) LoggerInstance.error(`[compute] ${error.message} `) } finally { setIsOrdering(false) @@ -477,6 +480,7 @@ export default function Compute({ algoOrderPriceAndFees={algoOrderPriceAndFees} providerFeeAmount={providerFeeAmount} validUntil={computeValidUntil} + retry={retry} /> )} diff --git a/src/components/Asset/AssetActions/Download.tsx b/src/components/Asset/AssetActions/Download.tsx index 61a31dc3c..1bcbb360b 100644 --- a/src/components/Asset/AssetActions/Download.tsx +++ b/src/components/Asset/AssetActions/Download.tsx @@ -48,6 +48,7 @@ export default function Download({ const [isOrderDisabled, setIsOrderDisabled] = useState(false) const [orderPriceAndFees, setOrderPriceAndFees] = useState() + const [retry, setRetry] = useState(false) const isUnsupportedPricing = asset?.accessDetails?.type === 'NOT_SUPPORTED' @@ -155,9 +156,10 @@ export default function Download({ } } catch (error) { LoggerInstance.error(error) + setRetry(true) const message = isOwned ? 'Failed to download file!' - : 'An error occurred. Check console for more information.' + : 'An error occurred, please retry. Check console for more information.' toast.error(message) } setIsLoading(false) @@ -181,6 +183,7 @@ export default function Download({ isConsumable={asset.accessDetails?.isPurchasable} isBalanceSufficient={isBalanceSufficient} consumableFeedback={consumableFeedback} + retry={retry} /> ) From 6ba95fe4d58ce94b83732dc515c71a876ee2a72e Mon Sep 17 00:00:00 2001 From: mihaisc Date: Fri, 11 Nov 2022 14:27:30 +0200 Subject: [PATCH 7/7] fix loading in preview (#1785) --- src/components/Asset/RelatedAssets/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Asset/RelatedAssets/index.tsx b/src/components/Asset/RelatedAssets/index.tsx index 9476e3230..34f4c0fb6 100644 --- a/src/components/Asset/RelatedAssets/index.tsx +++ b/src/components/Asset/RelatedAssets/index.tsx @@ -23,7 +23,6 @@ export default function RelatedAssets(): ReactElement { !asset?.nft || !asset?.metadata ) { - setIsLoading(false) return }