mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Atomic components stories (#1422)
This commit is contained in:
parent
9027fc1307
commit
6d2dea8c9d
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -127,4 +127,5 @@ jobs:
|
|||||||
restore-keys: ${{ runner.os }}-${{ matrix.node }}-storybook-${{ env.cache-name }}-
|
restore-keys: ${{ runner.os }}-${{ matrix.node }}-storybook-${{ env.cache-name }}-
|
||||||
|
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
- run: npm run pregenerate
|
||||||
- run: npm run storybook:build
|
- run: npm run storybook:build
|
||||||
|
13
.jest/__mocks__/matchMedia.js
Normal file
13
.jest/__mocks__/matchMedia.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Object.defineProperty(window, 'matchMedia', {
|
||||||
|
writable: true,
|
||||||
|
value: jest.fn().mockImplementation((query) => ({
|
||||||
|
matches: false,
|
||||||
|
media: query,
|
||||||
|
onchange: null,
|
||||||
|
addListener: jest.fn(), // deprecated
|
||||||
|
removeListener: jest.fn(), // deprecated
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
dispatchEvent: jest.fn()
|
||||||
|
}))
|
||||||
|
})
|
@ -1 +1,2 @@
|
|||||||
import '@testing-library/jest-dom/extend-expect'
|
import '@testing-library/jest-dom/extend-expect'
|
||||||
|
import './__mocks__/matchMedia'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
|
||||||
|
const webpack = require('webpack')
|
||||||
module.exports = {
|
module.exports = {
|
||||||
core: { builder: 'webpack5' },
|
core: { builder: 'webpack5' },
|
||||||
stories: ['../src/**/*.stories.tsx'],
|
stories: ['../src/**/*.stories.tsx'],
|
||||||
@ -47,6 +47,12 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
config.resolve.fallback = fallback
|
config.resolve.fallback = fallback
|
||||||
|
|
||||||
|
config.plugins = (config.plugins || []).concat([
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
process: 'process/browser',
|
||||||
|
Buffer: ['buffer', 'Buffer']
|
||||||
|
})
|
||||||
|
])
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ export const parameters = {
|
|||||||
controls: {
|
controls: {
|
||||||
matchers: {
|
matchers: {
|
||||||
color: /(background|color)$/i,
|
color: /(background|color)$/i,
|
||||||
date: /Date$/
|
date: /date$/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13808
package-lock.json
generated
13808
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@ -18,8 +18,8 @@
|
|||||||
"deploy:s3": "bash scripts/deploy-s3.sh",
|
"deploy:s3": "bash scripts/deploy-s3.sh",
|
||||||
"postinstall": "husky install",
|
"postinstall": "husky install",
|
||||||
"codegen:apollo": "apollo client:codegen --endpoint=https://v4.subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph --target typescript --tsFileExtension=d.ts --outputFlat src/@types/subgraph/",
|
"codegen:apollo": "apollo client:codegen --endpoint=https://v4.subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph --target typescript --tsFileExtension=d.ts --outputFlat src/@types/subgraph/",
|
||||||
"storybook": "start-storybook -p 6006 --quiet",
|
"storybook": "cross-env NODE_ENV=test start-storybook -p 6006 --quiet",
|
||||||
"storybook:build": "build-storybook"
|
"storybook:build": "cross-env NODE_ENV=test build-storybook"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coingecko/cryptoformat": "^0.4.4",
|
"@coingecko/cryptoformat": "^0.4.4",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"react-modal": "^3.15.1",
|
"react-modal": "^3.15.1",
|
||||||
"react-paginate": "^8.1.3",
|
"react-paginate": "^8.1.3",
|
||||||
"react-spring": "^9.4.5",
|
"react-spring": "^9.4.5",
|
||||||
"react-tabs": "^3.2.3",
|
"react-tabs": "^5.1.0",
|
||||||
"react-toastify": "^8.2.0",
|
"react-toastify": "^8.2.0",
|
||||||
"remark": "^13.0.0",
|
"remark": "^13.0.0",
|
||||||
"remark-gfm": "^1.0.0",
|
"remark-gfm": "^1.0.0",
|
||||||
@ -73,41 +73,40 @@
|
|||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/addon-essentials": "^6.4.22",
|
"@storybook/addon-essentials": "^6.5.4",
|
||||||
"@storybook/addon-storyshots": "^6.4.22",
|
"@storybook/addon-storyshots": "^6.5.4",
|
||||||
"@storybook/builder-webpack5": "^6.4.22",
|
"@storybook/builder-webpack5": "^6.5.4",
|
||||||
"@storybook/manager-webpack5": "^6.4.22",
|
"@storybook/manager-webpack5": "^6.5.4",
|
||||||
"@storybook/react": "^6.4.22",
|
"@storybook/react": "^6.5.4",
|
||||||
"@storybook/testing-library": "^0.0.11",
|
"@storybook/testing-library": "^0.0.11",
|
||||||
"@storybook/testing-react": "^1.2.4",
|
"@storybook/testing-react": "^1.3.0",
|
||||||
"@svgr/webpack": "^6.2.1",
|
"@svgr/webpack": "^6.2.1",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^13.2.0",
|
"@testing-library/react": "^13.2.0",
|
||||||
"@types/chart.js": "^2.9.37",
|
"@types/chart.js": "^2.9.37",
|
||||||
"@types/d3": "^7.1.0",
|
|
||||||
"@types/js-cookie": "^3.0.1",
|
"@types/js-cookie": "^3.0.1",
|
||||||
"@types/loadable__component": "^5.13.1",
|
"@types/loadable__component": "^5.13.1",
|
||||||
"@types/lodash.debounce": "^4.0.3",
|
"@types/lodash.debounce": "^4.0.3",
|
||||||
"@types/lodash.omit": "^4.5.6",
|
"@types/lodash.omit": "^4.5.6",
|
||||||
"@types/node": "^17.0.13",
|
"@types/node": "^17.0.35",
|
||||||
"@types/react": "^18.0.9",
|
"@types/react": "^18.0.9",
|
||||||
"@types/react-dom": "^18.0.3",
|
"@types/react-dom": "^18.0.4",
|
||||||
"@types/react-modal": "^3.13.1",
|
"@types/react-modal": "^3.13.1",
|
||||||
"@types/react-paginate": "^7.1.1",
|
"@types/react-paginate": "^7.1.1",
|
||||||
"@types/react-tabs": "^2.3.4",
|
|
||||||
"@types/remove-markdown": "^0.3.1",
|
"@types/remove-markdown": "^0.3.1",
|
||||||
"@types/yup": "^0.29.13",
|
"@types/yup": "^0.29.13",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
"@typescript-eslint/eslint-plugin": "^5.25.0",
|
||||||
"@typescript-eslint/parser": "^5.23.0",
|
"@typescript-eslint/parser": "^5.25.0",
|
||||||
"apollo": "^2.33.9",
|
"apollo": "^2.33.9",
|
||||||
"eslint": "^8.15.0",
|
"cross-env": "^7.0.3",
|
||||||
|
"eslint": "^8.16.0",
|
||||||
"eslint-config-oceanprotocol": "^2.0.1",
|
"eslint-config-oceanprotocol": "^2.0.1",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-jest-dom": "^4.0.1",
|
"eslint-plugin-jest-dom": "^4.0.2",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-react": "^7.29.4",
|
"eslint-plugin-react": "^7.30.0",
|
||||||
"eslint-plugin-react-hooks": "^4.5.0",
|
"eslint-plugin-react-hooks": "^4.5.0",
|
||||||
"eslint-plugin-testing-library": "^5.4.0",
|
"eslint-plugin-testing-library": "^5.5.0",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"https-browserify": "^1.0.0",
|
"https-browserify": "^1.0.0",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.1",
|
||||||
|
@ -58,11 +58,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.radio {
|
.radio {
|
||||||
composes: radio from '@shared/FormInput/InputElement.module.css';
|
composes: radio from '@shared/FormInput/InputRadio.module.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
composes: checkbox from '@shared/FormInput/InputElement.module.css';
|
composes: checkbox from '@shared/FormInput/InputRadio.module.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -76,10 +76,10 @@ export default function AssetSelection({
|
|||||||
<div className={styles.row} key={asset.did}>
|
<div className={styles.row} key={asset.did}>
|
||||||
<input
|
<input
|
||||||
id={slugify(asset.did)}
|
id={slugify(asset.did)}
|
||||||
type={multiple ? 'checkbox' : 'radio'}
|
|
||||||
className={styleClassesInput}
|
className={styleClassesInput}
|
||||||
defaultChecked={asset.checked}
|
|
||||||
{...props}
|
{...props}
|
||||||
|
defaultChecked={asset.checked}
|
||||||
|
type={multiple ? 'checkbox' : 'radio'}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={asset.did}
|
value={asset.did}
|
||||||
/>
|
/>
|
||||||
|
@ -45,11 +45,11 @@ export default function BoxSelection({
|
|||||||
<div key={option.name}>
|
<div key={option.name}>
|
||||||
<input
|
<input
|
||||||
id={option.name}
|
id={option.name}
|
||||||
type="radio"
|
|
||||||
className={styleClassesInput}
|
|
||||||
defaultChecked={option.checked}
|
defaultChecked={option.checked}
|
||||||
onChange={(event) => handleChange(event)}
|
onChange={(event) => handleChange(event)}
|
||||||
{...props}
|
{...props}
|
||||||
|
type="radio"
|
||||||
|
className={styleClassesInput}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={option.value ? option.value : option.name}
|
value={option.value ? option.value : option.name}
|
||||||
name={name}
|
name={name}
|
||||||
|
@ -81,92 +81,12 @@
|
|||||||
font-family: var(--font-family-base);
|
font-family: var(--font-family-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.radioGroup {
|
|
||||||
margin-top: calc(var(--spacer) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.radioWrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radioLabel {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-weight: var(--font-weight-bold);
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
padding-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.algorithmLabel {
|
.algorithmLabel {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacer);
|
gap: var(--spacer);
|
||||||
grid-template-columns: 2fr 1fr;
|
grid-template-columns: 2fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio,
|
|
||||||
.checkbox {
|
|
||||||
composes: input;
|
|
||||||
position: relative;
|
|
||||||
padding: 0;
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
min-height: 0;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-top: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio::after,
|
|
||||||
.checkbox::after {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
transition: transform 0.3s ease-out, opacity 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio:checked,
|
|
||||||
.checkbox:checked {
|
|
||||||
border-color: var(--color-primary);
|
|
||||||
background: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio:focus,
|
|
||||||
.checkbox:focus {
|
|
||||||
box-shadow: 0 0 0 var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio:checked::after,
|
|
||||||
.checkbox:checked::after {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio,
|
|
||||||
.radio::after {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio::after {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
top: 4px;
|
|
||||||
left: 4px;
|
|
||||||
background: var(--brand-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox::after {
|
|
||||||
width: 6px;
|
|
||||||
height: 9px;
|
|
||||||
border: 2px solid var(--brand-white);
|
|
||||||
border-top: 0;
|
|
||||||
border-left: 0;
|
|
||||||
left: 5px;
|
|
||||||
top: 2px;
|
|
||||||
transform: rotate(40deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.prefixGroup,
|
.prefixGroup,
|
||||||
.postfixGroup {
|
.postfixGroup {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import slugify from 'slugify'
|
|
||||||
import styles from './InputElement.module.css'
|
import styles from './InputElement.module.css'
|
||||||
import { InputProps } from '.'
|
import { InputProps } from '.'
|
||||||
import FilesInput from '../FormFields/FilesInput'
|
import FilesInput from '../FormFields/FilesInput'
|
||||||
@ -11,15 +10,20 @@ import AssetSelection, {
|
|||||||
AssetSelectionAsset
|
AssetSelectionAsset
|
||||||
} from '../FormFields/AssetSelection'
|
} from '../FormFields/AssetSelection'
|
||||||
import Nft from '../FormFields/Nft'
|
import Nft from '../FormFields/Nft'
|
||||||
|
import InputRadio from './InputRadio'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
const DefaultInput = ({
|
const DefaultInput = ({
|
||||||
size,
|
size,
|
||||||
className,
|
className,
|
||||||
|
// We filter out all props which are not allowed
|
||||||
|
// to be passed to HTML input so these stay unused.
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
prefix,
|
prefix,
|
||||||
postfix,
|
postfix,
|
||||||
additionalComponent,
|
additionalComponent,
|
||||||
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
...props
|
...props
|
||||||
}: InputProps) => (
|
}: InputProps) => (
|
||||||
<input
|
<input
|
||||||
@ -30,27 +34,28 @@ const DefaultInput = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
export default function InputElement({
|
export default function InputElement({
|
||||||
type,
|
|
||||||
options,
|
options,
|
||||||
sortOptions,
|
sortOptions,
|
||||||
name,
|
|
||||||
prefix,
|
prefix,
|
||||||
postfix,
|
postfix,
|
||||||
size,
|
size,
|
||||||
field,
|
field,
|
||||||
label,
|
|
||||||
multiple,
|
multiple,
|
||||||
disabled,
|
// We filter out all props which are not allowed
|
||||||
|
// to be passed to HTML input so these stay unused.
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
label,
|
||||||
help,
|
help,
|
||||||
prominentHelp,
|
prominentHelp,
|
||||||
form,
|
form,
|
||||||
additionalComponent,
|
additionalComponent,
|
||||||
disclaimer,
|
disclaimer,
|
||||||
disclaimerValues,
|
disclaimerValues,
|
||||||
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
...props
|
...props
|
||||||
}: InputProps): ReactElement {
|
}: InputProps): ReactElement {
|
||||||
const styleClasses = cx({ select: true, [size]: size })
|
const styleClasses = cx({ select: true, [size]: size })
|
||||||
switch (type) {
|
switch (props.type) {
|
||||||
case 'select': {
|
case 'select': {
|
||||||
const sortedOptions =
|
const sortedOptions =
|
||||||
!sortOptions && sortOptions === false
|
!sortOptions && sortOptions === false
|
||||||
@ -60,10 +65,9 @@ export default function InputElement({
|
|||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
id={name}
|
id={props.name}
|
||||||
className={styleClasses}
|
className={styleClasses}
|
||||||
{...props}
|
{...props}
|
||||||
disabled={disabled}
|
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
>
|
>
|
||||||
{field !== undefined && field.value === '' && <option value="" />}
|
{field !== undefined && field.value === '' && <option value="" />}
|
||||||
@ -77,39 +81,12 @@ export default function InputElement({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
case 'textarea':
|
case 'textarea':
|
||||||
return (
|
return <textarea id={props.name} className={styles.textarea} {...props} />
|
||||||
<textarea
|
|
||||||
name={name}
|
|
||||||
id={name}
|
|
||||||
className={styles.textarea}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'radio':
|
case 'radio':
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
return (
|
return <InputRadio options={options} inputSize={size} {...props} />
|
||||||
<div className={styles.radioGroup}>
|
|
||||||
{options &&
|
|
||||||
(options as string[]).map((option: string, index: number) => (
|
|
||||||
<div className={styles.radioWrap} key={index}>
|
|
||||||
<input
|
|
||||||
className={styles[type]}
|
|
||||||
id={slugify(option)}
|
|
||||||
type={type}
|
|
||||||
name={name}
|
|
||||||
defaultChecked={props.defaultChecked}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
className={cx({ [styles.radioLabel]: true, [size]: size })}
|
|
||||||
htmlFor={slugify(option)}
|
|
||||||
>
|
|
||||||
{option}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
case 'assetSelection':
|
case 'assetSelection':
|
||||||
return (
|
return (
|
||||||
<AssetSelection
|
<AssetSelection
|
||||||
@ -118,28 +95,27 @@ export default function InputElement({
|
|||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
case 'assetSelectionMultiple':
|
case 'assetSelectionMultiple':
|
||||||
return (
|
return (
|
||||||
<AssetSelection
|
<AssetSelection
|
||||||
assets={options as unknown as AssetSelectionAsset[]}
|
assets={options as unknown as AssetSelectionAsset[]}
|
||||||
multiple
|
multiple
|
||||||
disabled={disabled}
|
|
||||||
{...field}
|
{...field}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case 'files':
|
case 'files':
|
||||||
return <FilesInput name={name} {...field} {...props} />
|
return <FilesInput {...field} {...props} />
|
||||||
case 'providerUrl':
|
case 'providerUrl':
|
||||||
return <CustomProvider name={name} {...field} {...props} />
|
return <CustomProvider {...field} {...props} />
|
||||||
case 'nft':
|
case 'nft':
|
||||||
return <Nft name={name} {...field} {...props} />
|
return <Nft {...field} {...props} />
|
||||||
case 'datatoken':
|
case 'datatoken':
|
||||||
return <Datatoken name={name} {...field} {...props} />
|
return <Datatoken {...field} {...props} />
|
||||||
case 'boxSelection':
|
case 'boxSelection':
|
||||||
return (
|
return (
|
||||||
<BoxSelection
|
<BoxSelection
|
||||||
name={name}
|
|
||||||
options={options as unknown as BoxSelectionOption[]}
|
options={options as unknown as BoxSelectionOption[]}
|
||||||
{...field}
|
{...field}
|
||||||
{...props}
|
{...props}
|
||||||
@ -152,10 +128,8 @@ export default function InputElement({
|
|||||||
<div className={cx({ prefix: true, [size]: size })}>{prefix}</div>
|
<div className={cx({ prefix: true, [size]: size })}>{prefix}</div>
|
||||||
)}
|
)}
|
||||||
<DefaultInput
|
<DefaultInput
|
||||||
name={name}
|
type={props.type || 'text'}
|
||||||
type={type || 'text'}
|
|
||||||
size={size}
|
size={size}
|
||||||
disabled={disabled}
|
|
||||||
{...field}
|
{...field}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -165,10 +139,8 @@ export default function InputElement({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<DefaultInput
|
<DefaultInput
|
||||||
name={name}
|
type={props.type || 'text'}
|
||||||
type={type || 'text'}
|
|
||||||
size={size}
|
size={size}
|
||||||
disabled={disabled}
|
|
||||||
{...field}
|
{...field}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
79
src/components/@shared/FormInput/InputRadio.module.css
Normal file
79
src/components/@shared/FormInput/InputRadio.module.css
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
.radioGroup {
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.radioWrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radioLabel {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio,
|
||||||
|
.checkbox {
|
||||||
|
composes: input from './InputElement.module.css';
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
min-height: 0;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio:focus,
|
||||||
|
.checkbox:focus {
|
||||||
|
box-shadow: 0 0 0 var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio::after,
|
||||||
|
.checkbox::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
transition: transform 0.3s ease-out, opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio,
|
||||||
|
.radio::after {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio::after {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
top: 4px;
|
||||||
|
left: 4px;
|
||||||
|
background: var(--brand-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox::after {
|
||||||
|
width: 6px;
|
||||||
|
height: 9px;
|
||||||
|
border: 2px solid var(--brand-white);
|
||||||
|
border-top: 0;
|
||||||
|
border-left: 0;
|
||||||
|
left: 5px;
|
||||||
|
top: 2px;
|
||||||
|
transform: rotate(40deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio:checked,
|
||||||
|
.checkbox:checked {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
background: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio:checked::after,
|
||||||
|
.checkbox:checked::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
41
src/components/@shared/FormInput/InputRadio.tsx
Normal file
41
src/components/@shared/FormInput/InputRadio.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React, { InputHTMLAttributes, ReactElement } from 'react'
|
||||||
|
import slugify from 'slugify'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
import styles from './InputRadio.module.css'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
interface InputRadioProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||||
|
options: string[]
|
||||||
|
inputSize?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function InputRadio({
|
||||||
|
options,
|
||||||
|
inputSize,
|
||||||
|
...props
|
||||||
|
}: InputRadioProps): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={styles.radioGroup}>
|
||||||
|
{options &&
|
||||||
|
(options as string[]).map((option: string, index: number) => (
|
||||||
|
<div className={styles.radioWrap} key={index}>
|
||||||
|
<input
|
||||||
|
{...props}
|
||||||
|
className={styles[props.type]}
|
||||||
|
id={slugify(option)}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className={cx({
|
||||||
|
[styles.radioLabel]: true,
|
||||||
|
[inputSize]: inputSize
|
||||||
|
})}
|
||||||
|
htmlFor={slugify(option)}
|
||||||
|
>
|
||||||
|
{option}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -264,6 +264,7 @@ export default function PoolTransactions({
|
|||||||
minimal ? transactions?.length >= 4 : transactions?.length >= 9
|
minimal ? transactions?.length >= 4 : transactions?.length >= 9
|
||||||
}
|
}
|
||||||
paginationPerPage={minimal ? 5 : 10}
|
paginationPerPage={minimal ? 5 : 10}
|
||||||
|
emptyMessage={chainIds.length === 0 ? 'No network selected' : null}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div>Please connect your Web3 wallet.</div>
|
<div>Please connect your Web3 wallet.</div>
|
||||||
|
20
src/components/@shared/atoms/Badge/index.stories.tsx
Normal file
20
src/components/@shared/atoms/Badge/index.stories.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
|
||||||
|
import Badge, { BadgeProps } from '@shared/atoms/Badge'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Badge',
|
||||||
|
component: Badge
|
||||||
|
} as ComponentMeta<typeof Badge>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Badge> = (args) => <Badge {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: BadgeProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
label: 'Badge label'
|
||||||
|
}
|
@ -1,16 +1,15 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Badge.module.css'
|
import styles from './index.module.css'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
export default function Badge({
|
export interface BadgeProps {
|
||||||
label,
|
|
||||||
className
|
|
||||||
}: {
|
|
||||||
label: string
|
label: string
|
||||||
className?: string
|
className?: string
|
||||||
}): ReactElement {
|
}
|
||||||
|
|
||||||
|
export default function Badge({ label, className }: BadgeProps): ReactElement {
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
badge: true,
|
badge: true,
|
||||||
[className]: className
|
[className]: className
|
22
src/components/@shared/atoms/Blockies/index.stories.tsx
Normal file
22
src/components/@shared/atoms/Blockies/index.stories.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
|
||||||
|
import Blockies, { BlockiesProps } from '@shared/atoms/Blockies'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Blockies',
|
||||||
|
component: Blockies
|
||||||
|
} as ComponentMeta<typeof Blockies>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Blockies> = (args) => (
|
||||||
|
<Blockies {...args} />
|
||||||
|
)
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: BlockiesProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
accountId: '0x1xxxxxxxxxx3Exxxxxx7xxxxxxxxxxxxF1fd'
|
||||||
|
}
|
@ -1,14 +1,16 @@
|
|||||||
import { toDataUrl } from 'myetherwallet-blockies'
|
import { toDataUrl } from 'myetherwallet-blockies'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Blockies.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
|
export interface BlockiesProps {
|
||||||
|
accountId: string
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
export default function Blockies({
|
export default function Blockies({
|
||||||
accountId,
|
accountId,
|
||||||
className
|
className
|
||||||
}: {
|
}: BlockiesProps): ReactElement {
|
||||||
accountId: string
|
|
||||||
className?: string
|
|
||||||
}): ReactElement {
|
|
||||||
if (!accountId) return null
|
if (!accountId) return null
|
||||||
const blockies = toDataUrl(accountId)
|
const blockies = toDataUrl(accountId)
|
||||||
|
|
43
src/components/@shared/atoms/Container/index.stories.tsx
Normal file
43
src/components/@shared/atoms/Container/index.stories.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import Container, { ContainerProps } from '@shared/atoms/Container'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Container',
|
||||||
|
component: Container
|
||||||
|
} as ComponentMeta<typeof Container>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Container> = (args) => (
|
||||||
|
<Container {...args} />
|
||||||
|
)
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: ContainerProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam
|
||||||
|
facilisis molestie. Integer eget congue turpis, in pharetra lectus. Sed
|
||||||
|
urna dolor, porttitor luctus mauris eget, lacinia consectetur eros. Duis
|
||||||
|
consequat, turpis et porttitor cursus, ante lacus placerat arcu, vel
|
||||||
|
pellentesque enim orci ac sem.
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Narrow: Props = Template.bind({})
|
||||||
|
Narrow.args = {
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam
|
||||||
|
facilisis molestie. Integer eget congue turpis, in pharetra lectus. Sed
|
||||||
|
urna dolor, porttitor luctus mauris eget, lacinia consectetur eros. Duis
|
||||||
|
consequat, turpis et porttitor cursus, ante lacus placerat arcu, vel
|
||||||
|
pellentesque enim orci ac sem.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
narrow: true
|
||||||
|
}
|
@ -1,18 +1,20 @@
|
|||||||
import React, { ReactElement, ReactNode } from 'react'
|
import React, { ReactElement, ReactNode } from 'react'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import styles from './Container.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
export interface ContainerProps {
|
||||||
|
children: ReactNode
|
||||||
|
narrow?: boolean
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
export default function Container({
|
export default function Container({
|
||||||
children,
|
children,
|
||||||
narrow,
|
narrow,
|
||||||
className
|
className
|
||||||
}: {
|
}: ContainerProps): ReactElement {
|
||||||
children: ReactNode
|
|
||||||
narrow?: boolean
|
|
||||||
className?: string
|
|
||||||
}): ReactElement {
|
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
container: true,
|
container: true,
|
||||||
narrow,
|
narrow,
|
15
src/components/@shared/atoms/Copy/index.stories.tsx
Normal file
15
src/components/@shared/atoms/Copy/index.stories.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import Copy from '@shared/atoms/Copy'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Copy',
|
||||||
|
component: Copy
|
||||||
|
} as ComponentMeta<typeof Copy>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Copy> = (args) => <Copy {...args} />
|
||||||
|
|
||||||
|
export const Default = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie.'
|
||||||
|
}
|
27
src/components/@shared/atoms/Copy/index.test.tsx
Normal file
27
src/components/@shared/atoms/Copy/index.test.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render, act, screen, fireEvent } from '@testing-library/react'
|
||||||
|
import { Default } from './index.stories'
|
||||||
|
|
||||||
|
jest.useFakeTimers()
|
||||||
|
|
||||||
|
describe('Copy', () => {
|
||||||
|
test('should change class on click', () => {
|
||||||
|
render(<Default {...Default.args} />)
|
||||||
|
|
||||||
|
const element = screen.getByTitle('Copy to clipboard')
|
||||||
|
fireEvent.click(element)
|
||||||
|
expect(element).toHaveClass('copied')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should remove class after timer end', () => {
|
||||||
|
render(<Default {...Default.args} />)
|
||||||
|
|
||||||
|
const element = screen.getByTitle('Copy to clipboard')
|
||||||
|
fireEvent.click(element)
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
jest.advanceTimersToNextTimer()
|
||||||
|
})
|
||||||
|
expect(element).not.toHaveClass('copied')
|
||||||
|
})
|
||||||
|
})
|
@ -1,9 +1,13 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import styles from './Copy.module.css'
|
import styles from './index.module.css'
|
||||||
import IconCopy from '@images/copy.svg'
|
import IconCopy from '@images/copy.svg'
|
||||||
import Clipboard from 'react-clipboard.js'
|
import Clipboard from 'react-clipboard.js'
|
||||||
|
|
||||||
export default function Copy({ text }: { text: string }): ReactElement {
|
export interface CopyProps {
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Copy({ text }: CopyProps): ReactElement {
|
||||||
const [isCopied, setIsCopied] = useState(false)
|
const [isCopied, setIsCopied] = useState(false)
|
||||||
|
|
||||||
// Clear copy success style after 5 sec.
|
// Clear copy success style after 5 sec.
|
45
src/components/@shared/atoms/Lists/index.stories.tsx
Normal file
45
src/components/@shared/atoms/Lists/index.stories.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import { ListItem } from '@shared/atoms/Lists'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Lists',
|
||||||
|
component: ListItem
|
||||||
|
} as ComponentMeta<typeof ListItem>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof ListItem> = (args) => (
|
||||||
|
<ListItem {...args} />
|
||||||
|
)
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
'List item short',
|
||||||
|
'List item long ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie',
|
||||||
|
'List item long ipsum dolor sit amet, consectetur adipiscing elit',
|
||||||
|
'List item short',
|
||||||
|
'List item long ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie',
|
||||||
|
'List item long ipsum dolor sit amet, consectetur adipiscing elit'
|
||||||
|
]
|
||||||
|
|
||||||
|
export const Unordered = Template.bind({})
|
||||||
|
Unordered.decorators = [
|
||||||
|
() => (
|
||||||
|
<ul>
|
||||||
|
{items.map((item, key) => (
|
||||||
|
<Template key={key}>{item}</Template>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
export const Ordered = Template.bind({})
|
||||||
|
Ordered.decorators = [
|
||||||
|
() => (
|
||||||
|
<ol>
|
||||||
|
{items.map((item, key) => (
|
||||||
|
<Template ol key={key}>
|
||||||
|
{item}
|
||||||
|
</Template>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
)
|
||||||
|
]
|
@ -1,13 +1,12 @@
|
|||||||
import React, { ReactElement, ReactNode } from 'react'
|
import React, { ReactElement, ReactNode } from 'react'
|
||||||
import styles from './Lists.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
export function ListItem({
|
export interface ListItemProps {
|
||||||
children,
|
children?: ReactNode
|
||||||
ol
|
|
||||||
}: {
|
|
||||||
children: ReactNode
|
|
||||||
ol?: boolean
|
ol?: boolean
|
||||||
}): ReactElement {
|
}
|
||||||
|
|
||||||
|
export function ListItem({ children, ol }: ListItemProps): ReactElement {
|
||||||
const classes = ol
|
const classes = ol
|
||||||
? `${styles.item} ${styles.olItem}`
|
? `${styles.item} ${styles.olItem}`
|
||||||
: `${styles.item} ${styles.ulItem}`
|
: `${styles.item} ${styles.ulItem}`
|
23
src/components/@shared/atoms/Loader/index.stories.tsx
Normal file
23
src/components/@shared/atoms/Loader/index.stories.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
|
||||||
|
import Loader, { LoaderProps } from '@shared/atoms/Loader'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Loader',
|
||||||
|
component: Loader
|
||||||
|
} as ComponentMeta<typeof Loader>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Loader> = (args) => <Loader {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: LoaderProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {}
|
||||||
|
|
||||||
|
export const WithMessage: Props = Template.bind({})
|
||||||
|
WithMessage.args = {
|
||||||
|
message: 'Loading...'
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Loader.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
export default function Loader({
|
export interface LoaderProps {
|
||||||
message
|
|
||||||
}: {
|
|
||||||
message?: string
|
message?: string
|
||||||
}): ReactElement {
|
}
|
||||||
|
|
||||||
|
export default function Loader({ message }: LoaderProps): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div className={styles.loaderWrap}>
|
<div className={styles.loaderWrap}>
|
||||||
<span className={styles.loader} />
|
<span className={styles.loader} />
|
@ -14,7 +14,10 @@ interface Props {
|
|||||||
args: LogoProps
|
args: LogoProps
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Primary: Props = Template.bind({})
|
export const Default: Props = Template.bind({})
|
||||||
Primary.args = {
|
Default.args = {}
|
||||||
|
|
||||||
|
export const WithoutWordmark: Props = Template.bind({})
|
||||||
|
WithoutWordmark.args = {
|
||||||
noWordmark: true
|
noWordmark: true
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
composes: box from './Box.module.css';
|
composes: box from '../Box.module.css';
|
||||||
padding: var(--spacer);
|
padding: var(--spacer);
|
||||||
margin: var(--spacer) auto;
|
margin: var(--spacer) auto;
|
||||||
max-width: var(--break-point--small);
|
max-width: var(--break-point--small);
|
32
src/components/@shared/atoms/Modal/index.stories.tsx
Normal file
32
src/components/@shared/atoms/Modal/index.stories.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import Button from '@shared/atoms/Button'
|
||||||
|
import Modal, { ModalProps } from '@shared/atoms/Modal'
|
||||||
|
import { useArgs } from '@storybook/client-api'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Modal',
|
||||||
|
component: Modal
|
||||||
|
} as ComponentMeta<typeof Modal>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Modal> = (args: ModalProps) => {
|
||||||
|
const [{ isOpen }, updateArgs] = useArgs()
|
||||||
|
const handleClose = () => updateArgs({ isOpen: !isOpen })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button style="primary" onClick={() => updateArgs({ isOpen: !isOpen })}>
|
||||||
|
Open Modal
|
||||||
|
</Button>
|
||||||
|
<Modal {...args} onToggleModal={handleClose}>
|
||||||
|
<a>This is a modal</a>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: ModalProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement, ReactNode } from 'react'
|
import React, { ReactElement, ReactNode } from 'react'
|
||||||
import ReactModal from 'react-modal'
|
import ReactModal from 'react-modal'
|
||||||
import styles from './Modal.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'test') ReactModal.setAppElement('#__next')
|
if (process.env.NODE_ENV !== 'test') ReactModal.setAppElement('#__next')
|
||||||
|
|
28
src/components/@shared/atoms/Status/index.stories.tsx
Normal file
28
src/components/@shared/atoms/Status/index.stories.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
|
||||||
|
import Status, { StatusProps } from '@shared/atoms/Status'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Status',
|
||||||
|
component: Status
|
||||||
|
} as ComponentMeta<typeof Status>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Status> = (args) => <Status {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: StatusProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {}
|
||||||
|
|
||||||
|
export const Warning: Props = Template.bind({})
|
||||||
|
Warning.args = {
|
||||||
|
state: 'warning'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Error: Props = Template.bind({})
|
||||||
|
Error.args = {
|
||||||
|
state: 'error'
|
||||||
|
}
|
@ -1,16 +1,18 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import styles from './Status.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
|
export interface StatusProps {
|
||||||
|
state?: string
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
export default function Status({
|
export default function Status({
|
||||||
state,
|
state,
|
||||||
className
|
className
|
||||||
}: {
|
}: StatusProps): ReactElement {
|
||||||
state?: string
|
|
||||||
className?: string
|
|
||||||
}): ReactElement {
|
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
status: true,
|
status: true,
|
||||||
warning: state === 'warning',
|
warning: state === 'warning',
|
82
src/components/@shared/atoms/Table/index.stories.tsx
Normal file
82
src/components/@shared/atoms/Table/index.stories.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import Table, { TableProps } from '@shared/atoms/Table'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Table',
|
||||||
|
component: Table
|
||||||
|
} as ComponentMeta<typeof Table>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Table> = (args) => <Table {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: TableProps
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Name',
|
||||||
|
selector: (row: any) => row.name,
|
||||||
|
maxWidth: '45rem',
|
||||||
|
grow: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Symbol',
|
||||||
|
selector: (row: any) => row.symbol,
|
||||||
|
maxWidth: '10rem'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Price',
|
||||||
|
selector: (row: any) => row.price,
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
name: 'Title asset',
|
||||||
|
symbol: 'DATA-70',
|
||||||
|
price: '1.011'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Title asset Title asset Title asset Title asset Title asset',
|
||||||
|
symbol: 'DATA-71',
|
||||||
|
price: '1.011'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Title asset',
|
||||||
|
symbol: 'DATA-72',
|
||||||
|
price: '1.011'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset Title asset',
|
||||||
|
symbol: 'DATA-71',
|
||||||
|
price: '1.011'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const WithData: Props = Template.bind({})
|
||||||
|
WithData.args = {
|
||||||
|
columns,
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithPagination: Props = Template.bind({})
|
||||||
|
WithPagination.args = {
|
||||||
|
columns,
|
||||||
|
data: data.flatMap((i) => [i, i, i])
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Loading: Props = Template.bind({})
|
||||||
|
Loading.args = {
|
||||||
|
isLoading: true,
|
||||||
|
columns: [],
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Empty: Props = Template.bind({})
|
||||||
|
Empty.args = {
|
||||||
|
emptyMessage: 'I am empty',
|
||||||
|
columns: [],
|
||||||
|
data: []
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
import React, { ReactElement, ReactNode } from 'react'
|
import React, { ReactElement, ReactNode } from 'react'
|
||||||
import DataTable, { IDataTableProps } from 'react-data-table-component'
|
import DataTable, { IDataTableProps } from 'react-data-table-component'
|
||||||
import Loader from './Loader'
|
import Loader from '../Loader'
|
||||||
import Pagination from '@shared/Pagination'
|
import Pagination from '@shared/Pagination'
|
||||||
import styles from './Table.module.css'
|
import styles from './index.module.css'
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
|
||||||
|
|
||||||
interface TableProps extends IDataTableProps {
|
export interface TableProps extends IDataTableProps {
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
emptyMessage?: string
|
emptyMessage?: string
|
||||||
sortField?: string
|
sortField?: string
|
||||||
@ -14,14 +13,7 @@ interface TableProps extends IDataTableProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Empty({ message }: { message?: string }): ReactElement {
|
function Empty({ message }: { message?: string }): ReactElement {
|
||||||
const { chainIds } = useUserPreferences()
|
return <div className={styles.empty}>{message || 'No results found'}</div>
|
||||||
return (
|
|
||||||
<div className={styles.empty}>
|
|
||||||
{chainIds.length === 0
|
|
||||||
? 'No network selected'
|
|
||||||
: message || 'No results found'}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Table({
|
export default function Table({
|
@ -8,11 +8,10 @@
|
|||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: calc(var(--spacer) / 12) var(--spacer);
|
padding: calc(var(--spacer) / 8) var(--spacer);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
cursor: pointer;
|
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
background-color: var(--background-body);
|
background-color: var(--background-body);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
@ -20,6 +19,11 @@
|
|||||||
min-width: 90px;
|
min-width: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab,
|
||||||
|
.tab label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.tab:first-child {
|
.tab:first-child {
|
||||||
border-top-left-radius: var(--border-radius);
|
border-top-left-radius: var(--border-radius);
|
||||||
border-bottom-left-radius: var(--border-radius);
|
border-bottom-left-radius: var(--border-radius);
|
||||||
@ -53,3 +57,7 @@
|
|||||||
padding: var(--spacer);
|
padding: var(--spacer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radio {
|
||||||
|
composes: radio from '../../FormInput/InputRadio.module.css';
|
||||||
|
}
|
52
src/components/@shared/atoms/Tabs/index.stories.tsx
Normal file
52
src/components/@shared/atoms/Tabs/index.stories.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
|
||||||
|
import Tabs, { TabsProps } from '@shared/atoms/Tabs'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Tabs',
|
||||||
|
component: Tabs
|
||||||
|
} as ComponentMeta<typeof Tabs>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Tabs> = (args) => <Tabs {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: TabsProps
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
title: 'First tab',
|
||||||
|
content: 'this is the content for the first tab'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Second tab',
|
||||||
|
content: 'this is the content for the second tab'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Third tab',
|
||||||
|
content: 'this is the content for the third tab'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const Default = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithRadio: Props = Template.bind({})
|
||||||
|
WithRadio.args = {
|
||||||
|
items,
|
||||||
|
showRadio: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithDefaultIndex: Props = Template.bind({})
|
||||||
|
WithDefaultIndex.args = {
|
||||||
|
items,
|
||||||
|
defaultIndex: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LotsOfTabs: Props = Template.bind({})
|
||||||
|
LotsOfTabs.args = {
|
||||||
|
items: items.flatMap((i) => [i, i, i])
|
||||||
|
}
|
21
src/components/@shared/atoms/Tabs/index.test.tsx
Normal file
21
src/components/@shared/atoms/Tabs/index.test.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react'
|
||||||
|
import { Default } from './index.stories'
|
||||||
|
|
||||||
|
describe('Tabs', () => {
|
||||||
|
test('should be able to change', async () => {
|
||||||
|
render(<Default {...Default.args} />)
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByText('Second tab'))
|
||||||
|
const secondTab = await screen.findByText(/content for the second tab/i)
|
||||||
|
expect(secondTab).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should fire custom change handler', async () => {
|
||||||
|
const handler = jest.fn()
|
||||||
|
render(<Default {...Default.args} handleTabChange={handler} />)
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByText('Second tab'))
|
||||||
|
expect(handler).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
@ -1,7 +1,7 @@
|
|||||||
import React, { ReactElement, ReactNode } from 'react'
|
import React, { ReactElement, ReactNode, useState } from 'react'
|
||||||
import { Tab, Tabs as ReactTabs, TabList, TabPanel } from 'react-tabs'
|
import { Tab, Tabs as ReactTabs, TabList, TabPanel } from 'react-tabs'
|
||||||
import InputElement from '@shared/FormInput/InputElement'
|
import styles from './index.module.css'
|
||||||
import styles from './Tabs.module.css'
|
import InputRadio from '@shared/FormInput/InputRadio'
|
||||||
|
|
||||||
export interface TabsItem {
|
export interface TabsItem {
|
||||||
title: string
|
title: string
|
||||||
@ -9,37 +9,36 @@ export interface TabsItem {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TabsProps {
|
||||||
|
items: TabsItem[]
|
||||||
|
className?: string
|
||||||
|
handleTabChange?: (tabName: string) => void
|
||||||
|
defaultIndex?: number
|
||||||
|
showRadio?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export default function Tabs({
|
export default function Tabs({
|
||||||
items,
|
items,
|
||||||
className,
|
className,
|
||||||
handleTabChange,
|
handleTabChange,
|
||||||
defaultIndex,
|
defaultIndex,
|
||||||
showRadio
|
showRadio
|
||||||
}: {
|
}: TabsProps): ReactElement {
|
||||||
items: TabsItem[]
|
|
||||||
className?: string
|
|
||||||
handleTabChange?: (tabName: string) => void
|
|
||||||
defaultIndex?: number
|
|
||||||
showRadio?: boolean
|
|
||||||
}): ReactElement {
|
|
||||||
return (
|
return (
|
||||||
<ReactTabs
|
<ReactTabs className={`${className || ''}`} defaultIndex={defaultIndex}>
|
||||||
className={`${className && className}`}
|
|
||||||
defaultIndex={defaultIndex}
|
|
||||||
>
|
|
||||||
<TabList className={styles.tabList}>
|
<TabList className={styles.tabList}>
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<Tab
|
<Tab
|
||||||
className={styles.tab}
|
className={styles.tab}
|
||||||
key={item.title}
|
key={index}
|
||||||
onClick={handleTabChange ? () => handleTabChange(item.title) : null}
|
onClick={handleTabChange ? () => handleTabChange(item.title) : null}
|
||||||
disabled={item.disabled}
|
disabled={item.disabled}
|
||||||
>
|
>
|
||||||
{showRadio ? (
|
{showRadio ? (
|
||||||
<InputElement
|
<InputRadio
|
||||||
name={item.title}
|
name={item.title}
|
||||||
type="radio"
|
type="radio"
|
||||||
checked={defaultIndex === index}
|
checked={index === defaultIndex}
|
||||||
options={[item.title]}
|
options={[item.title]}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
@ -50,8 +49,8 @@ export default function Tabs({
|
|||||||
))}
|
))}
|
||||||
</TabList>
|
</TabList>
|
||||||
<div className={styles.tabContent}>
|
<div className={styles.tabContent}>
|
||||||
{items.map((item) => (
|
{items.map((item, index) => (
|
||||||
<TabPanel key={item.title}>{item.content}</TabPanel>
|
<TabPanel key={index}>{item.content}</TabPanel>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ReactTabs>
|
</ReactTabs>
|
40
src/components/@shared/atoms/Tags/index.stories.tsx
Normal file
40
src/components/@shared/atoms/Tags/index.stories.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
|
||||||
|
import Tags, { TagsProps } from '@shared/atoms/Tags'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Tags',
|
||||||
|
component: Tags
|
||||||
|
} as ComponentMeta<typeof Tags>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Tags> = (args) => <Tags {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: TagsProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
items: [' tag1 ', ' tag2 ', ' tag3 '],
|
||||||
|
className: 'custom-class'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MaxNumberOfTags: Props = Template.bind({})
|
||||||
|
MaxNumberOfTags.args = {
|
||||||
|
items: [' tag1 ', ' tag2 ', ' tag3 '],
|
||||||
|
max: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ShowMore: Props = Template.bind({})
|
||||||
|
ShowMore.args = {
|
||||||
|
items: [' tag1 ', ' tag2 ', ' tag3 '],
|
||||||
|
max: 2,
|
||||||
|
showMore: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithoutLinks: Props = Template.bind({})
|
||||||
|
WithoutLinks.args = {
|
||||||
|
items: [' tag1 ', ' tag2 ', ' tag3 '],
|
||||||
|
noLinks: true
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import styles from './Tags.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
declare type TagsProps = {
|
export interface TagsProps {
|
||||||
items: string[]
|
items: string[]
|
||||||
max?: number
|
max?: number
|
||||||
showMore?: boolean
|
showMore?: boolean
|
37
src/components/@shared/atoms/Time/index.stories.tsx
Normal file
37
src/components/@shared/atoms/Time/index.stories.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
|
||||||
|
import Time, { TimeProps } from '@shared/atoms/Time'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Time',
|
||||||
|
component: Time
|
||||||
|
} as ComponentMeta<typeof Time>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Time> = (args) => <Time {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: TimeProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
date: '2022-05-02T11:50:28.000Z'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Relative: Props = Template.bind({})
|
||||||
|
Relative.args = {
|
||||||
|
date: '2022-05-02T11:50:28.000Z',
|
||||||
|
relative: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IsUnix: Props = Template.bind({})
|
||||||
|
IsUnix.args = {
|
||||||
|
date: '1652448367',
|
||||||
|
isUnix: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Undefined: Props = Template.bind({})
|
||||||
|
Undefined.args = {
|
||||||
|
date: null
|
||||||
|
}
|
@ -1,19 +1,21 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { format, formatDistance } from 'date-fns'
|
import { format, formatDistance } from 'date-fns'
|
||||||
|
|
||||||
|
export interface TimeProps {
|
||||||
|
date: string
|
||||||
|
relative?: boolean
|
||||||
|
isUnix?: boolean
|
||||||
|
displayFormat?: string
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
export default function Time({
|
export default function Time({
|
||||||
date,
|
date,
|
||||||
relative,
|
relative,
|
||||||
isUnix,
|
isUnix,
|
||||||
displayFormat,
|
displayFormat,
|
||||||
className
|
className
|
||||||
}: {
|
}: TimeProps): ReactElement {
|
||||||
date: string
|
|
||||||
relative?: boolean
|
|
||||||
isUnix?: boolean
|
|
||||||
displayFormat?: string
|
|
||||||
className?: string
|
|
||||||
}): ReactElement {
|
|
||||||
const [dateIso, setDateIso] = useState<string>()
|
const [dateIso, setDateIso] = useState<string>()
|
||||||
const [dateNew, setDateNew] = useState<Date>()
|
const [dateNew, setDateNew] = useState<Date>()
|
||||||
|
|
@ -3,7 +3,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
composes: box from './Box.module.css';
|
composes: box from '../Box.module.css';
|
||||||
padding: calc(var(--spacer) / 4);
|
padding: calc(var(--spacer) / 4);
|
||||||
max-width: 25rem;
|
max-width: 25rem;
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
51
src/components/@shared/atoms/Tooltip/index.stories.tsx
Normal file
51
src/components/@shared/atoms/Tooltip/index.stories.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||||
|
import { TippyProps } from '@tippyjs/react'
|
||||||
|
import Tooltip from '@shared/atoms/Tooltip'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Component/@shared/atoms/Tooltip',
|
||||||
|
component: Tooltip
|
||||||
|
} as ComponentMeta<typeof Tooltip>
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Tooltip> = (args) => <Tooltip {...args} />
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
args: TippyProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Default: Props = Template.bind({})
|
||||||
|
Default.args = {
|
||||||
|
content:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie.'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithContentOpened: Props = Template.bind({})
|
||||||
|
WithContentOpened.args = {
|
||||||
|
content:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie.',
|
||||||
|
showOnCreate: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithCustomTriggerElement: Props = Template.bind({})
|
||||||
|
WithCustomTriggerElement.args = {
|
||||||
|
content:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie.',
|
||||||
|
children: <a>Tooltip trigger</a>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithCustomTriggerEvent: Props = Template.bind({})
|
||||||
|
WithCustomTriggerEvent.args = {
|
||||||
|
content:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie.',
|
||||||
|
children: <button>Click here</button>,
|
||||||
|
trigger: 'on click'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Disabled: Props = Template.bind({})
|
||||||
|
Disabled.args = {
|
||||||
|
content:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam facilisis molestie.',
|
||||||
|
children: <a>Tooltip disabled</a>,
|
||||||
|
disabled: true
|
||||||
|
}
|
@ -1,14 +1,8 @@
|
|||||||
import React, { ReactElement, ReactNode } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import loadable from '@loadable/component'
|
|
||||||
import { useSpring, animated } from 'react-spring'
|
import { useSpring, animated } from 'react-spring'
|
||||||
import styles from './Tooltip.module.css'
|
import stylesTooltip from './index.module.css'
|
||||||
import Info from '@images/info.svg'
|
import Info from '@images/info.svg'
|
||||||
import { Placement } from 'tippy.js'
|
import Tippy, { TippyProps } from '@tippyjs/react/headless'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
const Tippy = loadable(() => import('@tippyjs/react/headless'))
|
|
||||||
|
|
||||||
const animation = {
|
const animation = {
|
||||||
config: { tension: 400, friction: 20 },
|
config: { tension: 400, friction: 20 },
|
||||||
@ -19,28 +13,15 @@ const animation = {
|
|||||||
// Forward ref for Tippy.js
|
// Forward ref for Tippy.js
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const DefaultTrigger = React.forwardRef((props, ref: any) => {
|
const DefaultTrigger = React.forwardRef((props, ref: any) => {
|
||||||
return <Info className={styles.icon} ref={ref} />
|
return <Info className={stylesTooltip.icon} ref={ref} />
|
||||||
})
|
})
|
||||||
|
|
||||||
export default function Tooltip({
|
export default function Tooltip(props: TippyProps): ReactElement {
|
||||||
content,
|
const { content, children, trigger, disabled, className, placement } = props
|
||||||
children,
|
const [styles, api] = useSpring(() => animation.from)
|
||||||
trigger,
|
|
||||||
disabled,
|
|
||||||
className,
|
|
||||||
placement
|
|
||||||
}: {
|
|
||||||
content: ReactNode
|
|
||||||
children?: ReactNode
|
|
||||||
trigger?: string
|
|
||||||
disabled?: boolean
|
|
||||||
className?: string
|
|
||||||
placement?: Placement
|
|
||||||
}): ReactElement {
|
|
||||||
const [props, setSpring] = useSpring(() => animation.from)
|
|
||||||
|
|
||||||
function onMount() {
|
function onMount() {
|
||||||
setSpring({
|
api.start({
|
||||||
...animation.to,
|
...animation.to,
|
||||||
onRest: (): void => null,
|
onRest: (): void => null,
|
||||||
config: animation.config
|
config: animation.config
|
||||||
@ -48,17 +29,14 @@ export default function Tooltip({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onHide({ unmount }: { unmount: () => void }) {
|
function onHide({ unmount }: { unmount: () => void }) {
|
||||||
setSpring({
|
api.start({
|
||||||
...animation.from,
|
...animation.from,
|
||||||
onRest: unmount,
|
onRest: unmount,
|
||||||
config: { ...animation.config, clamp: true }
|
config: { ...animation.config, clamp: true }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const styleClasses = cx({
|
const styleClasses = `${stylesTooltip.tooltip} ${className || ''}`
|
||||||
tooltip: true,
|
|
||||||
[className]: className
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tippy
|
<Tippy
|
||||||
@ -68,23 +46,21 @@ export default function Tooltip({
|
|||||||
trigger={trigger || 'mouseenter focus'}
|
trigger={trigger || 'mouseenter focus'}
|
||||||
disabled={disabled || null}
|
disabled={disabled || null}
|
||||||
placement={placement || 'auto'}
|
placement={placement || 'auto'}
|
||||||
render={(attrs: any) => (
|
render={(attrs) => (
|
||||||
<animated.div style={props}>
|
<animated.div style={styles}>
|
||||||
<div className={styles.content} {...attrs}>
|
<div className={stylesTooltip.content} {...attrs}>
|
||||||
{content}
|
{content}
|
||||||
<div className={styles.arrow} data-popper-arrow />
|
<div className={stylesTooltip.arrow} data-popper-arrow />
|
||||||
</div>
|
</div>
|
||||||
</animated.div>
|
</animated.div>
|
||||||
)}
|
)}
|
||||||
appendTo={
|
appendTo={
|
||||||
typeof document !== 'undefined' && document.querySelector('body')
|
typeof document !== 'undefined' && document.querySelector('body')
|
||||||
}
|
}
|
||||||
animation
|
|
||||||
onMount={onMount}
|
onMount={onMount}
|
||||||
onHide={onHide}
|
onHide={onHide}
|
||||||
fallback={
|
// animation
|
||||||
<div className={styleClasses}>{children || <DefaultTrigger />}</div>
|
{...props}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div className={styleClasses}>{children || <DefaultTrigger />}</div>
|
<div className={styleClasses}>{children || <DefaultTrigger />}</div>
|
||||||
</Tippy>
|
</Tippy>
|
@ -1,5 +1,5 @@
|
|||||||
.radioWrap {
|
.radioWrap {
|
||||||
composes: radioWrap from '@shared/FormInput/InputElement.module.css';
|
composes: radioWrap from '@shared/FormInput/InputRadio.module.css';
|
||||||
padding: calc(var(--spacer) / 6) calc(var(--spacer) / 3);
|
padding: calc(var(--spacer) / 6) calc(var(--spacer) / 3);
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
@ -9,14 +9,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
composes: checkbox from '@shared/FormInput/InputElement.module.css';
|
composes: checkbox from '@shared/FormInput/InputRadio.module.css';
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
margin-right: calc(var(--spacer) / 3);
|
margin-right: calc(var(--spacer) / 3);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radioLabel {
|
.radioLabel {
|
||||||
composes: radioLabel from '@shared/FormInput/InputElement.module.css';
|
composes: radioLabel from '@shared/FormInput/InputRadio.module.css';
|
||||||
font-weight: var(--font-weight-base);
|
font-weight: var(--font-weight-base);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -46,6 +46,7 @@ export default function Networks(): ReactElement {
|
|||||||
trigger="click focus"
|
trigger="click focus"
|
||||||
className={`${stylesIndex.preferences} ${styles.networks}`}
|
className={`${stylesIndex.preferences} ${styles.networks}`}
|
||||||
>
|
>
|
||||||
|
<>
|
||||||
<Network aria-label="Networks" className={stylesIndex.icon} />
|
<Network aria-label="Networks" className={stylesIndex.icon} />
|
||||||
<Caret aria-hidden="true" className={stylesIndex.caret} />
|
<Caret aria-hidden="true" className={stylesIndex.caret} />
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ export default function Networks(): ReactElement {
|
|||||||
<span className={styles.chainsSelectedIndicator} key={chainId} />
|
<span className={styles.chainsSelectedIndicator} key={chainId} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,10 @@ export default function UserPreferences(): ReactElement {
|
|||||||
trigger="click focus"
|
trigger="click focus"
|
||||||
className={styles.preferences}
|
className={styles.preferences}
|
||||||
>
|
>
|
||||||
|
<>
|
||||||
<Cog aria-label="Preferences" className={styles.icon} />
|
<Cog aria-label="Preferences" className={styles.icon} />
|
||||||
<Caret aria-hidden="true" className={styles.caret} />
|
<Caret aria-hidden="true" className={styles.caret} />
|
||||||
|
</>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ const columns = [
|
|||||||
selector: function getAssetRow(row: AssetExtended) {
|
selector: function getAssetRow(row: AssetExtended) {
|
||||||
return (
|
return (
|
||||||
<Tooltip content={row.datatokens[0].name}>
|
<Tooltip content={row.datatokens[0].name}>
|
||||||
{row.datatokens[0].symbol}
|
<>{row.datatokens[0].symbol}</>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -96,7 +96,11 @@ export default function Bookmarks(): ReactElement {
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
data={pinned}
|
data={pinned}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
emptyMessage="Your bookmarks will appear here."
|
emptyMessage={
|
||||||
|
chainIds.length === 0
|
||||||
|
? 'No network selected'
|
||||||
|
: 'Your bookmarks will appear here.'
|
||||||
|
}
|
||||||
noTableHead
|
noTableHead
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -123,6 +123,7 @@ export default function ComputeJobs({
|
|||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
defaultSortField="row.dateCreated"
|
defaultSortField="row.dateCreated"
|
||||||
defaultSortAsc={false}
|
defaultSortAsc={false}
|
||||||
|
emptyMessage={chainIds.length === 0 ? 'No network selected' : null}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
@ -4,7 +4,7 @@ import Time from '@shared/atoms/Time'
|
|||||||
import AssetTitle from '@shared/AssetList/AssetListTitle'
|
import AssetTitle from '@shared/AssetList/AssetListTitle'
|
||||||
import NetworkName from '@shared/NetworkName'
|
import NetworkName from '@shared/NetworkName'
|
||||||
import { useProfile } from '@context/Profile'
|
import { useProfile } from '@context/Profile'
|
||||||
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'Data Set',
|
name: 'Data Set',
|
||||||
@ -38,6 +38,7 @@ export default function ComputeDownloads({
|
|||||||
accountId: string
|
accountId: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { downloads, isDownloadsLoading } = useProfile()
|
const { downloads, isDownloadsLoading } = useProfile()
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
|
||||||
return accountId ? (
|
return accountId ? (
|
||||||
<Table
|
<Table
|
||||||
@ -45,6 +46,7 @@ export default function ComputeDownloads({
|
|||||||
data={downloads}
|
data={downloads}
|
||||||
paginationPerPage={10}
|
paginationPerPage={10}
|
||||||
isLoading={isDownloadsLoading}
|
isLoading={isDownloadsLoading}
|
||||||
|
emptyMessage={chainIds.length === 0 ? 'No network selected' : null}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div>Please connect your Web3 wallet.</div>
|
<div>Please connect your Web3 wallet.</div>
|
||||||
|
@ -109,6 +109,7 @@ export default function PoolShares({
|
|||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
sortField="userLiquidity"
|
sortField="userLiquidity"
|
||||||
sortAsc={false}
|
sortAsc={false}
|
||||||
|
emptyMessage={chainIds.length === 0 ? 'No network selected' : null}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div>Please connect your Web3 wallet.</div>
|
<div>Please connect your Web3 wallet.</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user