From 3a12b3889dd1b36535df8332d1ad83fc70be0253 Mon Sep 17 00:00:00 2001 From: RishabhS7 <59636880+RishabhS7@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:48:56 +0530 Subject: [PATCH 1/3] fix: merge conflicts (#49) * fix: merge conflicts * fix: remove consoles --- .../ActionSelectionForm.tsx | 54 ++++++++------- src/components/common/Button/Button.tsx | 30 +++++++++ .../DocumentTransferMessage.tsx | 12 +++- src/components/common/Tag/Tag.tsx | 5 +- .../common/contexts/providerContext.tsx | 50 +++++++------- .../EndorsementChain/EndorsementChain.tsx | 2 +- .../home/VerifySection/DocumentRenderer.tsx | 3 +- .../home/VerifySection/VerifyResult.tsx | 6 +- .../home/VerifySection/VerifySection.tsx | 6 +- src/index.css | 65 ++++++++++--------- src/main.tsx | 2 +- src/pages/Home/index.tsx | 4 +- 12 files changed, 135 insertions(+), 104 deletions(-) diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionSelectionForm/ActionSelectionForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionSelectionForm/ActionSelectionForm.tsx index 04500fc..baa4e58 100644 --- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionSelectionForm/ActionSelectionForm.tsx +++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionSelectionForm/ActionSelectionForm.tsx @@ -173,35 +173,33 @@ export const ActionSelectionForm: FunctionComponent<
)} -
- {(isReturnedToIssuer || isTokenBurnt || isExpired) && ( -
- {(isReturnedToIssuer || isTokenBurnt) && ( - -

- {isReturnedToIssuer - ? 'ETR Returned to Issuer' - : 'ETR Taken Out of Circulation'} -

-
- )} - {isExpired && ( - -

ETR Expired

-
- )} -
-
- )} +
+
+ {(isReturnedToIssuer || isTokenBurnt) && ( + +

+ {isReturnedToIssuer + ? 'ETR Returned to Issuer' + : 'ETR Taken Out of Circulation'} +

+
+ )} + {isExpired && ( + +

ETR Expired

+
+ )} +
+
{!isTokenBurnt && ( -
-
+
+
{account ? ( <> {canManage ? ( diff --git a/src/components/common/Button/Button.tsx b/src/components/common/Button/Button.tsx index 2e0a191..428a1ec 100644 --- a/src/components/common/Button/Button.tsx +++ b/src/components/common/Button/Button.tsx @@ -154,3 +154,33 @@ export const LabelButton: FunctionComponent = ({ ) } + +interface TextButtonProps { + className?: string + onClick?: () => void + children: ReactNode + disabled?: boolean +} + +export const TextButton: FunctionComponent = ({ + className = '', + onClick, + children, + disabled = false, +}) => { + return ( + + ) +} diff --git a/src/components/common/Overlay/OverlayContent/DocumentTransferMessage.tsx b/src/components/common/Overlay/OverlayContent/DocumentTransferMessage.tsx index 32c2c69..fb40aa2 100644 --- a/src/components/common/Overlay/OverlayContent/DocumentTransferMessage.tsx +++ b/src/components/common/Overlay/OverlayContent/DocumentTransferMessage.tsx @@ -345,7 +345,9 @@ export const showDocumentTransferMessage = ( {(title === MessageTitle.ENDORSE_BENEFICIARY_SUCCESS || title === MessageTitle.TRANSFER_OWNER_SUCCESS || title === MessageTitle.TRANSFER_OWNER_FAILED || - title === MessageTitle.ENDORSE_BENEFICIARY_FAILED) && ( + title === MessageTitle.ENDORSE_BENEFICIARY_FAILED || + title === MessageTitle.REJECT_TRANSFER_OWNER_SUCCESS || + title === MessageTitle.REJECT_TRANSFER_OWNER_FAILED) && ( )} {(title === MessageTitle.NOMINATE_BENEFICIARY_SUCCESS || @@ -353,11 +355,15 @@ export const showDocumentTransferMessage = ( )} {(title === MessageTitle.TRANSFER_HOLDER_SUCCESS || - title === MessageTitle.TRANSFER_HOLDER_FAILED) && ( + title === MessageTitle.TRANSFER_HOLDER_FAILED || + title === MessageTitle.REJECT_TRANSFER_HOLDER_SUCCESS || + title === MessageTitle.REJECT_TRANSFER_HOLDER_FAILED) && ( )} {(title === MessageTitle.TRANSFER_OWNER_HOLDER_SUCCESS || - title === MessageTitle.TRANSFER_OWNER_HOLDER_FAILED) && ( + title === MessageTitle.TRANSFER_OWNER_HOLDER_FAILED || + title === MessageTitle.REJECT_TRANSFER_OWNER_HOLDER_SUCCESS || + title === MessageTitle.REJECT_TRANSFER_OWNER_HOLDER_FAILED) && ( = ({ ...props }) => { return ( -
+
{children}
) diff --git a/src/components/common/contexts/providerContext.tsx b/src/components/common/contexts/providerContext.tsx index 8323800..ab5a269 100644 --- a/src/components/common/contexts/providerContext.tsx +++ b/src/components/common/contexts/providerContext.tsx @@ -1,4 +1,4 @@ -import { ProviderDetails, utils, CHAIN_ID, chainInfo } from '@trustvc/trustvc' +import { CHAIN_ID, chainInfo } from '@trustvc/trustvc' import { ethers, providers } from 'ethers' import { Magic } from 'magic-sdk' import React, { @@ -10,7 +10,7 @@ import React, { useRef, useState, } from 'react' -import { INFURA_API_KEY, NETWORK_NAME } from '../../../configs/chain-config' +import { NETWORK_NAME } from '../../../configs/chain-config' import { ChainInfo } from '../../../utils/chain-info' // import { UnsupportedNetworkError } from '../errors' import { @@ -35,34 +35,32 @@ export enum SIGNER_TYPE { } const createProvider = (chainId: CHAIN_ID) => { - const url = ChainInfo[chainId]?.rpcUrl - if (url) { - const chainMeta = getChainInfo(chainId) - return new providers.StaticJsonRpcProvider(url, { - chainId: Number(chainId), - name: chainMeta?.name ?? `chain-${chainId}`, - }) - } - const chainMeta = getChainInfo(chainId) - const opts: ProviderDetails = { - network: chainMeta?.name ?? `chain-${chainId}`, - providerType: 'infura', - apiKey: INFURA_API_KEY, + // First try to get RPC URL from env (VITE_RPC_URL_*), then fall back to chain defaults + const url = getRpcUrl(String(chainId)) ?? ChainInfo[chainId]?.rpcUrl + + if (!url) { + throw new Error(`No RPC URL configured for chain ${chainId}`) } - return utils.generateProvider(opts) + + return new providers.JsonRpcProvider(url) } // Utility function for use in non-react components that cannot get through hooks let currentProvider: providers.Provider | undefined -try { - currentProvider = createProvider(getChainInfoFromNetworkName(NETWORK_NAME).id) -} catch (e) { - console.error('Invalid NETWORK_NAME; provider init failed', e) - currentProvider = undefined -} -export const getCurrentProvider = (): providers.Provider | undefined => - currentProvider +export const getCurrentProvider = (): providers.Provider | undefined => { + if (!currentProvider) { + try { + currentProvider = createProvider( + getChainInfoFromNetworkName(NETWORK_NAME).id + ) + } catch (e) { + console.error('Invalid NETWORK_NAME; provider init failed', e) + currentProvider = undefined + } + } + return currentProvider +} export interface ProviderContextProps { providerType: SIGNER_TYPE @@ -88,8 +86,8 @@ export const ProviderContext = createContext({ reloadNetwork: async () => {}, supportedChainInfoObjects: [], currentChainId: undefined, - provider: currentProvider, - providerOrSigner: currentProvider, + provider: undefined, + providerOrSigner: undefined, account: undefined, networkChangeLoading: false, setNetworkChangeLoading: () => {}, diff --git a/src/components/home/EndorsementChain/EndorsementChain.tsx b/src/components/home/EndorsementChain/EndorsementChain.tsx index 0d310a8..f5f4eb9 100644 --- a/src/components/home/EndorsementChain/EndorsementChain.tsx +++ b/src/components/home/EndorsementChain/EndorsementChain.tsx @@ -291,7 +291,7 @@ const EndorsementChainLayout: React.FC = ({
{status === 'loading' && (
- Loading + Loading Endorsement Chain..
)} diff --git a/src/components/home/VerifySection/DocumentRenderer.tsx b/src/components/home/VerifySection/DocumentRenderer.tsx index 76eb9ba..be14283 100644 --- a/src/components/home/VerifySection/DocumentRenderer.tsx +++ b/src/components/home/VerifySection/DocumentRenderer.tsx @@ -328,8 +328,7 @@ const DocumentRenderer: React.FC = ({ {!isRendererReady && selectedTemplate !== 'attachmentTab' && (
- Loading document - preview.. + Loading document preview..
)} diff --git a/src/components/home/VerifySection/VerifyResult.tsx b/src/components/home/VerifySection/VerifyResult.tsx index 73ead45..f06d00f 100644 --- a/src/components/home/VerifySection/VerifyResult.tsx +++ b/src/components/home/VerifySection/VerifyResult.tsx @@ -12,6 +12,7 @@ import { useProviderContext, } from '../../common/contexts/providerContext' import { AssetManagementApplication } from '../../AssetManagementPanel/AssetManagementApplication' +import { TextButton } from '../../common/Button/Button' interface VerifyResultProps { fileName: string @@ -285,14 +286,13 @@ const VerifyResult: React.FC = ({ ) })()} - +
)} diff --git a/src/components/home/VerifySection/VerifySection.tsx b/src/components/home/VerifySection/VerifySection.tsx index f45540f..918105b 100644 --- a/src/components/home/VerifySection/VerifySection.tsx +++ b/src/components/home/VerifySection/VerifySection.tsx @@ -140,7 +140,7 @@ const VerifySection: React.FC = ({ isDarkMode }) => {
- + Verifying {fileName}...
@@ -148,7 +148,9 @@ const VerifySection: React.FC = ({ isDarkMode }) => { ) return ( -
+
{showEndorsementChain && ( { return ( -
-
+
+
From bfde7d2e1ad7ff1d2b732ffeace4597e0eb97450 Mon Sep 17 00:00:00 2001 From: manishdex25 Date: Tue, 28 Apr 2026 13:28:21 +0530 Subject: [PATCH 2/3] fix: stabilize dropdown and news text styling (#50) * fix: stabilize dropdown and news text styling Prevent global button/text styles from breaking Contact enquiry dropdown visuals and News article link readability, while aligning news sub-body text to the left for consistent content presentation across states. Made-with: Cursor * feat: enhance styling for news articles and select field components --- .../common/SelectField/SelectField.tsx | 67 +++---- src/index.css | 169 +++++++++++++++++- .../News/components/NewsArticlesContent.tsx | 4 +- .../NewsDetail/components/NewsDetailBody.tsx | 4 +- 4 files changed, 207 insertions(+), 37 deletions(-) diff --git a/src/components/common/SelectField/SelectField.tsx b/src/components/common/SelectField/SelectField.tsx index 3b075ae..fa76ff8 100644 --- a/src/components/common/SelectField/SelectField.tsx +++ b/src/components/common/SelectField/SelectField.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react' +import clsx from 'clsx' import type { EnquiryType } from '@/hooks/useContactForm' import { FieldError } from '@/components/common/FieldError' @@ -31,6 +32,29 @@ const SelectField = ({ const selected = options.find(o => o.value === value) ?? options[0] const [isOpen, setIsOpen] = useState(false) const optionItems = options.slice(1) + const hasError = Boolean(error) + + const triggerClassName = clsx( + 'select-field-trigger', + isDarkMode ? 'select-field-trigger--dark' : 'select-field-trigger--light', + hasError && 'select-field-trigger--error' + ) + + const selectedTextClassName = clsx( + 'select-field-value', + selected.value === '' + ? isDarkMode + ? 'select-field-value--placeholder-dark' + : 'select-field-value--placeholder-light' + : isDarkMode + ? 'select-field-value--selected-dark' + : 'select-field-value--selected-light' + ) + + const menuClassName = clsx( + 'select-field-menu', + isDarkMode ? 'select-field-menu--dark' : 'select-field-menu--light' + ) const handleTriggerKeyDown: React.KeyboardEventHandler< HTMLButtonElement @@ -98,27 +122,11 @@ const SelectField = ({ aria-expanded={isOpen} aria-invalid={!!error} aria-describedby={error ? `${id}-error` : undefined} - className={`w-full min-h-[48px] sm:min-h-[40px] rounded-lg border px-3 pr-10 py-3 sm:py-2 text-left text-base sm:text-sm font-gilroy flex items-center justify-between cursor-pointer transition-colors ${ - error - ? 'border-red-500' - : isDarkMode - ? 'bg-black/10 border-white/15' - : 'bg-white/90 border-black/15' - }`} + className={triggerClassName} onClick={() => setIsOpen(open => !open)} onKeyDown={handleTriggerKeyDown} > - - {selected.label} - + {selected.label} {isOpen && ( -
+
{optionItems.map((option, index) => ( diff --git a/src/index.css b/src/index.css index 6812ff3..40426b9 100644 --- a/src/index.css +++ b/src/index.css @@ -309,6 +309,42 @@ body.dark-mode { linear-gradient(97.83deg, #686ad2 10%, #167eb0 90%); } +/* Keep article body links readable (global p uses transparent text-fill). */ +.news-article-body p, +.news-article-body a { + background: none; + -webkit-background-clip: initial; + background-clip: initial; + -webkit-text-fill-color: currentColor; +} + +.news-article-body p, +.news-article-body li { + text-align: left; +} + +.news-article-body a, +.news-article-link { + color: #5b5bb3; + text-decoration: underline; + transition: color 150ms ease; +} + +.news-article-body a:hover, +.news-article-link:hover { + color: #43439a; +} + +.dark-mode .news-article-body a, +.dark-mode .news-article-link { + color: #7d80d7; +} + +.dark-mode .news-article-body a:hover, +.dark-mode .news-article-link:hover { + color: #8f92e5; +} + .news-detail-page--light .news-next-card { background: linear-gradient( @@ -3008,7 +3044,7 @@ p.small { min-width: 160px; } -`@media` (max-width: 480px) { +@media (max-width: 480px) { .endorsement-chain .footer-subsection.is-error { height: auto; flex-direction: column; @@ -4488,3 +4524,134 @@ button.transparent:active, label.transparent:active { background: var(--Primary-33-90, #aaaee654); } + +/* Shared SelectField styles (Contact/support dropdown) */ +.select-field-trigger { + width: 100%; + min-height: 48px; + border-radius: 8px; + border: 1px solid; + padding: 12px 40px 12px 12px; + text-align: left; + font-size: 16px; + font-family: Gilroy, sans-serif; + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + transition: border-color 150ms ease; +} + +@media (min-width: 640px) { + .select-field-trigger { + min-height: 40px; + padding-top: 8px; + padding-bottom: 8px; + font-size: 14px; + } +} + +.select-field-trigger--light { + background-color: rgba(255, 255, 255, 0.7); + border-color: rgba(0, 0, 0, 0.1); +} + +.select-field-trigger--light:hover { + border-color: #5b5bb3; +} + +.select-field-trigger--dark { + background-color: transparent; + border-color: rgba(255, 255, 255, 0.1); +} + +.select-field-trigger--dark:hover { + border-color: #7d80d7; +} + +.select-field-trigger:focus-visible, +.select-field-trigger[aria-expanded='true'] { + border-color: #686ad2; + outline: none; +} + +.select-field-trigger--error, +.select-field-trigger--error:hover { + border-color: #ef4444; +} + +.select-field-value--placeholder-light { + color: #5b6571; +} + +.select-field-value--placeholder-dark { + color: #808894; +} + +.select-field-value--selected-light { + color: #1e2026; +} + +.select-field-value--selected-dark { + color: #c8cdd3; +} + +.select-field-menu { + position: absolute; + z-index: 10; + width: 100%; + margin-top: 4px; + border-radius: 8px; + border: 1px solid; + box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1); +} + +.select-field-menu--light { + border-color: rgba(169, 178, 187, 0.7); + background-color: #ffffff; +} + +.select-field-menu--dark { + border-color: #3d444d; + background-color: #1e2026; +} + +.select-field-option { + width: 100%; + display: block; + padding: 8px 12px; + text-align: left; + font-size: 14px; + font-family: Gilroy, sans-serif; + transition: background-color 150ms ease; + background: transparent; + border: 0; +} + +.select-field-option--light { + color: #1e2026; + background: #ffffff; +} + +.select-field-option--light:hover { + background-color: rgba(222, 228, 233, 0.4); +} + +.select-field-option--dark { + color: #a9b2bb; + background: #1e2026; +} + +.select-field-option--dark:hover { + background-color: rgba(255, 255, 255, 0.1); +} + +.select-field-option--selected-light { + color: #312d62; + background-color: rgba(91, 91, 179, 0.1); +} + +.select-field-option--selected-dark { + color: #c8cdd3; + background-color: rgba(125, 128, 215, 0.2); +} diff --git a/src/pages/News/components/NewsArticlesContent.tsx b/src/pages/News/components/NewsArticlesContent.tsx index 6db7a18..36c265d 100644 --- a/src/pages/News/components/NewsArticlesContent.tsx +++ b/src/pages/News/components/NewsArticlesContent.tsx @@ -39,7 +39,7 @@ const NewsArticlesContent = ({ isDarkMode ? 'text-[#E6EBFF]' : 'text-[#1E2026]' ) const excerptTextClass = clsx( - 'mt-3 text-[18px] leading-[136%] font-medium line-clamp-3', + 'mt-3 text-[18px] leading-[136%] font-medium line-clamp-3 text-left', isDarkMode ? 'text-[#A9B2BB]' : 'text-[#3D444D]' ) const cardTitleTextClass = clsx( @@ -47,7 +47,7 @@ const NewsArticlesContent = ({ isDarkMode ? 'text-[#E6EBFF]' : 'text-[#1E2026]' ) const cardExcerptTextClass = clsx( - 'mt-2 text-sm line-clamp-2', + 'mt-2 text-sm line-clamp-2 text-left', isDarkMode ? 'text-[#A9B2BB]' : 'text-[#3D444D]' ) diff --git a/src/pages/NewsDetail/components/NewsDetailBody.tsx b/src/pages/NewsDetail/components/NewsDetailBody.tsx index efb0e20..277ea19 100644 --- a/src/pages/NewsDetail/components/NewsDetailBody.tsx +++ b/src/pages/NewsDetail/components/NewsDetailBody.tsx @@ -10,7 +10,7 @@ interface NewsDetailBodyProps { const NewsDetailBody = ({ isDarkMode, blocks }: NewsDetailBodyProps) => { const articleBodyClass = clsx( - 'mt-8 max-w-3xl mx-auto space-y-6 text-[15px] leading-7', + 'news-article-body mt-8 max-w-3xl mx-auto space-y-6 text-[15px] leading-7', isDarkMode ? 'text-[#A9B2BB]' : 'text-[#3D444D]' ) @@ -53,7 +53,7 @@ const NewsDetailBody = ({ isDarkMode, blocks }: NewsDetailBodyProps) => { href={linkDef.href} target={isExternal ? '_blank' : undefined} rel={isExternal ? 'noopener noreferrer' : undefined} - className="underline text-[#5B5BB3] hover:opacity-80" + className="news-article-link" > {content} From 688f8c2259ce3bdf68c5b94a2681cec0f35fef0a Mon Sep 17 00:00:00 2001 From: RishabhS7 <59636880+RishabhS7@users.noreply.github.com> Date: Wed, 29 Apr 2026 11:21:44 +0530 Subject: [PATCH 3/3] fix: metamask transaction message (#51) * fix: metamask traction error messages * fix: test cases * fix: update links and improve code formatting --------- --- .../AssetManagementApplication/index.tsx | 4 + .../AssetManagementForm.test.tsx | 21 +- .../AssetManagementForm.tsx | 9 +- .../ActionForm/ActionForm.test.tsx | 164 ++++++++ .../FormVariants/ActionForm/ActionForm.tsx | 79 ++-- .../FormVariants/ActionForm/types.ts | 1 + src/components/common/Navbar/Navbar.tsx | 2 +- .../DocumentTransferMessage.test.tsx | 269 +++++++++++++ .../DocumentTransferMessage.tsx | 9 +- .../TokenInformationContext.tsx | 25 ++ .../home/VerifySection/VerifyError.tsx | 2 +- .../home/VerifySection/VerifySection.test.tsx | 10 +- .../home/VerifySection/VerifySection.tsx | 11 +- src/hooks/useContractFunctionHook.test.tsx | 367 ++++++++++++++++++ src/hooks/useContractFunctionHook.tsx | 63 ++- 15 files changed, 974 insertions(+), 62 deletions(-) create mode 100644 src/components/common/Overlay/OverlayContent/DocumentTransferMessage.test.tsx create mode 100644 src/hooks/useContractFunctionHook.test.tsx diff --git a/src/components/AssetManagementPanel/AssetManagementApplication/index.tsx b/src/components/AssetManagementPanel/AssetManagementApplication/index.tsx index d07b971..5515a75 100644 --- a/src/components/AssetManagementPanel/AssetManagementApplication/index.tsx +++ b/src/components/AssetManagementPanel/AssetManagementApplication/index.tsx @@ -94,7 +94,10 @@ export const AssetManagementApplication: FunctionComponent< // reject transfer owner holder rejectTransferOwnerHolder, rejectTransferOwnerHolderState, + // reset providers resetProviders, + //errorMessage + errorMessage, } = useTokenInformationContext() const [assetManagementAction, setAssetManagementAction] = useState(AssetManagementActions.None) @@ -193,6 +196,7 @@ export const AssetManagementApplication: FunctionComponent< onRestoreToken={onRestoreToken} restoreTokenState={restoreTokenState} isExpired={isExpired} + errorMessage={errorMessage} /> ) : ( isExpired && ( diff --git a/src/components/AssetManagementPanel/AssetManagementForm/AssetManagementForm.test.tsx b/src/components/AssetManagementPanel/AssetManagementForm/AssetManagementForm.test.tsx index 997db58..b418acb 100644 --- a/src/components/AssetManagementPanel/AssetManagementForm/AssetManagementForm.test.tsx +++ b/src/components/AssetManagementPanel/AssetManagementForm/AssetManagementForm.test.tsx @@ -98,10 +98,29 @@ describe('AssetManagementForm', () => { ) }) - it('enables reject holdership only for token registry v5 after holder transfer context', () => { + it('disables reject holdership when holder and beneficiary are the same account (isHolderAndBeneficiary)', () => { mockUseTokenRegistryVersion.mockReturnValue(TokenRegistryVersions.V5) + // baseProps has beneficiary === holder === account, so isHolderAndBeneficiary=true render() + expect(mockActionSelectionForm).toHaveBeenCalledWith( + expect.objectContaining({ + canRejectHolderTransfer: false, + }) + ) + }) + + it('enables reject holdership for token registry v5 when holder differs from beneficiary', () => { + mockUseTokenRegistryVersion.mockReturnValue(TokenRegistryVersions.V5) + // Different beneficiary means isHolderAndBeneficiary=false, so canRejectHolderTransfer can be true + render( + + ) + expect(mockActionSelectionForm).toHaveBeenCalledWith( expect.objectContaining({ canRejectHolderTransfer: true, diff --git a/src/components/AssetManagementPanel/AssetManagementForm/AssetManagementForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/AssetManagementForm.tsx index c5eb0b7..df11d81 100644 --- a/src/components/AssetManagementPanel/AssetManagementForm/AssetManagementForm.tsx +++ b/src/components/AssetManagementPanel/AssetManagementForm/AssetManagementForm.tsx @@ -89,6 +89,7 @@ interface AssetManagementFormProps onSetFormAction: (nextFormAction: AssetManagementActions) => void setShowEndorsementChain: (payload: boolean) => void refreshEndorsementChain?: () => void + errorMessage?: string } export const AssetManagementForm: FunctionComponent< @@ -133,6 +134,7 @@ export const AssetManagementForm: FunctionComponent< destroyTokenState, onRestoreToken, restoreTokenState, + errorMessage, }) => { const tokenRegistryVersion = useTokenRegistryVersion() const isTokenRegistryV5 = tokenRegistryVersion === TokenRegistryVersions.V5 @@ -170,8 +172,8 @@ export const AssetManagementForm: FunctionComponent< hasPreviousHolder && hasPreviousBeneficiary && canRejectAfterTransferOwners - const canRejectHolderTransfer = // Allow reject after holder is transferred back to original owner - (isHolderAndBeneficiary ? !hasPreviousBeneficiary : true) && + const canRejectHolderTransfer = + !isHolderAndBeneficiary && isTokenRegistryV5 && isActiveTitleEscrow && isHolder && @@ -179,6 +181,7 @@ export const AssetManagementForm: FunctionComponent< !(isBeneficiary && hasPreviousBeneficiary) const canRejectOwnerTransfer = !isHolderAndBeneficiary && + isTokenRegistryV5 && isActiveTitleEscrow && isBeneficiary && hasPreviousBeneficiary && @@ -296,6 +299,8 @@ export const AssetManagementForm: FunctionComponent< // reject return to issuer handleRestoreToken={onRestoreToken} restoreTokenState={restoreTokenState} + //error message + errorMessage={errorMessage} /> )} diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/ActionForm.test.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/ActionForm.test.tsx index 040815f..d8f321c 100644 --- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/ActionForm.test.tsx +++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/ActionForm.test.tsx @@ -1854,3 +1854,167 @@ describe('ActionForm - EndorseBeneficiary', () => { }) }) }) + +describe('ActionForm - errorMessage prop passthrough', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it('passes errorMessage to overlay when TransferHolder fails', async () => { + const mockHandleTransfer = vi.fn() + + renderWithOverlay( + + ) + + await waitFor(() => { + expect(mockShowOverlay).toHaveBeenCalled() + }) + + const overlayNode = mockShowOverlay.mock.calls[0][0] as any + expect(overlayNode.props.title).toBe('Transfer Holder Failed') + expect(overlayNode.props.isSuccess).toBe(false) + expect(overlayNode.props.errorMessage).toBe('User Rejected Transaction') + }) + + it('passes errorMessage to overlay when TransferOwnerHolder fails', async () => { + const mockHandleTransferOwnerHolder = vi.fn() + + renderWithOverlay( + + ) + + await waitFor(() => { + expect(mockShowOverlay).toHaveBeenCalled() + }) + + const overlayNode = mockShowOverlay.mock.calls[0][0] as any + expect(overlayNode.props.title).toBe('Transfer Ownership/Holdership Failed') + expect(overlayNode.props.errorMessage).toBe('Insufficient Funds') + }) + + it('passes errorMessage to overlay when NominateBeneficiary fails', async () => { + const mockHandleNomination = vi.fn() + + renderWithOverlay( + + ) + + await waitFor(() => { + expect(mockShowOverlay).toHaveBeenCalled() + }) + + const overlayNode = mockShowOverlay.mock.calls[0][0] as any + expect(overlayNode.props.title).toBe('Nomination Failed') + expect(overlayNode.props.errorMessage).toBe('Network Error') + }) + + it('passes errorMessage to overlay when RejectTransferOwnerHolder fails', async () => { + const mockHandleRejectTransferOwnerHolder = vi.fn() + + renderWithOverlay( + + ) + + await waitFor(() => { + expect(mockShowOverlay).toHaveBeenCalled() + }) + + const overlayNode = mockShowOverlay.mock.calls[0][0] as any + expect(overlayNode.props.title).toBe( + 'Holdership/Ownership Rejection Failed' + ) + expect(overlayNode.props.errorMessage).toBe('Transaction Rejected') + }) + + it('passes errorMessage to overlay when ReturnToIssuer fails', async () => { + const mockHandleReturnToIssuer = vi.fn() + + renderWithOverlay( + + ) + + await waitFor(() => { + expect(mockShowOverlay).toHaveBeenCalled() + }) + + const overlayNode = mockShowOverlay.mock.calls[0][0] as any + expect(overlayNode.props.title).toBe('Return of ETR Failed') + expect(overlayNode.props.errorMessage).toBe('Contract Call Failed') + }) + + it('passes errorMessage to overlay on successful TransferOwner', async () => { + const mockHandleBeneficiaryTransfer = vi.fn() + + renderWithOverlay( + + ) + + await waitFor(() => { + expect(mockShowOverlay).toHaveBeenCalled() + }) + + const overlayNode = mockShowOverlay.mock.calls[0][0] as any + expect(overlayNode.props.title).toBe('Transfer Owner Success') + expect(overlayNode.props.isSuccess).toBe(true) + expect(overlayNode.props.errorMessage).toBe('Some Error') + }) + + it('passes undefined errorMessage when prop is not provided', async () => { + const mockHandleTransfer = vi.fn() + + renderWithOverlay( + + ) + + await waitFor(() => { + expect(mockShowOverlay).toHaveBeenCalled() + }) + + const overlayNode = mockShowOverlay.mock.calls[0][0] as any + expect(overlayNode.props.errorMessage).toBeUndefined() + }) +}) diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/ActionForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/ActionForm.tsx index 5f4c6d9..64a28cb 100644 --- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/ActionForm.tsx +++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/ActionForm.tsx @@ -48,6 +48,7 @@ export const ActionForm: FunctionComponent = props => { setFormActionNone, setShowEndorsementChain, refreshEndorsementChain, + errorMessage, } = props const [remark, setRemark] = useState('') const { showOverlay } = useOverlayContext() @@ -64,7 +65,7 @@ export const ActionForm: FunctionComponent = props => { const { holderTransferringState } = props const isConfirmed = holderTransferringState === FormState.CONFIRMED const isFailed = holderTransferringState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.TRANSFER_HOLDER_SUCCESS : MessageTitle.TRANSFER_HOLDER_FAILED @@ -74,12 +75,13 @@ export const ActionForm: FunctionComponent = props => { } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, holderAddress: isConfirmed ? newHolder : holder, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -90,7 +92,7 @@ export const ActionForm: FunctionComponent = props => { const { transferOwnerHoldersState } = props const isConfirmed = transferOwnerHoldersState === FormState.CONFIRMED const isFailed = transferOwnerHoldersState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.TRANSFER_OWNER_HOLDER_SUCCESS : MessageTitle.TRANSFER_OWNER_HOLDER_FAILED const beneficiaryAddress = isConfirmed ? newOwner : beneficiary @@ -102,13 +104,14 @@ export const ActionForm: FunctionComponent = props => { } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, beneficiaryAddress, holderAddress, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -119,7 +122,7 @@ export const ActionForm: FunctionComponent = props => { const { rejectTransferOwnerHolderState } = props const isConfirmed = rejectTransferOwnerHolderState === FormState.CONFIRMED const isFailed = rejectTransferOwnerHolderState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.REJECT_TRANSFER_OWNER_HOLDER_SUCCESS : MessageTitle.REJECT_TRANSFER_OWNER_HOLDER_FAILED const beneficiaryAddress = isConfirmed ? prevBeneficiary : beneficiary @@ -131,13 +134,14 @@ export const ActionForm: FunctionComponent = props => { } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, beneficiaryAddress, holderAddress, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -148,23 +152,23 @@ export const ActionForm: FunctionComponent = props => { const { rejectTransferHolderState } = props const isConfirmed = rejectTransferHolderState === FormState.CONFIRMED const isFailed = rejectTransferHolderState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.REJECT_TRANSFER_HOLDER_SUCCESS : MessageTitle.REJECT_TRANSFER_HOLDER_FAILED const holderAddress = isConfirmed ? prevHolder : holder - if (isConfirmed || isFailed) { if (refreshEndorsementChain && isConfirmed) { refreshEndorsementChain() } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, holderAddress, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -175,7 +179,7 @@ export const ActionForm: FunctionComponent = props => { const { rejectTransferOwnerState } = props const isConfirmed = rejectTransferOwnerState === FormState.CONFIRMED const isFailed = rejectTransferOwnerState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.REJECT_TRANSFER_OWNER_SUCCESS : MessageTitle.REJECT_TRANSFER_OWNER_FAILED const beneficiaryAddress = isConfirmed ? prevBeneficiary : beneficiary @@ -186,12 +190,13 @@ export const ActionForm: FunctionComponent = props => { } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, beneficiaryAddress, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -202,18 +207,19 @@ export const ActionForm: FunctionComponent = props => { const { nominationState } = props const isConfirmed = nominationState === FormState.CONFIRMED const isFailed = nominationState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.NOMINATE_BENEFICIARY_SUCCESS : MessageTitle.NOMINATE_BENEFICIARY_FAILED if (isConfirmed || isFailed) { showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -224,7 +230,7 @@ export const ActionForm: FunctionComponent = props => { const { nominee, endorseBeneficiaryState } = props const isConfirmed = endorseBeneficiaryState === FormState.CONFIRMED const isFailed = endorseBeneficiaryState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.ENDORSE_BENEFICIARY_SUCCESS : MessageTitle.ENDORSE_BENEFICIARY_FAILED const beneficiaryAddress = isConfirmed ? nominee : beneficiary @@ -235,12 +241,13 @@ export const ActionForm: FunctionComponent = props => { } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, beneficiaryAddress, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -251,7 +258,7 @@ export const ActionForm: FunctionComponent = props => { const { transferOwnersState } = props const isConfirmed = transferOwnersState === FormState.CONFIRMED const isFailed = transferOwnersState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.TRANSFER_OWNER_SUCCESS : MessageTitle.TRANSFER_OWNER_FAILED const beneficiaryAddress = isConfirmed ? newOwner : beneficiary @@ -262,12 +269,13 @@ export const ActionForm: FunctionComponent = props => { } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, beneficiaryAddress, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -278,7 +286,7 @@ export const ActionForm: FunctionComponent = props => { const { returnToIssuerState } = props const isConfirmed = returnToIssuerState === FormState.CONFIRMED const isFailed = returnToIssuerState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.RETURN_TO_ISSUER_DOCUMENT_SUCCESS : MessageTitle.RETURN_TO_ISSUER_DOCUMENT_FAILED const beneficiaryAddress = isConfirmed ? '' : beneficiary @@ -291,13 +299,14 @@ export const ActionForm: FunctionComponent = props => { showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, beneficiaryAddress, holderAddress, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -308,7 +317,7 @@ export const ActionForm: FunctionComponent = props => { const { restoreTokenState } = props const isConfirmed = restoreTokenState === FormState.CONFIRMED const isFailed = restoreTokenState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.REJECT_RETURN_TO_ISSUER_DOCUMENT_SUCCESS : MessageTitle.REJECT_RETURN_TO_ISSUER_DOCUMENT_FAILED const beneficiaryAddress = isConfirmed ? beneficiary : '' @@ -320,13 +329,14 @@ export const ActionForm: FunctionComponent = props => { } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed, beneficiaryAddress, holderAddress, }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() @@ -337,7 +347,7 @@ export const ActionForm: FunctionComponent = props => { const { destroyTokenState } = props const isConfirmed = destroyTokenState === FormState.CONFIRMED const isFailed = destroyTokenState === FormState.ERROR - const msg = isConfirmed + const title = isConfirmed ? MessageTitle.ACCEPT_RETURN_TO_ISSUER_DOCUMENT_SUCCESS : MessageTitle.ACCEPT_RETURN_TO_ISSUER_DOCUMENT_FAILED @@ -347,9 +357,10 @@ export const ActionForm: FunctionComponent = props => { } showOverlay( showDocumentTransferMessage( - msg, + title, { isSuccess: isConfirmed }, - setShowEndorsementChain + setShowEndorsementChain, + errorMessage ) ) setFormActionNone() diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/types.ts b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/types.ts index 5de69d8..628dcf8 100644 --- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/types.ts +++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionForm/types.ts @@ -11,6 +11,7 @@ export interface BaseActionFormProps { setFormActionNone: () => void setShowEndorsementChain: (payload: boolean) => void refreshEndorsementChain?: () => void + errorMessage?: string } // Props for TransferHolderForm diff --git a/src/components/common/Navbar/Navbar.tsx b/src/components/common/Navbar/Navbar.tsx index 755ea4c..158ba04 100644 --- a/src/components/common/Navbar/Navbar.tsx +++ b/src/components/common/Navbar/Navbar.tsx @@ -344,7 +344,7 @@ const Navbar = ({ isDarkMode, setIsDarkMode: _setIsDarkMode }: NavbarProps) => { /> ({ + mockCloseOverlay: vi.fn(), +})) + +vi.mock('../../contexts/OverlayContext', async importOriginal => { + const actual = + await importOriginal() + return { + ...actual, + useOverlayContext: () => ({ + closeOverlay: mockCloseOverlay, + showOverlay: vi.fn(), + }), + } +}) + +vi.mock('./MessageAddressResolver', () => ({ + MessageAddressResolver: ({ address }: { address: string }) => ( + {address} + ), +})) + +vi.mock('@/components/icons/Success', () => ({ + default: () =>
, +})) + +vi.mock('@/components/icons/Error', () => ({ + default: () =>
, +})) + +const mockSetShowEndorsementChain = vi.fn() + +const renderWithOverlay = (ui: React.ReactElement) => + render({ui}) + +describe('DocumentTransferMessage', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + describe('errorMessage prop', () => { + it('renders errorMessage text instead of children when errorMessage is provided', () => { + renderWithOverlay( + + default child content + + ) + + expect(screen.getByText('User Rejected Transaction')).toBeInTheDocument() + expect( + screen.queryByText('default child content') + ).not.toBeInTheDocument() + }) + + it('renders children when errorMessage is undefined', () => { + renderWithOverlay( + + child content visible + + ) + + expect(screen.getByText('child content visible')).toBeInTheDocument() + }) + + it('renders children when errorMessage is empty string', () => { + renderWithOverlay( + + child content visible + + ) + + expect(screen.getByText('child content visible')).toBeInTheDocument() + }) + + it('renders the errorMessage inside a paragraph element', () => { + const { container } = renderWithOverlay( + + child + + ) + + const paragraph = container.querySelector('p.mt-3') + expect(paragraph?.textContent).toBe('Insufficient Funds') + }) + }) + + describe('title and icon rendering', () => { + it('renders the title text', () => { + renderWithOverlay( + + content + + ) + + expect(screen.getByText('Transfer Holder Success')).toBeInTheDocument() + }) + + it('renders success icon when isSuccess is true', () => { + renderWithOverlay( + + content + + ) + + expect(screen.getByTestId('success-icon')).toBeInTheDocument() + expect(screen.queryByTestId('error-icon')).not.toBeInTheDocument() + }) + + it('renders error icon when isSuccess is false', () => { + renderWithOverlay( + + content + + ) + + expect(screen.getByTestId('error-icon')).toBeInTheDocument() + expect(screen.queryByTestId('success-icon')).not.toBeInTheDocument() + }) + }) + + describe('buttons', () => { + it('calls closeOverlay when Dismiss button is clicked', () => { + renderWithOverlay( + + content + + ) + + fireEvent.click(screen.getByRole('button', { name: 'Dismiss' })) + expect(mockCloseOverlay).toHaveBeenCalled() + }) + + it('calls setShowEndorsementChain and closeOverlay when View Endorsement Chain is clicked', () => { + renderWithOverlay( + + content + + ) + + fireEvent.click( + screen.getByRole('button', { name: 'View Endorsement Chain' }) + ) + + expect(mockSetShowEndorsementChain).toHaveBeenCalledWith(true) + expect(mockCloseOverlay).toHaveBeenCalled() + }) + }) +}) + +describe('showDocumentTransferMessage', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it('passes errorMessage to DocumentTransferMessage', () => { + const node = showDocumentTransferMessage( + MessageTitle.TRANSFER_HOLDER_FAILED, + { isSuccess: false }, + mockSetShowEndorsementChain, + 'User Rejected Transaction' + ) as React.ReactElement + + expect(node.props.errorMessage).toBe('User Rejected Transaction') + }) + + it('passes undefined errorMessage when not provided', () => { + const node = showDocumentTransferMessage( + MessageTitle.TRANSFER_HOLDER_SUCCESS, + { isSuccess: true }, + mockSetShowEndorsementChain + ) as React.ReactElement + + expect(node.props.errorMessage).toBeUndefined() + }) + + it('passes isSuccess from option to DocumentTransferMessage', () => { + const node = showDocumentTransferMessage( + MessageTitle.TRANSFER_HOLDER_SUCCESS, + { isSuccess: true }, + mockSetShowEndorsementChain + ) as React.ReactElement + + expect(node.props.isSuccess).toBe(true) + }) + + it('passes the title to DocumentTransferMessage', () => { + const node = showDocumentTransferMessage( + MessageTitle.NOMINATE_BENEFICIARY_FAILED, + { isSuccess: false }, + mockSetShowEndorsementChain, + 'Network Error' + ) as React.ReactElement + + expect(node.props.title).toBe(MessageTitle.NOMINATE_BENEFICIARY_FAILED) + expect(node.props.errorMessage).toBe('Network Error') + }) + + it('renders TRANSFER_HOLDER children when no errorMessage', () => { + const node = showDocumentTransferMessage( + MessageTitle.TRANSFER_HOLDER_FAILED, + { isSuccess: false, holderAddress: '0xabc' }, + mockSetShowEndorsementChain + ) as React.ReactElement + + const { getByText } = renderWithOverlay(node) + expect(getByText('Transfer Holder Failed')).toBeInTheDocument() + }) + + it('renders errorMessage text in overlay when errorMessage set', () => { + const node = showDocumentTransferMessage( + MessageTitle.TRANSFER_HOLDER_FAILED, + { isSuccess: false }, + mockSetShowEndorsementChain, + 'Insufficient Funds' + ) as React.ReactElement + + const { getByText } = renderWithOverlay(node) + expect(getByText('Insufficient Funds')).toBeInTheDocument() + }) +}) diff --git a/src/components/common/Overlay/OverlayContent/DocumentTransferMessage.tsx b/src/components/common/Overlay/OverlayContent/DocumentTransferMessage.tsx index fb40aa2..c1e3581 100644 --- a/src/components/common/Overlay/OverlayContent/DocumentTransferMessage.tsx +++ b/src/components/common/Overlay/OverlayContent/DocumentTransferMessage.tsx @@ -68,11 +68,12 @@ interface DocumentTransferMessageProps { isConfirmationMessage?: boolean onConfirmationAction?: () => void setShowEndorsementChain: (payload: boolean) => void + errorMessage?: string } export const DocumentTransferMessage: FunctionComponent< DocumentTransferMessageProps -> = ({ title, isSuccess, setShowEndorsementChain, children }) => { +> = ({ title, isSuccess, setShowEndorsementChain, children, errorMessage }) => { const { closeOverlay } = useOverlayContext() const handleViewEndorsementChain = () => { setShowEndorsementChain(true) @@ -94,7 +95,7 @@ export const DocumentTransferMessage: FunctionComponent< {/* Content */}
- {children} + {errorMessage ?

{errorMessage}

: children}
{/* Footer Buttons */} @@ -303,7 +304,8 @@ interface ShowDocumentTransferMessageOptionProps { export const showDocumentTransferMessage = ( title: string, option: ShowDocumentTransferMessageOptionProps, - setShowEndorsementChain: (payload: boolean) => void + setShowEndorsementChain: (payload: boolean) => void, + errorMessage?: string ): ReactNode => { return ( {title === MessageTitle.NO_METAMASK && } {title === MessageTitle.NO_MANAGE_ACCESS && } diff --git a/src/components/common/contexts/TokenInformationContext/TokenInformationContext.tsx b/src/components/common/contexts/TokenInformationContext/TokenInformationContext.tsx index e68d633..f094e04 100644 --- a/src/components/common/contexts/TokenInformationContext/TokenInformationContext.tsx +++ b/src/components/common/contexts/TokenInformationContext/TokenInformationContext.tsx @@ -65,6 +65,7 @@ interface ITokenInformationContext { restoreToken: (...args: any[]) => Promise restoreTokenState: ContractFunctionState resetProviders: () => void + errorMessage?: string } const contractFunctionStub: any = () => { @@ -185,6 +186,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: changeHolder, state: changeHolderState, reset: resetChangeHolder, + errorMessage: changeHolderErrorMessage, } = useContractFunctionHook( titleEscrow, 'transferHolder', @@ -196,6 +198,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: destroyToken, state: destroyTokenState, reset: resetDestroyingTokenState, + errorMessage: destroyTokenErrorMessage, } = useContractFunctionHook( tokenRegistry, 'acceptReturned', @@ -207,6 +210,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: endorseBeneficiary, state: endorseBeneficiaryState, reset: resetEndorseBeneficiary, + errorMessage: endorseBeneficiaryErrorMessage, } = useContractFunctionHook( titleEscrow, 'transferBeneficiary', @@ -218,6 +222,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: nominate, state: nominateState, reset: resetNominate, + errorMessage: nominateErrorMessage, } = useContractFunctionHook( titleEscrow, 'nominate', @@ -229,6 +234,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: rejectTransferHolder, state: rejectTransferHolderState, reset: resetRejectTransferHolder, + errorMessage: rejectTransferHolderErrorMessage, } = useContractFunctionHook( titleEscrow, 'rejectTransferHolder', @@ -240,6 +246,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: rejectTransferOwner, state: rejectTransferOwnerState, reset: resetRejectTransferOwner, + errorMessage: rejectTransferOwnerErrorMessage, } = useContractFunctionHook( titleEscrow, 'rejectTransferBeneficiary', @@ -251,6 +258,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: rejectTransferOwnerHolder, state: rejectTransferOwnerHolderState, reset: resetRejectTransferOwnerHolder, + errorMessage: rejectTransferOwnerHolderErrorMessage, } = useContractFunctionHook( titleEscrow, 'rejectTransferOwners', @@ -262,6 +270,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: restoreToken, // restoreToken function does not return any value state: restoreTokenState, reset: resetRestoreTokenState, + errorMessage: restoreTokenErrorMessage, } = useContractFunctionHook( tokenRegistry, 'rejectReturned', @@ -273,6 +282,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: returnToIssuer, state: returnToIssuerState, reset: resetReturnToIssuer, + errorMessage: returnToIssuerErrorMessage, } = useContractFunctionHook( titleEscrow, 'returnToIssuer', @@ -284,6 +294,7 @@ export const TokenInformationContextProvider: FunctionComponent< send: transferOwners, state: transferOwnerHoldersState, reset: resetTransferOwners, + errorMessage: transferOwnersErrorMessage, } = useContractFunctionHook( titleEscrow, 'transferOwners', @@ -400,6 +411,19 @@ export const TokenInformationContextProvider: FunctionComponent< // Reset states for all write functions when provider changes to allow methods to be called again without refreshing useEffect(resetProviders, [resetProviders, providerOrSigner]) + + const errorMessage = + changeHolderErrorMessage || + destroyTokenErrorMessage || + endorseBeneficiaryErrorMessage || + transferOwnersErrorMessage || + nominateErrorMessage || + rejectTransferOwnerErrorMessage || + rejectTransferHolderErrorMessage || + rejectTransferOwnerHolderErrorMessage || + restoreTokenErrorMessage || + returnToIssuerErrorMessage + return ( {children} diff --git a/src/components/home/VerifySection/VerifyError.tsx b/src/components/home/VerifySection/VerifyError.tsx index 000c67e..6f34d68 100644 --- a/src/components/home/VerifySection/VerifyError.tsx +++ b/src/components/home/VerifySection/VerifyError.tsx @@ -86,7 +86,7 @@ const VerifyError: React.FC = ({ errorType, onReset }) => { {/* What Should I Do? link */}
{ ).toBeInTheDocument() }) - it('navigates to root when Visit Document Gallery is clicked', () => { + it('opens the document gallery in a new tab when clicked', () => { + const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null) render() const ctaButton = screen .getByText(/Visit Document Gallery/i) .closest('.cta-button') fireEvent.click(ctaButton as HTMLElement) - expect(mockNavigate).toHaveBeenCalledWith('/') + expect(openSpy).toHaveBeenCalledWith( + 'https://gallery.tradetrust.io', + '_blank', + 'noopener,noreferrer' + ) + openSpy.mockRestore() }) }) }) diff --git a/src/components/home/VerifySection/VerifySection.tsx b/src/components/home/VerifySection/VerifySection.tsx index 918105b..741b37a 100644 --- a/src/components/home/VerifySection/VerifySection.tsx +++ b/src/components/home/VerifySection/VerifySection.tsx @@ -1,5 +1,4 @@ import React from 'react' -import { useNavigate } from 'react-router-dom' import { useVerify } from './useVerify' import NetworkModal from './NetworkModal' import VerifyResult from './VerifyResult' @@ -91,8 +90,6 @@ const VerifySection: React.FC = ({ isDarkMode }) => { ? (CHAIN_NAMES[verifiedChainId] ?? `Chain ${verifiedChainId}`) : undefined - const navigate = useNavigate() - const renderDropzone = () => (
= ({ isDarkMode }) => {