diff --git a/docs/styleguide.config.js b/docs/styleguide.config.js index eb5958852c..4aeb258b7a 100644 --- a/docs/styleguide.config.js +++ b/docs/styleguide.config.js @@ -95,8 +95,7 @@ module.exports = { '../react/SquareAppIcon', '../react/QualificationGrid', '../react/QualificationItem', - '../react/UploadQueue', - '../react/Viewer' + '../react/UploadQueue' ] }, { diff --git a/react/FileImageLoader/Readme.md b/react/FileImageLoader/Readme.md index 552c0b4a33..9a7d202cd0 100644 --- a/react/FileImageLoader/Readme.md +++ b/react/FileImageLoader/Readme.md @@ -3,14 +3,77 @@ A component to get the image in `links` prop of a file, according to its class (could be `image` or `pdf`). ```jsx -import DemoProvider from 'cozy-ui/transpiled/react/Viewer/docs/DemoProvider' - +import DemoProvider from 'cozy-ui/transpiled/react/providers/DemoProvider' import FileImageLoader from 'cozy-ui/transpiled/react/FileImageLoader' import Icon from 'cozy-ui/transpiled/react/Icon' import FileDuotoneIcon from "cozy-ui/transpiled/react/Icons/FileDuotone" import BankIcon from "cozy-ui/transpiled/react/Icons/Bank" import CloudWallpaper from 'cozy-ui/docs/cloud-wallpaper.jpg' +const demoTextFileResponse = { + text: () => new Promise(resolve => resolve('Hello World !')) +} + +const demoFilesByClass = { + pdf: 'https://raw.githubusercontent.com/rospdf/pdf-php/2ccf7591fc2f18e63342ebfedad7997b08c34ed2/readme.pdf', + audio: 'https://viewerdemo.cozycloud.cc/Z.mp3', + video: 'https://viewerdemo.cozycloud.cc/Nextcloud.mp4', + text: 'https://viewerdemo.cozycloud.cc/notes.md' +} + +const mockClient = { + plugins: { + realtime: { + subscribe: () => {}, + unsubscribe: () => {}, + unsubscribeAll: () => {} + } + }, + on: () => {}, + collection: () => ({ + getDownloadLinkById: id => + new Promise(resolve => resolve(demoFilesByClass[id])), + download: () => + alert( + "This is a demo, there's no actual Cozy to download the file from ¯\\_(ツ)_/¯" + ), + get: () => + new Promise(resolve => + resolve({ + data: { + links: { + large: CloudWallpaper + } + } + }) + ) + }), + getStackClient: () => ({ + uri: '', + fetch: () => new Promise(resolve => resolve(demoTextFileResponse)) + }), + getClient: () => mockClient, + store: { + getState: () => {}, + subscribe: () => {}, + unsubscribe: () => {} + }, + getQueryFromState: queryName => { + if (queryName === 'io.cozy.files/parent_folder') { + return { + data: { + _id: 'parent_id', + path: '/Parent' + } + } + } + }, + query: () => ({ + data: [{ attributes: { slug: 'mespapiers' }, links: { related: '' } }] + }), + getInstanceOptions: () => ({ app: { slug: 'mespapiers' }, subdomain: 'flat' }) +} + const file = { _id: 'image', class: 'image', @@ -31,7 +94,7 @@ const FallbackComp = () => { ; - + ({ ...jest.requireActual('./checkImageSource'), diff --git a/react/Viewer/Footer/BottomSheetContent.jsx b/react/Viewer/Footer/BottomSheetContent.jsx deleted file mode 100644 index 545f180016..0000000000 --- a/react/Viewer/Footer/BottomSheetContent.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' - -import { BottomSheetItem } from '../../BottomSheet' -import getPanelBlocks, { getPanelBlocksSpecs } from '../Panel/getPanelBlocks' - -const BottomSheetContent = ({ file, isPublic }) => { - const panelBlocks = getPanelBlocks({ - panelBlocksSpecs: getPanelBlocksSpecs(isPublic), - file - }) - - return panelBlocks.map((PanelBlock, index) => ( - - - - )) -} - -BottomSheetContent.propTypes = { - file: PropTypes.object.isRequired, - isPublic: PropTypes.bool -} - -export default BottomSheetContent diff --git a/react/Viewer/Footer/DownloadButton.jsx b/react/Viewer/Footer/DownloadButton.jsx deleted file mode 100644 index 2dd9c1a72f..0000000000 --- a/react/Viewer/Footer/DownloadButton.jsx +++ /dev/null @@ -1,67 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' - -import { useClient } from 'cozy-client' - -import Button from '../../Buttons' -import Icon from '../../Icon' -import IconButton from '../../IconButton' -import DownloadIcon from '../../Icons/Download' -import Alerter from '../../deprecated/Alerter' -import { useI18n } from '../../providers/I18n' - -const DownloadButton = ({ file, variant }) => { - const client = useClient() - const { t } = useI18n() - - const icon = - const label = t('Viewer.download') - - const handleClick = async () => { - try { - await client.collection('io.cozy.files').download(file) - } catch (error) { - Alerter.info('Viewer.error.generic') - } - } - - if (variant === 'iconButton') { - return ( - - {icon} - - ) - } - - if (variant === 'buttonIcon') { - return ( - - - -`; - -exports[`NoViewer should render the viewer with specific extra content 1`] = ` -
-
- - - - - - -

- notSupported.xyz -

-
- with specific extra content -
-
-
-`; diff --git a/react/Viewer/NoViewer/index.jsx b/react/Viewer/NoViewer/index.jsx deleted file mode 100644 index 2d5892c351..0000000000 --- a/react/Viewer/NoViewer/index.jsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from './NoViewer' diff --git a/react/Viewer/Panel/ActionMenuDesktop.jsx b/react/Viewer/Panel/ActionMenuDesktop.jsx deleted file mode 100644 index 0400c3b9ee..0000000000 --- a/react/Viewer/Panel/ActionMenuDesktop.jsx +++ /dev/null @@ -1,66 +0,0 @@ -import PropTypes from 'prop-types' -import React, { forwardRef } from 'react' - -import styles from './styles.styl' -import AppLinker from '../../AppLinker' -import Icon from '../../Icon' -import Copy from '../../Icons/Copy' -import Edit from '../../Icons/Rename' -import Typography from '../../Typography' -import ActionMenu, { ActionMenuItem } from '../../deprecated/ActionMenu' -import { useI18n } from '../../providers/I18n' - -const ActionMenuDesktop = forwardRef( - ({ onClose, isEditable, actions, appLink, appSlug }, ref) => { - const { handleCopy, handleEdit } = actions - const { t } = useI18n() - - return ( - - {isEditable && ( - - {({ onClick, href }) => { - return ( - handleEdit(onClick)}> - } - > - - {t(`Viewer.panel.qualification.actions.edit`)} - - - - ) - }} - - )} - } - > - - {t(`Viewer.panel.qualification.actions.copy`)} - - - - ) - } -) -ActionMenuDesktop.displayName = 'ActionMenuDesktop' - -ActionMenuDesktop.propTypes = { - onClose: PropTypes.func, - isEditable: PropTypes.bool, - actions: PropTypes.shape({ - handleCopy: PropTypes.func, - handleEdit: PropTypes.func - }), - appLink: PropTypes.string, - appSlug: PropTypes.string -} - -export default ActionMenuDesktop diff --git a/react/Viewer/Panel/ActionMenuMobile.jsx b/react/Viewer/Panel/ActionMenuMobile.jsx deleted file mode 100644 index 29bce2a8e1..0000000000 --- a/react/Viewer/Panel/ActionMenuMobile.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' - -import AppLinker from '../../AppLinker' -import BottomSheet, { BottomSheetItem } from '../../BottomSheet' -import Icon from '../../Icon' -import Copy from '../../Icons/Copy' -import Edit from '../../Icons/Rename' -import List from '../../List' -import ListItem from '../../ListItem' -import ListItemIcon from '../../ListItemIcon' -import ListItemText from '../../ListItemText' -import { useI18n } from '../../providers/I18n' - -const ActionMenuMobile = ({ - onClose, - isEditable, - actions, - appLink, - appSlug -}) => { - const { t } = useI18n() - const { handleCopy, handleEdit } = actions - - return ( - - - - {isEditable && ( - - {({ onClick, href }) => { - return ( - handleEdit(onClick)} - > - - - - - - ) - }} - - )} - - - - - - - - - - ) -} - -ActionMenuMobile.propTypes = { - onClose: PropTypes.func, - isEditable: PropTypes.bool, - actions: PropTypes.shape({ - handleCopy: PropTypes.func, - handleEdit: PropTypes.func - }), - appLink: PropTypes.string -} - -export default ActionMenuMobile diff --git a/react/Viewer/Panel/ActionMenuWrapper.jsx b/react/Viewer/Panel/ActionMenuWrapper.jsx deleted file mode 100644 index bfcf02de2d..0000000000 --- a/react/Viewer/Panel/ActionMenuWrapper.jsx +++ /dev/null @@ -1,104 +0,0 @@ -import PropTypes from 'prop-types' -import React, { forwardRef } from 'react' - -import { useAppLinkWithStoreFallback, useClient } from 'cozy-client' - -import ActionMenuDesktop from './ActionMenuDesktop' -import ActionMenuMobile from './ActionMenuMobile' -import { useAlert } from '../../providers/Alert' -import useBreakpoints from '../../providers/Breakpoints' -import { useI18n } from '../../providers/I18n' -import { - buildEditAttributePath, - isEditableAttribute, - getCurrentModel -} from '../helpers' -import useActionMenuContext from '../providers/ActionMenuProvider' - -const mespapiersAppSlug = 'mespapiers' - -const ActionMenuWrapper = forwardRef(({ onClose, file, optionFile }, ref) => { - const { name, value } = optionFile - const editPathByModelProps = useActionMenuContext() - const { isMobile } = useBreakpoints() - const { t } = useI18n() - const { showAlert } = useAlert() - const client = useClient() - - const currentModel = getCurrentModel(name) - const editPath = buildEditAttributePath( - editPathByModelProps, - currentModel, - optionFile.name - ) - - const { fetchStatus, url } = useAppLinkWithStoreFallback( - mespapiersAppSlug, - client, - editPath - ) - const isAppLinkLoaded = fetchStatus === 'loaded' - const isEditable = Boolean(editPath) && isEditableAttribute(name, file) - - const handleCopy = async () => { - try { - await navigator.clipboard.writeText(value) - showAlert({ - message: t(`Viewer.snackbar.copiedToClipboard.success`), - severity: 'success', - variant: 'filled', - icon: false - }) - } catch (error) { - showAlert({ - message: t(`Viewer.snackbar.copiedToClipboard.error`), - severity: 'error', - variant: 'filled', - icon: false - }) - } - onClose() - } - - const handleEdit = cb => { - if (isAppLinkLoaded) { - onClose() - cb && cb() - } - } - - if (isMobile) { - return ( - - ) - } - - return ( - - ) -}) -ActionMenuWrapper.displayName = 'ActionMenuWrapper' - -ActionMenuWrapper.propTypes = { - onClose: PropTypes.func, - file: PropTypes.object, - optionFile: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.string - }) -} - -export default ActionMenuWrapper diff --git a/react/Viewer/Panel/Certifications.jsx b/react/Viewer/Panel/Certifications.jsx deleted file mode 100644 index 089ccc55b5..0000000000 --- a/react/Viewer/Panel/Certifications.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import has from 'lodash/has' -import PropTypes from 'prop-types' -import React from 'react' - -import Icon, { iconPropType } from '../../Icon' -import CarbonCopyIcon from '../../Icons/CarbonCopy' -import SafeIcon from '../../Icons/Safe' -import Typography from '../../Typography' -import { Media, Img, Bd } from '../../deprecated/Media' -import { withViewerLocales } from '../hoc/withViewerLocales' - -const Certification = ({ icon, title, caption }) => { - return ( -
- - - - - - {title} - - - {caption} -
- ) -} - -Certification.propTypes = { - icon: iconPropType.isRequired, - title: PropTypes.string.isRequired, - caption: PropTypes.string.isRequired -} - -const Certifications = ({ file, t }) => { - const hasCarbonCopy = has(file, 'metadata.carbonCopy') - const hasElectronicSafe = has(file, 'metadata.electronicSafe') - - return ( - <> - {hasCarbonCopy && ( - - )} - {hasElectronicSafe && ( - - )} - - ) -} - -Certifications.propTypes = { - file: PropTypes.object.isRequired -} - -export default withViewerLocales(Certifications) diff --git a/react/Viewer/Panel/PanelContent.jsx b/react/Viewer/Panel/PanelContent.jsx deleted file mode 100644 index f91c66b17d..0000000000 --- a/react/Viewer/Panel/PanelContent.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import cx from 'classnames' -import PropTypes from 'prop-types' -import React from 'react' - -import getPanelBlocks, { getPanelBlocksSpecs } from './getPanelBlocks' -import Paper from '../../Paper' -import Stack from '../../Stack' -import Typography from '../../Typography' -import { withViewerLocales } from '../hoc/withViewerLocales' - -const PanelContent = ({ file, isPublic, t }) => { - const panelBlocks = getPanelBlocks({ - panelBlocksSpecs: getPanelBlocksSpecs(isPublic), - file - }) - - return ( - - - {t('Viewer.panel.title')} - - {panelBlocks.map((PanelBlock, index) => ( - - - - - - ))} - - ) -} - -PanelContent.propTypes = { - file: PropTypes.object.isRequired, - isPublic: PropTypes.bool -} - -export default withViewerLocales(PanelContent) diff --git a/react/Viewer/Panel/Qualification.jsx b/react/Viewer/Panel/Qualification.jsx deleted file mode 100644 index 0a49b719b9..0000000000 --- a/react/Viewer/Panel/Qualification.jsx +++ /dev/null @@ -1,114 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useRef, useState, createRef, useMemo, useEffect } from 'react' - -import { - isExpiringSoon, - formatMetadataQualification, - KNOWN_BILLS_ATTRIBUTES_NAMES, - getMetadataQualificationType -} from 'cozy-client/dist/models/paper' - -import ActionMenuWrapper from './ActionMenuWrapper' -import QualificationListItemContact from './QualificationListItemContact' -import QualificationListItemDate from './QualificationListItemDate' -import QualificationListItemInformation from './QualificationListItemInformation' -import QualificationListItemOther from './QualificationListItemOther' -import List from '../../List' -import ExpirationAlert from '../components/ExpirationAlert' -import { withViewerLocales } from '../hoc/withViewerLocales' - -const ComponentFromMetadataQualificationType = { - contact: QualificationListItemContact, - date: QualificationListItemDate, - information: QualificationListItemInformation, - other: QualificationListItemOther, - bills: QualificationListItemInformation -} - -const isExpirationAlertHidden = file => { - return file?.metadata?.hideExpirationAlert ?? false -} - -const Qualification = ({ file }) => { - const { metadata = {} } = file - const actionBtnRef = useRef([]) - const [optionFile, setOptionFile] = useState({ - id: '', - name: '', - value: '' - }) - - const hideActionsMenu = () => { - setOptionFile({ id: '', name: '', value: '' }) - } - - const toggleActionsMenu = (id, name, value) => { - setOptionFile(prev => { - if (prev.value) return { id: '', name: '', value: '' } - return { id, name, value } - }) - } - - const formattedMetadataQualification = useMemo(() => { - const relatedBills = file.bills?.data?.[0] - - if (relatedBills) { - const formattedBillsMetadata = KNOWN_BILLS_ATTRIBUTES_NAMES.map( - attrName => ({ name: attrName, value: relatedBills[attrName] }) - ) - - return formatMetadataQualification(metadata).concat( - formattedBillsMetadata - ) - } - - return formatMetadataQualification(metadata) - }, [metadata, file.bills?.data]) - - useEffect(() => { - actionBtnRef.current = formattedMetadataQualification.map( - (_, idx) => actionBtnRef.current[idx] ?? createRef() - ) - }, [formattedMetadataQualification]) - - return ( - <> - {isExpiringSoon(file) && !isExpirationAlertHidden(file) && ( - - )} - - {formattedMetadataQualification.map((meta, idx) => { - const { name } = meta - const metadataQualificationType = getMetadataQualificationType(name) - const QualificationListItemComp = - ComponentFromMetadataQualificationType[metadataQualificationType] - - return ( - toggleActionsMenu(idx, name, val)} - /> - ) - })} - - {optionFile.name && ( - - )} - - - ) -} - -Qualification.propTypes = { - file: PropTypes.object.isRequired -} - -export default withViewerLocales(Qualification) diff --git a/react/Viewer/Panel/QualificationListItemContact.jsx b/react/Viewer/Panel/QualificationListItemContact.jsx deleted file mode 100644 index 92a5fa119f..0000000000 --- a/react/Viewer/Panel/QualificationListItemContact.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useRef, useState } from 'react' - -import { - getTranslatedNameForContact, - formatContactValue -} from 'cozy-client/dist/models/paper' - -import ActionMenuWrapper from './ActionMenuWrapper' -import QualificationListItemText from './QualificationListItemText' -import Icon from '../../Icon' -import IconButton from '../../IconButton' -import Dots from '../../Icons/Dots' -import ListItem from '../../ListItem' -import ListItemSecondaryAction from '../../ListItemSecondaryAction' -import Spinner from '../../Spinner' -import { useI18n } from '../../providers/I18n' -import useReferencedContactName from '../hooks/useReferencedContactName' - -const QualificationListItemContact = ({ file }) => { - const { lang } = useI18n() - const actionBtnRef = useRef() - const [optionFile, setOptionFile] = useState({ - name: '', - value: '' - }) - - const hideActionsMenu = () => setOptionFile({ name: '', value: '' }) - const toggleActionsMenu = (name, value) => - setOptionFile(prev => { - if (prev.value) return { name: '', value: '' } - return { name, value } - }) - - const { contacts, isLoadingContacts } = useReferencedContactName(file) - - if (isLoadingContacts) { - return ( - - - - ) - } - - const formattedTitle = getTranslatedNameForContact({ lang }) - const formattedValue = formatContactValue(contacts) - - if (!isLoadingContacts && !formattedValue) { - return null - } - - return ( - <> - - - - toggleActionsMenu('contact', formattedValue)} - > - - - - - - {optionFile.value && ( - - )} - - ) -} - -QualificationListItemContact.propTypes = { - file: PropTypes.object.isRequired -} - -export default QualificationListItemContact diff --git a/react/Viewer/Panel/QualificationListItemDate.jsx b/react/Viewer/Panel/QualificationListItemDate.jsx deleted file mode 100644 index e5ec9c8eb2..0000000000 --- a/react/Viewer/Panel/QualificationListItemDate.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import PropTypes from 'prop-types' -import React, { forwardRef } from 'react' - -import { - isExpired, - isExpiringSoon, - getTranslatedNameForDateMetadata, - formatDateMetadataValue -} from 'cozy-client/dist/models/paper' - -import QualificationListItemText from './QualificationListItemText' -import Icon from '../../Icon' -import IconButton from '../../IconButton' -import Dots from '../../Icons/Dots' -import ListItem from '../../ListItem' -import ListItemSecondaryAction from '../../ListItemSecondaryAction' -import Typography from '../../Typography' -import { useI18n } from '../../providers/I18n' -import ExpirationAnnotation from '../components/ExpirationAnnotation' - -const QualificationListItemDate = forwardRef( - ({ file, formattedMetadataQualification, toggleActionsMenu }, ref) => { - const { f, lang } = useI18n() - const { name, value } = formattedMetadataQualification - const formattedTitle = getTranslatedNameForDateMetadata(name, { lang }) - const formattedDate = formatDateMetadataValue(value, { - f, - lang - }) - const isExpirationDate = name === 'expirationDate' - - return ( - - - - {formattedDate} - - {isExpirationDate && (isExpired(file) || isExpiringSoon(file)) && ( - <> - - {' · '} - - - - )} - - } - disabled={!value} - /> - - toggleActionsMenu(formattedDate)} - > - - - - - ) - } -) - -QualificationListItemDate.displayName = 'QualificationListItemDate' - -QualificationListItemDate.propTypes = { - file: PropTypes.object.isRequired, - formattedMetadataQualification: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.string - }).isRequired, - toggleActionsMenu: PropTypes.func.isRequired -} - -export default QualificationListItemDate diff --git a/react/Viewer/Panel/QualificationListItemInformation.jsx b/react/Viewer/Panel/QualificationListItemInformation.jsx deleted file mode 100644 index 8832cf877f..0000000000 --- a/react/Viewer/Panel/QualificationListItemInformation.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import PropTypes from 'prop-types' -import React, { forwardRef } from 'react' - -import { - getTranslatedNameForInformationMetadata, - formatInformationMetadataValue -} from 'cozy-client/dist/models/paper' - -import QualificationListItemText from './QualificationListItemText' -import Icon from '../../Icon' -import IconButton from '../../IconButton' -import Dots from '../../Icons/Dots' -import ListItem from '../../ListItem' -import ListItemSecondaryAction from '../../ListItemSecondaryAction' -import MidEllipsis from '../../MidEllipsis' -import { useI18n } from '../../providers/I18n' - -const QualificationListItemInformation = forwardRef( - ({ formattedMetadataQualification, file, toggleActionsMenu }, ref) => { - const { lang } = useI18n() - const { name, value } = formattedMetadataQualification - const qualificationLabel = file.metadata.qualification.label - - const formattedTitle = getTranslatedNameForInformationMetadata(name, { - lang, - qualificationLabel - }) - const formattedValue = formatInformationMetadataValue(value, { - lang, - name, - qualificationLabel - }) - - const titleComponent = - formattedTitle === name ? : formattedTitle - - return ( - - - - toggleActionsMenu(value)} - data-testid="toggleActionsMenuBtn" - > - - - - - ) - } -) - -QualificationListItemInformation.displayName = 'QualificationListItemNumber' - -QualificationListItemInformation.propTypes = { - formattedMetadataQualification: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - }).isRequired, - toggleActionsMenu: PropTypes.func.isRequired -} - -export default QualificationListItemInformation diff --git a/react/Viewer/Panel/QualificationListItemInformation.spec.jsx b/react/Viewer/Panel/QualificationListItemInformation.spec.jsx deleted file mode 100644 index e865644b24..0000000000 --- a/react/Viewer/Panel/QualificationListItemInformation.spec.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import { fireEvent, render } from '@testing-library/react' -import React from 'react' - -import QualificationListItemInformation from './QualificationListItemInformation' - -jest.mock('../../providers/I18n', () => ({ - useI18n: jest.fn(() => ({ t: x => x })) -})) - -const setup = ({ - formattedMetadataQualification = {}, - toggleActionsMenu = jest.fn() -} = {}) => { - return render( - - ) -} - -describe('QualificationListItemInformation', () => { - describe('formattedMetadataQualification', () => { - it('should display default text if value is falsy', () => { - const formattedMetadataQualification = { name: 'country', value: '' } - const { getByText } = setup({ formattedMetadataQualification }) - - expect(getByText('No information')) - }) - // eslint-disable-next-line jest/no-focused-tests - it.only('should display current value if it is truthy', () => { - const formattedMetadataQualification = { - name: 'country', - value: 'Italie' - } - const { queryByText } = setup({ - formattedMetadataQualification - }) - - expect(queryByText('No information')).toBeNull() - expect(queryByText('Italie')).toBeInTheDocument() - }) - it('should display current value if it number type', () => { - const formattedMetadataQualification = { name: 'country', value: 0 } - const { queryByText } = setup({ - formattedMetadataQualification - }) - - expect(queryByText('No information')).toBeNull() - expect(queryByText('0')).toBeInTheDocument() - }) - }) - describe('toggleActionsMenu', () => { - it('should call toggleActionsMenu with current value on click it', () => { - const formattedMetadataQualification = { - name: 'country', - value: 'Italie' - } - const toggleActionsMenu = jest.fn() - const { getByTestId } = setup({ - toggleActionsMenu, - formattedMetadataQualification - }) - const toggleActionsMenuBtn = getByTestId('toggleActionsMenuBtn') - fireEvent.click(toggleActionsMenuBtn) - - expect(toggleActionsMenu).toBeCalledWith('Italie') - }) - }) -}) diff --git a/react/Viewer/Panel/QualificationListItemOther.jsx b/react/Viewer/Panel/QualificationListItemOther.jsx deleted file mode 100644 index 90e4d68b7d..0000000000 --- a/react/Viewer/Panel/QualificationListItemOther.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import PropTypes from 'prop-types' -import React, { forwardRef } from 'react' - -import { - getTranslatedNameForOtherMetadata, - formatOtherMetadataValue -} from 'cozy-client/dist/models/paper' - -import QualificationListItemText from './QualificationListItemText' -import Icon from '../../Icon' -import IconButton from '../../IconButton' -import Dots from '../../Icons/Dots' -import ListItem from '../../ListItem' -import ListItemSecondaryAction from '../../ListItemSecondaryAction' -import MidEllipsis from '../../MidEllipsis' -import { useI18n } from '../../providers/I18n' - -const QualificationListItemOther = forwardRef( - ({ formattedMetadataQualification, toggleActionsMenu }, ref) => { - const { lang } = useI18n() - const { name, value } = formattedMetadataQualification - - if (!value) return null - - const formattedTitle = getTranslatedNameForOtherMetadata(name, { - lang - }) - const formattedValue = formatOtherMetadataValue(value, { - lang, - name - }) - - return ( - - } - /> - - toggleActionsMenu(formattedValue)} - > - - - - - ) - } -) -QualificationListItemOther.displayName = 'QualificationListItemOther' - -QualificationListItemOther.propTypes = { - formattedMetadataQualification: PropTypes.shape({ - name: PropTypes.string, - value: PropTypes.string - }).isRequired, - toggleActionsMenu: PropTypes.func.isRequired -} - -export default QualificationListItemOther diff --git a/react/Viewer/Panel/QualificationListItemText.jsx b/react/Viewer/Panel/QualificationListItemText.jsx deleted file mode 100644 index ff2b585a06..0000000000 --- a/react/Viewer/Panel/QualificationListItemText.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' - -import ListItemText from '../../ListItemText' -import Typography from '../../Typography' - -const QualificationListItemText = ({ primary, secondary, disabled }) => { - return ( - {primary}} - secondary={ - - {secondary} - - } - /> - ) -} - -QualificationListItemText.propTypes = { - primary: PropTypes.string.isRequired, - secondary: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired -} - -export default QualificationListItemText diff --git a/react/Viewer/Panel/getPanelBlocks.jsx b/react/Viewer/Panel/getPanelBlocks.jsx deleted file mode 100644 index fa2a25f79d..0000000000 --- a/react/Viewer/Panel/getPanelBlocks.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import { models } from 'cozy-client' -import KonnectorBlock from 'cozy-harvest-lib/dist/components/KonnectorBlock' - -import Certifications from './Certifications' -import Qualification from './Qualification' - -const { isFromKonnector, hasQualifications, hasCertifications } = models.file - -/** - * @typedef {Object} PanelBlockSpec - * @property {Function} condition - Function that returns true if the block should be displayed - * @property {React.Component} component - Component to display - */ - -/** - * @typedef {Object.} PanelBlocksSpecs - */ - -/** - * Returns the specs of the blocks to display in the panel - * @param {boolean} isPublic - Whether the panel is displayed in public view - * @returns {PanelBlocksSpecs} - */ -export const getPanelBlocksSpecs = (isPublic = false) => ({ - qualifications: { - condition: hasQualifications, - component: Qualification - }, - konnector: { - condition: file => isFromKonnector(file) && !isPublic, - component: KonnectorBlock - }, - certifications: { - condition: hasCertifications, - component: Certifications - } -}) - -/** - * Returns the blocks to display in the panel - * @param {Object} options - * @param {PanelBlocksSpecs} options.panelBlocksSpecs - Specs of the blocks to display in the panel - * @param {import('cozy-client/types/types').FileDocument} options.file - File object - * @returns {Array.} - */ -const getPanelBlocks = ({ panelBlocksSpecs, file }) => { - const panelBlocks = [] - - Object.values(panelBlocksSpecs).forEach(panelBlock => { - panelBlock.condition(file) && panelBlocks.push(panelBlock.component) - }) - - return panelBlocks -} - -export default getPanelBlocks diff --git a/react/Viewer/Panel/getPanelBlocks.spec.jsx b/react/Viewer/Panel/getPanelBlocks.spec.jsx deleted file mode 100644 index c758e60248..0000000000 --- a/react/Viewer/Panel/getPanelBlocks.spec.jsx +++ /dev/null @@ -1,79 +0,0 @@ -import getPanelBlocks, { getPanelBlocksSpecs } from './getPanelBlocks' - -jest.mock('cozy-harvest-lib/dist/components/KonnectorBlock', () => jest.fn()) -const block1Component = jest.fn() -const block2Component = jest.fn() - -describe('getPanelBlocks', () => { - it('should return only blocks with truthy condition', () => { - // with two truthy component - expect( - getPanelBlocks({ - panelBlocksSpecs: { - block1: { - condition: () => true, - component: block1Component - }, - block2: { - condition: () => true, - component: block2Component - } - } - }) - ).toMatchObject([block1Component, block2Component]) - - // with one truthy component - expect( - getPanelBlocks({ - panelBlocksSpecs: { - block1: { - condition: () => false, - component: block1Component - }, - block2: { - condition: () => true, - component: block2Component - } - } - }) - ).toMatchObject([block2Component]) - - // with no truthy component - expect( - getPanelBlocks({ - panelBlocksSpecs: { - block1: { - condition: () => false, - component: block1Component - }, - block2: { - condition: () => false, - component: block2Component - } - } - }) - ).toMatchObject([]) - - // with no specs - expect(getPanelBlocks({ panelBlocksSpecs: {} })).toMatchObject([]) - }) -}) - -describe('getPanelBlocksSpecs', () => { - it('should return the specs of the blocks to display in the panel', () => { - expect(getPanelBlocksSpecs()).toEqual({ - qualifications: { - condition: expect.any(Function), - component: expect.anything() - }, - konnector: { - condition: expect.any(Function), - component: expect.anything() - }, - certifications: { - condition: expect.any(Function), - component: expect.anything() - } - }) - }) -}) diff --git a/react/Viewer/Panel/styles.styl b/react/Viewer/Panel/styles.styl deleted file mode 100644 index 02bf565af0..0000000000 --- a/react/Viewer/Panel/styles.styl +++ /dev/null @@ -1,13 +0,0 @@ -.ActionMenuDesktop-ActionMenu - a - padding 0 !important // Waiting for the migration of the ActionMenu on the Viewer - .ActionMenuDesktop-ActionMenu-link-disabled - > div - cursor default - &:hover - background-color initial - > div - svg - fill var(--disabledTextColor) - p - color var(--disabledTextColor) diff --git a/react/Viewer/Readme.md b/react/Viewer/Readme.md deleted file mode 100644 index 3070fb366b..0000000000 --- a/react/Viewer/Readme.md +++ /dev/null @@ -1,352 +0,0 @@ -The `Viewer` component can be used to display the content of various file types. - -Once rendered, the `Viewer` will take up all the available space in it's container (using `position: absolute`). - -The `Viewer` can display an **information panel** to show additional information about the current file (e.g. whether a file is certified). - -### ⚠️ Requirement - -* You must have [WebviewIntent Provider](https://github.com/cozy/cozy-libs/blob/b1ad6f5933b463878f641d9fbb63eddd4c45b0d0/packages/cozy-intent/src/view/components/WebviewIntentProvider.tsx#L89) & [CozySharing Provider](https://github.com/cozy/cozy-libs/tree/master/packages/cozy-sharing) -* In order to download and display the files, it will need a `cozy-client` instance in the React context. -* To have the panels, the app need to have [cozy-harvest-lib](https://github.com/cozy/cozy-libs/tree/master/packages/cozy-harvest-lib) installed - -### Props - -* **files** : `` – One or more `io.cozy.files` to display -* **currentIndex** : `` – Index of the file to show -* **currentURL** : `` – Optionnal URL of the file -* **className** : `` – CSS classes -* **showNavigation** : `` – Whether to show left and right arrows to navigate between files -* **renderFallbackExtraContent** : `` – A render prop that is called when a file can't be displayed -* **disablePanel** : `` – Show/Hide the panel containing more information about the file only on Desktop -* **disableFooter** : `` – Show/Hide the panel containing more information about the file only on Phone & Tablet devices -* **disableModal** : `` – To avoid wrapping the Viewer with a Modal component (wrapper of Viewer) -* **editPathByModelProps** : `` – Edit path by model properties - * **information** : `` – URL used to edit the file when editing a `information` type metadata (text, date) - * **page** : `` – URL used to edit the file when editing a `page` type metadata (side of the document) -* **onChangeRequest** : `` - Called with (nextFile, nextIndex) when the user requests to navigate to another file -* **onCloseRequest** : `` - Called when the user wants to leave the Viewer -* **isPublic**: `` - Whether the viewer is used in a public page or not -* **componentsProps** : `` – Props passed to components with the same name - * **modalProps** : `` – Props passed to Modal component - * **OnlyOfficeViewer** : `` – Used to open an Only Office file - * **isEnabled** : `` – Whether Only Office is enabled on the server - * **opener** : `` – To open the Only Office file - * **toolbarProps** : `` – Toolbar properties - * **toolbarRef** : `` – React reference of the toolbar node - * **showToolbar** : `` – Whether to show the toolbar or not. Note that the built-in close button is in the toolbar - * **showClose** : `` – Whether to show close button in toolbar - * **showFilePath** : `` – Whether to show file path below his name - -### Demo - -```jsx -import cx from 'classnames' -import { makeStyles } from 'cozy-ui/transpiled/react/styles' -import Variants from 'cozy-ui/docs/components/Variants' -import Card from 'cozy-ui/transpiled/react/Card' -import Checkbox from 'cozy-ui/transpiled/react/Checkbox' -import Viewer, { ToolbarButtons, FooterActionButtons, ForwardOrDownloadButton } from 'cozy-ui/transpiled/react/Viewer' -import Stack from 'cozy-ui/transpiled/react/Stack' -import Paper from 'cozy-ui/transpiled/react/Paper' -import Typography from 'cozy-ui/transpiled/react/Typography' -import { Media, Img, Bd } from 'cozy-ui/transpiled/react/deprecated/Media' -import Icon from 'cozy-ui/transpiled/react/Icon' -import CarbonCopyIcon from 'cozy-ui/transpiled/react/Icons/CarbonCopy' -// The DemoProvider inserts a fake cozy-client in the React context. -import DemoProvider from './docs/DemoProvider' -import Button from 'cozy-ui/transpiled/react/Buttons' -import DownloadIcon from 'cozy-ui/transpiled/react/Icons/Download' -import ShareIcon from 'cozy-ui/transpiled/react/Icons/Share' -import { isValidForPanel } from 'cozy-ui/transpiled/react/Viewer/helpers' -import getPanelBlocks, { panelBlocksSpecs } from 'cozy-ui/transpiled/react/Viewer/Panel/getPanelBlocks' -import Sprite from 'cozy-ui/transpiled/react/Icon/Sprite' -import IconButton from 'cozy-ui/transpiled/react/IconButton' - -// We provide a collection of (fake) io.cozy.files to be rendered -const files = [ - { - _id: 'audio', - class: 'audio', - type: 'file', - name: 'Sample.mp3', - mime: 'audio/mp3', - dir_id: 'parent_folder' - }, - { - _id: 'slide', - class: 'slide', - type: 'file', - name: 'Slide.pptx', - mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - dir_id: 'parent_folder' - }, - { - _id: 'pdf', - class: 'pdf', - type: 'file', - name: 'My vehicle registration.pdf', - mime: 'application/pdf', - bills: { data: [{ amount: '500' }] }, - metadata: { - carbonCopy: true, - AObtentionDate: null, - BObtentionDate: "2022-02-09T09:05:38.000Z", - CObtentionDate: null, - DObtentionDate: null, - datetime: "2022-09-23T07:50:22.000Z", - datetimeLabel: "BObtentionDate", - expirationDate: new Date(Date.now() + 10 * 24 * 60 * 60 * 1000).toISOString(), - noticePeriod: "90", - number: "", - page: "front", - qualification: { - label: "driver_license", - purpose: "attestation", - sourceCategory: "gov", - sourceSubCategory: "transport", - subjects: ["permit", "driving"] - } - }, - dir_id: 'parent_folder' - }, - { - _id: 'text', - class: 'text', - type: 'file', - name: 'LoremipsumdolorsitametconsecteturadipiscingelitSednonrisusSuspendisselectustortordignissimsitametadipiscingnecultriciesseddolorCraselementumultricesdiamMaecenasligulamassavariusasempercongueeuismodnonmiProinporttitororcinecnonummymolestieenimesteleifendminonfermentumdiamnislsitameteratDuissemperDuisarcumassascelerisquevitaeconsequatinpretiumaenimPellentesquecongueUtinrisusvolutpatliberopharetratemporCrasvestibulumbibendumauguePraesentegestasleoinpedePraesentblanditodioeuenimPellentesquesedduiutaugueblanditsodalesVestibulumanteipsumprimisinfaucibusorciluctusetultricesposuerecubiliaCuraeAliquamnibhMaurisacmaurissedpedepellentesquefermentumMaecenasadipiscingantenondiamsodaleshendrerit.txt', - mime: 'text/plain', - metadata: { - datetime: "2022-01-01T12:00:00.000Z", - datetimeLabel: "datetime", - qualification: { - label: 'tax_notice' - } - } - }, - { - _id: 'text', - class: 'text', - type: 'file', - name: 'encrypted-example.txt', - mime: 'text/plain', - encrypted: true - }, - { - _id: 'image', - class: 'image', - type: 'file', - name: 'Demo.jpg', - mime: 'image/jpg', - metadata: { - carbonCopy: true, - electronicSafe: true, - referencedDate: new Date(Date.now() - 357 * 24 * 60 * 60 * 1000).toISOString(), - datetimeLabel: "referencedDate", - qualification: { - label: 'personal_sporting_licence' - } - } - }, - { - _id: 'none', - class: 'unknown', - type: 'file', - name: 'Unsupported file type', - mime: '???/???' - }, - { - _id: 'none', - class: 'unknown', - type: 'file', - name: 'Unsupported file type', - mime: '???/???', - metadata: { - carbonCopy: true, - AObtentionDate: null, - BObtentionDate: "2022-02-09T09:05:38.000Z", - CObtentionDate: null, - DObtentionDate: null, - datetime: "2022-09-23T07:50:22.000Z", - datetimeLabel: "BObtentionDate", - number: "", - page: "front", - qualification: { - label: "driver_license", - purpose: "attestation", - sourceCategory: "gov", - sourceSubCategory: "transport", - subjects: ["permit", "driving"] - } - } - } -] - -const ShareButtonFake = () => { - return ( -