Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions app/_locales/en_GB/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,22 @@ describe('MultichainAccountList', () => {
expect(screen.getByText('Account 1 from wallet 2')).toBeInTheDocument();
});

it('does not render wallet headers based on prop', () => {
renderComponent({ displayWalletHeader: false });

expect(screen.queryByText('Wallet 1')).not.toBeInTheDocument();
expect(screen.queryByText('Wallet 2')).not.toBeInTheDocument();
expect(
screen.getByTestId(`multichain-account-cell-${walletOneGroupId}`),
).toBeInTheDocument();
expect(
screen.getByTestId(`multichain-account-cell-${walletTwoGroupId}`),
).toBeInTheDocument();

expect(screen.getByText('Account 1 from wallet 1')).toBeInTheDocument();
expect(screen.getByText('Account 1 from wallet 2')).toBeInTheDocument();
});

it('marks only the selected account with a check icon and dispatches action on click', () => {
renderComponent();

Expand Down Expand Up @@ -237,7 +253,7 @@ describe('MultichainAccountList', () => {
renderComponent({ wallets: multiGroupWallets });

expect(
screen.getAllByTestId('multichain-account-tree-wallet-header'),
screen.queryAllByTestId('multichain-account-tree-wallet-header'),
).toHaveLength(1);
expect(
screen.getByTestId(`multichain-account-cell-${walletOneGroupId}`),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,16 @@ export type MultichainAccountListProps = {
wallets: AccountTreeWallets;
selectedAccountGroups: AccountGroupId[];
handleAccountClick?: (accountGroupId: AccountGroupId) => void;
isInSearchMode?: boolean;
displayWalletHeader?: boolean;
};

export const MultichainAccountList = ({
wallets,
selectedAccountGroups,
handleAccountClick,
isInSearchMode = false,
displayWalletHeader = true,
}: MultichainAccountListProps) => {
const dispatch = useDispatch();
const history = useHistory();
Expand Down Expand Up @@ -144,7 +148,7 @@ export const MultichainAccountList = ({
},
);

if (walletData.type === AccountWalletType.Entropy) {
if (!isInSearchMode && walletData.type === AccountWalletType.Entropy) {
groupsItems.push(
<AddMultichainAccount
walletId={walletId as AccountWalletId}
Expand All @@ -153,7 +157,11 @@ export const MultichainAccountList = ({
);
}

return [...walletsAccumulator, walletHeader, ...groupsItems];
return [
...walletsAccumulator,
displayWalletHeader ? walletHeader : null,
...groupsItems,
];
},
[] as React.ReactNode[],
);
Expand All @@ -165,6 +173,8 @@ export const MultichainAccountList = ({
defaultHomeActiveTabName,
dispatch,
history,
isInSearchMode,
displayWalletHeader,
selectedAccountGroupsSet,
]);

Expand Down
135 changes: 90 additions & 45 deletions ui/pages/multichain-accounts/account-list/account-list.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { screen, fireEvent } from '@testing-library/react';
import { screen, fireEvent, within } from '@testing-library/react';

import { renderWithProvider } from '../../../../test/lib/render-helpers';
import mockState from '../../../../test/data/mock-state.json';
Expand All @@ -17,6 +17,10 @@ jest.mock('react-router-dom', () => ({
}),
}));

const searchContainerTestId = 'multichain-account-list-search';
const searchClearButtonTestId = 'text-field-search-clear-button';
const walletHeaderTestId = 'multichain-account-tree-wallet-header';

describe('AccountList', () => {
beforeEach(() => {
jest.clearAllMocks();
Expand All @@ -26,44 +30,6 @@ describe('AccountList', () => {
const store = configureStore({
metamask: {
...mockState.metamask,
accountTree: {
selectedAccountGroup: '01JKAF3DSGM3AB87EM9N0K41AJ:default',
wallets: {
'01JKAF3DSGM3AB87EM9N0K41AJ': {
id: '01JKAF3DSGM3AB87EM9N0K41AJ',
metadata: {
name: 'Wallet 1',
},
groups: {
'01JKAF3DSGM3AB87EM9N0K41AJ:default': {
id: '01JKAF3DSGM3AB87EM9N0K41AJ:default',
metadata: {
name: 'Account 1 from wallet 1',
},
accounts: [
'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3',
'07c2cfec-36c9-46c4-8115-3836d3ac9047',
],
},
},
},
'01JKAF3PJ247KAM6C03G5Q0NP8': {
id: '01JKAF3PJ247KAM6C03G5Q0NP8',
metadata: {
name: 'Wallet 2',
},
groups: {
'01JKAF3PJ247KAM6C03G5Q0NP8:default': {
id: '01JKAF3PJ247KAM6C03G5Q0NP8:default',
metadata: {
name: 'Account 1 from wallet 2',
},
accounts: ['784225f4-d30b-4e77-a900-c8bbce735b88'],
},
},
},
},
},
},
});

Expand All @@ -76,15 +42,13 @@ describe('AccountList', () => {
expect(screen.getByText('Accounts')).toBeInTheDocument();
expect(screen.getByLabelText('Back')).toBeInTheDocument();

const walletHeaders = screen.getAllByTestId(
'multichain-account-tree-wallet-header',
);
const walletHeaders = screen.getAllByTestId(walletHeaderTestId);

expect(walletHeaders.length).toBe(2);
expect(walletHeaders.length).toBe(5);
expect(screen.getByText('Wallet 1')).toBeInTheDocument();
expect(screen.getByText('Wallet 2')).toBeInTheDocument();
expect(screen.getByText('Account 1 from wallet 1')).toBeInTheDocument();
expect(screen.getByText('Account 1 from wallet 2')).toBeInTheDocument();
expect(screen.getByText('Account 1')).toBeInTheDocument();
expect(screen.getByText('Account 2')).toBeInTheDocument();
});

it('calls history.goBack when back button is clicked', () => {
Expand All @@ -110,4 +74,85 @@ describe('AccountList', () => {
expect(screen.getByText('Import an account')).toBeInTheDocument();
expect(screen.getByText('Add a hardware wallet')).toBeInTheDocument();
});

it('displays the search field with correct placeholder', () => {
renderComponent();

const searchContainer = screen.getByTestId(searchContainerTestId);

expect(searchContainer).toBeInTheDocument();

const searchInput = within(searchContainer).getByPlaceholderText(
'Search your accounts',
);

expect(searchInput).toBeInTheDocument();
});

it('updates search value when typing in the search field', () => {
renderComponent();

const searchContainer = screen.getByTestId(searchContainerTestId);
const searchInput = within(searchContainer).getByRole('searchbox');
fireEvent.change(searchInput, { target: { value: 'Account 2' } });

// @ts-expect-error Values does exist on the search input
expect(searchInput?.value).toBe('Account 2');
});

it('filters accounts when search text is entered', () => {
renderComponent();

// Verify all accounts are shown initially
const walletHeaders = screen.getAllByTestId(walletHeaderTestId);
expect(walletHeaders.length).toBe(5);
expect(screen.getByText('Account 1')).toBeInTheDocument();
expect(screen.getByText('Account 2')).toBeInTheDocument();

const searchContainer = screen.getByTestId(searchContainerTestId);
const searchInput = within(searchContainer).getByRole('searchbox');
fireEvent.change(searchInput, { target: { value: 'Account 2' } });

expect(screen.queryByText('Account 1')).not.toBeInTheDocument();
expect(screen.getByText('Account 2')).toBeInTheDocument();
});

it('shows "No accounts found" message when no accounts match search criteria', () => {
renderComponent();

const searchContainer = screen.getByTestId(searchContainerTestId);
const searchInput = within(searchContainer).getByRole('searchbox');
fireEvent.change(searchInput, { target: { value: 'nonexistent account' } });

expect(
screen.getByText('No accounts found for the given search query'),
).toBeInTheDocument();
});

it('clears search when clear button is clicked', () => {
renderComponent();

const searchContainer = screen.getByTestId(searchContainerTestId);
const searchInput = within(searchContainer).getByRole('searchbox');
fireEvent.change(searchInput, { target: { value: 'Account 2' } });

const clearButton = screen.getByTestId(searchClearButtonTestId);
fireEvent.click(clearButton);

// @ts-expect-error Value does exist on search input
expect(searchInput?.value).toBe('');
expect(screen.getByText('Account 1')).toBeInTheDocument();
expect(screen.getByText('Account 2')).toBeInTheDocument();
});

it('performs case-insensitive search', () => {
renderComponent();

const searchContainer = screen.getByTestId(searchContainerTestId);
const searchInput = within(searchContainer).getByRole('searchbox');
fireEvent.change(searchInput, { target: { value: 'account 2' } });

expect(screen.queryByText('Account 1')).not.toBeInTheDocument();
expect(screen.getByText('Account 2')).toBeInTheDocument();
});
});
Loading
Loading