Skip to content

Commit

Permalink
Feat/ext 5 (#424)
Browse files Browse the repository at this point in the history
* nit: change stop-opacity in svg to stopOpacity

* fix: improve query url state + fix no select select all

* lint: fix ts lint

* chores: add pre-commit hook to catch ts errors

* fix: replace undefined with null

---------

Co-authored-by: Majorfi <[email protected]>
  • Loading branch information
Majorfi and Majorfi authored Nov 15, 2023
1 parent 7d2f839 commit 49d74b0
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 99 deletions.
2 changes: 1 addition & 1 deletion .lintstagedrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"*.ts|*.tsx": ["yarn prettier-format", "eslint"],
"*.ts|*.tsx": ["yarn prettier-format", "yarn eslint --quiet --fix", "bash -c tsc --noEmit"],
"*.scss|*.css": "yarn prettier-format"
}
37 changes: 10 additions & 27 deletions apps/common/components/MultiSelectDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Fragment, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {Fragment, useCallback, useMemo, useRef, useState} from 'react';
import {Combobox, Transition} from '@headlessui/react';
import {useClickOutside, useThrottledState} from '@react-hookz/web';
import {Renderable} from '@yearn-finance/web-lib/components/Renderable';
Expand Down Expand Up @@ -120,26 +120,17 @@ const getFilteredOptions = ({

export function MultiSelectDropdown({options, onSelect, placeholder = '', ...props}: TMultiSelectProps): ReactElement {
const [isOpen, set_isOpen] = useThrottledState(false, 400);
const [currentOptions, set_currentOptions] = useState<TMultiSelectOptionProps[]>(options);
const [areAllSelected, set_areAllSelected] = useState(false);
const [query, set_query] = useState('');
const areAllSelected = useMemo((): boolean => options.every(({isSelected}): boolean => isSelected), [options]);
const componentRef = useRef(null);

useEffect((): void => {
set_currentOptions(options);
}, [options]);

useEffect((): void => {
set_areAllSelected(currentOptions.every(({isSelected}): boolean => isSelected));
}, [currentOptions]);

useClickOutside(componentRef, (): void => {
set_isOpen(false);
});

const filteredOptions = useMemo(
(): TMultiSelectOptionProps[] => getFilteredOptions({query, currentOptions}),
[currentOptions, query]
(): TMultiSelectOptionProps[] => getFilteredOptions({query, currentOptions: options}),
[options, query]
);

const getDisplayName = useCallback(
Expand All @@ -165,32 +156,29 @@ export function MultiSelectDropdown({options, onSelect, placeholder = '', ...pro

const handleOnCheckboxClick = useCallback(
({value}: TMultiSelectOptionProps): void => {
const currentState = currentOptions.map(
const currentState = options.map(
(o): TMultiSelectOptionProps => (o.value === value ? {...o, isSelected: !o.isSelected} : o)
);
set_areAllSelected(!currentState.some(({isSelected}): boolean => !isSelected));
set_currentOptions(currentState);
onSelect(currentState);
},
[currentOptions, onSelect]
[options, onSelect]
);

const handleOnContainerClick = useCallback(
({value}: TMultiSelectOptionProps): void => {
const currentState = currentOptions.map(
const currentState = options.map(
(o): TMultiSelectOptionProps =>
o.value === value ? {...o, isSelected: true} : {...o, isSelected: false}
);
set_areAllSelected(false);
onSelect(currentState);
},
[currentOptions, onSelect]
[options, onSelect]
);

return (
<Combobox
ref={componentRef}
value={currentOptions}
value={options}
onChange={(options): void => {
// Just used for the select/desect all options
const lastIndex = options.length - 1;
Expand All @@ -207,9 +195,6 @@ export function MultiSelectDropdown({options, onSelect, placeholder = '', ...pro
isSelected: !elementSelected.isSelected
})
);

set_areAllSelected(!elementSelected.isSelected);
set_currentOptions(currentState);
onSelect(currentState);
}}
multiple>
Expand Down Expand Up @@ -246,9 +231,7 @@ export function MultiSelectDropdown({options, onSelect, placeholder = '', ...pro
leave={'transition duration-75 ease-out'}
leaveFrom={'transform scale-100 opacity-100'}
leaveTo={'transform scale-95 opacity-0'}
afterLeave={(): void => {
set_query('');
}}>
afterLeave={(): void => set_query('')}>
<Combobox.Options
className={cl(
props.comboboxOptionsClassName,
Expand Down
39 changes: 30 additions & 9 deletions apps/common/hooks/useChains.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,44 @@
import {useMemo} from 'react';
import {useConnect} from 'wagmi';
import {useCustomCompareMemo, useDeepCompareMemo} from '@react-hookz/web';
import {ImageWithFallback} from '@common/components/ImageWithFallback';

import {useWhyDidYouUpdate} from './useWhyDidYouUpdate';

import type {Connector} from 'wagmi';
import type {TMultiSelectOptionProps} from '@common/components/MultiSelectDropdown';
import type {Chain} from '@wagmi/chains';

export function useChainOptions(chains: number[]): TMultiSelectOptionProps[] {
export function useChainOptions(chains: number[] | null): TMultiSelectOptionProps[] {
const {connectors} = useConnect();

const options = useMemo((): TMultiSelectOptionProps[] => {
const injectedConnector = connectors.find((e): boolean => e.id.toLocaleLowerCase() === 'injected');
if (!injectedConnector) {
return [];
const injectedChains = useCustomCompareMemo(
(): Chain[] | undefined => {
const injectedConnector = connectors.find((e): boolean => e.id.toLocaleLowerCase() === 'injected');
if (!injectedConnector) {
return [];
}
const noFork = injectedConnector.chains.filter(({id}): boolean => id !== 1337);
return noFork;
},
[connectors],
(savedDeps: [Connector[]], deps: [Connector[]]): boolean => {
for (const savedDep of savedDeps[0]) {
if (!deps[0].find((dep): boolean => dep.id === savedDep.id)) {
return false;
}
}
return true;
}
const noFork = injectedConnector.chains.filter(({id}): boolean => id !== 1337);
);

const options = useDeepCompareMemo((): TMultiSelectOptionProps[] => {
const _options = [];
for (const chain of noFork) {
for (const chain of injectedChains || []) {
_options.push({
label: chain.name,
value: chain.id,
isSelected: chains.includes(chain.id),
isSelected: chains?.includes(chain.id) || false,
icon: (
<ImageWithFallback
src={`${process.env.BASE_YEARN_CHAIN_URI}/${chain.id}/logo-128.png`}
Expand All @@ -31,7 +50,9 @@ export function useChainOptions(chains: number[]): TMultiSelectOptionProps[] {
});
}
return _options;
}, [chains, connectors]);
}, [injectedChains, chains]);

useWhyDidYouUpdate('useChainOptions', {chains, injectedChains});

return options;
}
Expand Down
16 changes: 8 additions & 8 deletions apps/vaults-v3/Mark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export function V3Mask(props: React.SVGProps<SVGSVGElement>): ReactElement {
<stop
offset={'1'}
stopColor={'white'}
stop-opacity={'0.18'}
stopOpacity={'0.18'}
/>
</linearGradient>
<linearGradient
Expand All @@ -253,7 +253,7 @@ export function V3Mask(props: React.SVGProps<SVGSVGElement>): ReactElement {
<stop
offset={'1'}
stopColor={'white'}
stop-opacity={'0.18'}
stopOpacity={'0.18'}
/>
</linearGradient>
<linearGradient
Expand All @@ -280,7 +280,7 @@ export function V3Mask(props: React.SVGProps<SVGSVGElement>): ReactElement {
<stop
offset={'1'}
stopColor={'white'}
stop-opacity={'0.1'}
stopOpacity={'0.1'}
/>
</linearGradient>
<linearGradient
Expand All @@ -307,7 +307,7 @@ export function V3Mask(props: React.SVGProps<SVGSVGElement>): ReactElement {
<stop
offset={'1'}
stopColor={'white'}
stop-opacity={'0.1'}
stopOpacity={'0.1'}
/>
</linearGradient>
<linearGradient
Expand All @@ -334,7 +334,7 @@ export function V3Mask(props: React.SVGProps<SVGSVGElement>): ReactElement {
<stop
offset={'1'}
stopColor={'white'}
stop-opacity={'0.1'}
stopOpacity={'0.1'}
/>
</linearGradient>
<linearGradient
Expand All @@ -361,7 +361,7 @@ export function V3Mask(props: React.SVGProps<SVGSVGElement>): ReactElement {
<stop
offset={'1'}
stopColor={'white'}
stop-opacity={'0.1'}
stopOpacity={'0.1'}
/>
</linearGradient>
<linearGradient
Expand All @@ -388,7 +388,7 @@ export function V3Mask(props: React.SVGProps<SVGSVGElement>): ReactElement {
<stop
offset={'1'}
stopColor={'white'}
stop-opacity={'0.1'}
stopOpacity={'0.1'}
/>
</linearGradient>
<linearGradient
Expand All @@ -415,7 +415,7 @@ export function V3Mask(props: React.SVGProps<SVGSVGElement>): ReactElement {
<stop
offset={'1'}
stopColor={'white'}
stop-opacity={'0.1'}
stopOpacity={'0.1'}
/>
</linearGradient>
</defs>
Expand Down
10 changes: 5 additions & 5 deletions apps/vaults-v3/components/Filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import type {ReactElement} from 'react';
import type {TMultiSelectOptionProps} from '@common/components/MultiSelectDropdown';

type TListHero = {
categories: string[];
chains: number[];
categories: string[] | null;
chains: number[] | null;
searchValue: string;
onChangeCategories: (categories: string[]) => void;
onChangeChains: (chains: number[]) => void;
onChangeCategories: (categories: string[] | null) => void;
onChangeChains: (chains: number[] | null) => void;
onSearch: (searchValue: string) => void;
};

Expand All @@ -31,7 +31,7 @@ export function Filters({
([key, value]): TMultiSelectOptionProps => ({
value: key,
label: value.replaceAll(' Vaults', ''),
isSelected: categories.includes(key)
isSelected: categories?.includes(key) || false
})
);
return options;
Expand Down
14 changes: 7 additions & 7 deletions apps/vaults/components/ListHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import type {TDict} from '@yearn-finance/web-lib/types';
import type {TMultiSelectOptionProps} from '@common/components/MultiSelectDropdown';

type TListHero = {
categories: string[];
categories: string[] | null;
possibleCategories: TDict<string>;
chains: number[];
chains: number[] | null;
searchValue: string;
onChangeCategories: (categories: string[]) => void;
onChangeChains: (chains: number[]) => void;
onChangeCategories: (categories: string[] | null) => void;
onChangeChains: (chains: number[] | null) => void;
onSearch: (searchValue: string) => void;
shouldHideChainSelector?: boolean;
};
Expand All @@ -35,7 +35,7 @@ export function ListHero({
([key, value]): TMultiSelectOptionProps => ({
value: key,
label: value,
isSelected: categories.includes(key)
isSelected: categories?.includes(key) || false
})
);
return options;
Expand All @@ -53,7 +53,7 @@ export function ListHero({
const selectedChains = options
.filter((o): boolean => o.isSelected)
.map((option): number => Number(option.value));
onChangeChains(selectedChains);
onChangeChains(selectedChains.length === 0 ? null : selectedChains);
}}
/>
</div>
Expand All @@ -67,7 +67,7 @@ export function ListHero({
const selectedCategories = options
.filter((o): boolean => o.isSelected)
.map((option): string => String(option.value));
onChangeCategories(selectedCategories);
onChangeCategories(selectedCategories.length === 0 ? null : selectedCategories);
}}
/>
</div>
Expand Down
6 changes: 4 additions & 2 deletions apps/vaults/components/list/VaultListFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ export function VaultListFactory(): ReactElement {
** It contains either the list of vaults, is some are available, or a message to the user.
**********************************************************************************************/
const VaultList = useMemo((): ReactNode => {
const filteredByChains = sortedVaultsToDisplay.filter(({chainID}): boolean => chains.includes(chainID));
const filteredByChains = sortedVaultsToDisplay.filter(
({chainID}): boolean => chains?.includes(chainID) || false
);

if (isLoadingVaultList || isZero(filteredByChains.length) || chains.length === 0) {
if (isLoadingVaultList || isZero(filteredByChains.length) || !chains || chains.length === 0) {
return (
<VaultsListEmptyFactory
isLoading={isLoadingVaultList}
Expand Down
26 changes: 12 additions & 14 deletions apps/vaults/components/list/VaultsListEmpty.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import type {TYDaemonVaults} from '@common/schemas/yDaemonVaultsSchemas';
type TVaultListEmpty = {
sortedVaultsToDisplay: TYDaemonVaults;
currentSearch: string;
currentCategories: string[];
currentChains: number[];
onChangeCategories: (value: string[]) => void;
onChangeChains: (value: number[]) => void;
currentCategories: string[] | null;
currentChains: number[] | null;
onChangeCategories: (value: string[] | null) => void;
onChangeChains: (value: number[] | null) => void;
isLoading: boolean;
};
export function VaultsListEmpty({
Expand All @@ -38,8 +38,8 @@ export function VaultsListEmpty({
if (
!isLoading &&
isZero(sortedVaultsToDisplay.length) &&
currentCategories.length === 1 &&
currentCategories.includes('holdings')
currentCategories?.length === 1 &&
currentCategories?.includes('holdings')
) {
return (
<div className={'mx-auto flex h-96 w-full flex-col items-center justify-center px-10 py-2 md:w-3/4'}>
Expand All @@ -55,7 +55,7 @@ export function VaultsListEmpty({
return (
<div className={'mx-auto flex h-96 w-full flex-col items-center justify-center gap-4 px-10 py-2 md:w-3/4'}>
<b className={'text-center text-lg'}>{'No data, reeeeeeeeeeee'}</b>
{currentCategories.length === ALL_VAULTS_CATEGORIES_KEYS.length ? (
{currentCategories?.length === ALL_VAULTS_CATEGORIES_KEYS.length ? (
<p className={'text-center text-neutral-600'}>{`The vault "${currentSearch}" does not exist`}</p>
) : (
<>
Expand All @@ -75,7 +75,7 @@ export function VaultsListEmpty({
</div>
);
}
if (!isLoading && currentChains.length === 0) {
if (!isLoading && currentChains && currentChains.length > 0) {
return (
<div className={'mx-auto flex h-96 w-full flex-col items-center justify-center gap-4 px-10 py-2 md:w-3/4'}>
<b className={'text-center text-lg'}>{'No data, reeeeeeeeeeee'}</b>
Expand Down Expand Up @@ -135,16 +135,14 @@ export function VaultsListEmptyFactory({
if (
!isLoading &&
isZero(sortedVaultsToDisplay.length) &&
currentCategories.length === 1 &&
currentCategories.includes('holdings')
currentCategories?.length === 1 &&
currentCategories?.includes('holdings')
) {
return (
<div className={'mx-auto flex h-96 w-full flex-col items-center justify-center px-10 py-2 md:w-3/4'}>
<b className={'text-center text-lg'}>{'Well this is awkward...'}</b>
<p className={'text-center text-neutral-600'}>
{
"You don't appear to have any deposits in our Factory Vaults. There's an easy way to change that 😏"
}
{"You don't appear to have any deposits in our Vaults. There's an easy way to change that 😏"}
</p>
</div>
);
Expand All @@ -161,7 +159,7 @@ export function VaultsListEmptyFactory({
</div>
);
}
if (!isLoading && currentChains.length === 0) {
if (!isLoading && (!currentChains || currentChains.length === 0)) {
return (
<div className={'mx-auto flex h-96 w-full flex-col items-center justify-center gap-4 px-10 py-2 md:w-3/4'}>
<b className={'text-center text-lg'}>{'No data, reeeeeeeeeeee'}</b>
Expand Down
Loading

0 comments on commit 49d74b0

Please sign in to comment.