From de74db4e909f1293d115f55a628b881d726c9f0d Mon Sep 17 00:00:00 2001 From: Ladislav Martincik Date: Wed, 10 Jul 2024 08:50:14 +0200 Subject: [PATCH 1/3] Add The Block as the main example --- html-production/index.html | 5 +++-- html-staging/index.html | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/html-production/index.html b/html-production/index.html index ae6d9b0..1dc4163 100644 --- a/html-production/index.html +++ b/html-production/index.html @@ -25,8 +25,9 @@

The shown widget is for demonstration purpose only

_acs_blink('init', { element: document.getElementById('acs-blink'), debug: true, - poolId: 'D9C7Yf5euSjpQ8Wo8XwJP7CWymm54oomeREkogiNf4yS', - poolName: "Coingecko", + poolName: "The Block", + poolId: 'Fxh4hDFHJuTfD3Eq4en36dTk8QvbsSMoTE5Y2hVX3qVt', + poolSlug: 'the-block', }); diff --git a/html-staging/index.html b/html-staging/index.html index 4f4b639..8a63e5c 100644 --- a/html-staging/index.html +++ b/html-staging/index.html @@ -25,7 +25,8 @@

The shown widget is for demonstration purpose only

_acs_blink('init', { element: document.getElementById('acs-blink'), debug: true, - poolId: '2hQSDVwJLbtwHzi3CKj8pmiQzLyfKZs5ZDhT1QZdHXv3', + poolId: 'Fxh4hDFHJuTfD3Eq4en36dTk8QvbsSMoTE5Y2hVX3qVt', + poolSlug: 'the-block', poolName: "The Block", }); From 66ebcd61b4c099fe60669f45c03f1dfe0d0ebe39 Mon Sep 17 00:00:00 2001 From: Ladislav Martincik Date: Wed, 10 Jul 2024 12:06:19 +0200 Subject: [PATCH 2/3] Run the tests on all branches and PRs --- .github/workflows/ci.yml | 14 +- jest.config.ts | 11 +- package.json | 14 +- src/App.tsx | 4 +- src/components/dialect/ui/ActionContainer.tsx | 4 +- src/components/dialect/ui/ActionLayout.tsx | 213 ++- .../dialect/ui/ActionLayoutSkeleton.tsx | 5 +- src/components/dialect/ui/Badge.tsx | 7 +- src/components/dialect/ui/Button.tsx | 6 +- src/components/dialect/ui/PulseLoader.tsx | 15 +- src/components/dialect/ui/Snackbar.tsx | 4 +- .../dialect/ui/SolanaWalletButton.tsx | 4 +- src/components/dialect/ui/icons/CheckIcon.tsx | 9 +- .../ui/icons/ExclamationShieldIcon.tsx | 8 +- .../dialect/ui/icons/InfoShieldIcon.tsx | 10 +- src/components/dialect/ui/icons/LinkIcon.tsx | 10 +- .../dialect/ui/icons/SpinnerDots.tsx | 4 +- src/index.d.ts | 4 +- src/layout/Main.tsx | 8 +- src/layout/Router.tsx | 4 +- src/libs/env.ts | 3 - src/loader.ts | 4 +- src/routes/Blink.tsx | 4 +- test/common.ts | 4 +- test/loader.spec.ts | 28 +- tsconfig.json | 6 +- yarn.lock | 1195 +++++++++++------ 27 files changed, 1042 insertions(+), 560 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf089f4..a3a0717 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,5 @@ name: CI -on: - push: - branches: - - main - pull_request: - branches: - - main - jobs: build-ci: runs-on: ubuntu-latest @@ -23,6 +15,6 @@ jobs: with: node-version: ${{ matrix.node-version }} - run: yarn install --immutable --immutable-cache --check-cache - - run: yarn run lint - - run: yarn run build-staging-release - - run: yarn run test + - run: yarn lint + - run: yarn build-staging-release + - run: yarn test diff --git a/jest.config.ts b/jest.config.ts index ca73169..d96f5a4 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,8 +1,13 @@ -import type { Config } from 'jest'; +import type { Config } from '@jest/types'; -const config: Config = { - verbose: true, +const config: Config.InitialOptions = { + preset: 'ts-jest', testEnvironment: 'jsdom', + globals: { + 'ts-jest': { + tsconfig: './tsconfig.json', + }, + }, }; export default config; diff --git a/package.json b/package.json index 97211be..12385fc 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dev": "NODE_ENV=development PORT=3000 webpack serve --env TARGET_ENV=development", "lint": "tslint --project tsconfig.json ./src/**/*.tsx ./src/**/*.ts ./src/**/*.js", "lint-fix": "tslint --project tsconfig.json ./src/**/*.tsx ./src/**/*.ts ./src/**/*.js --fix", - "test": "jest", + "test": "tsc && jest", "stats": "NODE_ENV=production webpack --env TARGET_ENV=staging --profile --json > stats.json", "knip": "knip" }, @@ -28,9 +28,10 @@ "@babel/preset-typescript": "^7.8.3", "@babel/runtime": "^7.19.4", "@statoscope/webpack-plugin": "^5.24.0", - "@tsconfig/recommended": "^1.0.1", + "@tsconfig/recommended": "^1.0.7", "@types/bn.js": "^5.1.1", - "@types/jest": "^25.2.1", + "@types/jest": "^29.5.12", + "@types/react": "^18.3.3", "@types/react-slider": "^1.3.1", "autoprefixer": "^10.4.19", "babel-loader": "^8.0.6", @@ -45,7 +46,7 @@ "dotenv-webpack": "^8.0.1", "glob": "^9.3.2", "inspectpack": "^4.7.1", - "jest": "^29.0.0", + "jest": "^29.7.0", "jest-environment-jsdom": "^29.2.1", "knip": "^0.9.0", "mini-css-extract-plugin": "^2.7.5", @@ -58,9 +59,10 @@ "style-loader": "^1.1.3", "svg-url-loader": "^8.0.0", "tailwindcss": "^3.4.4", + "ts-jest": "^29.2.1", "ts-node": "^10.9.1", "tslint": "^5.20.1", - "typescript": "^4.6.2", + "typescript": "^5", "webpack": "^5", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.11.1" @@ -70,9 +72,9 @@ "@solana/wallet-adapter-react": "^0.15.35", "@solana/wallet-adapter-react-ui": "^0.9.35", "@solana/web3.js": "^1.66.1", + "@supercharge/promise-pool": "^3.2.0", "@tiplink/wallet-adapter": "^2.1.16", "@tiplink/wallet-adapter-react-ui": "^0.1.11", - "@supercharge/promise-pool": "^3.2.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "clsx": "^1.2.1", diff --git a/src/App.tsx b/src/App.tsx index bd242a9..6d14d15 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ -import { h } from "preact"; -import React from 'react'; + // @ts-ignore +import React, { h } from "preact"; import { useMemo } from "preact/hooks"; import { diff --git a/src/components/dialect/ui/ActionContainer.tsx b/src/components/dialect/ui/ActionContainer.tsx index 7ee8844..44996cc 100644 --- a/src/components/dialect/ui/ActionContainer.tsx +++ b/src/components/dialect/ui/ActionContainer.tsx @@ -1,5 +1,5 @@ -import { h } from 'preact'; -import * as React from 'preact'; +// @ts-ignore +import React, { h } from 'preact'; import { useEffect, useMemo, useReducer, useState, useContext } from 'preact/compat'; import { Buffer } from 'buffer'; import { ActionLayout, ButtonProps } from './ActionLayout'; diff --git a/src/components/dialect/ui/ActionLayout.tsx b/src/components/dialect/ui/ActionLayout.tsx index 28d5b8d..a199ced 100644 --- a/src/components/dialect/ui/ActionLayout.tsx +++ b/src/components/dialect/ui/ActionLayout.tsx @@ -1,5 +1,5 @@ -import { h, Fragment, ComponentChildren } from 'preact'; -import * as React from 'preact'; +import * as React from 'react'; +import { h, Fragment, ComponentChildren, ComponentChild } from 'preact'; import { Button } from './Button'; import { @@ -9,12 +9,18 @@ import { LinkIcon, SpinnerDots, } from './icons'; -import { ReactNode, useState } from 'react'; +import clsx from 'clsx'; +import { useState } from 'preact/hooks'; import { Badge } from '../ui/Badge'; import { ExtendedActionState } from '../api/ActionsRegistry'; type ActionType = ExtendedActionState; +export interface FormProps { + inputs: Array>; + button: ButtonProps; +} + interface LayoutProps { image?: string; error?: string | null; @@ -27,6 +33,7 @@ interface LayoutProps { description: string; buttons?: ButtonProps[]; inputs?: InputProps[]; + form?: FormProps; } export interface ButtonProps { @@ -42,7 +49,8 @@ export interface InputProps { placeholder?: string; name: string; disabled: boolean; - button: ButtonProps; + required?: boolean; + button?: ButtonProps; } const Linkable = ({ @@ -50,7 +58,7 @@ const Linkable = ({ children, }: { url?: string | null; - children: ReactNode | ReactNode[]; + children: ComponentChildren | ComponentChild; }) => url ? ( @@ -60,64 +68,191 @@ const Linkable = ({ {children} ); +const ActionContent = ({ + form, + inputs, + buttons, +}: Pick) => { + if (form) { + return ; + } + + return ( +
+ {buttons && buttons.length > 0 && ( +
+ {buttons?.map((it, index) => ( +
+ +
+ ))} +
+ )} + {inputs?.map((input) => )} +
+ ); +}; + +const ActionForm = ({ form }: Required>) => { + const [values, setValues] = useState( + Object.fromEntries(form.inputs.map((i) => [i.name, ''])), + ); + + const onChange = (name: string, value: string) => { + setValues((prev) => ({ ...prev, [name]: value })); + }; + + const disabled = form.inputs.some((i) => i.required && values[i.name] === ''); + + return ( +
+ {form.inputs.map((input) => ( + onChange(input.name, v)} + /> + ))} + form.button.onClick(values)} + disabled={form.button.disabled || disabled} + /> +
+ ); +}; + export const ActionLayout = ({ + title, + description, + image, + websiteUrl, + websiteText, + type, + disclaimer, buttons, inputs, + form, error, success, }: LayoutProps) => { return ( -
-
- {buttons && buttons.length > 0 && ( -
- {buttons?.map((it, index) => ( -
- -
- ))} -
- )} - {inputs?.map((input) => )} -
- {success && ( - - {success} - +
+ {image && ( + + action-image + )} - {error && !success && ( - - {error} +
); }; -export const ActionInput = ({ +const ActionInput = ({ placeholder, name, button, disabled, -}: InputProps) => { + onChange: extOnChange, + required, +}: InputProps & { onChange?: (value: string) => void }) => { const [value, onChange] = useState(''); + const extendedChange = (e: React.ChangeEvent) => { + onChange(e.currentTarget.value); + extOnChange?.(e.currentTarget.value); + }; + + const placeholderWithRequired = + (placeholder || 'Type here...') + (required ? '*' : ''); + return (
onChange(e.target.value)} - className="bg-transparent ml-4 flex-1 truncate outline-none placeholder:text-quaternary disabled:bg-primary disabled:text-tertiary" + onChange={extendedChange} + className="bg-transparent my-3 ml-4 flex-1 truncate outline-none placeholder:text-quaternary disabled:text-tertiary" /> -
- button.onClick({ [name]: value })} - disabled={button.disabled || value === ''} - /> -
+ {button && ( +
+ button.onClick({ [name]: value })} + disabled={button.disabled || value === ''} + /> +
+ )}
); }; @@ -144,7 +279,7 @@ export const ActionButton = ({ ); - return text; + return text; }; return ( diff --git a/src/components/dialect/ui/ActionLayoutSkeleton.tsx b/src/components/dialect/ui/ActionLayoutSkeleton.tsx index d7c5872..a0950ed 100644 --- a/src/components/dialect/ui/ActionLayoutSkeleton.tsx +++ b/src/components/dialect/ui/ActionLayoutSkeleton.tsx @@ -1,6 +1,5 @@ -import { h } from 'preact'; -import React from 'preact'; -import PulseLoader from './PulseLoader'; +// @ts-ignore +import React, { h } from 'preact'; const ActionLayoutSkeleton = () => { return ( diff --git a/src/components/dialect/ui/Badge.tsx b/src/components/dialect/ui/Badge.tsx index 6d2fe09..3c77e5e 100644 --- a/src/components/dialect/ui/Badge.tsx +++ b/src/components/dialect/ui/Badge.tsx @@ -1,13 +1,12 @@ -import { h } from 'preact'; -import * as React from 'preact'; + // @ts-ignore +import React, { h, VNode } from 'preact'; import clsx from 'clsx'; -import type { ReactNode } from 'react'; type BadgeVariant = 'warning' | 'error' | 'default'; interface Props { variant?: BadgeVariant; - icon?: ReactNode; + icon?: VNode; children?: string; className?: string; } diff --git a/src/components/dialect/ui/Button.tsx b/src/components/dialect/ui/Button.tsx index 4287afc..3b84f7d 100644 --- a/src/components/dialect/ui/Button.tsx +++ b/src/components/dialect/ui/Button.tsx @@ -1,5 +1,5 @@ -import { h, ComponentChildren } from 'preact'; -import * as React from 'preact'; +// @ts-ignore +import React, { h, VNode } from 'preact'; import clsx from 'clsx'; export const Button = ({ @@ -13,7 +13,7 @@ export const Button = ({ disabled?: boolean; variant?: 'success' | 'default' | 'error'; className?: string; - children: ComponentChildren + children: VNode; }) => { return (