Skip to content

Commit

Permalink
fix: only display provided chains in ChainModal (#1156)
Browse files Browse the repository at this point in the history
* fix(ChainModal): only display provided chains

* refactor(ChainModal)

* ChainModal: remove transition prop

* setup react tests

* add ChainModal tests

* add changeset

* Revert "add ChainModal tests"

This reverts commit 0d4eba6.

* Revert "setup react tests"

This reverts commit 630fdc8.

* fix: merge conflicts

* test: List chains provided in <RainbowKitProvider />

* chore: amend changeset

---------

Co-authored-by: Daniel Sinclair <d@niel.nyc>
  • Loading branch information
greg-schrammel and DanielSinclair committed Jul 7, 2023
1 parent 6389fae commit 08e3f4c
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 62 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-papayas-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rainbow-me/rainbowkit': patch
---

Decoupled `chains` between `WagmiConfig` and `RainbowKitProvider` so that dApps can now supply a subset of supported chains to `RainbowKitProvider` to limit the chains a user can switch between, while maintaining a shared `WagmiConfig`.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import user from '@testing-library/user-event';
import React from 'react';
import { describe, expect, it } from 'vitest';
import { arbitrum, goerli, mainnet } from 'wagmi/chains';
import { arbitrum, goerli, mainnet, optimism } from 'wagmi/chains';
import { renderWithProviders } from '../../../test/';
import { ChainModal } from './ChainModal';

Expand Down Expand Up @@ -34,6 +34,31 @@ describe('<ChainModal />', () => {
expect(mainnetOption).toBeDisabled();
});

it('List chains provided in <RainbowKitProvider />', async () => {
const modal = renderWithProviders(<ChainModal onClose={() => {}} open />, {
chains: [mainnet, arbitrum, optimism],
mock: true,
props: { chains: [optimism] },
});

const optimismOption = await modal.findByTestId(
`rk-chain-option-${optimism.id}`
);

// optimism SHOULD be displayed
// as it was the only passed to RainbowKitProvider
expect(optimismOption).toBeInTheDocument();

// mainnet & arb SHOULD NOT be displayed
// even tho they're supported they were not passed to RainbowKitProvider
expect(
modal.queryByTestId(`rk-chain-option-${mainnet.id}`)
).not.toBeInTheDocument();
expect(
modal.queryByTestId(`rk-chain-option-${arbitrum.id}`)
).not.toBeInTheDocument();
});

it('Can switch chains', async () => {
let onCloseGotCalled = false;
const modal = renderWithProviders(
Expand Down
87 changes: 26 additions & 61 deletions packages/rainbowkit/src/components/ChainModal/ChainModal.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,41 @@
import React, {
Fragment,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import { useAccount, useDisconnect, useNetwork, useSwitchNetwork } from 'wagmi';
import React, { Fragment, useContext } from 'react';
import { useDisconnect, useNetwork, useSwitchNetwork } from 'wagmi';
import { isMobile } from '../../utils/isMobile';
import { AsyncImage } from '../AsyncImage/AsyncImage';
import { Box, BoxProps } from '../Box/Box';
import { Box } from '../Box/Box';
import { CloseButton } from '../CloseButton/CloseButton';
import { Dialog } from '../Dialog/Dialog';
import { DialogContent } from '../Dialog/DialogContent';
import { DisconnectSqIcon } from '../Icons/DisconnectSq';
import { MenuButton } from '../MenuButton/MenuButton';
import { AppContext } from '../RainbowKitProvider/AppContext';
import { useRainbowKitChainsById } from '../RainbowKitProvider/RainbowKitChainContext';
import { useRainbowKitChains } from '../RainbowKitProvider/RainbowKitChainContext';
import { Text } from '../Text/Text';

export interface ChainModalProps {
open: boolean;
onClose: () => void;
}

export function ChainModal({ onClose, open }: ChainModalProps) {
const { chain: activeChain } = useNetwork();
const { chains, error: networkError, switchNetwork } = useSwitchNetwork();
const { chains, pendingChainId, reset, switchNetwork } = useSwitchNetwork({
onSettled: () => {
reset(); // reset mutation variables (eg. pendingChainId, error)
onClose();
},
});

const { disconnect } = useDisconnect();
const { connector: activeConnector } = useAccount();
const [switchingToChain, setSwitchingToChain] = useState<number | null>();
const titleId = 'rk_chain_modal_title';
const mobile = isMobile();
const rainbowkitChainsById = useRainbowKitChainsById();
const unsupportedChain = activeChain?.unsupported ?? false;
const chainIconSize = mobile ? '36' : '28';

const stopSwitching = useCallback(() => {
setSwitchingToChain(null);
onClose();
}, [onClose]);

useEffect(() => {
if (!activeConnector) {
return;
}

const stopSwitching = () => {
setSwitchingToChain(null);
onClose();
};

let provider: any;
activeConnector?.getProvider?.().then((provider_: any) => {
provider = provider_;
provider.on('chainChanged', stopSwitching);
});

return () => {
provider?.removeListener('chainChanged', stopSwitching);
};
}, [activeConnector, onClose, stopSwitching]);

useEffect(() => {
if (networkError && networkError.name === 'UserRejectedRequestError') {
stopSwitching();
}
}, [networkError, stopSwitching]);

const { appName } = useContext(AppContext);

const rainbowkitChains = useRainbowKitChains();

if (!activeChain || !activeChain?.id) {
return null;
}
Expand Down Expand Up @@ -104,13 +72,13 @@ export function ChainModal({ onClose, open }: ChainModalProps) {
)}
<Box display="flex" flexDirection="column" gap="4" padding="2">
{switchNetwork ? (
chains.map((chain, idx) => {
rainbowkitChains.map(({ iconBackground, iconUrl, id }, idx) => {
const chain = chains.find(c => c.id === id);
if (!chain) return null;

const isCurrentChain = chain.id === activeChain?.id;
const switching = chain.id === switchingToChain;
const rainbowKitChain = rainbowkitChainsById[chain.id];
const chainIconSize: BoxProps['width'] = mobile ? '36' : '28';
const chainIconUrl = rainbowKitChain?.iconUrl;
const chainIconBackground = rainbowKitChain?.iconBackground;
const switching =
!isCurrentChain && chain.id === pendingChainId;

return (
<Fragment key={chain.id}>
Expand All @@ -119,10 +87,7 @@ export function ChainModal({ onClose, open }: ChainModalProps) {
onClick={
isCurrentChain
? undefined
: () => {
setSwitchingToChain(chain.id);
switchNetwork(chain.id);
}
: () => switchNetwork(chain.id)
}
testId={`chain-option-${chain.id}`}
>
Expand All @@ -140,18 +105,18 @@ export function ChainModal({ onClose, open }: ChainModalProps) {
gap="4"
height={chainIconSize}
>
{chainIconUrl ? (
{iconUrl && (
<Box height="full" marginRight="8">
<AsyncImage
alt={chain.name}
background={chainIconBackground}
background={iconBackground}
borderRadius="full"
height={chainIconSize}
src={chainIconUrl}
src={iconUrl}
width={chainIconSize}
/>
</Box>
) : null}
)}
<div>{chain.name}</div>
</Box>
{isCurrentChain && (
Expand Down Expand Up @@ -202,7 +167,7 @@ export function ChainModal({ onClose, open }: ChainModalProps) {
</Box>
</Box>
</MenuButton>
{mobile && idx < chains?.length - 1 && (
{mobile && idx < rainbowkitChains.length - 1 && (
<Box
background="generalBorderDim"
height="1"
Expand Down

2 comments on commit 08e3f4c

@vercel
Copy link

@vercel vercel bot commented on 08e3f4c Jul 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 08e3f4c Jul 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.