Skip to content

Commit

Permalink
Sort contacts alphabetically
Browse files Browse the repository at this point in the history
Contacts are grouped together by letter, and the groups are listed
alphabetically, but the contacts in each group are not sorted
alphabetically themselves.

Fixes #10318.
  • Loading branch information
mcmire committed Sep 1, 2021
1 parent 8d9989c commit f9fe3b9
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 27 deletions.
7 changes: 6 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,12 @@ module.exports = {
},
},
{
files: ['ui/**/*.test.js', 'ui/__mocks__/*.js', 'shared/**/*.test.js'],
files: [
'ui/**/*.test.js',
'ui/__mocks__/*.js',
'shared/**/*.test.js',
'test/jest/*.js',
],
extends: ['@metamask/eslint-config-jest'],
rules: {
'jest/no-restricted-matchers': 'off',
Expand Down
2 changes: 2 additions & 0 deletions test/jest/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import './matchers';

export { createSwapsMockStore } from './mock-store';
export { renderWithProvider } from './rendering';
export { setBackgroundConnection } from './background';
Expand Down
35 changes: 35 additions & 0 deletions test/jest/matchers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { shallow, mount } from 'enzyme';

const SHALLOW_WRAPPER_CONSTRUCTOR = 'ShallowWrapper';

function isShallowWrapper(wrapper) {
return wrapper.constructor.name
? wrapper.constructor.name === SHALLOW_WRAPPER_CONSTRUCTOR
: Boolean(`${wrapper.constructor}`.match(/^function ShallowWrapper\(/u));
}

function toMatchElement(
actualEnzymeWrapper,
reactInstance,
options = { ignoreProps: true },
) {
const expectedWrapper = isShallowWrapper(actualEnzymeWrapper)
? shallow(reactInstance)
: mount(reactInstance);

const actual = actualEnzymeWrapper.debug({ verbose: true, ...options });
const expected = expectedWrapper.debug({ verbose: true, ...options });
const pass = actual === expected;

return {
pass,
message: 'Expected actual value to match the expected value.',
negatedMessage: 'Did not expect actual value to match the expected value.',
contextualInformation: {
actual: `Actual:\n ${actual}`,
expected: `Expected:\n ${expected}`,
},
};
}

expect.extend({ toMatchElement });
1 change: 1 addition & 0 deletions test/jest/setup.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// This file is for Jest-specific setup only and runs before our Jest tests.
import '@testing-library/jest-dom';
import './matchers';
55 changes: 29 additions & 26 deletions ui/components/app/contact-list/contact-list.component.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { sortBy } from 'lodash';
import Button from '../../ui/button';
import RecipientGroup from './recipient-group/recipient-group.component';

Expand Down Expand Up @@ -50,34 +51,36 @@ export default class ContactList extends PureComponent {
}

renderAddressBook() {
const contacts = this.props.searchForContacts();
const unsortedContactsByLetter = this.props
.searchForContacts()
.reduce((obj, contact) => {
const firstLetter = contact.name[0].toUpperCase();
return {
...obj,
[firstLetter]: [...(obj[firstLetter] || []), contact],
};
}, {});

const contactGroups = contacts.reduce((acc, contact) => {
const firstLetter = contact.name.slice(0, 1).toUpperCase();
acc[firstLetter] = acc[firstLetter] || [];
const bucket = acc[firstLetter];
bucket.push(contact);
return acc;
}, {});
const letters = Object.keys(unsortedContactsByLetter).sort();

return Object.entries(contactGroups)
.sort(([letter1], [letter2]) => {
if (letter1 > letter2) {
return 1;
} else if (letter1 === letter2) {
return 0;
}
return -1;
})
.map(([letter, groupItems]) => (
<RecipientGroup
key={`${letter}-contract-group`}
label={letter}
items={groupItems}
onSelect={this.props.selectRecipient}
selectedAddress={this.props.selectedAddress}
/>
));
const sortedContactGroups = letters.map((letter) => {
return [
letter,
sortBy(unsortedContactsByLetter[letter], (contact) => {
return contact.name.toLowerCase();
}),
];
});

return sortedContactGroups.map(([letter, groupItems]) => (
<RecipientGroup
key={`${letter}-contact-group`}
label={letter}
items={groupItems}
onSelect={this.props.selectRecipient}
selectedAddress={this.props.selectedAddress}
/>
));
}

renderMyAccounts() {
Expand Down
60 changes: 60 additions & 0 deletions ui/components/app/contact-list/contact-list.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { shallowWithContext } from '../../../../test/lib/render-helpers';
import RecipientGroup from './recipient-group';
import ContactList from '.';

describe('Contact List', () => {
describe('given searchForContacts', () => {
it('sorts contacts by name within each letter group', () => {
const contacts = {
Al: { name: 'Al', address: '0x0' },
aa: { name: 'aa', address: '0x1' },
Az: { name: 'Az', address: '0x2' },
Bl: { name: 'Bl', address: '0x3' },
ba: { name: 'ba', address: '0x4' },
Bz: { name: 'Bz', address: '0x5' },
Ccc: { name: 'Ccc', address: '0x6' },
};
const searchForContacts = () => {
return Object.values(contacts);
};
const selectRecipient = () => null;
const selectedAddress = null;

const wrapper = shallowWithContext(
<ContactList
searchForContacts={searchForContacts}
selectRecipient={selectRecipient}
selectedAddress={selectedAddress}
/>,
);

expect(wrapper).toMatchElement(
<div className="send__select-recipient-wrapper__list">
<RecipientGroup
key="A-contact-group"
label="A"
items={[contacts.aa, contacts.Al, contacts.Az]}
onSelect={selectRecipient}
selectedAddress={selectedAddress}
/>
<RecipientGroup
key="B-contact-group"
label="B"
items={[contacts.ba, contacts.Bl, contacts.Bz]}
onSelect={selectRecipient}
selectedAddress={selectedAddress}
/>
<RecipientGroup
key="C-contact-group"
label="C"
items={[contacts.Ccc]}
onSelect={selectRecipient}
selectedAddress={selectedAddress}
/>
</div>,
{ ignoreProps: false },
);
});
});
});

0 comments on commit f9fe3b9

Please sign in to comment.