Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ListItem): Add ExpandedAttributes comp
expandedAttributes prop is an array of doc attributes like `['birthday', 'email']` we want to show as expanded
- Loading branch information
Showing
4 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
39 changes: 39 additions & 0 deletions
39
react/MuiCozyTheme/ListItem/ExpandedAttributes/ExpandedAttribute.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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
139
react/MuiCozyTheme/ListItem/ExpandedAttributes/helpers.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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
97
react/MuiCozyTheme/ListItem/ExpandedAttributes/helpers.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |