From 9fad955dd5db5719ae734c4ba449a9ace6408c1d Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Thu, 16 Apr 2026 16:23:24 -0700 Subject: [PATCH 1/9] inital commit --- public/locales/en-US/translations.json | 7 + .../Accounts/PermissionDelegation/index.tsx | 132 ++++++++++++++ .../Accounts/PermissionDelegation/styles.scss | 15 ++ .../test/PermissionDelegation.test.tsx | 169 ++++++++++++++++++ src/containers/Accounts/index.tsx | 2 + src/containers/Accounts/test/index.test.tsx | 7 + 6 files changed, 332 insertions(+) create mode 100644 src/containers/Accounts/PermissionDelegation/index.tsx create mode 100644 src/containers/Accounts/PermissionDelegation/styles.scss create mode 100644 src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index a6d95c18f..334ba0a50 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -757,6 +757,13 @@ "account_page_asset_table_no_lptoken": "No LP Tokens found", "account_page_asset_table_no_mpt": "No MPTs found", "account_page_asset_table_no_nft": "No NFTs found", + "account_page_permission_delegation": "Permission delegation", + "account_page_permission_delegation_column_permission": "Permission", + "account_page_permission_delegation_column_granted_to": "Granted To", + "account_page_permission_delegation_column_status": "Status", + "account_page_permission_delegation_status_active": "Active", + "account_page_permission_delegation_status_expired": "Expired", + "account_page_permission_delegation_no_delegations": "No permission delegations found", "tx_hash": "Tx Hash", "timestamp": "Timestamp (UTC)", "amount_in": "Amount In", diff --git a/src/containers/Accounts/PermissionDelegation/index.tsx b/src/containers/Accounts/PermissionDelegation/index.tsx new file mode 100644 index 000000000..823a247eb --- /dev/null +++ b/src/containers/Accounts/PermissionDelegation/index.tsx @@ -0,0 +1,132 @@ +import { useState, useContext } from 'react' +import { useTranslation } from 'react-i18next' +import { useQuery } from 'react-query' +import ArrowIcon from '../../shared/images/down_arrow.svg' +import { Account } from '../../shared/components/Account' +import { Loader } from '../../shared/components/Loader' +import { EmptyMessageTableRow } from '../../shared/EmptyMessageTableRow' +import { getAccountObjects } from '../../../rippled/lib/rippled' +import SocketContext from '../../shared/SocketContext' +import { shortenAccount } from '../../shared/utils' +import '../AccountAsset/styles.scss' +import './styles.scss' + +interface DelegateObject { + Account: string + Authorize: string + Permissions: Array<{ + Permission: { + PermissionValue: string + } + }> + LedgerEntryType: string +} + +interface PermissionDelegationProps { + accountId: string +} + +export const PermissionDelegation = ({ + accountId, +}: PermissionDelegationProps) => { + const { t } = useTranslation() + const rippledSocket = useContext(SocketContext) + const [isOpen, setIsOpen] = useState(true) + + const { data, isLoading } = useQuery( + ['accountDelegates', accountId], + () => getAccountObjects(rippledSocket, accountId, 'delegate'), + { enabled: !!accountId }, + ) + + const delegates: DelegateObject[] = data?.account_objects ?? [] + + // Don't render the section if there are no delegations and we're done loading + if (!isLoading && delegates.length === 0) { + return null + } + + return ( +
+
+

+ {t('account_page_permission_delegation')} +

+ +
+ {isOpen && ( +
+ {isLoading ? ( + + ) : ( +
+ + + + + + + + + + {delegates.length === 0 ? ( + + {t( + 'account_page_permission_delegation_no_delegations', + )} + + ) : ( + delegates.flatMap((delegate) => + delegate.Permissions.map((perm, idx) => ( + + + + + + )), + ) + )} + +
+ {t( + 'account_page_permission_delegation_column_permission', + )} + + {t( + 'account_page_permission_delegation_column_granted_to', + )} + + {t('account_page_permission_delegation_column_status')} +
{perm.Permission.PermissionValue} + + + {t( + 'account_page_permission_delegation_status_active', + )} +
+
+ )} +
+ )} +
+ ) +} + +export default PermissionDelegation diff --git a/src/containers/Accounts/PermissionDelegation/styles.scss b/src/containers/Accounts/PermissionDelegation/styles.scss new file mode 100644 index 000000000..4005797a0 --- /dev/null +++ b/src/containers/Accounts/PermissionDelegation/styles.scss @@ -0,0 +1,15 @@ +/* PermissionDelegation-specific overrides. + Layout, header, toggle, arrow, and table base styles are reused + from AccountAsset via the shared .account-asset / .asset-section-header / + .account-asset-table classes. */ + +.permission-delegation .permission-delegation-table-wrapper { + .loader { + min-height: 50px; + } + + /* Override the large min-width from account-asset-table since we only have 3 columns */ + .account-asset-table table { + min-width: 0; + } +} diff --git a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx new file mode 100644 index 000000000..20992c89f --- /dev/null +++ b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx @@ -0,0 +1,169 @@ +import { render, screen, cleanup, waitFor, fireEvent } from '@testing-library/react' +import { I18nextProvider } from 'react-i18next' +import { BrowserRouter as Router } from 'react-router-dom' +import { QueryClientProvider } from 'react-query' +import i18n from '../../../../i18n/testConfigEnglish' +import SocketContext from '../../../shared/SocketContext' +import { PermissionDelegation } from '../index' +import { getAccountObjects } from '../../../../rippled/lib/rippled' +import { queryClient } from '../../../shared/QueryClient' +import Mock = jest.Mock + +jest.mock('../../../../rippled/lib/rippled') +jest.mock('../../../../rippled/lib/logger', () => ({ + __esModule: true, + default: () => ({ + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + }), +})) + +const mockedGetAccountObjects = getAccountObjects as Mock + +const mockSocket = {} as any + +const TestWrapper = ({ children }: { children: React.ReactNode }) => ( + + + + {children} + + + +) + +const mockDelegateResponse = { + account_objects: [ + { + Account: 'rTestAccount123456789012345678901', + Authorize: 'rN7n7otQDd6FczFgLdlqtyMVrn5f4W01dn', + Permissions: [ + { Permission: { PermissionValue: 'Payment' } }, + { Permission: { PermissionValue: 'TrustSet' } }, + ], + LedgerEntryType: 'Delegate', + }, + { + Account: 'rTestAccount123456789012345678901', + Authorize: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', + Permissions: [ + { Permission: { PermissionValue: 'OfferCreate' } }, + ], + LedgerEntryType: 'Delegate', + }, + ], +} + +describe('PermissionDelegation component', () => { + beforeEach(() => { + jest.clearAllMocks() + queryClient.clear() + }) + + afterEach(cleanup) + + it('renders nothing when there are no delegations', async () => { + mockedGetAccountObjects.mockResolvedValue({ account_objects: [] }) + + const { container } = render( + + + , + ) + + await waitFor(() => { + expect(mockedGetAccountObjects).toHaveBeenCalledWith( + mockSocket, + 'rTestAccount123456789012345678901', + 'delegate', + ) + }) + + expect(container.querySelector('.permission-delegation')).toBeNull() + }) + + it('renders the section title and table headers when delegations exist', async () => { + mockedGetAccountObjects.mockResolvedValue(mockDelegateResponse) + + render( + + + , + ) + + await waitFor(() => { + expect(screen.getByText('Permission delegation')).toBeInTheDocument() + }) + + expect(screen.getByText('Permission')).toBeInTheDocument() + expect(screen.getByText('Granted To')).toBeInTheDocument() + expect(screen.getByText('Status')).toBeInTheDocument() + }) + + it('renders all permission rows from delegate objects', async () => { + mockedGetAccountObjects.mockResolvedValue(mockDelegateResponse) + + render( + + + , + ) + + await waitFor(() => { + expect(screen.getByText('Payment')).toBeInTheDocument() + }) + + expect(screen.getByText('TrustSet')).toBeInTheDocument() + expect(screen.getByText('OfferCreate')).toBeInTheDocument() + + const activeStatuses = screen.getAllByText('Active') + expect(activeStatuses).toHaveLength(3) + }) + + it('reuses AccountAsset CSS classes', async () => { + mockedGetAccountObjects.mockResolvedValue(mockDelegateResponse) + + const { container } = render( + + + , + ) + + await waitFor(() => { + expect(screen.getByText('Permission delegation')).toBeInTheDocument() + }) + + expect(container.querySelector('.account-asset')).not.toBeNull() + expect(container.querySelector('.asset-section-header')).not.toBeNull() + expect(container.querySelector('.account-asset-title')).not.toBeNull() + expect(container.querySelector('.asset-section-toggle')).not.toBeNull() + expect(container.querySelector('.account-asset-table')).not.toBeNull() + }) + + it('toggles the table visibility when clicking the toggle button', async () => { + mockedGetAccountObjects.mockResolvedValue(mockDelegateResponse) + + render( + + + , + ) + + await waitFor(() => { + expect(screen.getByText('Payment')).toBeInTheDocument() + }) + + expect(screen.getByText('Payment')).toBeVisible() + + const toggleButton = screen.getByLabelText('Toggle permission delegation') + fireEvent.click(toggleButton) + + expect(screen.queryByText('Payment')).toBeNull() + + fireEvent.click(toggleButton) + + expect(screen.getByText('Payment')).toBeInTheDocument() + }) +}) + diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index b2c65fe78..eceab78a3 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -15,6 +15,7 @@ import { AccountSummary } from './AccountSummary' import { useXRPToUSDRate } from '../shared/hooks/useXRPToUSDRate' import AccountAsset from './AccountAsset' import AccountHeader from './AccountHeader' +import PermissionDelegation from './PermissionDelegation' export const Accounts = () => { const { trackScreenLoaded, trackException } = useAnalytics() @@ -65,6 +66,7 @@ export const Accounts = () => { {showAccount && ( <> + ({ default: () =>
Account Asset
, })) +jest.mock('../PermissionDelegation', () => ({ + __esModule: true, + default: () => ( +
Permission Delegation
+ ), +})) + jest.mock('../AccountTransactionTable', () => ({ __esModule: true, AccountTransactionTable: () => ( From ca28cc1ca2ba79494f202b67ca913928cf7192e9 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 17 Apr 2026 12:11:01 -0700 Subject: [PATCH 2/9] added new definitions to all translation files --- public/locales/ca-CA/translations.json | 7 +++++++ public/locales/es-ES/translations.json | 7 +++++++ public/locales/fr-FR/translations.json | 7 +++++++ public/locales/ja-JP/translations.json | 7 +++++++ public/locales/ko-KR/translations.json | 7 +++++++ public/locales/my-MM/translations.json | 7 +++++++ 6 files changed, 42 insertions(+) diff --git a/public/locales/ca-CA/translations.json b/public/locales/ca-CA/translations.json index 8ab3e03a7..bebb33c19 100644 --- a/public/locales/ca-CA/translations.json +++ b/public/locales/ca-CA/translations.json @@ -756,6 +756,13 @@ "account_page_asset_table_no_lptoken": null, "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, + "account_page_permission_delegation": null, + "account_page_permission_delegation_column_permission": null, + "account_page_permission_delegation_column_granted_to": null, + "account_page_permission_delegation_column_status": null, + "account_page_permission_delegation_status_active": null, + "account_page_permission_delegation_status_expired": null, + "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, diff --git a/public/locales/es-ES/translations.json b/public/locales/es-ES/translations.json index fd75401d4..5155e5d4e 100644 --- a/public/locales/es-ES/translations.json +++ b/public/locales/es-ES/translations.json @@ -757,6 +757,13 @@ "account_page_asset_table_no_lptoken": null, "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, + "account_page_permission_delegation": null, + "account_page_permission_delegation_column_permission": null, + "account_page_permission_delegation_column_granted_to": null, + "account_page_permission_delegation_column_status": null, + "account_page_permission_delegation_status_active": null, + "account_page_permission_delegation_status_expired": null, + "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, diff --git a/public/locales/fr-FR/translations.json b/public/locales/fr-FR/translations.json index cf8caa92d..d7ee2c4a7 100644 --- a/public/locales/fr-FR/translations.json +++ b/public/locales/fr-FR/translations.json @@ -757,6 +757,13 @@ "account_page_asset_table_no_lptoken": null, "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, + "account_page_permission_delegation": null, + "account_page_permission_delegation_column_permission": null, + "account_page_permission_delegation_column_granted_to": null, + "account_page_permission_delegation_column_status": null, + "account_page_permission_delegation_status_active": null, + "account_page_permission_delegation_status_expired": null, + "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, diff --git a/public/locales/ja-JP/translations.json b/public/locales/ja-JP/translations.json index fca14b47c..053f83cc1 100644 --- a/public/locales/ja-JP/translations.json +++ b/public/locales/ja-JP/translations.json @@ -757,6 +757,13 @@ "account_page_asset_table_no_lptoken": null, "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, + "account_page_permission_delegation": null, + "account_page_permission_delegation_column_permission": null, + "account_page_permission_delegation_column_granted_to": null, + "account_page_permission_delegation_column_status": null, + "account_page_permission_delegation_status_active": null, + "account_page_permission_delegation_status_expired": null, + "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, diff --git a/public/locales/ko-KR/translations.json b/public/locales/ko-KR/translations.json index da64a4c6d..6a844f849 100644 --- a/public/locales/ko-KR/translations.json +++ b/public/locales/ko-KR/translations.json @@ -757,6 +757,13 @@ "account_page_asset_table_no_lptoken": null, "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, + "account_page_permission_delegation": null, + "account_page_permission_delegation_column_permission": null, + "account_page_permission_delegation_column_granted_to": null, + "account_page_permission_delegation_column_status": null, + "account_page_permission_delegation_status_active": null, + "account_page_permission_delegation_status_expired": null, + "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, diff --git a/public/locales/my-MM/translations.json b/public/locales/my-MM/translations.json index f83c121c6..80c299f40 100644 --- a/public/locales/my-MM/translations.json +++ b/public/locales/my-MM/translations.json @@ -757,6 +757,13 @@ "account_page_asset_table_no_lptoken": null, "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, + "account_page_permission_delegation": null, + "account_page_permission_delegation_column_permission": null, + "account_page_permission_delegation_column_granted_to": null, + "account_page_permission_delegation_column_status": null, + "account_page_permission_delegation_status_active": null, + "account_page_permission_delegation_status_expired": null, + "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, From ca076ad2741edbe16bd3672e336310e5b1999649 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 17 Apr 2026 12:37:24 -0700 Subject: [PATCH 3/9] linter fix --- .../Accounts/PermissionDelegation/index.tsx | 12 ++++-------- .../test/PermissionDelegation.test.tsx | 17 +++++++++++------ src/containers/Accounts/index.tsx | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/containers/Accounts/PermissionDelegation/index.tsx b/src/containers/Accounts/PermissionDelegation/index.tsx index 823a247eb..e0c817c85 100644 --- a/src/containers/Accounts/PermissionDelegation/index.tsx +++ b/src/containers/Accounts/PermissionDelegation/index.tsx @@ -91,23 +91,19 @@ export const PermissionDelegation = ({ {delegates.length === 0 ? ( - {t( - 'account_page_permission_delegation_no_delegations', - )} + {t('account_page_permission_delegation_no_delegations')} ) : ( delegates.flatMap((delegate) => - delegate.Permissions.map((perm, idx) => ( + delegate.Permissions.map((perm) => ( {perm.Permission.PermissionValue} diff --git a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx index 20992c89f..a9770ddcf 100644 --- a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx +++ b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx @@ -1,4 +1,10 @@ -import { render, screen, cleanup, waitFor, fireEvent } from '@testing-library/react' +import { + render, + screen, + cleanup, + waitFor, + fireEvent, +} from '@testing-library/react' import { I18nextProvider } from 'react-i18next' import { BrowserRouter as Router } from 'react-router-dom' import { QueryClientProvider } from 'react-query' @@ -27,7 +33,9 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => ( - {children} + + {children} + @@ -47,9 +55,7 @@ const mockDelegateResponse = { { Account: 'rTestAccount123456789012345678901', Authorize: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', - Permissions: [ - { Permission: { PermissionValue: 'OfferCreate' } }, - ], + Permissions: [{ Permission: { PermissionValue: 'OfferCreate' } }], LedgerEntryType: 'Delegate', }, ], @@ -166,4 +172,3 @@ describe('PermissionDelegation component', () => { expect(screen.getByText('Payment')).toBeInTheDocument() }) }) - diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index eceab78a3..d5045011e 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -15,7 +15,7 @@ import { AccountSummary } from './AccountSummary' import { useXRPToUSDRate } from '../shared/hooks/useXRPToUSDRate' import AccountAsset from './AccountAsset' import AccountHeader from './AccountHeader' -import PermissionDelegation from './PermissionDelegation' +import { PermissionDelegation } from './PermissionDelegation' export const Accounts = () => { const { trackScreenLoaded, trackException } = useAnalytics() From beb5a3b2e775786c9e7980cbae3d383dccc337ba Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 17 Apr 2026 12:58:49 -0700 Subject: [PATCH 4/9] fix test error --- src/containers/Accounts/test/index.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/Accounts/test/index.test.tsx b/src/containers/Accounts/test/index.test.tsx index b8cf92765..31903ef19 100644 --- a/src/containers/Accounts/test/index.test.tsx +++ b/src/containers/Accounts/test/index.test.tsx @@ -33,7 +33,7 @@ jest.mock('../AccountAsset', () => ({ jest.mock('../PermissionDelegation', () => ({ __esModule: true, - default: () => ( + PermissionDelegation: () => (
Permission Delegation
), })) From ea371bb362bd9b48d4464f7fc1aceea66edc8afc Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 24 Apr 2026 11:27:33 -0700 Subject: [PATCH 5/9] Refactor PermissionDelegation and unify collapsible sections - Extract shared CollapsibleSection component used by PermissionDelegation, AccountAsset, and AccountSummary (removes 3 duplicate implementations) - Redesign PermissionDelegation as a card layout (address + permission chips) instead of a full-width table - Drop the always-"Active" Status column and unused EmptyMessageTableRow branch - Remove unused in-branch translation keys (permission_delegation column/ status/no_delegations, assets_available, other_data, not_available, percent_of_supply, currency_toggle_help) --- public/locales/ca-CA/translations.json | 11 -- public/locales/en-US/translations.json | 13 +- public/locales/es-ES/translations.json | 11 -- public/locales/fr-FR/translations.json | 11 -- public/locales/ja-JP/translations.json | 11 -- public/locales/ko-KR/translations.json | 11 -- public/locales/my-MM/translations.json | 11 -- .../Accounts/AccountAsset/index.tsx | 58 ++------- .../Accounts/AccountAsset/styles.scss | 50 -------- .../AccountAsset/test/AccountAsset.test.tsx | 8 +- .../Accounts/AccountSummary/index.tsx | 41 +++---- .../Accounts/AccountSummary/styles.scss | 107 ++++------------ .../Accounts/PermissionDelegation/index.tsx | 114 ++++++------------ .../Accounts/PermissionDelegation/styles.scss | 60 +++++++-- .../test/PermissionDelegation.test.tsx | 31 +++-- src/containers/Accounts/test/index.test.tsx | 4 +- .../components/CollapsibleSection/index.tsx | 56 +++++++++ .../components/CollapsibleSection/styles.scss | 65 ++++++++++ 18 files changed, 279 insertions(+), 394 deletions(-) create mode 100644 src/containers/shared/components/CollapsibleSection/index.tsx create mode 100644 src/containers/shared/components/CollapsibleSection/styles.scss diff --git a/public/locales/ca-CA/translations.json b/public/locales/ca-CA/translations.json index bebb33c19..a368c138b 100644 --- a/public/locales/ca-CA/translations.json +++ b/public/locales/ca-CA/translations.json @@ -757,12 +757,6 @@ "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, "account_page_permission_delegation": null, - "account_page_permission_delegation_column_permission": null, - "account_page_permission_delegation_column_granted_to": null, - "account_page_permission_delegation_column_status": null, - "account_page_permission_delegation_status_active": null, - "account_page_permission_delegation_status_expired": null, - "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, @@ -823,12 +817,9 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, - "assets_available": null, "unrealized_loss": null, - "other_data": null, "max_total_supply": null, "available_to_borrow": null, - "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -854,9 +845,7 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, - "percent_of_supply": null, "value": null, - "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 334ba0a50..68b8b7c4d 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -757,13 +757,7 @@ "account_page_asset_table_no_lptoken": "No LP Tokens found", "account_page_asset_table_no_mpt": "No MPTs found", "account_page_asset_table_no_nft": "No NFTs found", - "account_page_permission_delegation": "Permission delegation", - "account_page_permission_delegation_column_permission": "Permission", - "account_page_permission_delegation_column_granted_to": "Granted To", - "account_page_permission_delegation_column_status": "Status", - "account_page_permission_delegation_status_active": "Active", - "account_page_permission_delegation_status_expired": "Expired", - "account_page_permission_delegation_no_delegations": "No permission delegations found", + "account_page_permission_delegation": "Permission Delegation", "tx_hash": "Tx Hash", "timestamp": "Timestamp (UTC)", "amount_in": "Amount In", @@ -824,12 +818,9 @@ "perm_domain_id": "Permissioned Domain ID", "total_value_locked": "Total Value Locked (TVL)", "shares": "Shares", - "assets_available": "Assets Available", "unrealized_loss": "Unrealized Loss", - "other_data": "Other Data", "max_total_supply": "Max Total Supply", "available_to_borrow": "Available to Borrow", - "not_available": "Not available", "first_come_first_served": "First Come First Served", "loans": "Loans", "loan_broker": "Loan Broker", @@ -855,9 +846,7 @@ "depositors_fetch_error": "Unable to fetch depositors information", "no_depositors_message": "No depositors found for this vault.", "depositors": "Depositors", - "percent_of_supply": "% of Supply", "value": "Value", - "currency_toggle_help": "Toggle to view values in native-currency or USD", "currency_toggle_description": "Toggle to view values in native-currency or USD", "currency_toggle_loading": "Loading USD conversion rate...", "currency_toggle_loading_description": "Loading USD conversion rate...", diff --git a/public/locales/es-ES/translations.json b/public/locales/es-ES/translations.json index 5155e5d4e..148dd1825 100644 --- a/public/locales/es-ES/translations.json +++ b/public/locales/es-ES/translations.json @@ -758,12 +758,6 @@ "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, "account_page_permission_delegation": null, - "account_page_permission_delegation_column_permission": null, - "account_page_permission_delegation_column_granted_to": null, - "account_page_permission_delegation_column_status": null, - "account_page_permission_delegation_status_active": null, - "account_page_permission_delegation_status_expired": null, - "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, @@ -824,12 +818,9 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, - "assets_available": null, "unrealized_loss": null, - "other_data": null, "max_total_supply": null, "available_to_borrow": null, - "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -855,9 +846,7 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, - "percent_of_supply": null, "value": null, - "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/fr-FR/translations.json b/public/locales/fr-FR/translations.json index d7ee2c4a7..b12dea129 100644 --- a/public/locales/fr-FR/translations.json +++ b/public/locales/fr-FR/translations.json @@ -758,12 +758,6 @@ "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, "account_page_permission_delegation": null, - "account_page_permission_delegation_column_permission": null, - "account_page_permission_delegation_column_granted_to": null, - "account_page_permission_delegation_column_status": null, - "account_page_permission_delegation_status_active": null, - "account_page_permission_delegation_status_expired": null, - "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, @@ -824,12 +818,9 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, - "assets_available": null, "unrealized_loss": null, - "other_data": null, "max_total_supply": null, "available_to_borrow": null, - "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -855,9 +846,7 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, - "percent_of_supply": null, "value": null, - "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/ja-JP/translations.json b/public/locales/ja-JP/translations.json index 053f83cc1..126de316f 100644 --- a/public/locales/ja-JP/translations.json +++ b/public/locales/ja-JP/translations.json @@ -758,12 +758,6 @@ "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, "account_page_permission_delegation": null, - "account_page_permission_delegation_column_permission": null, - "account_page_permission_delegation_column_granted_to": null, - "account_page_permission_delegation_column_status": null, - "account_page_permission_delegation_status_active": null, - "account_page_permission_delegation_status_expired": null, - "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, @@ -824,12 +818,9 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, - "assets_available": null, "unrealized_loss": null, - "other_data": null, "max_total_supply": null, "available_to_borrow": null, - "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -855,9 +846,7 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, - "percent_of_supply": null, "value": null, - "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/ko-KR/translations.json b/public/locales/ko-KR/translations.json index 6a844f849..5ac59ea5f 100644 --- a/public/locales/ko-KR/translations.json +++ b/public/locales/ko-KR/translations.json @@ -758,12 +758,6 @@ "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, "account_page_permission_delegation": null, - "account_page_permission_delegation_column_permission": null, - "account_page_permission_delegation_column_granted_to": null, - "account_page_permission_delegation_column_status": null, - "account_page_permission_delegation_status_active": null, - "account_page_permission_delegation_status_expired": null, - "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, @@ -824,12 +818,9 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, - "assets_available": null, "unrealized_loss": null, - "other_data": null, "max_total_supply": null, "available_to_borrow": null, - "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -855,9 +846,7 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, - "percent_of_supply": null, "value": null, - "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/my-MM/translations.json b/public/locales/my-MM/translations.json index 80c299f40..a841b9af0 100644 --- a/public/locales/my-MM/translations.json +++ b/public/locales/my-MM/translations.json @@ -758,12 +758,6 @@ "account_page_asset_table_no_mpt": null, "account_page_asset_table_no_nft": null, "account_page_permission_delegation": null, - "account_page_permission_delegation_column_permission": null, - "account_page_permission_delegation_column_granted_to": null, - "account_page_permission_delegation_column_status": null, - "account_page_permission_delegation_status_active": null, - "account_page_permission_delegation_status_expired": null, - "account_page_permission_delegation_no_delegations": null, "tx_hash": null, "timestamp": null, "amount_in": null, @@ -824,12 +818,9 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, - "assets_available": null, "unrealized_loss": null, - "other_data": null, "max_total_supply": null, "available_to_borrow": null, - "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -855,9 +846,7 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, - "percent_of_supply": null, "value": null, - "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/src/containers/Accounts/AccountAsset/index.tsx b/src/containers/Accounts/AccountAsset/index.tsx index e883a1487..cbd3633b6 100644 --- a/src/containers/Accounts/AccountAsset/index.tsx +++ b/src/containers/Accounts/AccountAsset/index.tsx @@ -3,7 +3,7 @@ import './styles.scss' import { useTranslation } from 'react-i18next' import { localizeNumber } from '../../shared/utils' import { useLanguage } from '../../shared/hooks' -import ArrowIcon from '../../shared/images/down_arrow.svg' +import { CollapsibleSection } from '../../shared/components/CollapsibleSection' import { HeldIOUs } from './assetTables/HeldIOUs' import { HeldMPTs } from './assetTables/HeldMPTs' import { HeldLPTokens } from './assetTables/HeldLPTokens' @@ -143,32 +143,14 @@ export default function AccountAsset({ const [heldTab, setHeldTab] = useState('iou') const [issuedTab, setIssuedTab] = useState('iou') - // Collapse state - default to expanded (true means open) - const [heldSectionOpen, setHeldSectionOpen] = useState(true) - const [issuedSectionOpen, setIssuedSectionOpen] = useState(true) - return (
{/* Assets Held */} -
-

- {t('account_page_asset_held_title')} -

- -
-
-
+ {/* Assets Issued */} -
-

- {t('account_page_asset_issued_title')} -

- -
-
-
+
) } diff --git a/src/containers/Accounts/AccountAsset/styles.scss b/src/containers/Accounts/AccountAsset/styles.scss index 7434c1211..bf740fa13 100644 --- a/src/containers/Accounts/AccountAsset/styles.scss +++ b/src/containers/Accounts/AccountAsset/styles.scss @@ -4,51 +4,6 @@ padding: 24px 0px; } -.account-asset-title { - @include bold; - - font-size: 20px; -} - -.asset-section-header { - display: flex; - align-items: center; - justify-content: flex-start; - margin: 24px 0 20px; - gap: 2px; - - &:first-child { - margin-top: 0; - } - - .account-asset-title { - margin: 0; - } - - .asset-section-toggle { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 6px; - border: 0; - background: transparent; - color: inherit; - cursor: pointer; - } - - .asset-section-arrow { - display: inline-block; - width: 18px; - height: 18px; - transform-origin: center; - transition: transform 180ms ease; - } - - .asset-section-arrow.open { - transform: rotate(180deg); - } -} - .account-asset-tabs { display: flex; height: 30px; @@ -255,8 +210,3 @@ } } -@media (max-width: $tablet-portrait-upper-boundary) { - .account-asset-title { - font-size: 18px; - } -} diff --git a/src/containers/Accounts/AccountAsset/test/AccountAsset.test.tsx b/src/containers/Accounts/AccountAsset/test/AccountAsset.test.tsx index a385e9341..b82e128af 100644 --- a/src/containers/Accounts/AccountAsset/test/AccountAsset.test.tsx +++ b/src/containers/Accounts/AccountAsset/test/AccountAsset.test.tsx @@ -362,7 +362,9 @@ describe('AccountAsset Component', () => { }) // Verify all 4 held asset table wrappers and asset tables are rendered - const allSections = container.querySelectorAll('.account-asset-content') + const allSections = container.querySelectorAll( + '.collapsible-section-body', + ) const heldSection = allSections[0] // First section (Held) const heldWrappers = heldSection.querySelectorAll( '.account-asset-table-wrapper', @@ -391,7 +393,9 @@ describe('AccountAsset Component', () => { }) // Verify all 3 issued asset table wrappers and asset tables are rendered - const allSections = container.querySelectorAll('.account-asset-content') + const allSections = container.querySelectorAll( + '.collapsible-section-body', + ) const issuedSection = allSections[1] // Second section (Issued) const issuedWrappers = issuedSection.querySelectorAll( '.account-asset-table-wrapper', diff --git a/src/containers/Accounts/AccountSummary/index.tsx b/src/containers/Accounts/AccountSummary/index.tsx index b045242a3..09154cc2d 100644 --- a/src/containers/Accounts/AccountSummary/index.tsx +++ b/src/containers/Accounts/AccountSummary/index.tsx @@ -1,8 +1,7 @@ -import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useLanguage } from '../../shared/hooks' -import ArrowIcon from '../../shared/images/down_arrow.svg' +import { CollapsibleSection } from '../../shared/components/CollapsibleSection' import Balances from './Balances' import DetailsCard from './DetailsCard' import FlagsCard from './FlagsCard' @@ -21,37 +20,25 @@ export const AccountSummary = ({ }: AccountSummaryProps) => { const { t } = useTranslation() const lang = useLanguage() - const [propertiesOpen, setPropertiesOpen] = useState(false) return (
-
-
-

{t('account_page_account_properties')}

- + +
+ + {account.signerList?.signers && ( + + )} +
- {propertiesOpen && ( -
- - {account.signerList?.signers && ( - - )} - -
- )} -
+
) } diff --git a/src/containers/Accounts/AccountSummary/styles.scss b/src/containers/Accounts/AccountSummary/styles.scss index 77723eada..9fff7cdb8 100644 --- a/src/containers/Accounts/AccountSummary/styles.scss +++ b/src/containers/Accounts/AccountSummary/styles.scss @@ -92,44 +92,6 @@ } .properties { - /* layout for header and the chevron toggle */ - .properties-header { - display: flex; - align-items: center; - justify-content: flex-start; - margin: 24px 0; - gap: 2px; - - h3 { - margin: 0; - color: $black-0; - font-size: 20px; - } - - .properties-toggle { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 6px; - border: 0; - background: transparent; - color: inherit; - cursor: pointer; - } - - .properties-arrow { - display: inline-block; - width: 18px; - height: 18px; - transform-origin: center; - transition: transform 180ms ease; - } - - .properties-arrow.open { - transform: rotate(180deg); - } - } - .properties-grid { display: grid; align-items: start; @@ -465,63 +427,40 @@ } } - .properties { - .properties-header { - gap: 8px; - - .properties-toggle { - padding: 4px; - } + .properties .properties-grid { + margin-top: 12px; + gap: 12px; + grid-template-columns: 1fr; - .properties-arrow { - width: 16px; - height: 16px; - } + .flags-card, + .signers-card { + padding: 16px; } - .properties-grid { - margin-top: 12px; - gap: 12px; - grid-template-columns: 1fr; - - .flags-card, - .signers-card { - padding: 16px; - } - - .flags-card { - grid-column: auto; - } + .flags-card { + grid-column: auto; + } - .signers-card { - grid-column: auto; - } + .signers-card { + grid-column: auto; + } - .flags-list, - .signers-list { - max-height: calc(5 * 64px); - padding-inline: 4px; - } + .flags-list, + .signers-list { + max-height: calc(5 * 64px); + padding-inline: 4px; + } - .flag-item, - .signer-item { - padding: 10px; - } + .flag-item, + .signer-item { + padding: 10px; } } } @media (max-width: $tablet-portrait-upper-boundary) { - .properties { - .properties-header { - h3 { - font-size: 18px; - } - } - - .properties-grid .card-header .header-title { - font-size: 18px; - } + .properties .properties-grid .card-header .header-title { + font-size: 18px; } } } diff --git a/src/containers/Accounts/PermissionDelegation/index.tsx b/src/containers/Accounts/PermissionDelegation/index.tsx index e0c817c85..a29024d1c 100644 --- a/src/containers/Accounts/PermissionDelegation/index.tsx +++ b/src/containers/Accounts/PermissionDelegation/index.tsx @@ -1,14 +1,12 @@ -import { useState, useContext } from 'react' +import { useContext } from 'react' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' -import ArrowIcon from '../../shared/images/down_arrow.svg' import { Account } from '../../shared/components/Account' +import { CollapsibleSection } from '../../shared/components/CollapsibleSection' import { Loader } from '../../shared/components/Loader' -import { EmptyMessageTableRow } from '../../shared/EmptyMessageTableRow' import { getAccountObjects } from '../../../rippled/lib/rippled' import SocketContext from '../../shared/SocketContext' import { shortenAccount } from '../../shared/utils' -import '../AccountAsset/styles.scss' import './styles.scss' interface DelegateObject { @@ -31,7 +29,6 @@ export const PermissionDelegation = ({ }: PermissionDelegationProps) => { const { t } = useTranslation() const rippledSocket = useContext(SocketContext) - const [isOpen, setIsOpen] = useState(true) const { data, isLoading } = useQuery( ['accountDelegates', accountId], @@ -47,80 +44,39 @@ export const PermissionDelegation = ({ } return ( -
-
-

- {t('account_page_permission_delegation')} -

- -
- {isOpen && ( -
- {isLoading ? ( - - ) : ( -
- - - - - - - - - - {delegates.length === 0 ? ( - - {t('account_page_permission_delegation_no_delegations')} - - ) : ( - delegates.flatMap((delegate) => - delegate.Permissions.map((perm) => ( - - - - - - )), - ) - )} - -
- {t( - 'account_page_permission_delegation_column_permission', - )} - - {t( - 'account_page_permission_delegation_column_granted_to', - )} - - {t('account_page_permission_delegation_column_status')} -
{perm.Permission.PermissionValue} - - - {t( - 'account_page_permission_delegation_status_active', - )} -
-
- )} -
- )} +
+ + {isLoading ? ( + + ) : ( +
+ {delegates.map((delegate) => ( +
+
+ +
+
+ {delegate.Permissions.map((perm) => ( + + {perm.Permission.PermissionValue} + + ))} +
+
+ ))} +
+ )} +
) } diff --git a/src/containers/Accounts/PermissionDelegation/styles.scss b/src/containers/Accounts/PermissionDelegation/styles.scss index 4005797a0..f541e0ecf 100644 --- a/src/containers/Accounts/PermissionDelegation/styles.scss +++ b/src/containers/Accounts/PermissionDelegation/styles.scss @@ -1,15 +1,55 @@ -/* PermissionDelegation-specific overrides. - Layout, header, toggle, arrow, and table base styles are reused - from AccountAsset via the shared .account-asset / .asset-section-header / - .account-asset-table classes. */ +@use '../../shared/css/variables' as *; -.permission-delegation .permission-delegation-table-wrapper { - .loader { - min-height: 50px; +.permission-delegation { + .delegate-list { + display: flex; + flex-direction: column; + gap: 8px; + } + + .delegate-item { + display: flex; + align-items: center; + gap: 16px; + padding: 12px 16px; + border-radius: 8px; + background: $black-80; + + @media (max-width: $tablet-portrait-upper-boundary) { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + } + + .delegate-authorize { + flex-shrink: 0; + font-size: 14px; } - /* Override the large min-width from account-asset-table since we only have 3 columns */ - .account-asset-table table { - min-width: 0; + .delegate-permissions { + display: flex; + flex: 1; + flex-wrap: wrap; + justify-content: flex-end; + gap: 6px; + + @media (max-width: $tablet-portrait-upper-boundary) { + justify-content: flex-start; + } + } + + .permission-chip { + padding: 4px 10px; + border-radius: 999px; + background: $black-70; + color: $black-0; + font-size: 12px; + letter-spacing: 0.03em; + @include semibold; + } + + .loader { + min-height: 50px; } } diff --git a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx index a9770ddcf..7b7590e3b 100644 --- a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx +++ b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx @@ -89,7 +89,7 @@ describe('PermissionDelegation component', () => { expect(container.querySelector('.permission-delegation')).toBeNull() }) - it('renders the section title and table headers when delegations exist', async () => { + it('renders the section title when delegations exist', async () => { mockedGetAccountObjects.mockResolvedValue(mockDelegateResponse) render( @@ -99,12 +99,8 @@ describe('PermissionDelegation component', () => { ) await waitFor(() => { - expect(screen.getByText('Permission delegation')).toBeInTheDocument() + expect(screen.getByText('Permission Delegation')).toBeInTheDocument() }) - - expect(screen.getByText('Permission')).toBeInTheDocument() - expect(screen.getByText('Granted To')).toBeInTheDocument() - expect(screen.getByText('Status')).toBeInTheDocument() }) it('renders all permission rows from delegate objects', async () => { @@ -122,12 +118,9 @@ describe('PermissionDelegation component', () => { expect(screen.getByText('TrustSet')).toBeInTheDocument() expect(screen.getByText('OfferCreate')).toBeInTheDocument() - - const activeStatuses = screen.getAllByText('Active') - expect(activeStatuses).toHaveLength(3) }) - it('reuses AccountAsset CSS classes', async () => { + it('renders with collapsible section and delegate cards', async () => { mockedGetAccountObjects.mockResolvedValue(mockDelegateResponse) const { container } = render( @@ -137,14 +130,20 @@ describe('PermissionDelegation component', () => { ) await waitFor(() => { - expect(screen.getByText('Permission delegation')).toBeInTheDocument() + expect(screen.getByText('Permission Delegation')).toBeInTheDocument() }) - expect(container.querySelector('.account-asset')).not.toBeNull() - expect(container.querySelector('.asset-section-header')).not.toBeNull() - expect(container.querySelector('.account-asset-title')).not.toBeNull() - expect(container.querySelector('.asset-section-toggle')).not.toBeNull() - expect(container.querySelector('.account-asset-table')).not.toBeNull() + expect(container.querySelector('.permission-delegation')).not.toBeNull() + expect( + container.querySelector('.collapsible-section-header'), + ).not.toBeNull() + expect(container.querySelector('.collapsible-section-title')).not.toBeNull() + expect( + container.querySelector('.collapsible-section-toggle'), + ).not.toBeNull() + expect(container.querySelector('.delegate-list')).not.toBeNull() + expect(container.querySelectorAll('.delegate-item').length).toBe(2) + expect(container.querySelectorAll('.permission-chip').length).toBe(3) }) it('toggles the table visibility when clicking the toggle button', async () => { diff --git a/src/containers/Accounts/test/index.test.tsx b/src/containers/Accounts/test/index.test.tsx index 31903ef19..2d1f6c691 100644 --- a/src/containers/Accounts/test/index.test.tsx +++ b/src/containers/Accounts/test/index.test.tsx @@ -33,9 +33,7 @@ jest.mock('../AccountAsset', () => ({ jest.mock('../PermissionDelegation', () => ({ __esModule: true, - PermissionDelegation: () => ( -
Permission Delegation
- ), + PermissionDelegation: () => null, })) jest.mock('../AccountTransactionTable', () => ({ diff --git a/src/containers/shared/components/CollapsibleSection/index.tsx b/src/containers/shared/components/CollapsibleSection/index.tsx new file mode 100644 index 000000000..345db96ad --- /dev/null +++ b/src/containers/shared/components/CollapsibleSection/index.tsx @@ -0,0 +1,56 @@ +import { useState, ReactNode } from 'react' +import ArrowIcon from '../../images/down_arrow.svg' +import './styles.scss' + +interface CollapsibleSectionProps { + title: ReactNode + ariaLabel: string + children: ReactNode + defaultOpen?: boolean + className?: string + keepMounted?: boolean +} + +export const CollapsibleSection = ({ + title, + ariaLabel, + children, + defaultOpen = true, + className, + keepMounted = false, +}: CollapsibleSectionProps) => { + const [isOpen, setIsOpen] = useState(defaultOpen) + + const classes = ['collapsible-section', className].filter(Boolean).join(' ') + + return ( +
+
+

{title}

+ +
+ {keepMounted ? ( +
+ {children} +
+ ) : ( + isOpen &&
{children}
+ )} +
+ ) +} + +export default CollapsibleSection diff --git a/src/containers/shared/components/CollapsibleSection/styles.scss b/src/containers/shared/components/CollapsibleSection/styles.scss new file mode 100644 index 000000000..d9c016db6 --- /dev/null +++ b/src/containers/shared/components/CollapsibleSection/styles.scss @@ -0,0 +1,65 @@ +@use '../../css/variables' as *; + +.collapsible-section-header { + display: flex; + align-items: center; + justify-content: flex-start; + margin: 24px 0 20px; + gap: 2px; + + &:first-child { + margin-top: 0; + } + + .collapsible-section-title { + @include bold; + + margin: 0; + color: $black-0; + font-size: 20px; + } + + .collapsible-section-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 6px; + border: 0; + background: transparent; + color: inherit; + cursor: pointer; + } + + .collapsible-section-arrow { + display: inline-block; + width: 18px; + height: 18px; + transform-origin: center; + transition: transform 180ms ease; + } + + .collapsible-section-arrow.open { + transform: rotate(180deg); + } +} + +@media (max-width: $tablet-landscape-upper-boundary) { + .collapsible-section-header { + gap: 8px; + + .collapsible-section-toggle { + padding: 4px; + } + + .collapsible-section-arrow { + width: 16px; + height: 16px; + } + } +} + +@media (max-width: $tablet-portrait-upper-boundary) { + .collapsible-section-header .collapsible-section-title { + font-size: 18px; + } +} From 5e01781403cf6a8b4c319426a67a0f0f52a49197 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 24 Apr 2026 13:32:37 -0700 Subject: [PATCH 6/9] Switch PermissionDelegation test to MemoryRouter from react-router CI fails to resolve react-router-dom from the new test file. Use MemoryRouter from react-router (the pattern used by QuickHarness) to avoid the resolution issue. --- .../PermissionDelegation/test/PermissionDelegation.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx index 7b7590e3b..9e5b67367 100644 --- a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx +++ b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx @@ -6,7 +6,7 @@ import { fireEvent, } from '@testing-library/react' import { I18nextProvider } from 'react-i18next' -import { BrowserRouter as Router } from 'react-router-dom' +import { MemoryRouter as Router } from 'react-router' import { QueryClientProvider } from 'react-query' import i18n from '../../../../i18n/testConfigEnglish' import SocketContext from '../../../shared/SocketContext' From fe5e0aba3389dade734124c1df227844a44e45d8 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 24 Apr 2026 13:46:12 -0700 Subject: [PATCH 7/9] Fix lint and deflake PermissionDelegation tests - Reorder padding/gap in delegate-item to satisfy stylelint - Wait for rendered state (not just mock call) in the empty and card-structure tests to avoid CI timing failures - Re-prettier AccountAsset styles.scss --- src/containers/Accounts/AccountAsset/styles.scss | 1 - src/containers/Accounts/PermissionDelegation/styles.scss | 2 +- .../PermissionDelegation/test/PermissionDelegation.test.tsx | 6 ++++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/containers/Accounts/AccountAsset/styles.scss b/src/containers/Accounts/AccountAsset/styles.scss index bf740fa13..98fce409c 100644 --- a/src/containers/Accounts/AccountAsset/styles.scss +++ b/src/containers/Accounts/AccountAsset/styles.scss @@ -209,4 +209,3 @@ max-height: 400px; } } - diff --git a/src/containers/Accounts/PermissionDelegation/styles.scss b/src/containers/Accounts/PermissionDelegation/styles.scss index f541e0ecf..cfc2c8e96 100644 --- a/src/containers/Accounts/PermissionDelegation/styles.scss +++ b/src/containers/Accounts/PermissionDelegation/styles.scss @@ -10,10 +10,10 @@ .delegate-item { display: flex; align-items: center; - gap: 16px; padding: 12px 16px; border-radius: 8px; background: $black-80; + gap: 16px; @media (max-width: $tablet-portrait-upper-boundary) { flex-direction: column; diff --git a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx index 9e5b67367..9424061ea 100644 --- a/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx +++ b/src/containers/Accounts/PermissionDelegation/test/PermissionDelegation.test.tsx @@ -86,7 +86,9 @@ describe('PermissionDelegation component', () => { ) }) - expect(container.querySelector('.permission-delegation')).toBeNull() + await waitFor(() => { + expect(container.querySelector('.permission-delegation')).toBeNull() + }) }) it('renders the section title when delegations exist', async () => { @@ -130,7 +132,7 @@ describe('PermissionDelegation component', () => { ) await waitFor(() => { - expect(screen.getByText('Permission Delegation')).toBeInTheDocument() + expect(screen.getByText('Payment')).toBeInTheDocument() }) expect(container.querySelector('.permission-delegation')).not.toBeNull() From f38c14d7ffc1bc3d9784884f958ac8a19a8a27a1 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 24 Apr 2026 14:19:21 -0700 Subject: [PATCH 8/9] Restore translation keys removed from branch Restore assets_available, other_data, not_available, percent_of_supply, and currency_toggle_help at their original positions. These were added earlier in this branch and should be preserved even if not yet wired up. --- public/locales/ca-CA/translations.json | 5 +++++ public/locales/en-US/translations.json | 5 +++++ public/locales/es-ES/translations.json | 5 +++++ public/locales/fr-FR/translations.json | 5 +++++ public/locales/ja-JP/translations.json | 5 +++++ public/locales/ko-KR/translations.json | 5 +++++ public/locales/my-MM/translations.json | 5 +++++ 7 files changed, 35 insertions(+) diff --git a/public/locales/ca-CA/translations.json b/public/locales/ca-CA/translations.json index a368c138b..0b6b9dc62 100644 --- a/public/locales/ca-CA/translations.json +++ b/public/locales/ca-CA/translations.json @@ -817,9 +817,12 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, + "assets_available": null, "unrealized_loss": null, + "other_data": null, "max_total_supply": null, "available_to_borrow": null, + "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -845,7 +848,9 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, + "percent_of_supply": null, "value": null, + "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 68b8b7c4d..757d2c295 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -818,9 +818,12 @@ "perm_domain_id": "Permissioned Domain ID", "total_value_locked": "Total Value Locked (TVL)", "shares": "Shares", + "assets_available": "Assets Available", "unrealized_loss": "Unrealized Loss", + "other_data": "Other Data", "max_total_supply": "Max Total Supply", "available_to_borrow": "Available to Borrow", + "not_available": "Not available", "first_come_first_served": "First Come First Served", "loans": "Loans", "loan_broker": "Loan Broker", @@ -846,7 +849,9 @@ "depositors_fetch_error": "Unable to fetch depositors information", "no_depositors_message": "No depositors found for this vault.", "depositors": "Depositors", + "percent_of_supply": "% of Supply", "value": "Value", + "currency_toggle_help": "Toggle to view values in native-currency or USD", "currency_toggle_description": "Toggle to view values in native-currency or USD", "currency_toggle_loading": "Loading USD conversion rate...", "currency_toggle_loading_description": "Loading USD conversion rate...", diff --git a/public/locales/es-ES/translations.json b/public/locales/es-ES/translations.json index 148dd1825..919a419b9 100644 --- a/public/locales/es-ES/translations.json +++ b/public/locales/es-ES/translations.json @@ -818,9 +818,12 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, + "assets_available": null, "unrealized_loss": null, + "other_data": null, "max_total_supply": null, "available_to_borrow": null, + "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -846,7 +849,9 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, + "percent_of_supply": null, "value": null, + "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/fr-FR/translations.json b/public/locales/fr-FR/translations.json index b12dea129..137181d69 100644 --- a/public/locales/fr-FR/translations.json +++ b/public/locales/fr-FR/translations.json @@ -818,9 +818,12 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, + "assets_available": null, "unrealized_loss": null, + "other_data": null, "max_total_supply": null, "available_to_borrow": null, + "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -846,7 +849,9 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, + "percent_of_supply": null, "value": null, + "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/ja-JP/translations.json b/public/locales/ja-JP/translations.json index 126de316f..5d58953ed 100644 --- a/public/locales/ja-JP/translations.json +++ b/public/locales/ja-JP/translations.json @@ -818,9 +818,12 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, + "assets_available": null, "unrealized_loss": null, + "other_data": null, "max_total_supply": null, "available_to_borrow": null, + "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -846,7 +849,9 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, + "percent_of_supply": null, "value": null, + "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/ko-KR/translations.json b/public/locales/ko-KR/translations.json index 5ac59ea5f..4137cee49 100644 --- a/public/locales/ko-KR/translations.json +++ b/public/locales/ko-KR/translations.json @@ -818,9 +818,12 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, + "assets_available": null, "unrealized_loss": null, + "other_data": null, "max_total_supply": null, "available_to_borrow": null, + "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -846,7 +849,9 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, + "percent_of_supply": null, "value": null, + "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, diff --git a/public/locales/my-MM/translations.json b/public/locales/my-MM/translations.json index a841b9af0..51730a27a 100644 --- a/public/locales/my-MM/translations.json +++ b/public/locales/my-MM/translations.json @@ -818,9 +818,12 @@ "perm_domain_id": null, "total_value_locked": null, "shares": null, + "assets_available": null, "unrealized_loss": null, + "other_data": null, "max_total_supply": null, "available_to_borrow": null, + "not_available": null, "first_come_first_served": null, "loans": null, "loan_broker": null, @@ -846,7 +849,9 @@ "depositors_fetch_error": null, "no_depositors_message": null, "depositors": null, + "percent_of_supply": null, "value": null, + "currency_toggle_help": null, "currency_toggle_description": null, "currency_toggle_loading": null, "currency_toggle_loading_description": null, From 3090e55c6e8d39f7b5811efafc66436f3872e1da Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 22 May 2026 15:31:41 -0700 Subject: [PATCH 9/9] test: assert permission-delegation renders in static parts test Updates the PermissionDelegation mock to return a testid element and adds a getByTestId assertion alongside the other child-component checks in the 'renders static parts' test. --- src/containers/Accounts/test/index.test.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/containers/Accounts/test/index.test.tsx b/src/containers/Accounts/test/index.test.tsx index 2d1f6c691..580777dc7 100644 --- a/src/containers/Accounts/test/index.test.tsx +++ b/src/containers/Accounts/test/index.test.tsx @@ -33,7 +33,9 @@ jest.mock('../AccountAsset', () => ({ jest.mock('../PermissionDelegation', () => ({ __esModule: true, - PermissionDelegation: () => null, + PermissionDelegation: () => ( +
Permission Delegation
+ ), })) jest.mock('../AccountTransactionTable', () => ({ @@ -77,6 +79,7 @@ describe('Account container', () => { await waitFor(() => { expect(screen.getByTestId('account-header')).toBeInTheDocument() expect(screen.getByTestId('account-summary')).toBeInTheDocument() + expect(screen.getByTestId('permission-delegation')).toBeInTheDocument() expect(screen.getByTestId('account-asset')).toBeInTheDocument() expect( screen.getByTestId('account-transaction-table'),