Skip to content

Commit

Permalink
feat: Add AddContactDialog to add a contact from the contact list
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the app must now have `POST` permission on `io.cozy.contacts` doctype to use `ContactsListModal`
  • Loading branch information
JF-Cozy committed Apr 26, 2022
1 parent e315f40 commit 7333bf9
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 74 deletions.
15 changes: 15 additions & 0 deletions react/ContactsListModal/AddContact/AddContactActions.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react'

import Button from '../../Buttons'
import { withContactsListLocales } from '../withContactsListLocales'

const AddContactActions = ({ t, onCancel, onSave }) => {
return (
<>
<Button variant="secondary" label={t('cancel')} onClick={onCancel} />
<Button label={t('save')} onClick={onSave} />
</>
)
}

export default withContactsListLocales(AddContactActions)
52 changes: 52 additions & 0 deletions react/ContactsListModal/AddContact/AddContactContent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Typography } from '@material-ui/core'
import React from 'react'

import { Media, Img, Bd } from '../../Media'
import Icon from '../../Icon'
import PeopleIcon from 'cozy-ui/transpiled/react/Icons/People'
import TextField from '../../MuiCozyTheme/TextField'

import { withContactsListLocales } from '../withContactsListLocales'
import styles from './styles.styl'

const AddContactContent = ({ t, setContactValues }) => {
const handleChange = ev => {
const { name, value } = ev.target
setContactValues(v => ({ ...v, [name]: value }))
}

return (
<>
<Typography variant="h5">{t('coordinates')}</Typography>
<Media>
<Img className={styles.icon}>
<Icon icon={PeopleIcon} />
</Img>
<Bd className="u-mr-1">
<TextField
className="u-mt-1"
variant="outlined"
fullWidth={true}
name="givenName"
label={t('givenName')}
onChange={handleChange}
/>
</Bd>
</Media>
<Media>
<Bd className="u-ml-3 u-mr-1">
<TextField
className="u-mt-1"
variant="outlined"
fullWidth={true}
name="familyName"
label={t('familyName')}
onChange={handleChange}
/>
</Bd>
</Media>
</>
)
}

export default withContactsListLocales(AddContactContent)
38 changes: 38 additions & 0 deletions react/ContactsListModal/AddContact/AddContactDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useState } from 'react'

import { useClient } from 'cozy-client'

import { FixedDialog } from '../../CozyDialogs'
import AddContactTitle from './AddContactTitle'
import AddContactContent from './AddContactContent'
import AddContactActions from './AddContactActions'
import { handleSubmit } from './helpers'

const AddContactDialog = ({ onListClose, onCreate, onClose }) => {
const [contactValues, setContactValues] = useState({})
const client = useClient()

return (
<FixedDialog
open={true}
onClose={onClose}
title={<AddContactTitle />}
content={<AddContactContent setContactValues={setContactValues} />}
actions={
<AddContactActions
onCancel={onClose}
onSave={() =>
handleSubmit({
client,
contactValues,
onCreate,
onListClose
})
}
/>
}
/>
)
}

export default AddContactDialog
9 changes: 9 additions & 0 deletions react/ContactsListModal/AddContact/AddContactTitle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react'

import { withContactsListLocales } from '../withContactsListLocales'

const AddContactTitle = ({ t }) => {
return <>{t('newContact')}</>
}

export default withContactsListLocales(AddContactTitle)
18 changes: 18 additions & 0 deletions react/ContactsListModal/AddContact/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const handleSubmit = async ({
client,
contactValues,
onCreate,
onListClose
}) => {
const { givenName, familyName } = contactValues

if (!givenName && !familyName) return

const { data: contact } = await client.save({
_type: 'io.cozy.contacts',
name: { familyName, givenName }
})

onCreate(contact)
onListClose()
}
74 changes: 74 additions & 0 deletions react/ContactsListModal/AddContact/helpers.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createMockClient } from 'cozy-client'

import { handleSubmit } from './helpers'

const client = createMockClient({})
client.save = jest.fn(x => ({ data: x }))
const onCreate = jest.fn()
const onListClose = jest.fn()

describe('handleSubmit', () => {
it('should not create the contact if no givenName of familyName', async () => {
await handleSubmit({ contactValues: {}, client, onCreate, onListClose })

expect(onCreate).not.toHaveBeenCalled()
expect(onListClose).not.toHaveBeenCalled()
})

it('should create the contact if there is only the givenName', async () => {
await handleSubmit({
contactValues: { givenName: 'John' },
client,
onCreate,
onListClose
})

expect(client.save).toHaveBeenCalled()
expect(onCreate).toHaveBeenCalledWith({
_type: 'io.cozy.contacts',
name: {
familyName: undefined,
givenName: 'John'
}
})
expect(onListClose).toHaveBeenCalled()
})

it('should create the contact if there is only the familyName', async () => {
await handleSubmit({
contactValues: { familyName: 'Connor' },
client,
onCreate,
onListClose
})

expect(client.save).toHaveBeenCalled()
expect(onCreate).toHaveBeenCalledWith({
_type: 'io.cozy.contacts',
name: {
familyName: 'Connor',
givenName: undefined
}
})
expect(onListClose).toHaveBeenCalled()
})

it('should create the contact if there is the givenName and familyName', async () => {
await handleSubmit({
contactValues: { givenName: 'John', familyName: 'Connor' },
client,
onCreate,
onListClose
})

expect(client.save).toHaveBeenCalled()
expect(onCreate).toHaveBeenCalledWith({
_type: 'io.cozy.contacts',
name: {
familyName: 'Connor',
givenName: 'John'
}
})
expect(onListClose).toHaveBeenCalled()
})
})
2 changes: 2 additions & 0 deletions react/ContactsListModal/AddContact/styles.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.icon
margin 1rem 1.5rem 0 0.5rem
72 changes: 0 additions & 72 deletions react/ContactsListModal/AddContactButton.jsx

This file was deleted.

2 changes: 2 additions & 0 deletions react/ContactsListModal/DemoProvider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ mockClient.plugins = {
}
}

mockClient.save = () => ({ data: contacts[0] })

const Wrapper = ({ children }) => {
return <CozyProvider client={mockClient}>{children}</CozyProvider>
}
Expand Down
20 changes: 18 additions & 2 deletions react/ContactsListModal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ import {
import useRealtime from '../hooks/useRealtime'
import useEventListener from '../hooks/useEventListener.js'
import useBreakpoints from '../hooks/useBreakpoints'
import Button from '../Buttons'
import PlusIcon from '../Icons/Plus'
import Icon from '../Icon'
import MobileHeader from './MobileHeader'
import AddContactButton from './AddContactButton'
import ContactsListContent from './ContactsListContent'
import AddContactDialog from './AddContact/AddContactDialog'

import styles from './styles.styl'

const thirtySeconds = 30000
Expand All @@ -39,6 +43,7 @@ const ContactsListModal = ({
dismissAction
}) => {
const [filter, setFilter] = useState('')
const [showAddDialog, setShowAddDialog] = useState(false)
const { isMobile } = useBreakpoints()
const { dialogProps, dialogTitleProps } = useCozyDialog({
size: 'large',
Expand Down Expand Up @@ -96,9 +101,13 @@ const ContactsListModal = ({
<DialogContent className="u-p-0">
<div className="dialogContentInner">
<div className={styles.ContactsListModal__addContactContainer}>
<AddContactButton
<Button
className={isMobile && 'u-mt-1'}
variant="secondary"
theme="secondary"
label={addContactLabel}
startIcon={<Icon icon={PlusIcon} />}
onClick={setShowAddDialog}
/>
</div>
<ContactsListContent
Expand All @@ -110,6 +119,13 @@ const ContactsListModal = ({
/>
</div>
</DialogContent>
{showAddDialog && (
<AddContactDialog
onListClose={dismissAction}
onCreate={onItemClick}
onClose={() => setShowAddDialog(false)}
/>
)}
</TopAnchoredDialog>
)
}
Expand Down

0 comments on commit 7333bf9

Please sign in to comment.