diff --git a/.github/workflows/webapp-deploy.yml b/.github/workflows/webapp-deploy.yml index 981012a14..8cac77950 100644 --- a/.github/workflows/webapp-deploy.yml +++ b/.github/workflows/webapp-deploy.yml @@ -74,7 +74,7 @@ jobs: env: COMMIT_SHA: ${{ github.sha }} run: | - for f in $(find ./dist/assets/ -name *.js.map) ; do + for f in $(find ./dist/assets/ -name *.js.map) ; do curl -X POST "https://kibana-sourcemaps.aragon.org/api/apm/sourcemaps" \ -H 'Content-Type: multipart/form-data' \ -H 'kbn-xsrf: true' \ diff --git a/.jest/setup.ts b/.jest/setup.ts index c44951a68..7b0828bfa 100644 --- a/.jest/setup.ts +++ b/.jest/setup.ts @@ -1 +1 @@ -import '@testing-library/jest-dom' +import '@testing-library/jest-dom'; diff --git a/package.json b/package.json index 4bb491751..c98080406 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@apollo/client": "^3.5.8", - "@aragon/ods": "^0.2.18", + "@aragon/ods": "^0.3.0", "@aragon/sdk-client": "^1.14.0", "@elastic/apm-rum-react": "^2.0.0", "@radix-ui/react-accordion": "^1.1.2", @@ -46,17 +46,17 @@ "framer-motion": "^5.2.1", "graphql": "^16.8.1", "graphql-request": "^4.3.0", - "i18next": "^21.3.3", + "i18next": "^23.5.1", "numeral": "^2.0.6", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", "react-hook-form": "^7.22.2", - "react-i18next": "^11.12.0", + "react-i18next": "^13.2.2", "react-responsive-carousel": "^3.2.23", "react-router-dom": "^6.0.2", "rudder-sdk-js": "^2.20.0", "sanitize-html": "^2.10.0", - "styled-components": "^5.3.1", + "styled-components": "^6.0.8", "use-react-router-breadcrumbs": "^3.0.1", "viem": "^1.4.2", "wagmi": "^1.3.9" @@ -67,27 +67,27 @@ "@babel/preset-react": "^7.14.5", "@babel/preset-typescript": "^7.14.5", "@synthetixio/synpress": "^3.5.1", - "@testing-library/jest-dom": "^5.14.1", - "@testing-library/react": "^12.0.0", + "@testing-library/jest-dom": "^6.1.3", + "@testing-library/react": "^14.0.0", "@types/big.js": "^6.1.5", "@types/file-saver": "^2.0.5", + "@types/jest": "^29.5.5", "@types/lodash": "^4.14.196", "@types/node": "^16.18.11", "@types/numeral": "^2.0.2", - "@types/react": "^17.0.14", - "@types/react-dom": "^17.0.9", + "@types/react": "^18.2.23", + "@types/react-dom": "^18.2.8", "@types/react-router-dom": "^5.3.2", "@types/sanitize-html": "^2.9.0", - "@types/styled-components": "^5.1.15", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", "@vitejs/plugin-react": "^4.0.4", "@walletconnect/types": "^2.9.0", "autoprefixer": "^10.3.1", - "babel-jest": "^27.0.6", + "babel-jest": "^29.7.0", "babel-preset-vite": "^1.0.4", "cross-env": "^7.0.3", - "cypress": "^12.3.0", + "cypress": "^13.2.0", "env-cmd": "^10.1.0", "eslint": "^7.30.0", "eslint-config-prettier": "^8.3.0", @@ -95,7 +95,8 @@ "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-tailwindcss": "^3.13.0", - "jest": "^26", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "postcss": "^8.4.31", "prettier": "2.3.2", "rollup-plugin-analyzer": "^4.0.0", diff --git a/src/components/accordionMethod/index.tsx b/src/components/accordionMethod/index.tsx index 64212190b..0cd6798e9 100644 --- a/src/components/accordionMethod/index.tsx +++ b/src/components/accordionMethod/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {ReactNode} from 'react'; import * as Accordion from '@radix-ui/react-accordion'; import { AlertInline, @@ -12,8 +12,10 @@ import { } from '@aragon/ods'; import styled from 'styled-components'; +export type AccordionType = 'action-builder' | 'execution-widget'; + export type AccordionMethodType = { - type: 'action-builder' | 'execution-widget'; + type: AccordionType; methodName: string; smartContractName?: string; verified?: boolean; @@ -22,6 +24,7 @@ export type AccordionMethodType = { additionalInfo?: string; dropdownItems?: ListItemProps[]; customHeader?: React.ReactNode; + children: ReactNode; }; export const AccordionMethod: React.FC = ({ @@ -38,6 +41,7 @@ export const AccordionMethod: React.FC = ({ export const AccordionMultiple: React.FC<{ defaultValue: string; className?: string; + children: ReactNode; }> = ({defaultValue, className, children}) => ( = ({ ); -export type AccordionType = Pick; - -const AccordionHeader = styled(Accordion.Header).attrs( - ({type}: AccordionType) => ({ +const AccordionHeader = styled(Accordion.Header).attrs<{type: AccordionType}>( + ({type}) => ({ className: `p-2 tablet:px-3 rounded-xl border border-ui-100 ${ type === 'action-builder' ? 'bg-white' : 'bg-ui-50' }`, }) -)` +)<{type: AccordionType}>` &[data-state='open'] { border-bottom-right-radius: 0; border-bottom-left-radius: 0; diff --git a/src/components/addLinks/header.tsx b/src/components/addLinks/header.tsx index 1705611a2..82162dcf2 100644 --- a/src/components/addLinks/header.tsx +++ b/src/components/addLinks/header.tsx @@ -23,7 +23,7 @@ const AddLinksHeader: React.FC = ({bgWhite}) => { export default AddLinksHeader; -const Container = styled.div.attrs(({bgWhite}: BgWhite) => ({ +const Container = styled.div.attrs<{bgWhite: BgWhite}>(({bgWhite}) => ({ className: `hidden tablet:flex p-2 space-x-2 ${ bgWhite ? 'bg-ui-50 border border-ui-100 rounded-t-xl' : 'bg-ui-0' }`, diff --git a/src/components/addLinks/row.tsx b/src/components/addLinks/row.tsx index 387a296da..f6598fb8f 100644 --- a/src/components/addLinks/row.tsx +++ b/src/components/addLinks/row.tsx @@ -203,7 +203,7 @@ const LinkRow: React.FC = ({ export default LinkRow; -const Container = styled.div.attrs(({bgWhite}: BgWhite) => ({ +const Container = styled.div.attrs<{bgWhite: BgWhite}>(({bgWhite}) => ({ className: `flex flex-wrap gap-x-2 gap-y-1.5 p-2 ${ bgWhite ? 'bg-ui-50 border border-t-0 border-ui-100 last:rounded-b-xl' diff --git a/src/components/daoCard/index.tsx b/src/components/daoCard/index.tsx index 304d6e061..724c1f36a 100644 --- a/src/components/daoCard/index.tsx +++ b/src/components/daoCard/index.tsx @@ -77,11 +77,11 @@ const Container = styled.button.attrs({ bg-white rounded-xl `, })` - :hover { + &:hover { box-shadow: 0px 4px 8px rgba(31, 41, 51, 0.04), 0px 0px 2px rgba(31, 41, 51, 0.06), 0px 0px 1px rgba(31, 41, 51, 0.04); } - :focus { + &:focus { box-shadow: 0px 0px 0px 2px #003bf5; } `; diff --git a/src/components/descriptionList/index.tsx b/src/components/descriptionList/index.tsx index d811db61e..0a2c1d495 100644 --- a/src/components/descriptionList/index.tsx +++ b/src/components/descriptionList/index.tsx @@ -5,7 +5,7 @@ import { CheckboxListItemProps, Tag, } from '@aragon/ods'; -import React from 'react'; +import React, {ReactNode} from 'react'; import {useTranslation} from 'react-i18next'; import styled from 'styled-components'; @@ -17,6 +17,7 @@ export type DescriptionListProps = { checkedState?: CheckboxListItemProps['type']; onChecked?: () => void; tagLabel?: string; + children: ReactNode; }; // TODO: This needs to be reworked, as it currently leads to nested DL (DLs get @@ -79,15 +80,15 @@ export const DescriptionListContainer: React.FC = ({ ); }; -export const Dt: React.FC = ({children}) => ( +export const Dt: React.FC<{children: ReactNode}> = ({children}) => ( {children} ); -export const Dd: React.FC = ({children}) => ( +export const Dd: React.FC<{children: ReactNode}> = ({children}) => ( {children} ); -export const Dl: React.FC = ({children}) => ( +export const Dl: React.FC<{children: ReactNode}> = ({children}) => ( {children} diff --git a/src/components/executionWidget/actions/walletConnectActionCard.tsx b/src/components/executionWidget/actions/walletConnectActionCard.tsx index 3025f4f5a..d8c9883b8 100644 --- a/src/components/executionWidget/actions/walletConnectActionCard.tsx +++ b/src/components/executionWidget/actions/walletConnectActionCard.tsx @@ -100,13 +100,13 @@ export const WCActionCard: React.FC = ({ ); }; -type ContentProps = Pick; - -const Content = styled.div.attrs(({type}: ContentProps) => ({ - className: `px-2 desktop:px-3 p-3 border border-ui-100 border-t-0 space-y-2 desktop:space-y-3 rounded-b-xl ${ - type === 'action-builder' ? 'bg-ui-0' : 'bg-ui-50' - }`, -}))``; +const Content = styled.div.attrs<{type: WCActionCardActionCardProps['type']}>( + ({type}) => ({ + className: `px-2 desktop:px-3 p-3 border border-ui-100 border-t-0 space-y-2 desktop:space-y-3 rounded-b-xl ${ + type === 'action-builder' ? 'bg-ui-0' : 'bg-ui-50' + }`, + }) +)<{type: WCActionCardActionCardProps['type']}>``; const FormGroup = styled.div.attrs({ className: 'space-y-2 desktop:space-y-3', diff --git a/src/components/fullScreenStepper/fullScreenStepper.tsx b/src/components/fullScreenStepper/fullScreenStepper.tsx index 3afc94722..1f2d0cbf2 100644 --- a/src/components/fullScreenStepper/fullScreenStepper.tsx +++ b/src/components/fullScreenStepper/fullScreenStepper.tsx @@ -31,7 +31,7 @@ export type FullScreenStepperProps = { type FullScreenStepperContextType = { currentStep: number; - setStep: React.Dispatch>; + setStep: (newStep: number) => void; prev: () => void; next: () => void; }; @@ -209,11 +209,13 @@ type FormLayoutProps = { fullWidth: boolean; }; -const FormLayout = styled.div.attrs(({fullWidth}: FormLayoutProps) => ({ - className: `mt-5 desktop:mt-8 mx-auto space-y-5 ${ - !fullWidth && 'desktop:w-3/5' - }`, -}))``; +const FormLayout = styled.div.attrs<{fullWidth: FormLayoutProps}>( + ({fullWidth}) => ({ + className: `mt-5 desktop:mt-8 mx-auto space-y-5 ${ + !fullWidth && 'desktop:w-3/5' + }`, + }) +)``; const FormFooter = styled.div.attrs({ className: 'flex justify-between desktop:pt-3', diff --git a/src/components/fullScreenStepper/step.tsx b/src/components/fullScreenStepper/step.tsx index 0b80b3ac2..252f20038 100644 --- a/src/components/fullScreenStepper/step.tsx +++ b/src/components/fullScreenStepper/step.tsx @@ -1,4 +1,4 @@ -import React, {ReactElement} from 'react'; +import React, {ReactElement, ReactNode} from 'react'; export type StepProps = { includeStepper?: boolean; @@ -16,6 +16,7 @@ export type StepProps = { onNextButtonClicked?: (next: () => void) => void; // This method can use to trigger validations once user clicked on disabled next button onNextButtonDisabledClicked?: () => void; + children: ReactNode; }; export const Step: React.FC = ({children}) => { diff --git a/src/components/multisigMinimumApproval/index.tsx b/src/components/multisigMinimumApproval/index.tsx index 3083b196d..f19cf88da 100644 --- a/src/components/multisigMinimumApproval/index.tsx +++ b/src/components/multisigMinimumApproval/index.tsx @@ -7,7 +7,8 @@ import { useWatch, ValidateResult, } from 'react-hook-form'; -import {TFunction, useTranslation} from 'react-i18next'; +import {useTranslation} from 'react-i18next'; +import {TFunction} from 'i18next'; import {CORRECTION_DELAY} from 'utils/constants'; import MinimumApproval from './minimumApproval'; diff --git a/src/components/navLink/index.tsx b/src/components/navLink/index.tsx index 405129629..ee7ab8ca4 100644 --- a/src/components/navLink/index.tsx +++ b/src/components/navLink/index.tsx @@ -81,7 +81,7 @@ const NavLink = ({caller, data, onItemClick}: NavLinkProps) => { } }; -const NavItem = styled.button.attrs(({isSelected}: {isSelected: boolean}) => { +const NavItem = styled.button.attrs<{isSelected: boolean}>(({isSelected}) => { let className = 'py-1.5 px-2 rounded-xl font-bold hover:text-primary-500 ' + 'active:text-primary-700 disabled:text-ui-300 disabled:bg-ui-50' + diff --git a/src/components/proposalList/index.tsx b/src/components/proposalList/index.tsx index 7286b34ce..28194f03e 100644 --- a/src/components/proposalList/index.tsx +++ b/src/components/proposalList/index.tsx @@ -2,7 +2,8 @@ import {MultisigProposalListItem} from '@aragon/sdk-client'; import {CardProposal, CardProposalProps, Spinner} from '@aragon/ods'; import {BigNumber} from 'ethers'; import React, {useMemo} from 'react'; -import {TFunction, useTranslation} from 'react-i18next'; +import {useTranslation} from 'react-i18next'; +import {TFunction} from 'i18next'; import {NavigateFunction, generatePath, useNavigate} from 'react-router-dom'; import {useNetwork} from 'context/network'; diff --git a/src/components/temporary/section.tsx b/src/components/temporary/section.tsx index 9fd4a05c9..2c8234865 100644 --- a/src/components/temporary/section.tsx +++ b/src/components/temporary/section.tsx @@ -1,6 +1,7 @@ -import React from 'react'; +import React, {ReactNode} from 'react'; type TemporarySectionProps = { + children?: ReactNode; purpose?: string; }; diff --git a/src/containers/actionBuilder/addAddresses/accordionSummary.tsx b/src/containers/actionBuilder/addAddresses/accordionSummary.tsx index 39419f706..afd0d9e0e 100644 --- a/src/containers/actionBuilder/addAddresses/accordionSummary.tsx +++ b/src/containers/actionBuilder/addAddresses/accordionSummary.tsx @@ -70,11 +70,11 @@ const AccordionSummary: React.FC = ({ ); }; -const Footer = styled.div.attrs(({type}: AccordionType) => ({ +const Footer = styled.div.attrs<{type: AccordionType}>(({type}) => ({ className: `space-y-1.5 bg-ui-0 rounded-b-xl border border-t-0 border-ui-100 ${ type === 'action-builder' ? 'bg-white p-3' : 'bg-ui-50 p-2' }`, -}))``; +}))<{type: AccordionType}>``; const BoldedText = styled.span.attrs({ className: 'font-bold text-ui-800 ft-text-base', diff --git a/src/containers/actionBuilder/addAddresses/index.tsx b/src/containers/actionBuilder/addAddresses/index.tsx index ce927487d..4bdfcb2af 100644 --- a/src/containers/actionBuilder/addAddresses/index.tsx +++ b/src/containers/actionBuilder/addAddresses/index.tsx @@ -49,7 +49,7 @@ const AddAddresses: React.FC = ({ control, }); - const {fields, update, append, remove} = useFieldArray({ + const {fields, update, replace, append, remove} = useFieldArray({ control, name: memberListKey, }); @@ -66,11 +66,11 @@ const AddAddresses: React.FC = ({ *************************************************/ useEffect(() => { if (controlledWallets.length === 0) { - append({address: '', ensName: ''}); + replace({address: '', ensName: ''}); } setValue(`actions.${actionIndex}.name`, 'add_address'); - }, [actionIndex, append, controlledWallets.length, setValue]); + }, [actionIndex, replace, controlledWallets.length, setValue]); /************************************************* * Callbacks and Handlers * diff --git a/src/containers/actionBuilder/mintTokens/index.tsx b/src/containers/actionBuilder/mintTokens/index.tsx index 92aeb6acd..a7f4c2464 100644 --- a/src/containers/actionBuilder/mintTokens/index.tsx +++ b/src/containers/actionBuilder/mintTokens/index.tsx @@ -132,7 +132,7 @@ export const MintTokenForm: React.FC = ({ ); const {setValue, trigger, formState, control, resetField} = useFormContext(); - const {fields, append, remove, update} = useFieldArray({ + const {fields, append, replace, remove, update} = useFieldArray({ name: `actions.${actionIndex}.inputs.mintTokensToWallets`, }); @@ -171,15 +171,15 @@ export const MintTokenForm: React.FC = ({ *************************************************/ useEffect(() => { - // set-up form on first load/reset + // Setup form on first load/reset using replace to avoid calling append multiple times if (fields.length === 0) { - append({web3Address: {address: '', ensName: ''}, amount: '0'}); + replace({web3Address: {address: '', ensName: ''}, amount: '0'}); } if (!actionName) { setValue(`actions.${actionIndex}.name`, 'mint_tokens'); } - }, [actionIndex, actionName, append, fields.length, setValue]); + }, [actionIndex, actionName, replace, fields.length, setValue]); useEffect(() => { // check for empty address fields on blur. diff --git a/src/containers/actionBuilder/scc/index.tsx b/src/containers/actionBuilder/scc/index.tsx index 7cfb4320a..1c4b0c5b0 100644 --- a/src/containers/actionBuilder/scc/index.tsx +++ b/src/containers/actionBuilder/scc/index.tsx @@ -1,5 +1,5 @@ import {AlertInline, ListItemAction} from '@aragon/ods'; -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {useWatch} from 'react-hook-form'; import {useTranslation} from 'react-i18next'; @@ -18,14 +18,21 @@ const SCCAction: React.FC = ({ }) => { const {t} = useTranslation(); const {removeAction} = useActionsContext(); - const [actionData] = useWatch({ - name: [`actions.${actionIndex}`], + const actionData = useWatch({ + name: `actions.${actionIndex}`, }); const {alert} = useAlertContext(); const {network} = useNetwork(); const [isValid, setIsValid] = useState(true); - validateSCCAction(actionData, network).then(res => setIsValid(res)); + useEffect(() => { + const validateAction = async () => { + const isValid = await validateSCCAction(actionData, network); + setIsValid(isValid); + }; + + validateAction(); + }, [actionData, network]); const methodActions = (() => { const result = []; diff --git a/src/containers/configureActions/index.tsx b/src/containers/configureActions/index.tsx index 8be9d8233..c164c5d4d 100644 --- a/src/containers/configureActions/index.tsx +++ b/src/containers/configureActions/index.tsx @@ -57,16 +57,11 @@ const ConfigureActions: React.FC = ({ const existentActions = actions.map(actionItem => actionItem.name); initialActions.forEach(actionType => { - const alreadyAddedActionIndex = existentActions.indexOf(actionType); - - if (alreadyAddedActionIndex === -1) { - addAction({name: actionType}); - } else { - existentActions.splice(alreadyAddedActionIndex, 1); + if (!existentActions.includes(actionType)) { + addAction({name: actionType}, false); } }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [actions, addAction, initialActions]); const handleAddNewActionClick = () => { if (onAddNewActionClick) { diff --git a/src/containers/exploreFooter/__tests__/footer.test.tsx b/src/containers/exploreFooter/__tests__/footer.test.tsx index f0ccd749c..471fb338d 100644 --- a/src/containers/exploreFooter/__tests__/footer.test.tsx +++ b/src/containers/exploreFooter/__tests__/footer.test.tsx @@ -4,7 +4,6 @@ import {render, screen} from '@testing-library/react'; import Footer from '..'; describe('Footer', () => { - // eslint-disable-next-line function setup() { render(