@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
217 lines (190 loc) • 5.87 kB
JavaScript
/** @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
(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>
)
}
}