Merge branch 'main' into menu-lightning-label

This commit is contained in:
Max Alekseenko
2024-05-27 15:09:39 +02:00
committed by GitHub
680 changed files with 3838 additions and 4379 deletions
+1
View File
@@ -4,6 +4,7 @@ const RESTRICTED_MODULES = {
{ name: '@chakra-ui/icons', message: 'Using @chakra-ui/icons is prohibited. Please use regular svg-icon instead (see examples in "icons/" folder)' },
{ name: '@metamask/providers', message: 'Please lazy-load @metamask/providers or use useProvider hook instead' },
{ name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' },
{ name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' },
],
patterns: [
'icons/*',
+21
View File
@@ -0,0 +1,21 @@
changelog:
categories:
- title: 🚀 New Features
labels:
- client feature
- feature
- title: 🐛 Bug Fixes
labels:
- bug
- title: ⚡ Performance Improvements
labels:
- performance
- title: 📦 Dependencies Updates
labels:
- dependencies
- title: 🚨 Changes in ENV variables
labels:
- ENVs
- title: ✨ Other Changes
labels:
- "*"
+116
View File
@@ -0,0 +1,116 @@
name: Copy issues labels to pull request
on:
workflow_dispatch:
inputs:
pr_number:
description: Pull request number
required: true
type: string
issues:
description: JSON encoded list of issue ids
required: true
type: string
workflow_call:
inputs:
pr_number:
description: Pull request number
required: true
type: string
issues:
description: JSON encoded list of issue ids
required: true
type: string
jobs:
run:
name: Run
runs-on: ubuntu-latest
steps:
- name: Find unique labels
id: find_unique_labels
uses: actions/github-script@v7
env:
ISSUES: ${{ inputs.issues }}
with:
script: |
const issues = JSON.parse(process.env.ISSUES);
const WHITE_LISTED_LABELS = [
'client feature',
'feature',
'bug',
'dependencies',
'performance',
'chore',
'enhancement',
'refactoring',
'tech',
'ENVs',
]
const labels = await Promise.all(issues.map(getIssueLabels));
const uniqueLabels = uniqueStringArray(labels.flat().filter((label) => WHITE_LISTED_LABELS.includes(label)));
if (uniqueLabels.length === 0) {
core.info('No labels found.\n');
return [];
}
core.info(`Found following labels: ${ uniqueLabels.join(', ') }.\n`);
return uniqueLabels;
async function getIssueLabels(issue) {
core.info(`Obtaining labels list for the issue #${ issue }...`);
try {
const response = await github.request('GET /repos/{owner}/{repo}/issues/{issue_number}/labels', {
owner: 'blockscout',
repo: 'frontend',
issue_number: issue,
});
return response.data.map(({ name }) => name);
} catch (error) {
core.error(`Failed to obtain labels for the issue #${ issue }: ${ error.message }`);
return [];
}
}
function uniqueStringArray(array) {
return Array.from(new Set(array));
}
- name: Update pull request labels
id: update_pr_labels
uses: actions/github-script@v7
env:
LABELS: ${{ steps.find_unique_labels.outputs.result }}
PR_NUMBER: ${{ inputs.pr_number }}
with:
script: |
const labels = JSON.parse(process.env.LABELS);
const prNumber = Number(process.env.PR_NUMBER);
if (labels.length === 0) {
core.info('Nothing to update.\n');
return;
}
for (const label of labels) {
await addLabelToPr(prNumber, label);
}
core.info('Done.\n');
async function addLabelToPr(prNumber, label) {
console.log(`Adding label to the pull request #${ prNumber }...`);
return await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/labels', {
owner: 'blockscout',
repo: 'frontend',
issue_number: prNumber,
labels: [ label ],
});
}
+3
View File
@@ -55,7 +55,10 @@ jobs:
label_description: Tasks in pre-release right now
secrets: inherit
# Temporary disable this step because it is broken
# There is an issue with building web3modal deps
upload_source_maps:
name: Upload source maps to Sentry
if: false
uses: './.github/workflows/upload-source-maps.yml'
secrets: inherit
+15 -5
View File
@@ -28,7 +28,7 @@ jobs:
issues: "[${{ github.event.issue.number }}]"
secrets: inherit
review_requested_issues:
pr_linked_issues:
name: Get issues linked to PR
runs-on: ubuntu-latest
if: ${{ github.event.pull_request && github.event.action == 'review_requested' }}
@@ -76,14 +76,24 @@ jobs:
return issues;
review_requested_tasks:
issues_in_review:
name: Update status for issues in review
needs: [ review_requested_issues ]
if: ${{ needs.review_requested_issues.outputs.issues }}
needs: [ pr_linked_issues ]
if: ${{ needs.pr_linked_issues.outputs.issues }}
uses: './.github/workflows/update-project-cards.yml'
secrets: inherit
with:
project_name: ${{ vars.PROJECT_NAME }}
field_name: Status
field_value: Review
issues: ${{ needs.review_requested_issues.outputs.issues }}
issues: ${{ needs.pr_linked_issues.outputs.issues }}
copy_labels:
name: Copy issues labels to pull request
needs: [ pr_linked_issues ]
if: ${{ needs.pr_linked_issues.outputs.issues }}
uses: './.github/workflows/copy-issues-labels.yml'
secrets: inherit
with:
pr_number: ${{ github.event.pull_request.number }}
issues: ${{ needs.pr_linked_issues.outputs.issues }}
+3
View File
@@ -81,8 +81,11 @@ jobs:
with:
platforms: linux/amd64,linux/arm64/v8
# Temporary disable this step because it is broken
# There is an issue with building web3modal deps
upload_source_maps:
name: Upload source maps to Sentry
if: false
needs: publish_image
uses: './.github/workflows/upload-source-maps.yml'
secrets: inherit
+2 -2
View File
@@ -12,8 +12,8 @@
# next.js
/.next/
/out/
/public/assets/
/public/envs.js
/public/assets/envs.js
/public/assets/configs
/public/icons/sprite.svg
/public/icons/README.md
/analyze
+4 -2
View File
@@ -34,11 +34,13 @@ RUN yarn --frozen-lockfile
FROM node:20.11.0-alpine AS builder
RUN apk add --no-cache --upgrade libc6-compat bash
# pass commit sha and git tag to the app image
# pass build args to env variables
ARG GIT_COMMIT_SHA
ENV NEXT_PUBLIC_GIT_COMMIT_SHA=$GIT_COMMIT_SHA
ARG GIT_TAG
ENV NEXT_PUBLIC_GIT_TAG=$GIT_TAG
ARG NEXT_OPEN_TELEMETRY_ENABLED
ENV NEXT_OPEN_TELEMETRY_ENABLED=$NEXT_OPEN_TELEMETRY_ENABLED
ENV NODE_ENV production
@@ -58,8 +60,8 @@ RUN ./collect_envs.sh ./docs/ENVS.md
# ENV NEXT_TELEMETRY_DISABLED 1
# Build app for production
RUN yarn build
RUN yarn svg:build-sprite
RUN yarn build
### FEATURE REPORTER
+1
View File
@@ -15,6 +15,7 @@ export { default as growthBook } from './growthBook';
export { default as marketplace } from './marketplace';
export { default as metasuites } from './metasuites';
export { default as mixpanel } from './mixpanel';
export { default as multichainButton } from './multichainButton';
export { default as nameService } from './nameService';
export { default as restApiDocs } from './restApiDocs';
export { default as rollup } from './rollup';
+29
View File
@@ -0,0 +1,29 @@
import type { Feature } from './types';
import type { MultichainProviderConfig } from 'types/client/multichainProviderConfig';
import { getEnvValue, parseEnvJson } from '../utils';
import marketplace from './marketplace';
const value = parseEnvJson<MultichainProviderConfig>(getEnvValue('NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG'));
const title = 'Multichain balance';
const config: Feature<{name: string; logoUrl?: string; urlTemplate: string; dappId?: string }> = (() => {
if (value) {
return Object.freeze({
title,
isEnabled: true,
name: value.name,
logoUrl: value.logo,
urlTemplate: value.url_template,
dappId: marketplace.isEnabled ? value.dapp_id : undefined,
});
}
return Object.freeze({
title,
isEnabled: false,
});
})();
export default config;
+1 -1
View File
@@ -41,7 +41,7 @@ export const buildExternalAssetFilePath = (name: string, value: string) => {
const fileName = name.replace(/^NEXT_PUBLIC_/, '').replace(/_URL$/, '').toLowerCase();
const url = new URL(value);
const fileExtension = url.pathname.match(regexp.FILE_EXTENSION)?.[1];
return `/assets/${ fileName }.${ fileExtension }`;
return `/assets/configs/${ fileName }.${ fileExtension }`;
} catch (error) {
return;
}
+4 -1
View File
@@ -48,13 +48,16 @@ NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_FEATURED_APP=gearbox-protocol
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}
#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth.jpg?raw=true
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
+8 -4
View File
@@ -18,14 +18,14 @@ NEXT_PUBLIC_NETWORK_RPC_URL=https://eth-sepolia.public.blastapi.io
NEXT_PUBLIC_IS_TESTNET=true
# api configuration
NEXT_PUBLIC_API_HOST=eth-sepolia.blockscout.com
NEXT_PUBLIC_API_HOST=eth-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgba(51, 53, 67, 1)'
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='rgba(165, 252, 122, 1)'
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(51,53,67,1)
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(165,252,122,1)
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
@@ -48,7 +48,11 @@ NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000/login
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
+1 -1
View File
@@ -36,7 +36,7 @@ export_envs_from_preset() {
export_envs_from_preset
# Download external assets
./download_assets.sh ./public/assets
./download_assets.sh ./public/assets/configs
# Check run-time ENVs values
./validate_envs.sh
+1 -1
View File
@@ -10,7 +10,7 @@ if [ $? -ne 0 ]; then
exit 1
else
cd ../../../
favicon_folder="./public/favicon/"
favicon_folder="./public/assets/favicon/"
echo "⏳ Replacing default favicons with freshly generated pack..."
if [ -d "$favicon_folder" ]; then
+1 -1
View File
@@ -3,7 +3,7 @@
echo "🌀 Creating client script with ENV values..."
# Define the output file name
output_file="${1:-./public/envs.js}"
output_file="${1:-./public/assets/envs.js}"
touch $output_file;
truncate -s 0 $output_file;
+14
View File
@@ -15,6 +15,7 @@ import type { ContractCodeIde } from '../../../types/client/contract';
import { GAS_UNITS } from '../../../types/client/gasTracker';
import type { GasUnit } from '../../../types/client/gasTracker';
import type { MarketplaceAppOverview, MarketplaceAppSecurityReportRaw, MarketplaceAppSecurityReport } from '../../../types/client/marketplace';
import type { MultichainProviderConfig } from '../../../types/client/multichainProviderConfig';
import { NAVIGATION_LINK_IDS } from '../../../types/client/navigation-items';
import type { NavItemExternal, NavigationLinkId } from '../../../types/client/navigation-items';
import { ROLLUP_TYPES } from '../../../types/client/rollup';
@@ -625,6 +626,19 @@ const schema = yup
NEXT_PUBLIC_HAS_USER_OPS: yup.boolean(),
NEXT_PUBLIC_METASUITES_ENABLED: yup.boolean(),
NEXT_PUBLIC_SWAP_BUTTON_URL: yup.string(),
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG: yup
.mixed()
.test('shape', 'Invalid schema were provided for NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG, it should have name and url template', (data) => {
const isUndefined = data === undefined;
const valueSchema = yup.object<MultichainProviderConfig>().transform(replaceQuotes).json().shape({
name: yup.string().required(),
url_template: yup.string().required(),
logo: yup.string(),
dapp_id: yup.string(),
});
return isUndefined || valueSchema.isValidSync(data);
}),
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE: yup.string<ValidatorsChainType>().oneOf(VALIDATORS_CHAIN_TYPE),
NEXT_PUBLIC_GAS_TRACKER_ENABLED: yup.boolean(),
NEXT_PUBLIC_GAS_TRACKER_UNITS: yup.array().transform(replaceQuotes).json().of(yup.string<GasUnit>().oneOf(GAS_UNITS)),
+1 -1
View File
@@ -9,7 +9,7 @@ export NEXT_PUBLIC_GIT_TAG=$(git describe --tags --abbrev=0)
../../scripts/collect_envs.sh ../../../docs/ENVS.md
# Copy test assets
mkdir -p "./public/assets"
mkdir -p "./public/assets/configs"
cp -r ${test_folder}/assets ./public/
# Build validator script
@@ -75,3 +75,5 @@ NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=false
NEXT_PUBLIC_WEB3_WALLETS=['coinbase','metamask','token_pocket']
NEXT_PUBLIC_SWAP_BUTTON_URL=uniswap
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=stability
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}
-1
View File
@@ -94,7 +94,6 @@ frontend:
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: "{ \"id\": \"632018\", \"width\": \"320\", \"height\": \"100\" }"
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true
NEXT_PUBLIC_COLOR_THEME_DEFAULT: "dim"
envFromSecret:
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
+24 -2
View File
@@ -57,6 +57,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
- [OpenTelemetry](ENVS.md#opentelemetry)
- [Swap button](ENVS.md#swap-button)
- [Multichain balance button](ENVS.md#multichain-button)
- [3rd party services configuration](ENVS.md#external-services-configuration)
&nbsp;
@@ -662,11 +663,11 @@ The feature enables the Validators page which provides detailed information abou
### OpenTelemetry
OpenTelemetry SDK for Node.js app could be enabled by passing `OTEL_SDK_ENABLED=true` variable. Configure the OpenTelemetry Protocol Exporter by using the generic environment variables described in the [OT docs](https://opentelemetry.io/docs/specs/otel/protocol/exporter/#configuration-options).
OpenTelemetry SDK for Node.js app could be enabled by passing `OTEL_SDK_ENABLED=true` variable. Configure the OpenTelemetry Protocol Exporter by using the generic environment variables described in the [OT docs](https://opentelemetry.io/docs/specs/otel/protocol/exporter/#configuration-options). Note that this Next.js feature is currently experimental. The Docker image should be built with the `NEXT_OPEN_TELEMETRY_ENABLED=true` argument to enable it.
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| OTEL_SDK_ENABLED | `boolean` | Flag to enable the feature | Required | `false` | `true` |
| OTEL_SDK_ENABLED | `boolean` | Run-time flag to enable the feature | Required | `false` | `true` |
&nbsp;
@@ -680,6 +681,27 @@ If the feature is enabled, a Swap button will be displayed at the top of the exp
&nbsp;
### Multichain balance button
If the feature is enabled, a Multichain balance button will be displayed on the address page, which will take you to the portfolio application in the marketplace or to an external site.
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG | `{ name: string; url_template: string; dapp_id?: string; logo?: string }` | Multichain portfolio application config See [below](#multichain-button-configuration-properties) | - | - | `{ name: 'zerion', url_template: 'https://app.zerion.io/{address}/overview', logo: 'https://example.com/icon.svg'` |
&nbsp;
#### Multichain button configuration properties
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| name | `string` | Multichain portfolio application name | Required | - | `zerion` |
| url_template | `string` | Url template to the portfolio. Should be a template with `{address}` variable | Required | - | `https://app.zerion.io/{address}/overview` |
| dapp_id | `string` | Set for open a Blockscout dapp page with the portfolio instead of opening external app page | - | - | `zerion` |
| logo | `string` | Multichain portfolio application logo (.svg) url | - | - | `https://example.com/icon.svg` |
&nbsp;
## External services configuration
### Google ReCaptcha

Before

Width:  |  Height:  |  Size: 752 B

After

Width:  |  Height:  |  Size: 752 B

+1 -3
View File
@@ -46,9 +46,7 @@ const sdk = new NodeSDK({
url.pathname.startsWith('/_next/static/') ||
url.pathname.startsWith('/_next/data/') ||
url.pathname.startsWith('/assets/') ||
url.pathname.startsWith('/static/') ||
url.pathname.startsWith('/favicon/') ||
url.pathname.startsWith('/envs.js')
url.pathname.startsWith('/static/')
) {
return true;
}
+2
View File
@@ -16,6 +16,7 @@ export default function parseMetaPayload(meta: AddressMetadataTag['meta']): Addr
const stringFields: Array<keyof MetaParsed> = [
'textColor',
'bgColor',
'tagIcon',
'tagUrl',
'tooltipIcon',
'tooltipTitle',
@@ -25,6 +26,7 @@ export default function parseMetaPayload(meta: AddressMetadataTag['meta']): Addr
'appMarketplaceURL',
'appLogoURL',
'appActionButtonText',
'warpcastHandle',
];
for (const stringField of stringFields) {
+20 -32
View File
@@ -1,47 +1,35 @@
import { useQuery } from '@tanstack/react-query';
import type { AddressMetadataInfo } from 'types/api/addressMetadata';
import type { AddressMetadataInfoFormatted, AddressMetadataTagFormatted } from 'types/client/addressMetadata';
import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch';
import { getResourceKey } from 'lib/api/useApiQuery';
import useApiQuery from 'lib/api/useApiQuery';
import parseMetaPayload from './parseMetaPayload';
export default function useAddressMetadataInfoQuery(addresses: Array<string>) {
const apiFetch = useApiFetch();
const queryParams = {
addresses,
chainId: config.chain.id,
tagsLimit: '20',
};
const resource = 'address_metadata_info';
// TODO @tom2drum: Improve the typing here
// since we are formatting the API data in the select function here
// we cannot use the useApiQuery hook because of its current typing
// enhance useApiQuery so it can accept an API data and the formatted data types
return useQuery<AddressMetadataInfo, unknown, AddressMetadataInfoFormatted>({
queryKey: getResourceKey(resource, { queryParams }),
queryFn: async() => {
return apiFetch(resource, { queryParams }) as Promise<AddressMetadataInfo>;
return useApiQuery<typeof resource, unknown, AddressMetadataInfoFormatted>(resource, {
queryParams: {
addresses,
chainId: config.chain.id,
tagsLimit: '20',
},
enabled: addresses.length > 0 && config.features.addressMetadata.isEnabled,
select: (data) => {
const addresses = Object.entries(data.addresses)
.map(([ address, { tags, reputation } ]) => {
const formattedTags: Array<AddressMetadataTagFormatted> = tags.map((tag) => ({ ...tag, meta: parseMetaPayload(tag.meta) }));
return [ address.toLowerCase(), { tags: formattedTags, reputation } ] as const;
})
.reduce((result, item) => {
result[item[0]] = item[1];
return result;
}, {} as AddressMetadataInfoFormatted['addresses']);
queryOptions: {
enabled: addresses.length > 0 && config.features.addressMetadata.isEnabled,
select: (data) => {
const addresses = Object.entries(data.addresses)
.map(([ address, { tags, reputation } ]) => {
const formattedTags: Array<AddressMetadataTagFormatted> = tags.map((tag) => ({ ...tag, meta: parseMetaPayload(tag.meta) }));
return [ address.toLowerCase(), { tags: formattedTags, reputation } ] as const;
})
.reduce((result, item) => {
result[item[0]] = item[1];
return result;
}, {} as AddressMetadataInfoFormatted['addresses']);
return { addresses };
return { addresses };
},
},
});
}
+5 -5
View File
@@ -5,8 +5,8 @@ import type { ResourceError, ResourceName, ResourcePayload } from './resources';
import type { Params as ApiFetchParams } from './useApiFetch';
import useApiFetch from './useApiFetch';
export interface Params<R extends ResourceName, E = unknown> extends ApiFetchParams<R> {
queryOptions?: Omit<UseQueryOptions<ResourcePayload<R>, ResourceError<E>, ResourcePayload<R>>, 'queryKey' | 'queryFn'>;
export interface Params<R extends ResourceName, E = unknown, D = ResourcePayload<R>> extends ApiFetchParams<R> {
queryOptions?: Omit<UseQueryOptions<ResourcePayload<R>, ResourceError<E>, D>, 'queryKey' | 'queryFn'>;
}
export function getResourceKey<R extends ResourceName>(resource: R, { pathParams, queryParams }: Params<R> = {}) {
@@ -17,13 +17,13 @@ export function getResourceKey<R extends ResourceName>(resource: R, { pathParams
return [ resource ];
}
export default function useApiQuery<R extends ResourceName, E = unknown>(
export default function useApiQuery<R extends ResourceName, E = unknown, D = ResourcePayload<R>>(
resource: R,
{ queryOptions, pathParams, queryParams, fetchParams }: Params<R, E> = {},
{ queryOptions, pathParams, queryParams, fetchParams }: Params<R, E, D> = {},
) {
const apiFetch = useApiFetch();
return useQuery<ResourcePayload<R>, ResourceError<E>, ResourcePayload<R>>({
return useQuery<ResourcePayload<R>, ResourceError<E>, D>({
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey: getResourceKey(resource, { pathParams, queryParams }),
queryFn: async() => {
+1 -1
View File
@@ -128,7 +128,7 @@ Type extends EventTypes.FILTERS ? {
'Filter name': string;
} :
Type extends EventTypes.BUTTON_CLICK ? {
'Content': 'Swap button';
'Content': 'Swap button' | 'Multichain';
'Source': string;
} :
Type extends EventTypes.PROMO_BANNER ? {
+1 -1
View File
@@ -75,7 +75,7 @@ export const token: Address = {
coin_balance: '1',
creation_tx_hash: '0xc38cf7377bf72d6436f63c37b01b24d032101f20ec1849286dc703c712f10c98',
creator_address_hash: '0x34A9c688512ebdB575e82C50c9803F6ba2916E72',
exchange_rate: null,
exchange_rate: '0.04311',
implementation_address: null,
has_decompiled_code: false,
has_logs: false,
+9 -5
View File
@@ -1,4 +1,4 @@
import type { AddressCollectionsResponse, AddressNFTsResponse, AddressTokenBalance } from 'types/api/address';
import type { AddressCollectionsResponse, AddressNFTsResponse, AddressTokenBalance, AddressTokensResponse } from 'types/api/address';
import * as tokens from 'mocks/tokens/tokenInfo';
import * as tokenInstance from 'mocks/tokens/tokenInstance';
@@ -119,35 +119,39 @@ export const erc404b: AddressTokenBalance = {
token_id: null,
};
export const erc20List = {
export const erc20List: AddressTokensResponse = {
items: [
erc20a,
erc20b,
erc20c,
],
next_page_params: null,
};
export const erc721List = {
export const erc721List: AddressTokensResponse = {
items: [
erc721a,
erc721b,
erc721c,
],
next_page_params: null,
};
export const erc1155List = {
export const erc1155List: AddressTokensResponse = {
items: [
erc1155withoutName,
erc1155a,
erc1155b,
],
next_page_params: null,
};
export const erc404List = {
export const erc404List: AddressTokensResponse = {
items: [
erc404a,
erc404b,
],
next_page_params: null,
};
export const nfts: AddressNFTsResponse = {
+5
View File
@@ -51,6 +51,11 @@ export const verified: SmartContract = {
minimal_proxy_address_hash: null,
};
export const certified: SmartContract = {
...verified,
certified: true,
};
export const withMultiplePaths: SmartContract = {
...verified,
file_path: './simple_storage.sol',
+8 -3
View File
@@ -1,5 +1,8 @@
export const solidityscanReportAverage = {
import type { SolidityscanReport } from 'types/api/contract';
export const solidityscanReportAverage: SolidityscanReport = {
scan_report: {
contractname: 'foo',
scan_status: 'scan_done',
scan_summary: {
issue_severity_distribution: {
@@ -20,8 +23,9 @@ export const solidityscanReportAverage = {
},
};
export const solidityscanReportGreat = {
export const solidityscanReportGreat: SolidityscanReport = {
scan_report: {
contractname: 'foo',
scan_status: 'scan_done',
scan_summary: {
issue_severity_distribution: {
@@ -42,8 +46,9 @@ export const solidityscanReportGreat = {
},
};
export const solidityscanReportLow = {
export const solidityscanReportLow: SolidityscanReport = {
scan_report: {
contractname: 'foo',
scan_status: 'scan_done',
scan_summary: {
issue_severity_distribution: {
+1
View File
@@ -35,6 +35,7 @@ export const contract2: VerifiedContract = {
watchlist_names: [],
ens_domain_name: null,
},
certified: true,
coin_balance: '9078234570352343999',
compiler_version: 'v0.3.1+commit.0463ea4c',
has_constructor_args: true,
File diff suppressed because one or more lines are too long
+9
View File
@@ -96,6 +96,15 @@ export const contract1: SearchResultAddressOrContract = {
url: '/address/0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131a',
};
export const contract2: SearchResultAddressOrContract = {
address: '0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131a',
name: 'Super utko',
type: 'contract' as const,
is_smart_contract_verified: true,
certified: true,
url: '/address/0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131a',
};
export const label1: SearchResultLabel = {
address: '0xb64a30399f7F6b0C154c2E7Af0a3ec7B0A5b131a',
name: 'utko',
+17 -7
View File
@@ -1,9 +1,12 @@
import _mapValues from 'lodash/mapValues';
export const base = {
import type { HomeStats } from 'types/api/stats';
export const base: HomeStats = {
average_block_time: 6212.0,
coin_price: '0.00199678',
coin_price_change_percentage: -7.42,
coin_image: 'http://localhost:3100/utia.jpg',
gas_prices: {
average: {
fiat_price: '1.39',
@@ -41,35 +44,42 @@ export const base = {
tvl: '1767425.102766552',
};
export const withBtcLocked = {
export const withBtcLocked: HomeStats = {
...base,
rootstock_locked_btc: '3337493406696977561374',
};
export const withoutFiatPrices = {
export const withoutFiatPrices: HomeStats = {
...base,
gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, fiat_price: null }) : null),
};
export const withoutGweiPrices = {
export const withoutGweiPrices: HomeStats = {
...base,
gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null }) : null),
};
export const withoutBothPrices = {
export const withoutBothPrices: HomeStats = {
...base,
gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null, fiat_price: null }) : null),
};
export const withSecondaryCoin = {
export const withSecondaryCoin: HomeStats = {
...base,
secondary_coin_price: '3.398',
};
export const noChartData = {
export const noChartData: HomeStats = {
...base,
transactions_today: null,
coin_price: null,
market_cap: null,
tvl: null,
};
export const indexingStatus = {
finished_indexing_blocks: false,
indexed_blocks_ratio: '0.1',
finished_indexing: true,
indexed_internal_transactions_ratio: '1',
};
+14 -14
View File
@@ -8,7 +8,7 @@ export const tokenInfo: TokenInfo = {
holders: '46554',
name: 'ARIANEE',
symbol: 'ARIA',
type: 'ERC-20',
type: 'ERC-20' as const,
total_supply: '1235',
icon_url: 'http://localhost:3000/token-icon.png',
};
@@ -27,7 +27,7 @@ export const tokenInfoERC20a: TokenInfo<'ERC-20'> = {
name: 'hyfi.token',
symbol: 'HyFi',
total_supply: '369000000000000000000000000',
type: 'ERC-20',
type: 'ERC-20' as const,
icon_url: 'http://localhost:3000/token-icon.png',
};
@@ -40,7 +40,7 @@ export const tokenInfoERC20b: TokenInfo<'ERC-20'> = {
name: 'USD Coin',
symbol: 'USDC',
total_supply: '900000000000000000000000000',
type: 'ERC-20',
type: 'ERC-20' as const,
icon_url: null,
};
@@ -53,7 +53,7 @@ export const tokenInfoERC20c: TokenInfo<'ERC-20'> = {
name: 'Ethereum',
symbol: 'ETH',
total_supply: '1000000000000000000000000',
type: 'ERC-20',
type: 'ERC-20' as const,
icon_url: null,
};
@@ -66,7 +66,7 @@ export const tokenInfoERC20d: TokenInfo<'ERC-20'> = {
name: 'Zeta',
symbol: 'ZETA',
total_supply: '2100000000000000000000000000',
type: 'ERC-20',
type: 'ERC-20' as const,
icon_url: null,
};
@@ -79,7 +79,7 @@ export const tokenInfoERC20LongSymbol: TokenInfo<'ERC-20'> = {
name: 'Zeta',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
total_supply: '2100000000000000000000000000',
type: 'ERC-20',
type: 'ERC-20' as const,
icon_url: null,
};
@@ -92,7 +92,7 @@ export const tokenInfoERC721a: TokenInfo<'ERC-721'> = {
name: 'HyFi Athena',
symbol: 'HYFI_ATHENA',
total_supply: '105',
type: 'ERC-721',
type: 'ERC-721' as const,
icon_url: null,
};
@@ -105,7 +105,7 @@ export const tokenInfoERC721b: TokenInfo<'ERC-721'> = {
name: 'World Of Women Galaxy',
symbol: 'WOWG',
total_supply: null,
type: 'ERC-721',
type: 'ERC-721' as const,
icon_url: null,
};
@@ -118,7 +118,7 @@ export const tokenInfoERC721c: TokenInfo<'ERC-721'> = {
name: 'Puma',
symbol: 'PUMA',
total_supply: null,
type: 'ERC-721',
type: 'ERC-721' as const,
icon_url: null,
};
@@ -131,7 +131,7 @@ export const tokenInfoERC721LongSymbol: TokenInfo<'ERC-721'> = {
name: 'Puma',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
total_supply: null,
type: 'ERC-721',
type: 'ERC-721' as const,
icon_url: null,
};
@@ -144,7 +144,7 @@ export const tokenInfoERC1155a: TokenInfo<'ERC-1155'> = {
name: 'HyFi Membership',
symbol: 'HYFI_MEMBERSHIP',
total_supply: '482',
type: 'ERC-1155',
type: 'ERC-1155' as const,
icon_url: null,
};
@@ -157,7 +157,7 @@ export const tokenInfoERC1155b: TokenInfo<'ERC-1155'> = {
name: 'WinkyVerse Collections',
symbol: 'WVC',
total_supply: '4943',
type: 'ERC-1155',
type: 'ERC-1155' as const,
icon_url: null,
};
@@ -170,7 +170,7 @@ export const tokenInfoERC1155WithoutName: TokenInfo<'ERC-1155'> = {
name: null,
symbol: null,
total_supply: '482',
type: 'ERC-1155',
type: 'ERC-1155' as const,
icon_url: null,
};
@@ -184,7 +184,7 @@ export const tokenInfoERC404: TokenInfo<'ERC-404'> = {
name: 'OMNI404',
symbol: 'O404',
total_supply: '6482275000000000000',
type: 'ERC-404',
type: 'ERC-404' as const,
};
export const bridgedTokenA: TokenInfo<'ERC-20'> = {
+10 -9
View File
@@ -1,4 +1,4 @@
import type { TxStateChange } from 'types/api/txStateChanges';
import type { TxStateChange, TxStateChanges } from 'types/api/txStateChanges';
export const mintToken: TxStateChange = {
address: {
@@ -35,7 +35,7 @@ export const mintToken: TxStateChange = {
type: 'ERC-721',
icon_url: null,
},
type: 'token',
type: 'token' as const,
};
export const receiveMintedToken: TxStateChange = {
@@ -73,7 +73,7 @@ export const receiveMintedToken: TxStateChange = {
type: 'ERC-721',
icon_url: null,
},
type: 'token',
type: 'token' as const,
};
export const transfer1155Token: TxStateChange = {
@@ -105,7 +105,7 @@ export const transfer1155Token: TxStateChange = {
type: 'ERC-1155',
},
token_id: '1',
type: 'token',
type: 'token' as const,
};
export const receiveCoin: TxStateChange = {
@@ -125,7 +125,7 @@ export const receiveCoin: TxStateChange = {
change: '29726406604060',
is_miner: true,
token: null,
type: 'coin',
type: 'coin' as const,
};
export const sendCoin: TxStateChange = {
@@ -145,12 +145,13 @@ export const sendCoin: TxStateChange = {
change: '-3844844822720562',
is_miner: false,
token: null,
type: 'coin',
type: 'coin' as const,
};
export const sendERC20Token = {
address: {
hash: '0x7f6479df95Aa3036a3BE02DB6300ea201ABd9981',
ens_domain_name: null,
implementation_name: null,
is_contract: false,
is_verified: false,
@@ -173,13 +174,13 @@ export const sendERC20Token = {
name: 'Tether USD',
symbol: 'USDT',
total_supply: '39030615894320966',
type: 'ERC-20',
type: 'ERC-20' as const,
token_id: null,
},
type: 'token',
type: 'token' as const,
};
export const baseResponse = {
export const baseResponse: TxStateChanges = {
items: [
mintToken,
receiveMintedToken,
+28 -2
View File
@@ -1,3 +1,4 @@
/* eslint-disable max-len */
import type { UserOp } from 'types/api/userOps';
export const userOpData: UserOp = {
@@ -47,7 +48,6 @@ export const userOpData: UserOp = {
max_fee_per_gas: '1575000898',
max_priority_fee_per_gas: '1575000898',
nonce: '79',
// eslint-disable-next-line max-len
paymaster_and_data: '0x7cea357b5ac0639f89f9e378a1f03aa5005c0a250000000000000000000000000000000000000000000000000000000065b3a8800000000000000000000000000000000000000000000000000000000065aa6e0028fa4c57ac1141bc9ecd8c9243f618ade8ea1db10ab6c1d1798a222a824764ff2269a72ae7a3680fa8b03a80d8a00cdc710eaf37afdcc55f8c9c4defa3fdf2471b',
pre_verification_gas: '48396',
sender: '0xF0C14FF4404b188fAA39a3507B388998c10FE284',
@@ -64,8 +64,34 @@ export const userOpData: UserOp = {
is_verified: null,
name: null,
},
// eslint-disable-next-line max-len
call_data: '0xb61d27f600000000000000000000000059f6aa952df7f048fd076e33e0ea8bb552d5ffd8000000000000000000000000000000000000000000000000003f3d017500800000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
execute_call_data: '0x3cf80e6c',
decoded_call_data: {
method_call: 'execute(address dest, uint256 value, bytes func)',
method_id: 'b61d27f6',
parameters: [
{
name: 'dest',
type: 'address',
value: '0xb0ccffd05f5a87c4c3ceffaa217900422a249915',
},
{
name: 'value',
type: 'uint256',
value: '0',
},
{
name: 'func',
type: 'bytes',
value: '0x3cf80e6c',
},
],
},
decoded_execute_call_data: {
method_call: 'advanceEpoch()',
method_id: '3cf80e6c',
parameters: [],
},
paymaster: {
ens_domain_name: null,
hash: '0x7ceA357B5AC0639F89F9e378a1f03Aa5005C0a25',
+1 -1
View File
@@ -46,7 +46,7 @@ const moduleExports = {
output: 'standalone',
productionBrowserSourceMaps: true,
experimental: {
instrumentationHook: true,
instrumentationHook: process.env.NEXT_OPEN_TELEMETRY_ENABLED === 'true',
turbo: {
rules: {
'*.svg': {
+5
View File
@@ -56,6 +56,7 @@ export function app(): CspDev.DirectiveDescriptor {
getFeaturePayload(config.features.verifiedTokens)?.api.endpoint,
getFeaturePayload(config.features.addressVerification)?.api.endpoint,
getFeaturePayload(config.features.nameService)?.api.endpoint,
getFeaturePayload(config.features.addressMetadata)?.api.endpoint,
marketplaceFeaturePayload && 'api' in marketplaceFeaturePayload ? marketplaceFeaturePayload.api.endpoint : '',
// chain RPC server
@@ -132,6 +133,10 @@ export function app(): CspDev.DirectiveDescriptor {
'*',
],
'frame-ancestors': [
KEY_WORDS.SELF,
],
...((() => {
if (!config.features.sentry.isEnabled) {
return {};
+3 -3
View File
@@ -60,7 +60,7 @@
"@tanstack/react-query-devtools": "^5.4.3",
"@types/papaparse": "^5.3.5",
"@types/react-scroll": "^1.8.4",
"@web3modal/wagmi": "4.1.3",
"@web3modal/wagmi": "4.2.1",
"bignumber.js": "^9.1.0",
"blo": "^1.1.1",
"chakra-react-select": "^4.4.3",
@@ -103,8 +103,8 @@
"react-scroll": "^1.8.7",
"swagger-ui-react": "^5.9.0",
"use-font-face-observer": "^1.2.1",
"viem": "2.9.6",
"wagmi": "2.5.16",
"viem": "2.10.9",
"wagmi": "2.9.2",
"xss": "^1.0.14"
},
"devDependencies": {
+6 -6
View File
@@ -44,14 +44,14 @@ class MyDocument extends Document {
/>
{ /* eslint-disable-next-line @next/next/no-sync-scripts */ }
<script src="/envs.js"/>
<script src="/assets/envs.js"/>
{ /* FAVICON */ }
<link rel="icon" href="/favicon/favicon.ico" sizes="48x48"/>
<link rel="icon" sizes="32x32" type="image/png" href="/favicon/favicon-32x32.png"/>
<link rel="icon" sizes="16x16" type="image/png"href="/favicon/favicon-16x16.png"/>
<link rel="apple-touch-icon" href="/favicon/apple-touch-icon-180x180.png"/>
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg"/>
<link rel="icon" href="/assets/favicon/favicon.ico" sizes="48x48"/>
<link rel="icon" sizes="32x32" type="image/png" href="/assets/favicon/favicon-32x32.png"/>
<link rel="icon" sizes="16x16" type="image/png"href="/assets/favicon/favicon-16x16.png"/>
<link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon-180x180.png"/>
<link rel="mask-icon" href="/assets/favicon/safari-pinned-tab.svg"/>
<link rel="preload" as="image" href={ svgSprite.href }/>
</Head>
+3 -2
View File
@@ -12,9 +12,10 @@ import type { Props as PageProps } from 'nextjs/getServerSideProps';
import config from 'configs/app';
import { AppContextProvider } from 'lib/contexts/app';
import { SocketProvider } from 'lib/socket/context';
import * as app from 'playwright/utils/app';
import theme from 'theme';
import { port as socketPort } from './utils/socket';
export type Props = {
children: React.ReactNode;
withSocket?: boolean;
@@ -74,7 +75,7 @@ const TestApp = ({ children, withSocket, withWalletClient = true, appContext = d
return (
<ChakraProvider theme={ theme }>
<QueryClientProvider client={ queryClient }>
<SocketProvider url={ withSocket ? `ws://${ config.app.host }:${ app.socketPort }` : undefined }>
<SocketProvider url={ withSocket ? `ws://${ config.app.host }:${ socketPort }` : undefined }>
<AppContextProvider { ...appContext }>
<GrowthBookProvider>
<WalletClientProvider withWalletClient={ withWalletClient }>
-24
View File
@@ -1,24 +0,0 @@
import type { test } from '@playwright/experimental-ct-react';
import createContextWithStorage from './createContextWithStorage';
interface Env {
name: string;
value: string;
}
/**
* @deprecated please use mockEnvs fixture
*
* @export
* @param {Array<Env>} envs
* @return {*} {Parameters<typeof test.extend>[0]['context']}
*/
export default function contextWithEnvsFixture(envs: Array<Env>): Parameters<typeof test.extend>[0]['context'] {
return async({ browser }, use) => {
const context = await createContextWithStorage(browser, envs);
await use(context);
await context.close();
};
}
@@ -1,25 +0,0 @@
import type { test } from '@playwright/experimental-ct-react';
import createContextWithStorage from './createContextWithStorage';
interface Feature {
id: string;
value: unknown;
}
/**
* @deprecated please use mockFeatures fixture
*
* @export
* @param {Array<Feature>} envs
* @return {*} {Parameters<typeof test.extend>[0]['context']}
*/
export default function contextWithFeaturesFixture(envs: Array<Feature>): Parameters<typeof test.extend>[0]['context'] {
return async({ browser }, use) => {
const storageItems = envs.map(({ id, value }) => ({ name: `pw_feature:${ id }`, value: JSON.stringify(value) }));
const context = await createContextWithStorage(browser, storageItems);
await use(context);
await context.close();
};
}
@@ -1,22 +0,0 @@
import type { Browser } from '@playwright/test';
import config from 'configs/app';
/**
* @deprecated please use mockEnvs or mockFeatures fixture
*
* @export
* @param {Browser} browser
* @param {Array<{ name: string; value: string }>} localStorage
* @return {*}
*/
export default async function createContextWithEnvs(browser: Browser, localStorage: Array<{ name: string; value: string }>) {
return browser.newContext({
storageState: {
origins: [
{ origin: config.app.baseUrl, localStorage },
],
cookies: [],
},
});
}
+2 -1
View File
@@ -6,6 +6,7 @@ import type { ResourceName, ResourcePayload } from 'lib/api/resources';
interface Options<R extends ResourceName> {
pathParams?: Parameters<typeof buildUrl<R>>[1];
queryParams?: Parameters<typeof buildUrl<R>>[2];
times?: number;
}
export type MockApiResponseFixture = <R extends ResourceName>(resourceName: R, responseMock: ResourcePayload<R>, options?: Options<R>) => Promise<string>;
@@ -17,7 +18,7 @@ const fixture: TestFixture<MockApiResponseFixture, { page: Page }> = async({ pag
await page.route(apiUrl, (route) => route.fulfill({
status: 200,
body: JSON.stringify(responseMock),
}));
}), { times: options?.times });
return apiUrl;
});
+6
View File
@@ -66,4 +66,10 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
navigationHighlightedRoutes: [
[ 'NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES', '["/blocks", "/apps"]' ],
],
dataAvailability: [
[ 'NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED', 'true' ],
],
nameService: [
[ 'NEXT_PUBLIC_NAME_SERVICE_API_HOST', 'https://localhost:3101' ],
],
};
+1
View File
@@ -1,3 +1,4 @@
/* eslint-disable no-restricted-imports */
import type { MountOptions } from '@playwright/experimental-ct-react';
import type { Locator, TestFixture } from '@playwright/test';
import type router from 'next/router';
+2 -2
View File
@@ -8,7 +8,7 @@ import type { SmartContractVerificationResponse } from 'types/api/contract';
import type { TokenTransfer } from 'types/api/tokenTransfer';
import type { Transaction } from 'types/api/transaction';
import * as app from 'playwright/utils/app';
import { port as socketPort } from '../utils/socket';
export type CreateSocketFixture = () => Promise<WebSocket>;
@@ -20,7 +20,7 @@ export interface SocketServerFixture {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const createSocket: TestFixture<CreateSocketFixture, { page: Page}> = async({ page }, use) => {
const socketServer = new WebSocketServer({ port: app.socketPort });
const socketServer = new WebSocketServer({ port: socketPort });
const connectionPromise = new Promise<WebSocket>((resolve) => {
socketServer.on('connection', (socket: WebSocket) => {
-1
View File
@@ -1 +0,0 @@
export const socketPort = 3200;
-20
View File
@@ -1,20 +0,0 @@
import { compile } from 'path-to-regexp';
import config from 'configs/app';
import type { ResourceName, ResourcePathParams } from 'lib/api/resources';
import { RESOURCES } from 'lib/api/resources';
/**
* @deprecated please use fixture mockApiResponse from playwright/lib.tsx for rendering test suite
*
* @export
* @template R
* @param {R} resourceName
* @param {ResourcePathParams<R>} [pathParams]
* @return {*} string
*/
export default function buildApiUrl<R extends ResourceName>(resourceName: R, pathParams?: ResourcePathParams<R>) {
const resource = RESOURCES[resourceName];
const origin = 'endpoint' in resource && resource.endpoint ? resource.endpoint + (resource.basePath ?? '') : config.api.endpoint;
return origin + compile(resource.path)(pathParams);
}
@@ -1,4 +1,3 @@
/* eslint-disable max-len */
import { devices } from '@playwright/test';
export const viewport = {
+1
View File
@@ -0,0 +1 @@
export const port = 3200;
+4
View File
@@ -0,0 +1,4 @@
**Directories**
- `/icons` - Folder for SVG-sprite assets, generated at build time.
- `/static` - Folder for static assets that are consistent between app re-runs but may differ from one build version to another.
- `/assets` - Folder for dynamically generated assets during the app start, such as the favicon bundle, ENV variables file, and external app configurations.

Before

Width:  |  Height:  |  Size: 736 B

After

Width:  |  Height:  |  Size: 736 B

Before

Width:  |  Height:  |  Size: 833 B

After

Width:  |  Height:  |  Size: 833 B

Before

Width:  |  Height:  |  Size: 791 B

After

Width:  |  Height:  |  Size: 791 B

Before

Width:  |  Height:  |  Size: 515 B

After

Width:  |  Height:  |  Size: 515 B

Before

Width:  |  Height:  |  Size: 561 B

After

Width:  |  Height:  |  Size: 561 B

Before

Width:  |  Height:  |  Size: 791 B

After

Width:  |  Height:  |  Size: 791 B

Before

Width:  |  Height:  |  Size: 383 B

After

Width:  |  Height:  |  Size: 383 B

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 439 B

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,3 @@
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180">
<path d="M34.9 2.9c-2.9 2.9-2.9 3-2.9 15 0 6.8-.5 13.1-1.1 14.5-1.9 4-6.6 5.6-16.6 5.6C4.7 38 1.7 39.4.5 44.3.2 45.5.1 75.4.2 110.7l.3 64.2 2.3 2.3c2.1 2.1 3.3 2.3 14.6 2.6 14.8.4 17.9-.6 19.5-6.6.7-2.4 1.1-26.2 1.1-66.9V43.1l2.4-2.8c2.3-2.7 2.9-2.8 13.6-3.3 15-.7 15-.6 15-18.2v-13l-2.9-2.9C63.2 0 63.2 0 50.5 0S37.8 0 34.9 2.9zm76.9-1.4c-3.9 2.2-5.1 7.8-4.6 20.4.5 9.8.6 10.6 3.2 12.8 2.3 2 3.8 2.3 10.5 2.3 4.3 0 9.2.5 11 1.1 6.3 2.2 6.1.3 6.1 71.4v64.7l2.9 2.9c2.9 2.9 3 2.9 15.4 2.9 12 0 12.7-.1 15.6-2.6l3.1-2.6V42.9l-2.5-2.4c-2.2-2.2-3.2-2.5-10.5-2.5-8.1 0-14-1.6-15.6-4.1-.5-.8-1.1-7.6-1.4-15.1C144.3.3 144.6.6 127.2.2c-9.8-.1-13.3.1-15.4 1.3zM72.3 74.4l-2.8 2.4-.3 30.6-.3 30.5 3 3.3 2.9 3.3h13.1c12.9 0 13.1 0 15.8-2.8l2.8-2.7V76.8l-2.8-2.4C101 72.1 100.2 72 88 72s-13 .1-15.7 2.4z"/>
</svg>

After

Width:  |  Height:  |  Size: 881 B

-3
View File
@@ -1,3 +0,0 @@
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180">
<path d="M34.9 2.9c-2.9 2.9-2.9 3-2.9 15 0 6.8-.5 13.1-1.1 14.5-1.9 4-6.6 5.6-16.6 5.6C4.7 38 1.7 39.4.5 44.3.2 45.5.1 75.4.2 110.7l.3 64.2 2.3 2.3c2.1 2.1 3.3 2.3 14.6 2.6 14.8.4 17.9-.6 19.5-6.6.7-2.4 1.1-26.2 1.1-66.9V43.1l2.4-2.8c2.3-2.7 2.9-2.8 13.6-3.3 15-.7 15-.6 15-18.2v-13l-2.9-2.9C63.2 0 63.2 0 50.5 0S37.8 0 34.9 2.9zM111.8 1.5c-3.9 2.2-5.1 7.8-4.6 20.4.5 9.8.6 10.6 3.2 12.8 2.3 2 3.8 2.3 10.5 2.3 4.3 0 9.2.5 11 1.1 6.3 2.2 6.1.3 6.1 71.4v64.7l2.9 2.9c2.9 2.9 3 2.9 15.4 2.9 12 0 12.7-.1 15.6-2.6l3.1-2.6V42.9l-2.5-2.4c-2.2-2.2-3.2-2.5-10.5-2.5-8.1 0-14-1.6-15.6-4.1-.5-.8-1.1-7.6-1.4-15.1C144.3.3 144.6.6 127.2.2c-9.8-.1-13.3.1-15.4 1.3zM72.3 74.4l-2.8 2.4-.3 30.6-.3 30.5 3 3.3 2.9 3.3h13.1c12.9 0 13.1 0 15.8-2.8l2.8-2.7V76.8l-2.8-2.4C101 72.1 100.2 72 88 72s-13 .1-15.7 2.4z"/>
</svg>

Before

Width:  |  Height:  |  Size: 882 B

+1 -1
View File
@@ -24,6 +24,7 @@
| "brands/safe"
| "brands/solidity_scan"
| "burger"
| "certified"
| "check"
| "clock-light"
| "clock"
@@ -147,7 +148,6 @@
| "user_op_slim"
| "user_op"
| "validator"
| "verified_token"
| "verified"
| "verify-contract"
| "wallet"
+3
View File
@@ -21,6 +21,9 @@ export const USER_OP: UserOp = {
sender: ADDRESS_HASH,
nonce: '0x00b',
call_data: '0x123',
execute_call_data: null,
decoded_call_data: null,
decoded_execute_call_data: null,
call_gas_limit: '71316',
verification_gas_limit: '91551',
pre_verification_gas: '53627',
+8 -11
View File
@@ -1,9 +1,8 @@
import type { AlertProps } from '@chakra-ui/react';
import { Alert, AlertIcon, AlertTitle } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import { test, expect } from 'playwright/lib';
test.use({ viewport: { width: 400, height: 720 } });
@@ -29,16 +28,14 @@ const TEST_CASES: Array<AlertProps> = [
TEST_CASES.forEach((props) => {
const testName = Object.entries(props).map(([ key, value ]) => `${ key }=${ value }`).join(', ');
test(`${ testName } +@dark-mode`, async({ mount }) => {
const component = await mount(
<TestApp>
<Alert { ...props }>
<AlertIcon/>
<AlertTitle>
test(`${ testName } +@dark-mode`, async({ render }) => {
const component = await render(
<Alert { ...props }>
<AlertIcon/>
<AlertTitle>
This is alert text
</AlertTitle>
</Alert>
</TestApp>,
</AlertTitle>
</Alert>,
);
await expect(component).toHaveScreenshot();
+9 -26
View File
@@ -1,8 +1,7 @@
import { Button } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import { test, expect } from 'playwright/lib';
[
{ variant: 'solid' },
@@ -16,40 +15,24 @@ import TestApp from 'playwright/TestApp';
{ variant: 'subtle', colorScheme: 'gray', withDarkMode: true },
].forEach(({ variant, colorScheme, withDarkMode }) => {
test.describe(`variant ${ variant }${ colorScheme ? ` with ${ colorScheme } color scheme` : '' }${ withDarkMode ? ' +@dark-mode' : '' }`, () => {
test('base', async({ mount }) => {
const component = await mount(
<TestApp>
<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>
</TestApp>,
);
test('base', async({ render }) => {
const component = await render(<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>);
await expect(component.locator('button')).toHaveScreenshot();
});
test('disabled', async({ mount }) => {
const component = await mount(
<TestApp>
<Button variant={ variant } colorScheme={ colorScheme } isDisabled>Click me</Button>
</TestApp>,
);
test('disabled', async({ render }) => {
const component = await render(<Button variant={ variant } colorScheme={ colorScheme } isDisabled>Click me</Button>);
await expect(component.locator('button')).toHaveScreenshot();
});
test('hovered', async({ mount }) => {
const component = await mount(
<TestApp>
<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>
</TestApp>,
);
test('hovered', async({ render }) => {
const component = await render(<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>);
await component.getByText(/click/i).hover();
await expect(component.locator('button')).toHaveScreenshot();
});
test('active', async({ mount }) => {
const component = await mount(
<TestApp>
<Button variant={ variant } colorScheme={ colorScheme } isActive>Click me</Button>
</TestApp>,
);
test('active', async({ render }) => {
const component = await render(<Button variant={ variant } colorScheme={ colorScheme } isActive>Click me</Button>);
await expect(component.locator('button')).toHaveScreenshot();
});
});
+29 -21
View File
@@ -1,9 +1,8 @@
import { formAnatomy as parts } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { getColor, mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors';
import getFormStyles from '../utils/getFormStyles';
import FancySelect from './FancySelect';
import FormLabel from './FormLabel';
import Input from './Input';
@@ -13,8 +12,7 @@ const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys);
function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunctionProps) {
const { theme } = props;
const { focusPlaceholderColor, errorColor } = getDefaultFormColors(props);
const formStyles = getFormStyles(props);
const activeLabelStyles = {
...FormLabel.variants?.floating?.(props)._focusWithin,
@@ -63,12 +61,29 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
// label styles
label: FormLabel.sizes?.[size](props) || {},
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': activeLabelStyles,
'textarea:not(:placeholder-shown) + label': {
bgColor: formStyles.input.filled.bgColor,
},
[`
input[readonly] + label,
textarea[readonly] + label,
&[aria-readonly=true] label
`]: {
bgColor: formStyles.input.readOnly.bgColor,
},
[`
input[aria-invalid=true] + label,
textarea[aria-invalid=true] + label,
&[aria-invalid=true] label
`]: {
color: getColor(theme, errorColor),
color: formStyles.placeholder.error.color,
},
[`
input[disabled] + label,
textarea[disabled] + label,
&[aria-disabled=true] label
`]: {
color: formStyles.placeholder.disabled.color,
},
// input styles
@@ -79,31 +94,24 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
padding: inputPx,
},
'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': activeInputStyles,
[`
input[disabled] + label,
&[aria-disabled=true] label
`]: {
backgroundColor: 'transparent',
},
// in textarea bg of label could not be transparent; it should match the background color of input but without alpha
// so we have to use non-standard colors here
'textarea[disabled] + label': {
backgroundColor: mode('#ececec', '#232425')(props),
},
'textarea[disabled] + label[data-in-modal=true]': {
backgroundColor: mode('#ececec', '#292b34')(props),
},
// indicator styles
'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': {
color: getColor(theme, focusPlaceholderColor),
color: formStyles.placeholder.default.color,
},
[`
input[aria-invalid=true] + label .chakra-form__required-indicator,
textarea[aria-invalid=true] + label .chakra-form__required-indicator,
&[aria-invalid=true] .chakra-form__required-indicator
`]: {
color: getColor(theme, errorColor),
color: formStyles.placeholder.error.color,
},
[`
input[disabled] + label .chakra-form__required-indicator,
textarea[disabled] + label .chakra-form__required-indicator,
&[aria-disabled=true] .chakra-form__required-indicator
`]: {
color: formStyles.placeholder.disabled.color,
},
},
};
+54 -58
View File
@@ -1,20 +1,17 @@
import { FormControl, Input, FormLabel } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import { test, expect } from 'playwright/lib';
test.use({ viewport: { width: 500, height: 300 } });
test.describe('floating label size md +@dark-mode', () => {
test('empty', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value=""/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
test('empty', async({ render }) => {
const component = await render(
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value=""/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>,
);
await expect(component).toHaveScreenshot();
@@ -23,14 +20,12 @@ test.describe('floating label size md +@dark-mode', () => {
await expect(component).toHaveScreenshot();
});
test('empty error', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="" isInvalid/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
test('empty error', async({ render }) => {
const component = await render(
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="" isInvalid/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>,
);
await expect(component).toHaveScreenshot();
@@ -39,40 +34,45 @@ test.describe('floating label size md +@dark-mode', () => {
await expect(component).toHaveScreenshot();
});
test('filled', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo"/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
test('filled', async({ render }) => {
const component = await render(
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo"/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>,
);
await expect(component).toHaveScreenshot();
});
test('filled disabled', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isDisabled/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
test('filled disabled', async({ render }) => {
const component = await render(
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isDisabled/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>,
);
await expect(component).toHaveScreenshot();
});
test('filled error', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isInvalid/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
test('filled read-only', async({ render }) => {
const component = await render(
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isReadOnly/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>,
);
await expect(component).toHaveScreenshot();
});
test('filled error', async({ render }) => {
const component = await render(
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isInvalid/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>,
);
await expect(component).toHaveScreenshot();
@@ -80,14 +80,12 @@ test.describe('floating label size md +@dark-mode', () => {
});
test.describe('floating label size lg +@dark-mode', () => {
test('empty', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="lg">
<Input required value=""/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
test('empty', async({ render }) => {
const component = await render(
<FormControl variant="floating" id="name" isRequired size="lg">
<Input required value=""/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>,
);
await expect(component).toHaveScreenshot();
@@ -96,14 +94,12 @@ test.describe('floating label size lg +@dark-mode', () => {
await expect(component).toHaveScreenshot();
});
test('filled', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="lg">
<Input required value="foo"/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
test('filled', async({ render }) => {
const component = await render(
<FormControl variant="floating" id="name" isRequired size="lg">
<Input required value="foo"/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>,
);
await expect(component).toHaveScreenshot();
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

+8 -11
View File
@@ -1,7 +1,6 @@
import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import { getColor } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors';
import getFormStyles from '../utils/getFormStyles';
const baseStyle = defineStyle({
display: 'flex',
@@ -13,14 +12,12 @@ const baseStyle = defineStyle({
transitionDuration: 'normal',
opacity: 1,
_disabled: {
opacity: 0.4,
opacity: 0.2,
},
});
const variantFloating = defineStyle((props) => {
const { theme, backgroundColor } = props;
const { focusPlaceholderColor } = getDefaultFormColors(props);
const bc = backgroundColor || 'transparent';
const formStyles = getFormStyles(props);
return {
left: '2px',
@@ -29,8 +26,8 @@ const variantFloating = defineStyle((props) => {
position: 'absolute',
borderRadius: 'base',
boxSizing: 'border-box',
color: 'gray.500',
backgroundColor: 'transparent',
color: formStyles.placeholder.default.color,
backgroundColor: props.bgColor || props.backgroundColor || 'transparent',
pointerEvents: 'none',
margin: 0,
transformOrigin: 'top left',
@@ -39,8 +36,8 @@ const variantFloating = defineStyle((props) => {
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
_focusWithin: {
backgroundColor: bc,
color: getColor(theme, focusPlaceholderColor),
backgroundColor: props.bgColor || props.backgroundColor || 'transparent',
color: formStyles.placeholder.default.color,
fontSize: 'xs',
lineHeight: '16px',
borderTopRightRadius: 'none',
@@ -70,7 +67,7 @@ const sizes = {
return {
fontSize: 'md',
lineHeight: '24px',
padding: '28px 4px 28px 24px',
padding: '26px 4px 26px 24px',
right: '22px',
_focusWithin: {
padding: '16px 0 2px 24px',
+18
View File
@@ -29,6 +29,20 @@ const size = {
h: '40px',
borderRadius: 'base',
}),
// TEMPORARY INPUT SIZE!!!
// soon we will migrate to the new size and get rid off this one
// lg -> 60
// md -> 48
// sm -> 40
// xs ->32
sm_md: defineStyle({
fontSize: 'md',
lineHeight: '24px',
px: '8px',
py: '12px',
h: '48px',
borderRadius: 'base',
}),
md: defineStyle({
fontSize: 'md',
lineHeight: '20px',
@@ -71,6 +85,10 @@ const sizes = {
field: size.sm,
addon: size.sm,
}),
sm_md: definePartsStyle({
field: size.sm_md,
addon: size.sm_md,
}),
md: definePartsStyle({
field: size.md,
addon: size.md,
+3 -3
View File
@@ -10,11 +10,11 @@ import { runIfFn } from '@chakra-ui/utils';
const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys);
const baseStyleDialog = defineStyle((props) => {
const baseStyleDialog = defineStyle(() => {
return {
padding: 8,
borderRadius: 'lg',
bg: mode('white', 'gray.900')(props),
bg: 'dialog_bg',
margin: 'auto',
};
});
@@ -61,7 +61,7 @@ const baseStyleOverlay = defineStyle({
});
const baseStyle = definePartsStyle((props) => ({
dialog: runIfFn(baseStyleDialog, props),
dialog: runIfFn(baseStyleDialog),
dialogContainer: baseStyleDialogContainer,
header: runIfFn(baseStyleHeader, props),
+3
View File
@@ -15,6 +15,9 @@ const baseStyleTrack = defineStyle((props) => {
bg: mode(`${ c }.600`, `${ c }.400`)(props),
},
},
_focusVisible: {
boxShadow: 'none',
},
};
});
+8 -17
View File
@@ -1,29 +1,20 @@
import { Box, Tag } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import { test, expect } from 'playwright/lib';
[ 'blue', 'gray', 'gray-blue', 'orange', 'green', 'purple', 'cyan', 'teal' ].forEach((colorScheme) => {
test(`${ colorScheme } color scheme +@dark-mode`, async({ mount }) => {
const component = await mount(
<TestApp>
<Tag colorScheme={ colorScheme }>content</Tag>
</TestApp>,
);
test(`${ colorScheme } color scheme +@dark-mode`, async({ render }) => {
const component = await render(<Tag colorScheme={ colorScheme }>content</Tag>);
await expect(component.getByText(/content/i)).toHaveScreenshot();
});
});
test('with long text', async({ mount }) => {
const component = await mount(
<TestApp>
<Box w="100px">
<Tag>this is very looooooooooong text</Tag>
</Box>
</TestApp>,
test('with long text', async({ render }) => {
const component = await render(
<Box w="100px">
<Tag>this is very looooooooooong text</Tag>
</Box>,
);
await expect(component.getByText(/this/i)).toHaveScreenshot();
});
-15
View File
@@ -1,22 +1,8 @@
import { Textarea as TextareaComponent } from '@chakra-ui/react';
import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import { mode } from '@chakra-ui/theme-tools';
import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles';
const variantFilledInactive = defineStyle((props) => {
return {
// https://bugs.chromium.org/p/chromium/issues/detail?id=1362573
// there is a problem with scrollbar color in chromium
// so blackAlpha.50 here is replaced with #f5f5f6
// and whiteAlpha.50 is replaced with #1a1b1b
// bgColor: mode('blackAlpha.50', 'whiteAlpha.50')(props),
bgColor: mode('#f5f5f6', '#1a1b1b')(props),
};
});
const sizes = {
md: defineStyle({
fontSize: 'md',
@@ -38,7 +24,6 @@ const Textarea = defineStyleConfig({
sizes,
variants: {
outline: defineStyle(getOutlinedFieldStyles),
filledInactive: variantFilledInactive,
},
defaultProps: {
variant: 'outline',
+16 -21
View File
@@ -1,18 +1,15 @@
import { Box, Tooltip, Icon } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import { test, expect } from 'playwright/lib';
test('base view +@dark-mode', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Box m={ 10 }>
<Tooltip label="Tooltip content">
test('base view +@dark-mode', async({ render, page }) => {
const component = await render(
<Box m={ 10 }>
<Tooltip label="Tooltip content">
trigger
</Tooltip>
</Box>
</TestApp>,
</Tooltip>
</Box>,
);
await component.getByText(/trigger/i).hover();
@@ -22,17 +19,15 @@ test('base view +@dark-mode', async({ mount, page }) => {
// was not able to reproduce in tests issue when Icon is used as trigger for tooltip
// https://github.com/chakra-ui/chakra-ui/issues/7107
test.skip('with icon', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Box m={ 10 }>
<Tooltip label="Tooltip content">
<Icon viewBox="0 0 20 20" boxSize={ 5 } aria-label="Trigger">
<circle cx="10" cy="10" r="10"/>
</Icon>
</Tooltip>
</Box>
</TestApp>,
test.fixme('with icon', async({ render, page }) => {
const component = await render(
<Box m={ 10 }>
<Tooltip label="Tooltip content">
<Icon viewBox="0 0 20 20" boxSize={ 5 } aria-label="Trigger">
<circle cx="10" cy="10" r="10"/>
</Icon>
</Tooltip>
</Box>,
);
const tooltip = page.getByText(/tooltip content/i);
+4
View File
@@ -23,6 +23,10 @@ const semanticTokens = {
'default': 'red.400',
_dark: 'red.300',
},
dialog_bg: {
'default': 'white',
_dark: 'gray.900',
},
},
shadows: {
action_bar: '0 4px 4px -4px rgb(0 0 0 / 10%), 0 2px 4px -4px rgb(0 0 0 / 6%)',
-12
View File
@@ -1,12 +0,0 @@
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools';
export default function getDefaultFormColors(props: StyleFunctionProps) {
const { focusBorderColor: fc, errorBorderColor: ec, filledBorderColor: flc } = props;
return {
focusBorderColor: fc || mode('blue.500', 'blue.300')(props),
focusPlaceholderColor: fc || 'gray.500',
errorColor: ec || mode('red.400', 'red.300')(props),
filledColor: flc || mode('gray.300', 'gray.600')(props),
};
}
+55
View File
@@ -0,0 +1,55 @@
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode, transparentize } from '@chakra-ui/theme-tools';
export default function getFormStyles(props: StyleFunctionProps) {
return {
input: {
empty: {
// there is no text in the empty input
// color: ???,
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('gray.100', 'gray.700')(props),
},
hover: {
color: mode('gray.800', 'gray.50')(props),
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('gray.200', 'gray.500')(props),
},
focus: {
color: mode('gray.800', 'gray.50')(props),
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('blue.400', 'blue.400')(props),
},
filled: {
color: mode('gray.800', 'gray.50')(props),
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('gray.300', 'gray.600')(props),
},
readOnly: {
color: mode('gray.800', 'gray.50')(props),
bgColor: mode('gray.200', 'gray.800')(props),
borderColor: mode('gray.200', 'gray.800')(props),
},
// we use opacity to show the disabled state
disabled: {
opacity: 0.2,
},
error: {
color: mode('gray.800', 'gray.50')(props),
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('red.500', 'red.500')(props),
},
},
placeholder: {
'default': {
color: mode('gray.500', 'gray.500')(props),
},
disabled: {
color: transparentize('gray.500', 0.2)(props.theme),
},
error: {
color: mode('red.500', 'red.500')(props),
},
},
};
}
+25 -20
View File
@@ -1,59 +1,64 @@
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode, getColor } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from './getDefaultFormColors';
import getDefaultTransitionProps from './getDefaultTransitionProps';
import getFormStyles from './getFormStyles';
export default function getOutlinedFieldStyles(props: StyleFunctionProps) {
const { theme, borderColor } = props;
const { focusBorderColor, errorColor } = getDefaultFormColors(props);
const formStyles = getFormStyles(props);
const transitionProps = getDefaultTransitionProps();
return {
border: '2px solid',
// filled input
backgroundColor: 'transparent',
borderColor: mode('gray.300', 'gray.600')(props),
...formStyles.input.filled,
...transitionProps,
_hover: {
borderColor: mode('gray.200', 'gray.500')(props),
...formStyles.input.hover,
},
_readOnly: {
boxShadow: 'none !important',
userSelect: 'all',
pointerEvents: 'none',
...formStyles.input.readOnly,
_hover: {
...formStyles.input.readOnly,
},
_focus: {
...formStyles.input.readOnly,
},
},
_disabled: {
opacity: 1,
backgroundColor: mode('blackAlpha.200', 'whiteAlpha.200')(props),
borderColor: 'transparent',
...formStyles.input.disabled,
cursor: 'not-allowed',
_hover: {
borderColor: 'transparent',
},
':-webkit-autofill': {
// background color for disabled input which value was selected from browser autocomplete popup
'-webkit-box-shadow': `0 0 0px 1000px ${ mode('rgba(16, 17, 18, 0.08)', 'rgba(255, 255, 255, 0.08)')(props) } inset`,
},
},
_invalid: {
borderColor: getColor(theme, errorColor),
...formStyles.input.error,
boxShadow: `none`,
_placeholder: {
color: formStyles.placeholder.error.color,
},
},
_focusVisible: {
...formStyles.input.focus,
zIndex: 1,
borderColor: getColor(theme, focusBorderColor),
boxShadow: 'md',
},
_placeholder: {
color: mode('blackAlpha.600', 'whiteAlpha.600')(props),
color: formStyles.placeholder.default.color,
},
// not filled input
':placeholder-shown:not(:focus-visible):not(:hover):not([aria-invalid=true])': { borderColor: borderColor || mode('gray.100', 'gray.700')(props) },
':placeholder-shown:not(:focus-visible):not(:hover):not([aria-invalid=true]):not([aria-readonly=true])': {
...formStyles.input.empty,
},
// not filled input with type="date"
':not(:placeholder-shown)[value=""]:not(:focus-visible):not(:hover):not([aria-invalid=true])': {
borderColor: borderColor || mode('gray.100', 'gray.700')(props),
color: 'gray.500',
':not(:placeholder-shown)[value=""]:not(:focus-visible):not(:hover):not([aria-invalid=true]):not([aria-readonly=true])': {
...formStyles.input.empty,
},
':-webkit-autofill': { transition: 'background-color 5000s ease-in-out 0s' },
+1 -1
View File
@@ -17,7 +17,7 @@ fi
# download assets for the running instance
dotenv \
-e $config_file \
-- bash -c './deploy/scripts/download_assets.sh ./public/assets'
-- bash -c './deploy/scripts/download_assets.sh ./public/assets/configs'
yarn svg:build-sprite
echo ""
+2 -2
View File
@@ -6,7 +6,7 @@ dotenv \
-e .env.local \
-e .env.development \
-e .env \
-- bash -c './deploy/scripts/download_assets.sh ./public/assets'
-- bash -c './deploy/scripts/download_assets.sh ./public/assets/configs'
yarn svg:build-sprite
echo ""
@@ -20,5 +20,5 @@ dotenv \
-e .env.local \
-e .env.development \
-e .env \
-- bash -c './deploy/scripts/make_envs_script.sh && next dev -- -p $NEXT_PUBLIC_APP_PORT' |
-- bash -c './deploy/scripts/make_envs_script.sh && next dev -p $NEXT_PUBLIC_APP_PORT' |
pino-pretty
+1 -1
View File
@@ -1,5 +1,5 @@
secrets_file="./configs/envs/.env.secrets"
favicon_folder="./public/favicon/"
favicon_folder="./public/assets/favicon/"
master_url="https://raw.githubusercontent.com/blockscout/frontend/main/tools/scripts/favicon.svg"
if [ ! -f "$secrets_file" ]; then

Some files were not shown because too many files have changed in this diff Show More