Skip to content

Commit

Permalink
feat(ContactsList): Show myself always at the top of the list
Browse files Browse the repository at this point in the history
  • Loading branch information
Merkur39 committed Jan 19, 2024
1 parent 2efa596 commit c4bd940
Show file tree
Hide file tree
Showing 9 changed files with 1,444 additions and 1,374 deletions.
9 changes: 6 additions & 3 deletions react/ContactsList/ContactsList.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React from 'react'
import PropTypes from 'prop-types'

import { useI18n } from '../providers/I18n'
import { Table } from '../Table'
import List from '../List'
import ListSubheader from '../ListSubheader'
import { sortContacts, categorizeContacts, sortHeaders } from './helpers'
import ContactRow from './ContactRow'
import useBreakpoints from '../providers/Breakpoints'
import withContactsListLocales from './locales/withContactsListLocales'

const ContactsList = ({ contacts, onItemClick, ...rest }) => {
const { t } = useI18n()
const sortedContacts = sortContacts(contacts)
const categorizedContacts = categorizeContacts(sortedContacts)
const sortedHeaders = sortHeaders(categorizedContacts)
const categorizedContacts = categorizeContacts(sortedContacts, t)
const sortedHeaders = sortHeaders(categorizedContacts, t)
const { isDesktop } = useBreakpoints()

return (
Expand Down Expand Up @@ -45,4 +48,4 @@ ContactsList.propTypes = {
onItemClick: PropTypes.func
}

export default ContactsList
export default withContactsListLocales(ContactsList)
12 changes: 8 additions & 4 deletions react/ContactsList/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,34 @@

```jsx
import ContactsList from 'cozy-ui/transpiled/react/ContactsList'
import mockClient from 'cozy-ui/transpiled/react/ContactsListModal/mockClient'
import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
import { BreakpointsProvider } from 'cozy-ui/transpiled/react/providers/Breakpoints'
import contacts from './_mockContacts.json'

;

<BreakpointsProvider>
<DemoProvider client={mockClient}>
<div style={{ height: 500, overflowY: 'scroll' }}>
<ContactsList contacts={contacts} />
</div>
</BreakpointsProvider>
</DemoProvider>
```

### Clickable items

```jsx
import ContactsList from 'cozy-ui/transpiled/react/ContactsList'
import mockClient from 'cozy-ui/transpiled/react/ContactsListModal/mockClient'
import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
import contacts from './_mockContacts.json'
import { BreakpointsProvider } from 'cozy-ui/transpiled/react/providers/Breakpoints'

initialState = { contact: null }

;

<BreakpointsProvider>
<DemoProvider client={mockClient}>
<p>
{state.contact ? (
`Clicked on contact ${state.contact._id}`
Expand All @@ -39,5 +43,5 @@ initialState = { contact: null }
onItemClick={contact => setState({ contact })}
/>
</div>
</BreakpointsProvider>
</DemoProvider>
```
1 change: 1 addition & 0 deletions react/ContactsList/_mockContacts.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"version": 1
},
"fullname": "Alexis Bickers",
"me": true,
"phone": [
{
"number": "+33 (9)7 58 16 92 55",
Expand Down
55 changes: 46 additions & 9 deletions react/ContactsList/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,61 @@ export const sortLastNameFirst = (contact, comparedContact) => {

export const sortContacts = contacts => contacts.sort(sortLastNameFirst)

export const categorizeContacts = contacts =>
/**
* Build header for a contact (first letter of last name)
* @param {object} contact
* @param {function} t translation function
* @returns {string} header
*/
const makeHeader = (contact, t) => {
if (contact.me) return t('me')

const name = buildLastNameFirst(contact)
return name[0] || t('empty')
}

/**
* @typedef {Object.<string, Object>} CategorizedContactsResult
*/

/**
* Categorize contacts by first letter of last name
* @param {object[]} contacts io.cozy.contacts documents
* @param {function} t translation function
* @returns {CategorizedContactsResult}
*/
export const categorizeContacts = (contacts, t) =>
contacts.reduce((acc, contact) => {
const name = buildLastNameFirst(contact)
const header = name[0] || 'EMPTY'
const header = makeHeader(contact, t)
acc[header] = acc[header] || []
acc[header].push(contact)
return acc
}, {})

export const sortHeaders = categorized => {
/**
* Sort headers (first letter of last name) alphabetically and put 'me' and 'empty' first
* @param {CategorizedContactsResult} categorized categorized contacts
* @param {function} t translation function
* @returns {string[]}
*/
export const sortHeaders = (categorized, t) => {
const headers = Object.keys(categorized)
const notEmptyHeaders = headers.filter(header => header !== 'EMPTY')
const notEmptyAndMyselfHeaders = headers.filter(
header => header !== t('empty') && header !== t('me')
)
const notEmptyAndMyselfSorted = notEmptyAndMyselfHeaders.slice().sort()

const notEmptySorted = notEmptyHeaders.sort()
if (headers.length === notEmptyAndMyselfHeaders.length) {
return notEmptyAndMyselfSorted
}

if (headers.length === notEmptyHeaders.length) {
return notEmptySorted
const headersSorted = []
if (headers.includes(t('me'))) {
headersSorted.push(t('me'))
}
if (headers.includes(t('empty'))) {
headersSorted.push(t('empty'))
}

return ['EMPTY', ...notEmptySorted]
return headersSorted.concat(notEmptyAndMyselfSorted)
}
12 changes: 7 additions & 5 deletions react/ContactsList/helpers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,28 +72,30 @@ describe('Sort contacts', () => {
})

describe('sortHeaders', () => {
const t = jest.fn(x => x)
it('should return headers sorted', () => {
const contacts = {
F: [],
B: [],
H: []
}

const sortedHeaders = sortHeaders(contacts)
const sortedHeaders = sortHeaders(contacts, t)

expect(sortedHeaders).toEqual(['B', 'F', 'H'])
})

it('should put EMPTY header first', () => {
it('Should sort in the following order "me", "empty", "A", "..."', () => {
const contacts = {
F: [],
B: [],
me: [],
H: [],
EMPTY: []
empty: []
}

const sortedHeaders = sortHeaders(contacts)
const sortedHeaders = sortHeaders(contacts, t)

expect(sortedHeaders).toEqual(['EMPTY', 'B', 'F', 'H'])
expect(sortedHeaders).toEqual(['me', 'empty', 'B', 'F', 'H'])
})
})
4 changes: 4 additions & 0 deletions react/ContactsList/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"empty": "EMPTY",
"me": "ME"
}
4 changes: 4 additions & 0 deletions react/ContactsList/locales/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"empty": "VIDE",
"me": "MOI"
}
11 changes: 11 additions & 0 deletions react/ContactsList/locales/withContactsListLocales.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import withOnlyLocales from '../../providers/I18n/withOnlyLocales'

import en from './en.json'
import fr from './fr.json'

const locales = {
en,
fr
}

export default withOnlyLocales(locales)
Loading

0 comments on commit c4bd940

Please sign in to comment.