diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7cdf112e..8cd39e19 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -48,7 +48,7 @@ jobs: key: ${{ runner.os }}-contracts-v${{ secrets.CONTRACTS_CACHE_VERSION }}-${{ hashFiles('packages/*/contracts/**/*.sol') }} - name: Install dependencies - run: yarn install --frozen-lockfile + run: yarn ci - name: Build contracts run: yarn compile diff --git a/packages/react-app/.babelrc b/packages/react-app/.babelrc index 189bb6c5..735f85dc 100644 --- a/packages/react-app/.babelrc +++ b/packages/react-app/.babelrc @@ -8,12 +8,12 @@ "corejs": 3 } ], - "@babel/preset-react" + "@babel/preset-react", + "@babel/preset-typescript" ], "plugins": [ ["styled-components", { "displayName": true }], - "@babel/plugin-transform-react-jsx-source", - "@babel/plugin-proposal-class-properties" + "@babel/plugin-transform-react-jsx-source" ], "env": { "test": { diff --git a/packages/react-app/.eslintrc.json b/packages/react-app/.eslintrc.json index a71a5eff..33bc1790 100644 --- a/packages/react-app/.eslintrc.json +++ b/packages/react-app/.eslintrc.json @@ -42,6 +42,7 @@ "react/no-unused-prop-types": "warn", "jsx-props-no-spreading": "off", "react/jsx-props-no-spreading": "off", + "react/state-in-constructor": "off", "no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }] }, "settings": { diff --git a/packages/react-app/package.json b/packages/react-app/package.json index e5105c53..aafd0edb 100644 --- a/packages/react-app/package.json +++ b/packages/react-app/package.json @@ -87,19 +87,20 @@ "@babel/plugin-transform-react-jsx-source": "^7.12.13", "@babel/preset-env": "^7.7.4", "@babel/preset-react": "^7.7.4", + "@babel/preset-typescript": "^7.16.7", "@sentry/webpack-plugin": "^1.15.1", - "@testing-library/react-hooks": "^7.0.2", + "@testing-library/react-hooks": "^8.0.0", "@types/jest": "^27.4.1", + "@types/lodash-es": "^4.17.5", "@types/node": "^16.11.0", "@types/react": "^17.0.30", "@types/react-dom": "^17.0.9", + "@types/react-router-dom": "^5.3.1", + "@types/styled-components": "^5.1.25", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", - "@types/lodash-es": "^4.17.5", - "@types/react-router-dom": "^5.3.1", - "@types/styled-components": "^5.1.15", "babel-eslint": "^10.1.0", - "babel-plugin-styled-components": "^1.10.6", + "babel-plugin-styled-components": "^2.0.7", "babel-plugin-transform-class-properties": "^6.24.1", "cross-env": "^7.0.2", "eslint-config-prettier": "^8.1.0", @@ -142,7 +143,9 @@ "ci": "yarn install --pure-lockfile" }, "resolutions": { - "styled-components": "^5" + "styled-components": "^5", + "@types/react": "17.0.44", + "@types/react-dom": "17.0.15" }, "jest": { "setupFiles": [ diff --git a/packages/react-app/src/app.tsx b/packages/react-app/src/app.tsx index 055c9e34..e884b969 100644 --- a/packages/react-app/src/app.tsx +++ b/packages/react-app/src/app.tsx @@ -37,7 +37,6 @@ const AppStyled = styled.div` function App() { const { currentTheme } = useThemeContext(); - return ( diff --git a/packages/react-app/src/assets/quest-logo.tsx b/packages/react-app/src/assets/quest-logo.tsx index 8abf88db..93325d9b 100644 --- a/packages/react-app/src/assets/quest-logo.tsx +++ b/packages/react-app/src/assets/quest-logo.tsx @@ -6,7 +6,9 @@ type Props = { animated?: boolean; }; -const DivStyled = styled.div` +const DivStyled = styled.div<{ + animated?: boolean; +}>` @keyframes textScale1 { 0% { transform: scale(1); @@ -18,7 +20,7 @@ const DivStyled = styled.div` transform: scale(1); } } - ${({ animated }: any) => animated && `animation: textScale1 1.15s infinite;`} + ${({ animated }) => animated && `animation: textScale1 1.15s infinite;`} margin-bottom: ${GUpx()}; `; diff --git a/packages/react-app/src/components/account/account-button.tsx b/packages/react-app/src/components/account/account-button.tsx index 6e1a8371..cb8a3581 100644 --- a/packages/react-app/src/components/account/account-button.tsx +++ b/packages/react-app/src/components/account/account-button.tsx @@ -7,14 +7,17 @@ import HeaderModule from '../header/header-module'; // #region StyledComponents -const AccountButtonBackgroundStyled = styled.div` +const AccountButtonBackgroundStyled = styled.div<{ + background: string; + borderColor: string; +}>` position: absolute; bottom: -3px; right: -3px; width: 10px; height: 10px; - background: ${({ background }: any) => background}; - border: 2px solid ${({ borderColor }: any) => borderColor}; + background: ${({ background }) => background}; + border: 2px solid ${({ borderColor }) => borderColor}; border-radius: 50%; `; diff --git a/packages/react-app/src/components/dashboard.tsx b/packages/react-app/src/components/dashboard.tsx index 811b4eac..4cc99330 100644 --- a/packages/react-app/src/components/dashboard.tsx +++ b/packages/react-app/src/components/dashboard.tsx @@ -1,4 +1,4 @@ -import { GU, useTheme, textStyle } from '@1hive/1hive-ui'; +import { useTheme, textStyle } from '@1hive/1hive-ui'; import { useEffect, useState } from 'react'; import { ENUM_QUEST_VIEW_MODE } from 'src/constants'; import { DashboardModel } from 'src/models/dashboard.model'; @@ -52,7 +52,7 @@ export default function Dashboard() { }, []); return ( - + Bounty Pool} diff --git a/packages/react-app/src/components/field-input/address-field-input.tsx b/packages/react-app/src/components/field-input/address-field-input.tsx index ac03726a..3af82db9 100644 --- a/packages/react-app/src/components/field-input/address-field-input.tsx +++ b/packages/react-app/src/components/field-input/address-field-input.tsx @@ -2,7 +2,7 @@ import { TextInput, EthIdenticon, AddressField } from '@1hive/1hive-ui'; import { noop } from 'lodash-es'; import React from 'react'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; import { FieldInput } from './field-input'; // #region Styled @@ -19,10 +19,17 @@ const EthIdenticonStyled = styled(EthIdenticon)` height: 38.4px; `; -const WrapperStyled = styled.div` +const WrapperStyled = styled.div<{ + wide?: boolean; +}>` display: flex; flex-wrap: nowrap; - ${(props: any) => (props.wide ? 'width:100%;' : '')} + ${(props) => + props.wide && + css` + width: 100%; + `} + max-width: 100%; input { cursor: default; diff --git a/packages/react-app/src/components/field-input/amount-field-input.tsx b/packages/react-app/src/components/field-input/amount-field-input.tsx index ecc7c6d0..d2aecc31 100644 --- a/packages/react-app/src/components/field-input/amount-field-input.tsx +++ b/packages/react-app/src/components/field-input/amount-field-input.tsx @@ -37,12 +37,12 @@ const TokenBadgeStyled = styled(TokenBadge)` width: fit-content; `; -const AutoCompleteWrapperStyled = styled.div` +const AutoCompleteWrapperStyled = styled.div<{ wide?: boolean }>` flex-grow: 3; input { & + div { pointer-events: none; - ${({ wide }: any) => (wide ? 'justify-content: end;' : '')} + ${({ wide }) => (wide ? 'justify-content: end;' : '')} } } ul[role='listbox'] { @@ -60,13 +60,16 @@ const LineStyled = styled.div` display: flex; justify-content: space-between; `; - -const AmountTokenWrapperStyled = styled.div` +interface AmountTokenWrapperStyledProps { + isEdit?: boolean; + wide?: boolean; +} +const AmountTokenWrapperStyled = styled.div` display: flex; justify-content: flex-end; align-items: center; - ${({ wide, isEdit }: any) => (wide && isEdit ? '' : `padding-right:${GUpx()};`)} - ${({ wide }: any) => (wide ? `width:100%;` : 'max-width:100%;')} + ${({ wide, isEdit }) => (wide && isEdit ? '' : `padding-right:${GUpx()};`)} + ${({ wide }) => (wide ? `width:100%;` : 'max-width:100%;')} `; const IconEditStyled = styled(IconEdit)` diff --git a/packages/react-app/src/components/field-input/date-field-input.tsx b/packages/react-app/src/components/field-input/date-field-input.tsx index a7430769..61762854 100644 --- a/packages/react-app/src/components/field-input/date-field-input.tsx +++ b/packages/react-app/src/components/field-input/date-field-input.tsx @@ -1,28 +1,41 @@ import { useTheme } from '@1hive/1hive-ui'; import { connect } from 'formik'; import { noop } from 'lodash-es'; -import { CSSProperties, ReactNode } from 'react'; +import { CSSProperties, FocusEventHandler, ReactNode } from 'react'; +import { ThemeInterface } from 'src/styles/theme'; import { GUpx, isDarkTheme } from 'src/utils/style.util'; -import styled from 'styled-components'; +import styled, { css as _css } from 'styled-components'; import { addTime, dateFormat, ONE_HOUR_IN_MS } from '../../utils/date.utils'; import { FieldInput } from './field-input'; // #region StyledComponents -const InputStyled = styled.input` - ${(props: any) => (props.wide ? 'width:100%;' : '')} - border: 1px solid ${(props: any) => props.borderColor}; +const InputStyled = styled.input<{ + theme: ThemeInterface; + wide?: boolean; + isDarkTheme?: boolean; +}>` + ${(props: any) => + props.wide && + _css` + width: 100%; + `} + border: 1px solid ${({ theme }: any) => theme.border}; border-radius: 12px; - background-color: ${({ background }: any) => background}; + background-color: ${({ theme }: any) => theme.surface}; height: 40px; padding: ${GUpx()}; font-size: 14px; &:focus-visible { - border: 1px solid ${(props: any) => props.focusBorderColor}; + border: 1px solid ${({ theme }: any) => theme.accent}; outline: none; } &::-webkit-calendar-picker-indicator { - ${(props: any) => (props.isDarkTheme ? 'filter: invert();' : '')}; + ${(props) => + props.isDarkTheme && + _css` + filter: invert(); + `}; } `; @@ -40,7 +53,7 @@ type Props = { compact?: boolean; wide?: boolean; formik?: any; - onBlur?: Function; + onBlur?: FocusEventHandler & Function; error?: string | false; }; @@ -81,10 +94,8 @@ function DateFieldInput({ onChange={handleChange} onBlur={onBlur} style={css} - background={theme.surface} wide={wide} - borderColor={theme.border} - focusBorderColor={theme.accent} + theme={theme} // eslint-disable-next-line no-underscore-dangle isDarkTheme={isDarkTheme(theme)} /> diff --git a/packages/react-app/src/components/field-input/field-input.tsx b/packages/react-app/src/components/field-input/field-input.tsx index 1aa4a030..4f3a2c70 100644 --- a/packages/react-app/src/components/field-input/field-input.tsx +++ b/packages/react-app/src/components/field-input/field-input.tsx @@ -2,19 +2,36 @@ import { useTheme } from '@1hive/1hive-ui'; import React from 'react'; import Skeleton from 'react-loading-skeleton'; import { GUpx } from 'src/utils/style.util'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; import { HelpTooltip } from './help-tooltip'; -const FieldStyled = styled.div` - ${({ compact }: any) => (!compact ? `margin-bottom:${GUpx(1)}` : '')}; - ${({ isLoading, wide }: any) => (isLoading || wide ? `width: 100%;` : 'max-width: 100%;')}; +const FieldStyled = styled.div<{ + compact?: boolean; + isLoading?: boolean; + wide?: boolean; +}>` + ${({ compact }) => + !compact && + css` + margin-bottom: ${GUpx(1)}; + `}; + ${({ isLoading, wide }) => + isLoading || wide + ? css` + width: 100%; + ` + : css` + max-width: 100%; + `}; `; + const ErrorStyled = styled.span` color: ${(props: any) => props.theme.negative}; font-size: small; margin-left: ${GUpx(2)}; font-style: italic; `; + const LabelStyled = styled.label` color: ${(props: any) => props.color}; font-size: 12px; @@ -33,16 +50,21 @@ const LineStyled = styled.div` margin-top: ${GUpx(0.5)}; `; -const ContentWrapperStyled = styled.div` +const ContentWrapperStyled = styled.div<{ + compact?: boolean; + wide?: boolean; + align: string; + direction: string; +}>` display: flex; - align-items: ${({ align }: any) => align}; - ${(props: any) => (!props.compact ? 'min-height: 45px;' : '')} + align-items: ${({ align }) => align}; + ${(props) => (!props.compact ? 'min-height: 45px;' : '')} & > div { input { - ${({ wide }: any) => (wide ? `max-width:none;` : '')} + ${({ wide }) => (wide ? `max-width:none;` : '')} } } - flex-direction: ${({ direction }: any) => direction}; + flex-direction: ${({ direction }) => direction}; padding-left: ${GUpx(0.5)}; `; @@ -81,7 +103,7 @@ export function FieldInput({ const theme = useTheme(); const labelComponent = label && ( - + {label} {tooltip && } diff --git a/packages/react-app/src/components/field-input/help-tooltip.tsx b/packages/react-app/src/components/field-input/help-tooltip.tsx index 8ff5ab36..b1866793 100644 --- a/packages/react-app/src/components/field-input/help-tooltip.tsx +++ b/packages/react-app/src/components/field-input/help-tooltip.tsx @@ -29,7 +29,7 @@ type Props = { export const HelpTooltip = ({ tooltip, children }: Props) => { const theme = useTheme(); - const wrapperRef = useRef(null); + const wrapperRef = useRef(null); const handleEnter = () => { // Simulate help button click diff --git a/packages/react-app/src/components/field-input/text-field-input.tsx b/packages/react-app/src/components/field-input/text-field-input.tsx index 725e15a9..478532e5 100644 --- a/packages/react-app/src/components/field-input/text-field-input.tsx +++ b/packages/react-app/src/components/field-input/text-field-input.tsx @@ -8,10 +8,10 @@ import { FieldInput } from './field-input'; // #region Styled -const MaxLineStyled = styled.div` +const MaxLineStyled = styled.div<{ maxLine: number }>` margin-bottom: ${GUpx()}; display: -webkit-box; - -webkit-line-clamp: ${(props: any) => props.maxLine}; + -webkit-line-clamp: ${(props) => props.maxLine}; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; @@ -21,8 +21,8 @@ const MaxLineStyled = styled.div` } `; -const BlockStyled = styled.div` - ${({ wide }: any) => wide && 'width: 100%;'} +const BlockStyled = styled.div<{ wide?: boolean }>` + ${({ wide }) => wide && 'width: 100%;'} `; // #endregion diff --git a/packages/react-app/src/components/filter.tsx b/packages/react-app/src/components/filter.tsx index 37adb528..5ff2a472 100644 --- a/packages/react-app/src/components/filter.tsx +++ b/packages/react-app/src/components/filter.tsx @@ -1,7 +1,7 @@ import { Button, SearchInput, DropDown, useTheme, useViewport } from '@1hive/1hive-ui'; import { useFilterContext } from 'src/contexts/filter.context'; import { GUpx } from 'src/utils/style.util'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; import { DEFAULT_FILTER, ENUM_QUEST_STATE } from '../constants'; import DateFieldInput from './field-input/date-field-input'; import { FieldInput } from './field-input/field-input'; @@ -12,23 +12,36 @@ const StatusDropdownStyled = styled(DropDown)` border: 1px solid ${(props: any) => props.borderColor}; `; -const FilterWrapperStyled = styled.div` +const FilterWrapperStyled = styled.div<{ + colDisplay?: boolean; + isSmallResolution?: boolean; +}>` display: flex; flex-direction: row; align-items: center; justify-content: space-between; - flex-wrap: ${({ colDisplay }: any) => (colDisplay ? 'wrap' : 'no-wrap')}; + + flex-wrap: ${({ colDisplay }) => (colDisplay ? 'wrap' : 'no-wrap')}; + padding: 0 ${GUpx(2)}; - ${({ isSmallResolution }: any) => + + ${({ isSmallResolution }) => isSmallResolution - ? ` - margin-right: 20px; - padding-bottom: ${GUpx(2)}; - ` - : 'height:80px;'} // Size of scrollbar because parent is 100% + 20px + ? css` + margin-right: 20px; + padding-bottom: ${GUpx(2)}; + ` + : css` + height: 80px; + `} // Size of scrollbar because parent is 100% + 20px + // Each filter having a sibling & > div + div { - ${({ isSmallResolution }: any) => !isSmallResolution && `margin-left: ${GUpx()};`} + ${({ isSmallResolution }) => + !isSmallResolution && + css` + margin-left: ${GUpx()}; + `} } `; diff --git a/packages/react-app/src/components/footer.tsx b/packages/react-app/src/components/footer.tsx index f76c9edc..6436994c 100644 --- a/packages/react-app/src/components/footer.tsx +++ b/packages/react-app/src/components/footer.tsx @@ -9,7 +9,7 @@ import QuestModal from './modals/quest-modal'; // #region StyledComponent -const FooterContainerStyled = styled.div` +const FooterContainerStyled = styled.div<{ color: string }>` margin: auto; box-shadow: rgb(0 0 0 / 5%) 3px -2px 0px; display: flex; @@ -17,7 +17,7 @@ const FooterContainerStyled = styled.div` justify-content: space-between; a { - color: ${({ color }: any) => color} !important; + color: ${({ color }) => color} !important; } padding: ${GUpx(8)}; @@ -83,7 +83,7 @@ export default function footer() { const year = new Date().getFullYear(); return ( - + diff --git a/packages/react-app/src/components/header/header-title.tsx b/packages/react-app/src/components/header/header-title.tsx index 98faf9d0..4f02ed87 100644 --- a/packages/react-app/src/components/header/header-title.tsx +++ b/packages/react-app/src/components/header/header-title.tsx @@ -2,6 +2,7 @@ import { noop } from 'lodash'; import { Link } from 'react-router-dom'; import { GUpx } from 'src/utils/style.util'; import styled from 'styled-components'; +import { MouseEventHandler } from 'react'; import { ENUM_PAGES } from '../../constants'; import { LogoTitle } from '../../assets/logo-title'; @@ -22,7 +23,7 @@ const TitleLinkWrapperStyled = styled.div` // #endregion type Props = { - onClick?: Function; + onClick?: MouseEventHandler; }; export default function HeaderTitle({ onClick = noop }: Props) { diff --git a/packages/react-app/src/components/main-view.tsx b/packages/react-app/src/components/main-view.tsx index 8cae59ab..973bc3cd 100644 --- a/packages/react-app/src/components/main-view.tsx +++ b/packages/react-app/src/components/main-view.tsx @@ -1,12 +1,11 @@ import { Root, useViewport, Button, IconFilter } from '@1hive/1hive-ui'; -import React, { useEffect, useRef } from 'react'; +import React, { useEffect } from 'react'; import { useWallet } from 'src/contexts/wallet.context'; import { Logger } from 'src/utils/logger'; import { isConnected } from 'src/utils/web3.utils'; import styled from 'styled-components'; import { usePageContext } from 'src/contexts/page.context'; import Skeleton from 'react-loading-skeleton'; -import { useThemeContext } from 'src/contexts/theme.context'; import { GUpx } from 'src/utils/style.util'; import { useFilterContext } from 'src/contexts/filter.context'; import Header from './header'; @@ -22,8 +21,10 @@ const HeaderWrapperStyled = styled.div` width: 100%; `; -const ContentWrapperStyled = styled.div` - padding: ${({ isSmallResolution }: any) => (isSmallResolution ? GUpx() : GUpx(4))}; +const ContentWrapperStyled = styled.div<{ + isSmallResolution?: boolean; +}>` + padding: ${({ isSmallResolution }) => (isSmallResolution ? GUpx() : GUpx(4))}; min-height: calc(100vh - 80px); `; @@ -61,8 +62,6 @@ function MainView({ children }: Props) { const { activateWallet, walletAddress } = useWallet(); const { page } = usePageContext(); const { below } = useViewport(); - const headerRef = useRef(null); - const { currentTheme } = useThemeContext(); const { toggleFilter } = useFilterContext(); useEffect(() => { @@ -78,7 +77,7 @@ function MainView({ children }: Props) { return ( - +
{below('medium') && (