diff --git a/packages/@uppy/core/src/Uppy.js b/packages/@uppy/core/src/Uppy.js
index 2a687a61d3..91d273428f 100644
--- a/packages/@uppy/core/src/Uppy.js
+++ b/packages/@uppy/core/src/Uppy.js
@@ -26,12 +26,13 @@ class RestrictionError extends Error {
if (typeof AggregateError === 'undefined') {
// eslint-disable-next-line no-global-assign
globalThis.AggregateError = class AggregateError extends Error {
- constructor (message, errors) {
+ constructor (errors, message) {
super(message)
this.errors = errors
}
}
}
+
class AggregateRestrictionError extends AggregateError {
constructor (...args) {
super(...args)
@@ -557,27 +558,39 @@ class Uppy {
}
/**
- * Check if requiredMetaField restriction is met before uploading.
+ * Check if requiredMetaField restriction is met for a specific file.
*
*/
- #checkRequiredMetaFields (files) {
+ #checkRequiredMetaFieldsOnFile (file) {
const { requiredMetaFields } = this.opts.restrictions
const { hasOwnProperty } = Object.prototype
const errors = []
- for (const fileID of Object.keys(files)) {
- const file = this.getFile(fileID)
- for (let i = 0; i < requiredMetaFields.length; i++) {
- if (!hasOwnProperty.call(file.meta, requiredMetaFields[i]) || file.meta[requiredMetaFields[i]] === '') {
- const err = new RestrictionError(`${this.i18n('missingRequiredMetaFieldOnFile', { fileName: file.name })}`)
- errors.push(err)
- this.#showOrLogErrorAndThrow(err, { file, showInformer: false, throwErr: false })
- }
+ const missingFields = []
+ for (let i = 0; i < requiredMetaFields.length; i++) {
+ if (!hasOwnProperty.call(file.meta, requiredMetaFields[i]) || file.meta[requiredMetaFields[i]] === '') {
+ const err = new RestrictionError(`${this.i18n('missingRequiredMetaFieldOnFile', { fileName: file.name })}`)
+ errors.push(err)
+ missingFields.push(requiredMetaFields[i])
+ this.#showOrLogErrorAndThrow(err, { file, showInformer: false, throwErr: false })
}
}
+ this.setFileState(file.id, { missingRequiredMetaFields: missingFields })
+ return errors
+ }
+
+ /**
+ * Check if requiredMetaField restriction is met before uploading.
+ *
+ */
+ #checkRequiredMetaFields (files) {
+ const errors = Object.keys(files).flatMap((fileID) => {
+ const file = this.getFile(fileID)
+ return this.#checkRequiredMetaFieldsOnFile(file)
+ })
if (errors.length) {
- throw new AggregateRestrictionError(`${this.i18n('missingRequiredMetaField')}`, errors)
+ throw new AggregateRestrictionError(errors, `${this.i18n('missingRequiredMetaField')}`)
}
}
@@ -1277,6 +1290,12 @@ class Uppy {
this.calculateTotalProgress()
})
+ this.on('dashboard:file-edit-complete', (file) => {
+ if (file) {
+ this.#checkRequiredMetaFieldsOnFile(file)
+ }
+ })
+
// show informer if offline
if (typeof window !== 'undefined' && window.addEventListener) {
window.addEventListener('online', this.#updateOnlineStatus)
diff --git a/packages/@uppy/core/src/__snapshots__/Uppy.test.js.snap b/packages/@uppy/core/src/__snapshots__/Uppy.test.js.snap
index 1762b1101e..c0d9282118 100644
--- a/packages/@uppy/core/src/__snapshots__/Uppy.test.js.snap
+++ b/packages/@uppy/core/src/__snapshots__/Uppy.test.js.snap
@@ -24,6 +24,7 @@ Object {
"name": "foo.jpg",
"type": "image/jpeg",
},
+ "missingRequiredMetaFields": Array [],
"name": "foo.jpg",
"preview": undefined,
"progress": Object {
@@ -47,6 +48,7 @@ Object {
"name": "bar.jpg",
"type": "image/jpeg",
},
+ "missingRequiredMetaFields": Array [],
"name": "bar.jpg",
"preview": undefined,
"progress": Object {
diff --git a/packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.js b/packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.js
index 843e1b3ca9..c9c974b286 100644
--- a/packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.js
+++ b/packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.js
@@ -1,5 +1,6 @@
const { h, Fragment } = require('preact')
const prettierBytes = require('@transloadit/prettier-bytes')
+const MetaErrorMessage = require('../MetaErrorMessage')
const truncateString = require('@uppy/utils/lib/truncateString')
const renderFileName = (props) => {
@@ -54,22 +55,22 @@ const renderAuthor = (props) => {
}
const renderFileSize = (props) => props.file.size && (
-
- {prettierBytes(props.file.size)}
-
+
+ {prettierBytes(props.file.size)}
+
)
const ReSelectButton = (props) => props.file.isGhost && (
-
- {' \u2022 '}
-
-
+
+ {' \u2022 '}
+
+
)
const ErrorButton = ({ file, onClick }) => {
@@ -107,6 +108,12 @@ module.exports = function FileInfo (props) {
onClick={() => alert(props.file.error)} // TODO: move to a custom alert implementation
/>
+
)
}
diff --git a/packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.scss b/packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.scss
index b2a10f4a51..4f7dab37fa 100644
--- a/packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.scss
+++ b/packages/@uppy/dashboard/src/components/FileItem/FileInfo/index.scss
@@ -46,6 +46,7 @@
display: inline-block;
text-transform: uppercase;
vertical-align: bottom;
+ margin-bottom: 5px;
}
.uppy-Dashboard-Item-reSelect {
@@ -54,5 +55,49 @@
font-size: inherit;
font-family: inherit;
}
-// ...uppy-Dashboard-Item-status|
-// ...uppy-Dashboard-Item-fileInfo|
+
+.uppy-Dashboard-Item-errorMessage {
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 1.3;
+ color: darken($red, 15%);
+ background-color: lighten($red, 45%);
+ padding: 5px 6px;
+}
+
+.uppy-Dashboard-Item-errorMessageBtn {
+ text-decoration: underline;
+ cursor: pointer;
+ font-weight: 500;
+}
+
+// Error message desktop / large screen
+.uppy-Dashboard-Item-preview .uppy-Dashboard-Item-errorMessage {
+ display: none;
+
+ .uppy-size--md & {
+ display: block;
+ border-top: 1px solid lighten($red, 35%);
+ padding: 6px 8px;
+ line-height: 1.4;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+ }
+}
+
+// Error message mobile / small screen
+.uppy-Dashboard-Item-fileInfo .uppy-Dashboard-Item-errorMessage {
+ display: inline-block;
+ position: static;
+ border: 1px solid lighten($red, 35%);
+ border-radius: 3px;
+
+ .uppy-size--md & {
+ display: none;
+ }
+}
+
diff --git a/packages/@uppy/dashboard/src/components/FileItem/FilePreviewAndLink/index.js b/packages/@uppy/dashboard/src/components/FileItem/FilePreviewAndLink/index.js
index 2b99484f35..b0b95a4ef3 100644
--- a/packages/@uppy/dashboard/src/components/FileItem/FilePreviewAndLink/index.js
+++ b/packages/@uppy/dashboard/src/components/FileItem/FilePreviewAndLink/index.js
@@ -1,5 +1,6 @@
const { h } = require('preact')
const FilePreview = require('../../FilePreview')
+const MetaErrorMessage = require('../MetaErrorMessage')
const getFileTypeIcon = require('../../../utils/getFileTypeIcon')
module.exports = function FilePreviewAndLink (props) {
@@ -19,11 +20,17 @@ module.exports = function FilePreviewAndLink (props) {
target="_blank"
aria-label={props.file.meta.name}
>
- props.file.meta.name
+ {props.file.meta.name}
)
}
+
)
}
diff --git a/packages/@uppy/dashboard/src/components/FileItem/MetaErrorMessage.js b/packages/@uppy/dashboard/src/components/FileItem/MetaErrorMessage.js
new file mode 100644
index 0000000000..00d2bcc2e1
--- /dev/null
+++ b/packages/@uppy/dashboard/src/components/FileItem/MetaErrorMessage.js
@@ -0,0 +1,35 @@
+const { h } = require('preact')
+
+const metaFieldIdToName = (metaFieldId, metaFields) => {
+ const field = metaFields.filter(f => f.id === metaFieldId)
+ return field[0].name
+}
+
+module.exports = function renderMissingMetaFieldsError (props) {
+ const { file, toggleFileCard, i18n, metaFields } = props
+ const { missingRequiredMetaFields } = file
+ if (!missingRequiredMetaFields?.length) {
+ return null
+ }
+
+ const metaFieldsString = missingRequiredMetaFields.map(missingMetaField => (
+ metaFieldIdToName(missingMetaField, metaFields)
+ )).join(', ')
+
+ return (
+
+ {i18n('missingRequiredMetaFields', {
+ smart_count: missingRequiredMetaFields.length,
+ fields: metaFieldsString,
+ })}
+ {' '}
+
+
+ )
+}
diff --git a/packages/@uppy/dashboard/src/components/FileItem/index.js b/packages/@uppy/dashboard/src/components/FileItem/index.js
index 3eed378abe..3a9c57b54e 100644
--- a/packages/@uppy/dashboard/src/components/FileItem/index.js
+++ b/packages/@uppy/dashboard/src/components/FileItem/index.js
@@ -76,6 +76,9 @@ module.exports = class FileItem extends Component {