UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

217 lines (190 loc) • 5.87 kB
/** @jsx jsx */ import {Component} from 'react' import PropTypes from 'prop-types' import {jsx} from '@instructure/emotion' import {IconButton} from '@instructure/ui-buttons' import {Text} from '@instructure/ui-text' import {Spinner} from '@instructure/ui-spinner' import {IconDocumentLine, IconTrashLine} from '@instructure/ui-icons' import {SimpleModal} from '@instructure/quiz-common/components/SimpleModal/index' import {withStyleOverrides} from '@instructure/quiz-common/util/withStyleOverrides' import generateStyle from './styles' import generateComponentTheme from './theme' import isImageString from '../isImage' import t from '@instructure/quiz-i18n/format-message' const SPACE_KEY = 0x20 const ENTER_KEY = 0x0d @withStyleOverrides(generateStyle, generateComponentTheme) export default class FilesList extends Component { static displayName = 'FilesList' static componentId = `Quizzes${this.displayName}` static propTypes = { files: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number.isRequired, name: PropTypes.string.isRequired, url: PropTypes.string, size: PropTypes.number.isRequired, isUploading: PropTypes.bool, }), ), makeRemoveHandler: PropTypes.func, showIfEmpty: PropTypes.bool, readOnly: PropTypes.bool, styles: PropTypes.object, } static defaultProps = { files: void 0, makeRemoveHandler: void 0, showIfEmpty: false, readOnly: false, } state = { isModalOpen: false, modalContent: { name: 'empty', url: '', }, } handleCloseModal = () => { this.setState({isModalOpen: false}) } handleOpenModal = modalContent => { this.setState({ isModalOpen: true, modalContent, }) } handleOpenUrl = url => { window.open(url, '_blank') } makeKeyPressHandler(callback) { return event => { if (event.charCode === SPACE_KEY || event.charCode === ENTER_KEY) { event.preventDefault() callback() } } } renderImagePreview = (url, name) => ( <div css={this.props.styles.previewContainer}> <img src={url} alt={name} css={this.props.styles.previewImg} /> </div> ) renderFilePreview = () => ( <div css={this.props.styles.previewContainer}> <IconDocumentLine size="medium" /> </div> ) renderPreview = file => { const {url, size, name, isUploading} = file if (!url && !isUploading) { return ( <div css={this.props.styles.userResponse}> <Text color="primary">{t('(no answer)')}</Text> </div> ) } const isImage = isImageString(name) const onClick = isImage ? () => this.handleOpenModal(file) : () => this.handleOpenUrl(url) const containerProps = this.props.readOnly ? {} : { tabIndex: '0', role: 'button', } return ( <div css={this.props.styles.fileLinkWrapper}> {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */} <div {...containerProps} data-testid="button-container" onClick={onClick} css={this.props.styles.fileLink} onKeyPress={this.makeKeyPressHandler(onClick)} > <div css={this.props.styles.userResponse}> {isImage ? this.renderImagePreview(url, name) : this.renderFilePreview()} <div css={this.props.styles.nameContainer}> <div> <Text color="brand">{name}</Text> </div> <div> <Text color="primary"> {t('{size, number}Kb', { size: Math.round(size * 0.001), description: 'size of file in kilobytes', })} </Text> </div> {!isUploading ? null : ( <Spinner themeOverride={this.props.styles.spinner} renderTitle={t('Loading')} size="small" /> )} </div> </div> </div> {this.props.makeRemoveHandler && ( <div css={this.props.styles.removeButton} data-role="removeButton"> <IconButton onClick={this.props.makeRemoveHandler(file)} name="Close" size="small" renderIcon={IconTrashLine} withBackground={false} withBorder={false} readOnly={this.props.readOnly} screenReaderLabel={t('Remove File Named {name}', {name})} /> </div> )} </div> ) } renderPreviews = () => { const {files, showIfEmpty} = this.props if (typeof files === 'undefined') { return ( <div css={this.props.styles.entryWrapper}> <Text color="primary">{t('(response not displayed)')}</Text> </div> ) } if (showIfEmpty && (!files || files.length === 0)) { return ( <div css={this.props.styles.entryWrapper}> <Text color="primary">{t('(no answer)')}</Text> </div> ) } return files.map(file => ( <div css={this.props.styles.entryWrapper} key={file.id}> {this.renderPreview(file)} </div> )) } render() { const {modalContent, isModalOpen} = this.state return ( <div> <div className="fs-mask">{this.renderPreviews()}</div> <SimpleModal isModalOpen={isModalOpen} title={modalContent.name} label={modalContent.name} onModalDismiss={this.handleCloseModal} > <img css={this.props.styles.modalImage} className="fs-mask" src={modalContent.url} alt={modalContent.name} /> </SimpleModal> </div> ) } }