@lifi/widget 4.7.1 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/AmountInput/AmountInput.style.d.ts.map +1 -1
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +5 -2
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -1
- package/dist/esm/components/Card/Card.d.ts.map +1 -1
- package/dist/esm/components/Card/CardIconButton.d.ts.map +1 -1
- package/dist/esm/components/ContractComponent/ItemPrice.d.ts.map +1 -1
- package/dist/esm/components/ListItem/ListItemButton.d.ts.map +1 -1
- package/dist/esm/components/ListItemButton.d.ts.map +1 -1
- package/dist/esm/components/SelectChainAndToken.js +5 -2
- package/dist/esm/components/SelectChainAndToken.js.map +1 -1
- package/dist/esm/components/SendToWallet/SendToWalletButton.js +5 -4
- package/dist/esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
- package/dist/esm/components/Token/Token.d.ts.map +1 -1
- package/dist/esm/components/TokenList/types.d.ts.map +1 -1
- package/dist/esm/components/TokenList/useTokenSelect.d.ts.map +1 -1
- package/dist/esm/components/TokenList/useTokenSelect.js +10 -0
- package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
- package/dist/esm/config/constants.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/config/version.js.map +1 -1
- package/dist/esm/hooks/useAccountsBalancesData.js +5 -2
- package/dist/esm/hooks/useAccountsBalancesData.js.map +1 -1
- package/dist/esm/hooks/useAddressValidation.d.ts +3 -2
- package/dist/esm/hooks/useAddressValidation.d.ts.map +1 -1
- package/dist/esm/hooks/useAddressValidation.js +48 -21
- package/dist/esm/hooks/useAddressValidation.js.map +1 -1
- package/dist/esm/hooks/useAvailableChains.d.ts.map +1 -1
- package/dist/esm/hooks/useAvailableChains.js +21 -38
- package/dist/esm/hooks/useAvailableChains.js.map +1 -1
- package/dist/esm/hooks/useFromTokenSufficiency.js +3 -3
- package/dist/esm/hooks/useFromTokenSufficiency.js.map +1 -1
- package/dist/esm/hooks/useGasRecommendation.js +3 -3
- package/dist/esm/hooks/useGasRecommendation.js.map +1 -1
- package/dist/esm/hooks/useGasRefuel.js +4 -1
- package/dist/esm/hooks/useGasRefuel.js.map +1 -1
- package/dist/esm/hooks/useGasSufficiency.js +3 -3
- package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
- package/dist/esm/hooks/useIsBatchingSupported.js +2 -2
- package/dist/esm/hooks/useIsBatchingSupported.js.map +1 -1
- package/dist/esm/hooks/useRouteExecution.js +6 -6
- package/dist/esm/hooks/useRouteExecution.js.map +1 -1
- package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
- package/dist/esm/hooks/useRoutes.js +18 -13
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useToAddressAutoPopulate.js +6 -8
- package/dist/esm/hooks/useToAddressAutoPopulate.js.map +1 -1
- package/dist/esm/hooks/useToAddressRequirements.d.ts +3 -2
- package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
- package/dist/esm/hooks/useToAddressRequirements.js +8 -7
- package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
- package/dist/esm/hooks/useToAddressReset.js +6 -3
- package/dist/esm/hooks/useToAddressReset.js.map +1 -1
- package/dist/esm/hooks/useTokenBalance.js +6 -5
- package/dist/esm/hooks/useTokenBalance.js.map +1 -1
- package/dist/esm/hooks/useTokenBalancesQueries.js +3 -3
- package/dist/esm/hooks/useTokenBalancesQueries.js.map +1 -1
- package/dist/esm/hooks/useTokenSearch.js +3 -3
- package/dist/esm/hooks/useTokenSearch.js.map +1 -1
- package/dist/esm/hooks/useTokens.js +15 -15
- package/dist/esm/hooks/useTokens.js.map +1 -1
- package/dist/esm/hooks/useTools.d.ts.map +1 -1
- package/dist/esm/hooks/useTools.js +26 -27
- package/dist/esm/hooks/useTools.js.map +1 -1
- package/dist/esm/hooks/useTransactionDetails.js +2 -2
- package/dist/esm/hooks/useTransactionDetails.js.map +1 -1
- package/dist/esm/hooks/useTransactionHistory.js +3 -3
- package/dist/esm/hooks/useTransactionHistory.js.map +1 -1
- package/dist/esm/i18n/bn.json +2 -1
- package/dist/esm/i18n/de.json +2 -1
- package/dist/esm/i18n/en.json +2 -1
- package/dist/esm/i18n/es.json +2 -1
- package/dist/esm/i18n/fr.json +2 -1
- package/dist/esm/i18n/hi.json +2 -1
- package/dist/esm/i18n/id.json +2 -1
- package/dist/esm/i18n/it.json +2 -1
- package/dist/esm/i18n/ja.json +2 -1
- package/dist/esm/i18n/ko.json +2 -1
- package/dist/esm/i18n/pl.json +2 -1
- package/dist/esm/i18n/pt.json +2 -1
- package/dist/esm/i18n/th.json +2 -1
- package/dist/esm/i18n/tr.json +2 -1
- package/dist/esm/i18n/uk.json +2 -1
- package/dist/esm/i18n/vi.json +2 -1
- package/dist/esm/i18n/zh.json +2 -1
- package/dist/esm/index.d.ts +5 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/pages/RoutesPage/RoutesPage.style.d.ts.map +1 -1
- package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js +7 -4
- package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js.map +1 -1
- package/dist/esm/pages/SendToWallet/BookmarksPage.js +35 -31
- package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +4 -3
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +43 -39
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +32 -26
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToWalletPage.js +5 -10
- package/dist/esm/pages/SendToWallet/SendToWalletPage.js.map +1 -1
- package/dist/esm/providers/I18nProvider/types.d.ts.map +1 -1
- package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
- package/dist/esm/providers/WalletProvider/WalletProvider.js +14 -2
- package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js +15 -11
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
- package/dist/esm/providers/WidgetProvider/types.d.ts +2 -0
- package/dist/esm/providers/WidgetProvider/types.d.ts.map +1 -1
- package/dist/esm/queries/getChains.d.ts +38 -0
- package/dist/esm/queries/getChains.d.ts.map +1 -0
- package/dist/esm/queries/getChains.js +51 -0
- package/dist/esm/queries/getChains.js.map +1 -0
- package/dist/esm/queries/getTools.d.ts +26 -0
- package/dist/esm/queries/getTools.d.ts.map +1 -0
- package/dist/esm/queries/getTools.js +30 -0
- package/dist/esm/queries/getTools.js.map +1 -0
- package/dist/esm/queries/index.d.ts +4 -0
- package/dist/esm/queries/index.js +3 -0
- package/dist/esm/queries/types.d.ts +25 -0
- package/dist/esm/queries/types.d.ts.map +1 -0
- package/dist/esm/shared.d.ts +2 -1
- package/dist/esm/shared.js +2 -1
- package/dist/esm/stores/StoreProvider.d.ts.map +1 -1
- package/dist/esm/stores/StoreProvider.js +16 -17
- package/dist/esm/stores/StoreProvider.js.map +1 -1
- package/dist/esm/stores/bookmarks/types.d.ts +3 -0
- package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
- package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
- package/dist/esm/stores/chains/ChainOrderStore.js +4 -1
- package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
- package/dist/esm/stores/form/FormStore.js +3 -2
- package/dist/esm/stores/form/FormStore.js.map +1 -1
- package/dist/esm/stores/form/URLSearchParamsBuilder.js +7 -3
- package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +4 -1
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
- package/dist/esm/stores/header/useHeaderStore.d.ts +1 -1
- package/dist/esm/stores/header/useHeaderStore.d.ts.map +1 -1
- package/dist/esm/stores/header/useHeaderStore.js.map +1 -1
- package/dist/esm/themes/createTheme.d.ts.map +1 -1
- package/dist/esm/types/events.d.ts.map +1 -1
- package/dist/esm/types/token.d.ts.map +1 -1
- package/dist/esm/types/widget.d.ts +30 -0
- package/dist/esm/types/widget.d.ts.map +1 -1
- package/dist/esm/types/widget.js.map +1 -1
- package/dist/esm/utils/chainType.js +32 -1
- package/dist/esm/utils/chainType.js.map +1 -1
- package/dist/esm/utils/customReceiver.js +6 -1
- package/dist/esm/utils/customReceiver.js.map +1 -1
- package/dist/esm/utils/queries.js.map +1 -1
- package/dist/esm/utils/scopeKeys.d.ts +6 -0
- package/dist/esm/utils/scopeKeys.d.ts.map +1 -0
- package/dist/esm/utils/scopeKeys.js +8 -0
- package/dist/esm/utils/scopeKeys.js.map +1 -0
- package/package.json +15 -7
- package/src/components/AmountInputCard/AmountInputCardPair.tsx +6 -2
- package/src/components/SelectChainAndToken.tsx +5 -1
- package/src/components/SendToWallet/SendToWalletButton.tsx +13 -8
- package/src/components/TokenList/useTokenSelect.ts +15 -0
- package/src/config/constants.ts +3 -0
- package/src/config/version.ts +1 -1
- package/src/hooks/useAccountsBalancesData.ts +6 -2
- package/src/hooks/useAddressValidation.ts +87 -32
- package/src/hooks/useAvailableChains.test.tsx +170 -0
- package/src/hooks/useAvailableChains.ts +29 -47
- package/src/hooks/useFromTokenSufficiency.ts +2 -2
- package/src/hooks/useGasRecommendation.ts +2 -2
- package/src/hooks/useGasRefuel.ts +11 -1
- package/src/hooks/useGasSufficiency.ts +2 -2
- package/src/hooks/useIsBatchingSupported.ts +2 -2
- package/src/hooks/useRouteExecution.ts +5 -5
- package/src/hooks/useRoutes.ts +28 -23
- package/src/hooks/useToAddressAutoPopulate.ts +13 -17
- package/src/hooks/useToAddressRequirements.ts +19 -4
- package/src/hooks/useToAddressReset.ts +13 -4
- package/src/hooks/useTokenBalance.ts +10 -5
- package/src/hooks/useTokenBalancesQueries.ts +3 -3
- package/src/hooks/useTokenSearch.ts +7 -3
- package/src/hooks/useTokens.ts +13 -14
- package/src/hooks/useTools.sharedEntry.test.tsx +155 -0
- package/src/hooks/useTools.ts +42 -37
- package/src/hooks/useTransactionDetails.ts +3 -3
- package/src/hooks/useTransactionHistory.ts +2 -2
- package/src/index.ts +2 -1
- package/src/pages/SendToWallet/BookmarkAddressSheet.tsx +5 -1
- package/src/pages/SendToWallet/BookmarksPage.tsx +42 -52
- package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +4 -14
- package/src/pages/SendToWallet/RecentWalletsPage.tsx +59 -66
- package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +45 -50
- package/src/pages/SendToWallet/SendToWalletPage.tsx +4 -16
- package/src/providers/WalletProvider/WalletProvider.tsx +28 -3
- package/src/providers/WidgetProvider/WidgetProvider.test.tsx +73 -0
- package/src/providers/WidgetProvider/WidgetProvider.tsx +28 -14
- package/src/providers/WidgetProvider/types.ts +2 -0
- package/src/queries/getChains.ts +93 -0
- package/src/queries/getTools.ts +62 -0
- package/src/queries/index.ts +30 -0
- package/src/queries/sharedCache.test.tsx +223 -0
- package/src/queries/types.ts +38 -0
- package/src/shared.ts +1 -0
- package/src/stores/StoreProvider.test.tsx +107 -0
- package/src/stores/StoreProvider.tsx +8 -6
- package/src/stores/bookmarks/types.ts +3 -0
- package/src/stores/chains/ChainOrderStore.tsx +4 -1
- package/src/stores/form/FormStore.tsx +2 -0
- package/src/stores/form/URLSearchParamsBuilder.tsx +4 -2
- package/src/stores/form/getDefaultValuesFromQueryString.ts +19 -0
- package/src/stores/header/useHeaderStore.tsx +3 -4
- package/src/types/widget.ts +30 -0
- package/src/utils/chainType.ts +73 -1
- package/src/utils/customReceiver.ts +36 -0
- package/src/utils/queries.ts +1 -0
- package/src/utils/scopeKeys.ts +18 -0
|
@@ -12,13 +12,13 @@ import type { BottomSheetBase } from '../../components/BottomSheet/types.js'
|
|
|
12
12
|
import { ButtonTertiary } from '../../components/ButtonTertiary.js'
|
|
13
13
|
import { CardButton } from '../../components/Card/CardButton.js'
|
|
14
14
|
import { AccountDeployedMessage } from '../../components/Messages/AccountDeployedMessage.js'
|
|
15
|
+
import { addressInputMaxLength } from '../../config/constants.js'
|
|
15
16
|
import {
|
|
16
17
|
AddressType,
|
|
17
18
|
useAddressValidation,
|
|
18
19
|
} from '../../hooks/useAddressValidation.js'
|
|
19
20
|
import { useChain } from '../../hooks/useChain.js'
|
|
20
21
|
import { useHeader } from '../../hooks/useHeader.js'
|
|
21
|
-
import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'
|
|
22
22
|
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
23
23
|
import type { Bookmark } from '../../stores/bookmarks/types.js'
|
|
24
24
|
import { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'
|
|
@@ -57,7 +57,6 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
57
57
|
const [validatedWallet, setValidatedWallet] = useState<Bookmark>()
|
|
58
58
|
const [errorMessage, setErrorMessage] = useState('')
|
|
59
59
|
const { validateAddress, isValidating } = useAddressValidation()
|
|
60
|
-
const { requiredToChainType } = useToAddressRequirements()
|
|
61
60
|
const [toChainId] = useFieldValues('toChain')
|
|
62
61
|
const { chain: toChain } = useChain(toChainId)
|
|
63
62
|
const [isDoneButtonLoading, setIsDoneButtonLoading] = useState(false)
|
|
@@ -91,7 +90,6 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
91
90
|
setIsDoneButtonLoading(true)
|
|
92
91
|
const validationResult = await validateAddress({
|
|
93
92
|
value: inputAddressValue,
|
|
94
|
-
chainType: requiredToChainType,
|
|
95
93
|
chain: toChain,
|
|
96
94
|
})
|
|
97
95
|
setIsDoneButtonLoading(false)
|
|
@@ -100,18 +98,6 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
100
98
|
return
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
if (
|
|
104
|
-
requiredToChainType &&
|
|
105
|
-
requiredToChainType !== validationResult.chainType
|
|
106
|
-
) {
|
|
107
|
-
setErrorMessage(
|
|
108
|
-
t('error.title.walletChainTypeInvalid', {
|
|
109
|
-
chainName: toChain?.name,
|
|
110
|
-
})
|
|
111
|
-
)
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
|
|
115
101
|
setValidatedWallet({
|
|
116
102
|
name:
|
|
117
103
|
validationResult.addressType === AddressType.NameService
|
|
@@ -119,6 +105,7 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
119
105
|
: undefined,
|
|
120
106
|
address: validationResult.address,
|
|
121
107
|
chainType: validationResult.chainType,
|
|
108
|
+
chainId: validationResult.chainId,
|
|
122
109
|
})
|
|
123
110
|
confirmAddressSheetRef.current?.open()
|
|
124
111
|
}
|
|
@@ -155,6 +142,7 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
155
142
|
: undefined,
|
|
156
143
|
address: validationResult.address,
|
|
157
144
|
chainType: validationResult.chainType,
|
|
145
|
+
chainId: validationResult.chainId,
|
|
158
146
|
})
|
|
159
147
|
bookmarkAddressSheetRef.current?.open()
|
|
160
148
|
} else {
|
|
@@ -213,7 +201,7 @@ export const SendToWalletPage: React.FC = () => {
|
|
|
213
201
|
placeholder={placeholder}
|
|
214
202
|
aria-label={placeholder}
|
|
215
203
|
maxRows={2}
|
|
216
|
-
inputProps={{ maxLength:
|
|
204
|
+
inputProps={{ maxLength: addressInputMaxLength }}
|
|
217
205
|
multiline
|
|
218
206
|
/>
|
|
219
207
|
{errorMessage ? (
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { WalletManagementProviders } from '@lifi/wallet-management'
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
useSDKProviders,
|
|
4
|
+
type WidgetProviderProps,
|
|
5
|
+
} from '@lifi/widget-provider'
|
|
3
6
|
import {
|
|
4
7
|
type FC,
|
|
5
8
|
type JSX,
|
|
@@ -10,8 +13,15 @@ import {
|
|
|
10
13
|
import { useTranslation } from 'react-i18next'
|
|
11
14
|
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
12
15
|
import { useInitializeSDKProviders } from '../../hooks/useInitializeSDKProviders.js'
|
|
16
|
+
import {
|
|
17
|
+
withoutUnservedToChain,
|
|
18
|
+
withServedDestinationChains,
|
|
19
|
+
} from '../../utils/chainType.js'
|
|
13
20
|
import { getConfigItemSets, isItemAllowedForSets } from '../../utils/item.js'
|
|
14
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
useWidgetConfig,
|
|
23
|
+
WidgetContext,
|
|
24
|
+
} from '../WidgetProvider/WidgetProvider.js'
|
|
15
25
|
import { useExternalWalletProvider } from './useExternalWalletProvider.js'
|
|
16
26
|
|
|
17
27
|
interface WalletProviderProps extends PropsWithChildren {
|
|
@@ -78,5 +88,20 @@ export const WalletProvider = ({
|
|
|
78
88
|
|
|
79
89
|
const SDKProviderInitializer: FC<PropsWithChildren> = ({ children }) => {
|
|
80
90
|
useInitializeSDKProviders()
|
|
81
|
-
|
|
91
|
+
const config = useWidgetConfig()
|
|
92
|
+
const sdkProviders = useSDKProviders()
|
|
93
|
+
// Only below the provider components is it known which chains are served.
|
|
94
|
+
// Keyed on `config.chains`: the config object is new on every render.
|
|
95
|
+
const chains = useMemo(
|
|
96
|
+
() => withServedDestinationChains(config.chains, sdkProviders),
|
|
97
|
+
[config.chains, sdkProviders]
|
|
98
|
+
)
|
|
99
|
+
const value = useMemo(
|
|
100
|
+
() =>
|
|
101
|
+
withoutUnservedToChain(
|
|
102
|
+
chains === config.chains ? config : { ...config, chains }
|
|
103
|
+
),
|
|
104
|
+
[config, chains]
|
|
105
|
+
)
|
|
106
|
+
return <WidgetContext value={value}>{children}</WidgetContext>
|
|
82
107
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** @vitest-environment happy-dom */
|
|
2
|
+
|
|
3
|
+
import { act } from 'react'
|
|
4
|
+
import { createRoot, type Root } from 'react-dom/client'
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
6
|
+
import type { WidgetConfig } from '../../types/widget.js'
|
|
7
|
+
import type { WidgetContextProps } from './types.js'
|
|
8
|
+
|
|
9
|
+
const settingsActions = { setDefaultSettings: vi.fn() }
|
|
10
|
+
vi.mock('../../stores/settings/useSettingsActions.js', () => ({
|
|
11
|
+
useSettingsActions: () => settingsActions,
|
|
12
|
+
}))
|
|
13
|
+
|
|
14
|
+
import { useWidgetConfig, WidgetProvider } from './WidgetProvider.js'
|
|
15
|
+
|
|
16
|
+
let root: Root
|
|
17
|
+
let seen: WidgetContextProps | undefined
|
|
18
|
+
|
|
19
|
+
const Probe = () => {
|
|
20
|
+
seen = useWidgetConfig()
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const render = async (config: WidgetConfig) => {
|
|
25
|
+
await act(async () => {
|
|
26
|
+
root.render(
|
|
27
|
+
<WidgetProvider config={config}>
|
|
28
|
+
<Probe />
|
|
29
|
+
</WidgetProvider>
|
|
30
|
+
)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('WidgetProvider scope keys', () => {
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
seen = undefined
|
|
37
|
+
const container = document.createElement('div')
|
|
38
|
+
document.body.appendChild(container)
|
|
39
|
+
root = createRoot(container)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
afterEach(async () => {
|
|
43
|
+
await act(async () => root.unmount())
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// An integration that only ever set the deprecated keyPrefix keeps its
|
|
47
|
+
// isolation: everything below reads the resolved keys, never keyPrefix.
|
|
48
|
+
it('resolves both scopes from keyPrefix alone', async () => {
|
|
49
|
+
await render({ integrator: 'test', keyPrefix: 'legacy' })
|
|
50
|
+
|
|
51
|
+
expect(seen?.storageScopeKey).toBe('legacy')
|
|
52
|
+
expect(seen?.queryScopeKey).toBe('legacy')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('prefers the explicit scopes over keyPrefix', async () => {
|
|
56
|
+
await render({
|
|
57
|
+
integrator: 'test',
|
|
58
|
+
keyPrefix: 'legacy',
|
|
59
|
+
storageScopeKey: 'store',
|
|
60
|
+
queryScopeKey: 'query',
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expect(seen?.storageScopeKey).toBe('store')
|
|
64
|
+
expect(seen?.queryScopeKey).toBe('query')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('keeps the historical storage names and the default query scope when nothing is set', async () => {
|
|
68
|
+
await render({ integrator: 'test' })
|
|
69
|
+
|
|
70
|
+
expect(seen?.storageScopeKey).toBeUndefined()
|
|
71
|
+
expect(seen?.queryScopeKey).toBe('li.fi')
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import type { Context } from 'react'
|
|
2
2
|
import { createContext, use, useId, useMemo } from 'react'
|
|
3
3
|
import { useSettingsActions } from '../../stores/settings/useSettingsActions.js'
|
|
4
|
+
import {
|
|
5
|
+
isDestinationOnlyChain,
|
|
6
|
+
withDestinationOnlyChains,
|
|
7
|
+
} from '../../utils/chainType.js'
|
|
8
|
+
import {
|
|
9
|
+
defaultQueryScopeKey,
|
|
10
|
+
resolveQueryScopeKey,
|
|
11
|
+
resolveStorageScopeKey,
|
|
12
|
+
} from '../../utils/scopeKeys.js'
|
|
4
13
|
import type { WidgetContextProps, WidgetProviderProps } from './types.js'
|
|
5
14
|
|
|
6
15
|
const initialContext: WidgetContextProps = {
|
|
7
16
|
elementId: '',
|
|
8
17
|
integrator: '',
|
|
18
|
+
queryScopeKey: defaultQueryScopeKey,
|
|
9
19
|
}
|
|
10
20
|
|
|
11
21
|
export const WidgetContext: Context<WidgetContextProps> =
|
|
@@ -23,26 +33,30 @@ export const WidgetProvider: React.FC<
|
|
|
23
33
|
throw new Error('Required property "integrator" is missing.')
|
|
24
34
|
}
|
|
25
35
|
|
|
36
|
+
// Keyed on the integrator's `chains`: the config object is new on every render.
|
|
37
|
+
const chains = useMemo(
|
|
38
|
+
() => withDestinationOnlyChains(widgetConfig.chains),
|
|
39
|
+
[widgetConfig.chains]
|
|
40
|
+
)
|
|
41
|
+
|
|
26
42
|
const value = useMemo((): WidgetContextProps => {
|
|
43
|
+
// Omitted, not undefined: an own undefined key resets the form field.
|
|
44
|
+
const { fromChain, fromToken, ...config } = widgetConfig
|
|
45
|
+
const value = {
|
|
46
|
+
...(isDestinationOnlyChain(fromChain) ? config : widgetConfig),
|
|
47
|
+
chains,
|
|
48
|
+
// Resolved once, so nothing below reads the deprecated keyPrefix.
|
|
49
|
+
storageScopeKey: resolveStorageScopeKey(widgetConfig),
|
|
50
|
+
queryScopeKey: resolveQueryScopeKey(widgetConfig),
|
|
51
|
+
elementId,
|
|
52
|
+
} as WidgetContextProps
|
|
27
53
|
try {
|
|
28
|
-
// Create widget configuration object
|
|
29
|
-
const value = {
|
|
30
|
-
...widgetConfig,
|
|
31
|
-
elementId,
|
|
32
|
-
} as WidgetContextProps
|
|
33
|
-
|
|
34
54
|
// Set default settings for widget settings store
|
|
35
55
|
setDefaultSettings(value)
|
|
36
|
-
|
|
37
|
-
return value
|
|
38
56
|
} catch (e) {
|
|
39
57
|
console.warn(e)
|
|
40
|
-
return {
|
|
41
|
-
...widgetConfig,
|
|
42
|
-
elementId,
|
|
43
|
-
integrator: widgetConfig.integrator,
|
|
44
|
-
}
|
|
45
58
|
}
|
|
46
|
-
|
|
59
|
+
return value
|
|
60
|
+
}, [elementId, widgetConfig, chains, setDefaultSettings])
|
|
47
61
|
return <WidgetContext value={value}>{children}</WidgetContext>
|
|
48
62
|
}
|
|
@@ -2,6 +2,8 @@ import type { WidgetConfig } from '../../types/widget.js'
|
|
|
2
2
|
|
|
3
3
|
export type WidgetContextProps = WidgetConfig & {
|
|
4
4
|
elementId: string
|
|
5
|
+
/** Always resolved by `WidgetProvider` — see `resolveQueryScopeKey`. */
|
|
6
|
+
queryScopeKey: string
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
export interface WidgetProviderProps {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ExtendedChain, SDKClient } from '@lifi/sdk'
|
|
2
|
+
import { ChainType, getChains } from '@lifi/sdk'
|
|
3
|
+
import type {
|
|
4
|
+
FactoryQueryOptions,
|
|
5
|
+
QueryParameter,
|
|
6
|
+
ScopeKeyParameter,
|
|
7
|
+
} from './types.js'
|
|
8
|
+
|
|
9
|
+
/** Every chain type the widget supports, in the order keys are built in. */
|
|
10
|
+
export const supportedChainTypes: readonly ChainType[] = [
|
|
11
|
+
ChainType.EVM,
|
|
12
|
+
ChainType.SVM,
|
|
13
|
+
ChainType.UTXO,
|
|
14
|
+
ChainType.MVM,
|
|
15
|
+
ChainType.TVM,
|
|
16
|
+
ChainType.STL,
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
export type GetChainsQueryFnData = ExtendedChain[]
|
|
20
|
+
|
|
21
|
+
export type GetChainsQueryKeyParameters = {
|
|
22
|
+
/** API base the client talks to. Different bases return different chains. */
|
|
23
|
+
apiUrl: string
|
|
24
|
+
chainTypes: readonly ChainType[]
|
|
25
|
+
} & ScopeKeyParameter
|
|
26
|
+
|
|
27
|
+
export type GetChainsQueryKey = readonly [
|
|
28
|
+
'chains',
|
|
29
|
+
{ apiUrl: string; chainTypes: ChainType[]; scopeKey?: string },
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
const rank = (type: ChainType): number => {
|
|
33
|
+
const index = supportedChainTypes.indexOf(type)
|
|
34
|
+
return index === -1 ? supportedChainTypes.length : index
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The key alone — to read, set or invalidate the entry. */
|
|
38
|
+
export function getChainsQueryKey(
|
|
39
|
+
parameters: GetChainsQueryKeyParameters
|
|
40
|
+
): GetChainsQueryKey {
|
|
41
|
+
// Array order is part of the query hash, so sort into one canonical order.
|
|
42
|
+
const chainTypes = [...new Set(parameters.chainTypes)].sort(
|
|
43
|
+
(a, b) => rank(a) - rank(b) || a.localeCompare(b)
|
|
44
|
+
)
|
|
45
|
+
return [
|
|
46
|
+
'chains',
|
|
47
|
+
{
|
|
48
|
+
apiUrl: parameters.apiUrl,
|
|
49
|
+
chainTypes,
|
|
50
|
+
...(parameters.scopeKey === undefined
|
|
51
|
+
? {}
|
|
52
|
+
: { scopeKey: parameters.scopeKey }),
|
|
53
|
+
},
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type GetChainsOptions<selectData = GetChainsQueryFnData> = {
|
|
58
|
+
/** Chain types to request, in any order. Defaults to every supported type. */
|
|
59
|
+
chainTypes?: readonly ChainType[] | undefined
|
|
60
|
+
} & ScopeKeyParameter &
|
|
61
|
+
QueryParameter<GetChainsQueryFnData, selectData, GetChainsQueryKey>
|
|
62
|
+
|
|
63
|
+
export type GetChainsQueryOptions<selectData = GetChainsQueryFnData> =
|
|
64
|
+
FactoryQueryOptions<GetChainsQueryFnData, selectData, GetChainsQueryKey>
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Query options for the chain list of one API base.
|
|
68
|
+
*
|
|
69
|
+
* The key is `['chains', { apiUrl, chainTypes, scopeKey }]`: the API base and
|
|
70
|
+
* the requested types decide the response, the scope isolates integrations.
|
|
71
|
+
* The widget and a host app that build their options here, with the same
|
|
72
|
+
* scope, therefore share one cache entry and one request.
|
|
73
|
+
*
|
|
74
|
+
* The query has no side effects. A caller that needs the chains in its SDK
|
|
75
|
+
* client's storage calls `client.setChains` on the data — the entry may have
|
|
76
|
+
* been filled by another caller, so a `queryFn` cannot be relied on to run.
|
|
77
|
+
*/
|
|
78
|
+
export function getChainsQueryOptions<selectData = GetChainsQueryFnData>(
|
|
79
|
+
client: SDKClient,
|
|
80
|
+
options: GetChainsOptions<selectData> = {}
|
|
81
|
+
): GetChainsQueryOptions<selectData> {
|
|
82
|
+
return {
|
|
83
|
+
...options.query,
|
|
84
|
+
queryKey: getChainsQueryKey({
|
|
85
|
+
apiUrl: client.config.apiUrl,
|
|
86
|
+
chainTypes: options.chainTypes ?? supportedChainTypes,
|
|
87
|
+
scopeKey: options.scopeKey,
|
|
88
|
+
}),
|
|
89
|
+
// No signal: the data still fills the cache if all observers leave.
|
|
90
|
+
queryFn: ({ queryKey: [, { chainTypes }] }) =>
|
|
91
|
+
getChains(client, { chainTypes }),
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { SDKClient, ToolsResponse } from '@lifi/sdk'
|
|
2
|
+
import { getTools } from '@lifi/sdk'
|
|
3
|
+
import type {
|
|
4
|
+
FactoryQueryOptions,
|
|
5
|
+
QueryParameter,
|
|
6
|
+
ScopeKeyParameter,
|
|
7
|
+
} from './types.js'
|
|
8
|
+
|
|
9
|
+
export type GetToolsQueryFnData = ToolsResponse
|
|
10
|
+
|
|
11
|
+
export type GetToolsQueryKeyParameters = {
|
|
12
|
+
/** API base the client talks to. Different bases return different tools. */
|
|
13
|
+
apiUrl: string
|
|
14
|
+
} & ScopeKeyParameter
|
|
15
|
+
|
|
16
|
+
export type GetToolsQueryKey = readonly [
|
|
17
|
+
'tools',
|
|
18
|
+
{ apiUrl: string; scopeKey?: string },
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
/** The key alone — to read, set or invalidate the entry. */
|
|
22
|
+
export function getToolsQueryKey(
|
|
23
|
+
parameters: GetToolsQueryKeyParameters
|
|
24
|
+
): GetToolsQueryKey {
|
|
25
|
+
return [
|
|
26
|
+
'tools',
|
|
27
|
+
{
|
|
28
|
+
apiUrl: parameters.apiUrl,
|
|
29
|
+
...(parameters.scopeKey === undefined
|
|
30
|
+
? {}
|
|
31
|
+
: { scopeKey: parameters.scopeKey }),
|
|
32
|
+
},
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type GetToolsOptions<selectData = GetToolsQueryFnData> =
|
|
37
|
+
ScopeKeyParameter &
|
|
38
|
+
QueryParameter<GetToolsQueryFnData, selectData, GetToolsQueryKey>
|
|
39
|
+
|
|
40
|
+
export type GetToolsQueryOptions<selectData = GetToolsQueryFnData> =
|
|
41
|
+
FactoryQueryOptions<GetToolsQueryFnData, selectData, GetToolsQueryKey>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Query options for the bridges and exchanges of one API base.
|
|
45
|
+
*
|
|
46
|
+
* The key is `['tools', { apiUrl, scopeKey }]`. The cache holds the raw,
|
|
47
|
+
* unfiltered response so every caller can share it; narrow it per caller with
|
|
48
|
+
* `query.select`, never in the `queryFn`.
|
|
49
|
+
*/
|
|
50
|
+
export function getToolsQueryOptions<selectData = GetToolsQueryFnData>(
|
|
51
|
+
client: SDKClient,
|
|
52
|
+
options: GetToolsOptions<selectData> = {}
|
|
53
|
+
): GetToolsQueryOptions<selectData> {
|
|
54
|
+
return {
|
|
55
|
+
...options.query,
|
|
56
|
+
queryKey: getToolsQueryKey({
|
|
57
|
+
apiUrl: client.config.apiUrl,
|
|
58
|
+
scopeKey: options.scopeKey,
|
|
59
|
+
}),
|
|
60
|
+
queryFn: ({ signal }) => getTools(client, undefined, { signal }),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query option factories for LI.FI server data, shared by the widget and its
|
|
3
|
+
* host. Published as `@lifi/widget/queries`; it depends only on
|
|
4
|
+
* `@lifi/sdk` and on `@tanstack/react-query` types, so importing it does not
|
|
5
|
+
* pull in the widget itself.
|
|
6
|
+
*
|
|
7
|
+
* Keys follow wagmi's `@wagmi/core/query` convention: `[name, parameters]`,
|
|
8
|
+
* where the parameters hold everything that changes the response plus an
|
|
9
|
+
* optional `scopeKey`.
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
type GetChainsOptions,
|
|
13
|
+
type GetChainsQueryFnData,
|
|
14
|
+
type GetChainsQueryKey,
|
|
15
|
+
type GetChainsQueryKeyParameters,
|
|
16
|
+
type GetChainsQueryOptions,
|
|
17
|
+
getChainsQueryKey,
|
|
18
|
+
getChainsQueryOptions,
|
|
19
|
+
supportedChainTypes,
|
|
20
|
+
} from './getChains.js'
|
|
21
|
+
export {
|
|
22
|
+
type GetToolsOptions,
|
|
23
|
+
type GetToolsQueryFnData,
|
|
24
|
+
type GetToolsQueryKey,
|
|
25
|
+
type GetToolsQueryKeyParameters,
|
|
26
|
+
type GetToolsQueryOptions,
|
|
27
|
+
getToolsQueryKey,
|
|
28
|
+
getToolsQueryOptions,
|
|
29
|
+
} from './getTools.js'
|
|
30
|
+
export type { QueryParameter, ScopeKeyParameter } from './types.js'
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/** @vitest-environment happy-dom */
|
|
2
|
+
|
|
3
|
+
import type * as LifiSdk from '@lifi/sdk'
|
|
4
|
+
import type { SDKClient, ToolsResponse } from '@lifi/sdk'
|
|
5
|
+
import { ChainType } from '@lifi/sdk'
|
|
6
|
+
import {
|
|
7
|
+
QueryClient,
|
|
8
|
+
QueryClientProvider,
|
|
9
|
+
useQuery,
|
|
10
|
+
} from '@tanstack/react-query'
|
|
11
|
+
import type { ReactNode } from 'react'
|
|
12
|
+
import { act } from 'react'
|
|
13
|
+
import { createRoot, type Root } from 'react-dom/client'
|
|
14
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A host app and the widget that both build
|
|
18
|
+
* their options from these factories, inside one QueryClient, send each
|
|
19
|
+
* request once — whether they mount together or seconds apart.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const mocks = vi.hoisted(() => ({
|
|
23
|
+
getChains: vi.fn(),
|
|
24
|
+
getTools: vi.fn(),
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
vi.mock('@lifi/sdk', async (importOriginal) => ({
|
|
28
|
+
...(await importOriginal<typeof LifiSdk>()),
|
|
29
|
+
getChains: mocks.getChains,
|
|
30
|
+
getTools: mocks.getTools,
|
|
31
|
+
}))
|
|
32
|
+
|
|
33
|
+
import { getChainsQueryOptions } from './getChains.js'
|
|
34
|
+
import { getToolsQueryOptions } from './getTools.js'
|
|
35
|
+
|
|
36
|
+
const V1 = 'https://api.example/v1'
|
|
37
|
+
const OTHER_BASE = 'https://api-b.example/v1'
|
|
38
|
+
const clientFor = (apiUrl: string) =>
|
|
39
|
+
({ config: { apiUrl } }) as unknown as SDKClient
|
|
40
|
+
|
|
41
|
+
const TOOLS = {
|
|
42
|
+
bridges: [{ key: 'across' }, { key: 'stargate' }],
|
|
43
|
+
exchanges: [{ key: '1inch' }],
|
|
44
|
+
} as unknown as ToolsResponse
|
|
45
|
+
|
|
46
|
+
/** A host app: all chain types, its own refresh cadence. */
|
|
47
|
+
const Host = ({
|
|
48
|
+
client,
|
|
49
|
+
scopeKey,
|
|
50
|
+
}: {
|
|
51
|
+
client: SDKClient
|
|
52
|
+
scopeKey: string
|
|
53
|
+
}) => {
|
|
54
|
+
useQuery(
|
|
55
|
+
getChainsQueryOptions(client, {
|
|
56
|
+
scopeKey,
|
|
57
|
+
query: { staleTime: 300_000 },
|
|
58
|
+
})
|
|
59
|
+
)
|
|
60
|
+
useQuery(
|
|
61
|
+
getToolsQueryOptions(client, { scopeKey, query: { staleTime: 300_000 } })
|
|
62
|
+
)
|
|
63
|
+
return null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** What the widget does: chain types in another order, a filtered tools view. */
|
|
67
|
+
const Widget = ({
|
|
68
|
+
client,
|
|
69
|
+
scopeKey,
|
|
70
|
+
}: {
|
|
71
|
+
client: SDKClient
|
|
72
|
+
scopeKey: string
|
|
73
|
+
}) => {
|
|
74
|
+
useQuery(
|
|
75
|
+
getChainsQueryOptions(client, {
|
|
76
|
+
chainTypes: [
|
|
77
|
+
ChainType.STL,
|
|
78
|
+
ChainType.TVM,
|
|
79
|
+
ChainType.MVM,
|
|
80
|
+
ChainType.UTXO,
|
|
81
|
+
ChainType.SVM,
|
|
82
|
+
ChainType.EVM,
|
|
83
|
+
],
|
|
84
|
+
scopeKey,
|
|
85
|
+
query: { staleTime: 300_000 },
|
|
86
|
+
})
|
|
87
|
+
)
|
|
88
|
+
useQuery(
|
|
89
|
+
getToolsQueryOptions(client, {
|
|
90
|
+
scopeKey,
|
|
91
|
+
query: {
|
|
92
|
+
staleTime: 180_000,
|
|
93
|
+
select: (tools) => ({ ...tools, bridges: tools.bridges.slice(0, 1) }),
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let root: Root
|
|
101
|
+
let queryClient: QueryClient
|
|
102
|
+
|
|
103
|
+
const render = async (node: ReactNode) => {
|
|
104
|
+
await act(async () => {
|
|
105
|
+
root.render(
|
|
106
|
+
<QueryClientProvider client={queryClient}>{node}</QueryClientProvider>
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const settle = () =>
|
|
112
|
+
vi.waitFor(() => {
|
|
113
|
+
expect(queryClient.isFetching()).toBe(0)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe('shared chains and tools cache', () => {
|
|
117
|
+
beforeEach(() => {
|
|
118
|
+
vi.clearAllMocks()
|
|
119
|
+
mocks.getChains.mockResolvedValue([{ id: 1 }])
|
|
120
|
+
mocks.getTools.mockResolvedValue(TOOLS)
|
|
121
|
+
queryClient = new QueryClient({
|
|
122
|
+
defaultOptions: { queries: { retry: false } },
|
|
123
|
+
})
|
|
124
|
+
const container = document.createElement('div')
|
|
125
|
+
document.body.appendChild(container)
|
|
126
|
+
root = createRoot(container)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
afterEach(async () => {
|
|
130
|
+
await act(async () => root.unmount())
|
|
131
|
+
queryClient.clear()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('fetches once when the host and the widget mount together', async () => {
|
|
135
|
+
const client = clientFor(V1)
|
|
136
|
+
|
|
137
|
+
await render(
|
|
138
|
+
<>
|
|
139
|
+
<Host client={client} scopeKey="host" />
|
|
140
|
+
<Widget client={client} scopeKey="host" />
|
|
141
|
+
</>
|
|
142
|
+
)
|
|
143
|
+
await settle()
|
|
144
|
+
|
|
145
|
+
expect(mocks.getChains).toHaveBeenCalledTimes(1)
|
|
146
|
+
expect(mocks.getTools).toHaveBeenCalledTimes(1)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// A widget often mounts seconds after the host app. A later observer with a
|
|
150
|
+
// staleTime reads the entry instead of refetching it.
|
|
151
|
+
it('fetches once when the widget mounts after the host has settled', async () => {
|
|
152
|
+
const client = clientFor(V1)
|
|
153
|
+
|
|
154
|
+
await render(<Host client={client} scopeKey="host" />)
|
|
155
|
+
await settle()
|
|
156
|
+
await render(
|
|
157
|
+
<>
|
|
158
|
+
<Host client={client} scopeKey="host" />
|
|
159
|
+
<Widget client={client} scopeKey="host" />
|
|
160
|
+
</>
|
|
161
|
+
)
|
|
162
|
+
await settle()
|
|
163
|
+
|
|
164
|
+
expect(mocks.getChains).toHaveBeenCalledTimes(1)
|
|
165
|
+
expect(mocks.getTools).toHaveBeenCalledTimes(1)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('also shares when the host and widget hold different client objects on the same base', async () => {
|
|
169
|
+
await render(
|
|
170
|
+
<>
|
|
171
|
+
<Host client={clientFor(V1)} scopeKey="host" />
|
|
172
|
+
<Widget client={clientFor(V1)} scopeKey="host" />
|
|
173
|
+
</>
|
|
174
|
+
)
|
|
175
|
+
await settle()
|
|
176
|
+
|
|
177
|
+
expect(mocks.getChains).toHaveBeenCalledTimes(1)
|
|
178
|
+
expect(mocks.getTools).toHaveBeenCalledTimes(1)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('keeps the raw tools response in the cache under a filtering select', async () => {
|
|
182
|
+
const client = clientFor(V1)
|
|
183
|
+
|
|
184
|
+
await render(<Widget client={client} scopeKey="host" />)
|
|
185
|
+
await settle()
|
|
186
|
+
|
|
187
|
+
const cached = queryClient.getQueryData<ToolsResponse>(
|
|
188
|
+
getToolsQueryOptions(client, { scopeKey: 'host' }).queryKey
|
|
189
|
+
)
|
|
190
|
+
expect(cached?.bridges).toHaveLength(2)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// A partner's own queries and another integration's widget stay apart.
|
|
194
|
+
it('does not share across scopes', async () => {
|
|
195
|
+
const client = clientFor(V1)
|
|
196
|
+
|
|
197
|
+
await render(
|
|
198
|
+
<>
|
|
199
|
+
<Host client={client} scopeKey="host" />
|
|
200
|
+
<Widget client={client} scopeKey="partner" />
|
|
201
|
+
</>
|
|
202
|
+
)
|
|
203
|
+
await settle()
|
|
204
|
+
|
|
205
|
+
expect(mocks.getChains).toHaveBeenCalledTimes(2)
|
|
206
|
+
expect(mocks.getTools).toHaveBeenCalledTimes(2)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
// Different API bases can return different chains and tools, so they must
|
|
210
|
+
// never share an entry.
|
|
211
|
+
it('does not share across API bases', async () => {
|
|
212
|
+
await render(
|
|
213
|
+
<>
|
|
214
|
+
<Host client={clientFor(V1)} scopeKey="host" />
|
|
215
|
+
<Widget client={clientFor(OTHER_BASE)} scopeKey="host" />
|
|
216
|
+
</>
|
|
217
|
+
)
|
|
218
|
+
await settle()
|
|
219
|
+
|
|
220
|
+
expect(mocks.getChains).toHaveBeenCalledTimes(2)
|
|
221
|
+
expect(mocks.getTools).toHaveBeenCalledTimes(2)
|
|
222
|
+
})
|
|
223
|
+
})
|