Skip to content

Commit

Permalink
feat(ListItem): Add ExpandedAttributes comp
Browse files Browse the repository at this point in the history
expandedAttributes prop is an array of doc attributes like
`['birthday', 'email']` we want to show as expanded
  • Loading branch information
JF-Cozy committed Mar 21, 2023
1 parent d83876f commit 93c0471
Show file tree
Hide file tree
Showing 4 changed files with 337 additions and 0 deletions.
@@ -0,0 +1,39 @@
import React from 'react'
import PropTypes from 'prop-types'

import ListItem from '../../ListItem'
import ListItemText from '../../../ListItemText'
import ListItemIcon from '../../ListItemIcon'
import Typography from '../../../Typography'
import Icon from '../../../Icon'
import CopyIcon from '../../../Icons/Copy'
import { useI18n } from '../../../I18n'
import { copyToClipboard } from './helpers'

const ExpandedAttribute = ({ label, value, setAlertProps }) => {
const { t } = useI18n()

return (
<ListItem
className="u-pl-2"
button
onClick={copyToClipboard({ value, setAlertProps, t })}
>
<ListItemIcon>
<Icon icon={CopyIcon} />
</ListItemIcon>
<ListItemText
primary={<Typography variant="caption">{label}</Typography>}
secondary={<Typography variant="body1">{value}</Typography>}
/>
</ListItem>
)
}

ExpandedAttribute.propTypes = {
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
setAlertProps: PropTypes.func
}

export default ExpandedAttribute
139 changes: 139 additions & 0 deletions react/MuiCozyTheme/ListItem/ExpandedAttributes/helpers.js
@@ -0,0 +1,139 @@
import get from 'lodash/get'

import { formatDate } from '../../../Viewer/helpers'

export const normalizeExpandedAttribute = attr => attr.split('[]')[0]

// attributes not considered as expanded attributes
export const notExpandedAttributes = {
'io.cozy.contacts': ['fullname', 'civility', 'note'],
'io.cozy.files': ['name', 'flexsearchProps:translatedQualificationLabel']
}

// attributes that we want to display if no attribute of the document is related to the search
export const defaultExpandedAttributes = {
'io.cozy.contacts': ['email', 'phone', 'address', 'birthday'],
'io.cozy.files': [
'metadata.number',
'metadata.cafFileNumber',
'metadata.cardNumber',
'metadata.vinNumber',
'metadata.ibanNumber',
'metadata.passportNumber',
'metadata.noticePeriod',
'metadata.AObtentionDate',
'metadata.BObtentionDate',
'metadata.CObtentionDate',
'metadata.DObtentionDate',
'metadata.obtentionDate',
'metadata.referencedDate',
'metadata.issueDate',
'metadata.shootingDate',
'metadata.date',
'metadata.datetime',
'metadata.expirationDate',
'metadata.country',
'metadata.refTaxIncome',
'metadata.contractType'
]
}

export const hasAllElement = (arr1, arr2) => arr1.every(x => arr2.includes(x))

export const makeDefaultExpandedAttributes = (doc, expandedAttributes) => {
const doctype = doc?._type

if (!expandedAttributes || !doc || !doctype) return undefined

// checks if there are any expanded attributes.
// If there are none, the default expanded attributes are returned
if (hasAllElement(expandedAttributes, notExpandedAttributes[doctype])) {
return defaultExpandedAttributes[doctype]
}

return expandedAttributes
.map(expandedAttribute =>
notExpandedAttributes[doctype].includes(expandedAttribute)
? undefined
: normalizeExpandedAttribute(expandedAttribute)
)
.filter(x => x)
}

export const copyToClipboard = ({ value, setAlertProps, t }) => () => {
if (navigator?.clipboard) {
navigator.clipboard.writeText(value)

setAlertProps({
open: true,
severity: 'success',
message: t(`ListItem.snackbar.copyToClipboard.success`)
})
} else {
setAlertProps({
open: true,
severity: 'error',
message: t(`Viewer.snackbar.copyToClipboard.error`)
})
}
}

export const isDate = value => {
const dateTime = new Date(value).getTime()
const dateParsedValue = Date.parse(value)

return dateTime === dateParsedValue
}

export const formatAttrValue = ({ attribute, attrValue, f, lang }) => {
if (!attrValue || attrValue.length === 0) return undefined

switch (true) {
case isDate(attrValue):
return formatDate({ f, lang, date: attrValue })

case attribute === 'email':
return attrValue.find(x => x.primary === true)?.address

case attribute === 'address':
return attrValue.find(x => x.primary === true)?.formattedAddress

case attribute === 'phone':
return attrValue.find(x => x.primary === true)?.number

default:
return attrValue
}
}

export const makeAttrsKeyAndFormatedValue = ({
doc,
expandedAttributes,
f,
lang
}) => {
const attrsKeyAndFormatedValue = expandedAttributes
.map(expandedAttribute => {
const attrValue = get(doc, expandedAttribute)

const attrFormatedValue = formatAttrValue({
attribute: expandedAttribute,
attrValue,
f,
lang
})

if (!attrFormatedValue) return undefined

const attrKey =
expandedAttribute === 'metadata.number'
? `${expandedAttribute}.${doc.metadata.qualification.label}`
: expandedAttribute

return { attrKey, attrFormatedValue }
})
.filter(x => x)
.slice(0, 3)

return attrsKeyAndFormatedValue
}
97 changes: 97 additions & 0 deletions react/MuiCozyTheme/ListItem/ExpandedAttributes/helpers.spec.js
@@ -0,0 +1,97 @@
import { formatAttrValue } from './helpers'

const f = () => 'someMockedDate'
const lang = 'en'

describe('formatAttrValue', () => {
it('should return primary formattedAddress from addresses', () => {
const res = formatAttrValue({
attribute: 'address',
attrValue: [{ formattedAddress: '2 rue des coquelicots', primary: true }],
f,
lang
})

expect(res).toBe('2 rue des coquelicots')
})

it('should return undefined if no primary address', () => {
const res = formatAttrValue({
attribute: 'address',
attrValue: [{ formattedAddress: '2 rue des coquelicots' }],
f,
lang
})

expect(res).toBe(undefined)
})

it('should return primary address from emails', () => {
const res = formatAttrValue({
attribute: 'email',
attrValue: [
{ address: 'primary@cozycloud.cc', primary: true },
{ address: 'secondary@cozycloud.cc', primary: false }
],
f,
lang
})

expect(res).toBe('primary@cozycloud.cc')
})

it('should return undefined if no primary email', () => {
const res = formatAttrValue({
attribute: 'email',
attrValue: [{ address: 'secondary@cozycloud.cc' }],
f,
lang
})

expect(res).toBe(undefined)
})

it('should return primary number from phones', () => {
const res = formatAttrValue({
attribute: 'phone',
attrValue: [{ number: '06 15 64 47 63', primary: true }],
f,
lang
})

expect(res).toBe('06 15 64 47 63')
})

it('should return undefined if no primary phone', () => {
const res = formatAttrValue({
attribute: 'phone',
attrValue: [{ number: '06 15 64 47 63', primary: false }],
f,
lang
})

expect(res).toBe(undefined)
})

it('should return a number for a number value', () => {
const res = formatAttrValue({
attribute: 'metadata.number',
attrValue: 12345,
f,
lang
})

expect(res).toBe(12345)
})

it('should return a date for an ISO string formated date', () => {
const res = formatAttrValue({
attribute: 'metadata.date',
attrValue: '2023-03-08T12:48:18.000Z',
f,
lang
})

expect(res).toBe('someMockedDate')
})
})
62 changes: 62 additions & 0 deletions react/MuiCozyTheme/ListItem/ExpandedAttributes/index.jsx
@@ -0,0 +1,62 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'

import Snackbar from '../../../Snackbar'
import Alert from '../../../Alert'
import { withListItemLocales } from '../hoc/withListItemLocales'
import ExpandedAttribute from './ExpandedAttribute'
import { useI18n } from '../../../I18n'
import { makeAttrsKeyAndFormatedValue } from './helpers'

const ExpandedAttributes = ({ doc, expandedAttributes }) => {
const { t, f, lang } = useI18n()
const [alertProps, setAlertProps] = useState({
open: false,
severity: 'primary',
message: ''
})

const attrsKeyAndFormatedValue = makeAttrsKeyAndFormatedValue({
doc,
expandedAttributes,
f,
lang
})

const handleClose = () => setAlertProps({ open: false })

return (
<>
{attrsKeyAndFormatedValue.map(({ attrKey, attrFormatedValue }, index) => {
const label = t(`ListItem.attributes.${attrKey}`)

return (
<ExpandedAttribute
key={index}
label={label}
value={attrFormatedValue}
setAlertProps={setAlertProps}
/>
)
})}
{alertProps.open && (
<Snackbar open onClose={handleClose}>
<Alert
variant="filled"
severity={alertProps.severity}
onClose={handleClose}
>
{alertProps.message}
</Alert>
</Snackbar>
)}
</>
)
}

ExpandedAttributes.propTypes = {
doc: PropTypes.object,
expandedAttributes: PropTypes.array
}

export default withListItemLocales(ExpandedAttributes)

0 comments on commit 93c0471

Please sign in to comment.