소스 검색

@uppy/dashboard: fix metafield form validation (#3113)

* @uppy/dashboard: fix metafield form validation

Browsers without support for the HTML `form` attribute would misbehave,
making it impossible to validate the form at all, and potentially validate an
unrelated upper form if users are embeding Uppy inside a a `<form>`.

Fixes: https://github.com/transloadit/uppy/issues/3111

* Add comments

* Fix docs and types
Antoine du Hamel 3 년 전
부모
커밋
1f0e7a59c0
3개의 변경된 파일26개의 추가작업 그리고 7개의 파일을 삭제
  1. 18 1
      packages/@uppy/dashboard/src/components/FileCard/index.js
  2. 2 1
      packages/@uppy/dashboard/types/index.d.ts
  3. 6 5
      website/src/docs/dashboard.md

+ 18 - 1
packages/@uppy/dashboard/src/components/FileCard/index.js

@@ -62,6 +62,15 @@ class FileCard extends Component {
     this.props.toggleFileCard(false)
   }
 
+  saveOnEnter = (ev) => {
+    if (ev.keyCode === 13) {
+      ev.stopPropagation()
+      ev.preventDefault()
+      const file = this.props.files[this.props.fileCardFor]
+      this.props.saveFileCard(this.state.formState, file.id)
+    }
+  }
+
   renderMetaFields = () => {
     const metaFields = this.getMetaFields() || []
     const fieldCSSClasses = {
@@ -91,6 +100,11 @@ class FileCard extends Component {
                 required={required}
                 value={this.state.formState[field.id]}
                 placeholder={field.placeholder}
+                // If `form` attribute is not supported, we need to capture pressing Enter to avoid bubbling in case Uppy is
+                // embedded inside a <form>.
+                onKeyUp={'form' in HTMLInputElement.prototype ? undefined : this.saveOnEnter}
+                onKeyDown={'form' in HTMLInputElement.prototype ? undefined : this.saveOnEnter}
+                onKeyPress={'form' in HTMLInputElement.prototype ? undefined : this.saveOnEnter}
                 onInput={ev => this.updateMeta(ev.target.value, field.id)}
                 data-uppy-super-focusable
               />
@@ -153,7 +167,10 @@ class FileCard extends Component {
           <div className="uppy-Dashboard-FileCard-actions">
             <button
               className="uppy-u-reset uppy-c-btn uppy-c-btn-primary uppy-Dashboard-FileCard-actionsBtn"
-              type="submit"
+              // If `form` attribute is supported, we want a submit button to trigger the form validation.
+              // Otherwise, fallback to a classic button with a onClick event handler.
+              type={'form' in HTMLButtonElement.prototype ? 'submit' : 'button'}
+              onClick={'form' in HTMLButtonElement.prototype ? undefined : this.handleSave}
               form={this.form.id}
             >
               {this.props.i18n('saveChanges')}

+ 2 - 1
packages/@uppy/dashboard/types/index.d.ts

@@ -6,7 +6,8 @@ type FieldRenderOptions = {
   value: string,
   onChange: (newVal: string) => void
   fieldCSSClasses: { text: string }
-  required?: boolean
+  required: boolean
+  form: string
 }
 
 type PreactRender = (node: any, params: Record<string, unknown> | null, ...children: any[]) => any

+ 6 - 5
website/src/docs/dashboard.md

@@ -234,8 +234,8 @@ An array of UI field objects, or a function that takes a [File Object](https://u
 - `name`, the label shown in the interface.
 - `placeholder`, the text shown when no value is set in the field. (Not needed when a custom render function is provided)
 
-Optionally, you can specify `render: ({value, onChange, required}, h) => void`, a function for rendering a custom form element.
-It gets passed `({value, onChange, required}, h)` where `value` is the current value of the meta field, `required` is a boolean that's true if the field `id` is in the `restrictedMetaFields` restriction, and `onChange: (newVal) => void` is a function saving the new value and `h` is the `createElement` function from [preact](https://preactjs.com/guide/v10/api-reference#h--createelement).
+Optionally, you can specify `render: ({value, onChange, required, form}, h) => void`, a function for rendering a custom form element.
+It gets passed `({value, onChange, required, form}, h)` where `value` is the current value of the meta field, `required` is a boolean that's true if the field `id` is in the `restrictedMetaFields` restriction, `form` is the `id` of the associated `<form>` element, and `onChange: (newVal) => void` is a function saving the new value and `h` is the `createElement` function from [preact](https://preactjs.com/guide/v10/api-reference#h--createelement).
 `h` can be useful when using uppy from plain JavaScript, where you cannot write JSX.
 
 ```js
@@ -248,8 +248,8 @@ uppy.use(Dashboard, {
     {
       id: 'public',
       name: 'Public',
-      render ({ value, onChange, required }, h) {
-        return h('input', { type: 'checkbox', required, onChange: (ev) => onChange(ev.target.checked ? 'on' : 'off'), defaultChecked: value === 'on' })
+      render ({ value, onChange, required, form }, h) {
+        return h('input', { type: 'checkbox', required, form, onChange: (ev) => onChange(ev.target.checked ? 'on' : 'off'), defaultChecked: value === 'on' })
       },
     },
   ],
@@ -269,12 +269,13 @@ uppy.use(Dashboard, {
       fields.push({
         id: 'public',
         name: 'Public',
-        render: ({ value, onChange, required }, h) => {
+        render: ({ value, onChange, required, form }, h) => {
           return h('input', {
             type: 'checkbox',
             onChange: (ev) => onChange(ev.target.checked ? 'on' : 'off'),
             defaultChecked: value === 'on',
             required,
+            form,
           })
         },
       })