Browse Source

Allowed HTML Attributes to be passed via props (#2891)

* Allowed HTML Attributes to be passed via props

This fix still needs some work, as certain attributes could be passed to Uppy or as an HTML attribute, such as target

When this is fixed, this will resolve #2403

* Removed shared attributes

I removed the ability to use certain attributes ('width', 'height', and 'target') and pass them down as Props to the div due to that fact that Uppy relies on them

* Add typescript types for the HTML attributes

* Moved common.js to getHTMLProps.js

* Fixed import

* Converted `possibleStandardNames` to an Array

* Fix import

* Fix tests
Andrew 3 years ago
parent
commit
9a9860ac8f

+ 1 - 1
packages/@uppy/react/src/Dashboard.d.ts

@@ -6,7 +6,7 @@ type DashboardPropsInner = Omit<
   ToUppyProps<Dashboard.DashboardOptions>,
   ToUppyProps<Dashboard.DashboardOptions>,
   // Remove the modal-only props
   // Remove the modal-only props
   'animateOpenClose' | 'browserBackButtonClose' | 'inline' | 'onRequestCloseModal' | 'trigger'
   'animateOpenClose' | 'browserBackButtonClose' | 'inline' | 'onRequestCloseModal' | 'trigger'
->
+> & React.BaseHTMLAttributes<HTMLDivElement>
 
 
 export type DashboardProps = {
 export type DashboardProps = {
    [K in keyof DashboardPropsInner]: DashboardPropsInner[K]
    [K in keyof DashboardPropsInner]: DashboardPropsInner[K]

+ 9 - 2
packages/@uppy/react/src/Dashboard.js

@@ -1,6 +1,7 @@
 const React = require('react')
 const React = require('react')
 const DashboardPlugin = require('@uppy/dashboard')
 const DashboardPlugin = require('@uppy/dashboard')
 const basePropTypes = require('./propTypes').dashboard
 const basePropTypes = require('./propTypes').dashboard
+const getHTMLProps = require('./getHTMLProps')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -10,6 +11,11 @@ const h = React.createElement
  */
  */
 
 
 class Dashboard extends React.Component {
 class Dashboard extends React.Component {
+  constructor (props) {
+    super(props)
+    this.validProps = getHTMLProps(props)
+  }
+
   componentDidMount () {
   componentDidMount () {
     this.installPlugin()
     this.installPlugin()
   }
   }
@@ -26,7 +32,7 @@ class Dashboard extends React.Component {
   }
   }
 
 
   installPlugin () {
   installPlugin () {
-    const uppy = this.props.uppy
+    const { uppy } = this.props
     const options = {
     const options = {
       id: 'react:Dashboard',
       id: 'react:Dashboard',
       ...this.props,
       ...this.props,
@@ -39,7 +45,7 @@ class Dashboard extends React.Component {
   }
   }
 
 
   uninstallPlugin (props = this.props) {
   uninstallPlugin (props = this.props) {
-    const uppy = props.uppy
+    const { uppy } = props
 
 
     uppy.removePlugin(this.plugin)
     uppy.removePlugin(this.plugin)
   }
   }
@@ -49,6 +55,7 @@ class Dashboard extends React.Component {
       ref: (container) => {
       ref: (container) => {
         this.container = container
         this.container = container
       },
       },
+      ...this.validProps,
     })
     })
   }
   }
 }
 }

+ 1 - 1
packages/@uppy/react/src/DashboardModal.d.ts

@@ -9,7 +9,7 @@ type DashboardModalPropsInner = {
   ToUppyProps<Dashboard.DashboardOptions>,
   ToUppyProps<Dashboard.DashboardOptions>,
   // Remove the inline-only and force-overridden props
   // Remove the inline-only and force-overridden props
   'inline' | 'onRequestCloseModal'
   'inline' | 'onRequestCloseModal'
->
+> & React.BaseHTMLAttributes<HTMLDivElement>
 
 
 export type DashboardModalProps = {
 export type DashboardModalProps = {
   [K in keyof DashboardModalPropsInner]: DashboardModalPropsInner[K]
   [K in keyof DashboardModalPropsInner]: DashboardModalPropsInner[K]

+ 9 - 2
packages/@uppy/react/src/DashboardModal.js

@@ -2,6 +2,7 @@ const React = require('react')
 const PropTypes = require('prop-types')
 const PropTypes = require('prop-types')
 const DashboardPlugin = require('@uppy/dashboard')
 const DashboardPlugin = require('@uppy/dashboard')
 const basePropTypes = require('./propTypes').dashboard
 const basePropTypes = require('./propTypes').dashboard
+const getHTMLProps = require('./getHTMLProps')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -11,6 +12,11 @@ const h = React.createElement
  */
  */
 
 
 class DashboardModal extends React.Component {
 class DashboardModal extends React.Component {
+  constructor (props) {
+    super(props)
+    this.validProps = getHTMLProps(props)
+  }
+
   componentDidMount () {
   componentDidMount () {
     this.installPlugin()
     this.installPlugin()
   }
   }
@@ -32,7 +38,7 @@ class DashboardModal extends React.Component {
   }
   }
 
 
   installPlugin () {
   installPlugin () {
-    const uppy = this.props.uppy
+    const { uppy } = this.props
     const options = {
     const options = {
       id: 'react:DashboardModal',
       id: 'react:DashboardModal',
       ...this.props,
       ...this.props,
@@ -53,7 +59,7 @@ class DashboardModal extends React.Component {
   }
   }
 
 
   uninstallPlugin (props = this.props) {
   uninstallPlugin (props = this.props) {
-    const uppy = props.uppy
+    const { uppy } = props
 
 
     uppy.removePlugin(this.plugin)
     uppy.removePlugin(this.plugin)
   }
   }
@@ -63,6 +69,7 @@ class DashboardModal extends React.Component {
       ref: (container) => {
       ref: (container) => {
         this.container = container
         this.container = container
       },
       },
+      ...this.validProps,
     })
     })
   }
   }
 }
 }

+ 1 - 1
packages/@uppy/react/src/DragDrop.d.ts

@@ -1,7 +1,7 @@
 import { ToUppyProps } from './CommonTypes'
 import { ToUppyProps } from './CommonTypes'
 import DragDrop = require('@uppy/drag-drop')
 import DragDrop = require('@uppy/drag-drop')
 
 
-export type DragDropProps = ToUppyProps<DragDrop.DragDropOptions>
+export type DragDropProps = ToUppyProps<DragDrop.DragDropOptions>  & React.BaseHTMLAttributes<HTMLDivElement>
 
 
 /**
 /**
  * React component that renders an area in which files can be dropped to be
  * React component that renders an area in which files can be dropped to be

+ 9 - 2
packages/@uppy/react/src/DragDrop.js

@@ -1,6 +1,7 @@
 const React = require('react')
 const React = require('react')
 const DragDropPlugin = require('@uppy/drag-drop')
 const DragDropPlugin = require('@uppy/drag-drop')
 const propTypes = require('./propTypes')
 const propTypes = require('./propTypes')
+const getHTMLProps = require('./getHTMLProps')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -10,6 +11,11 @@ const h = React.createElement
  */
  */
 
 
 class DragDrop extends React.Component {
 class DragDrop extends React.Component {
+  constructor (props) {
+    super(props)
+    this.validProps = getHTMLProps(props)
+  }
+
   componentDidMount () {
   componentDidMount () {
     this.installPlugin()
     this.installPlugin()
   }
   }
@@ -26,7 +32,7 @@ class DragDrop extends React.Component {
   }
   }
 
 
   installPlugin () {
   installPlugin () {
-    const uppy = this.props.uppy
+    const { uppy } = this.props
     const options = {
     const options = {
       id: 'react:DragDrop',
       id: 'react:DragDrop',
       ...this.props,
       ...this.props,
@@ -40,7 +46,7 @@ class DragDrop extends React.Component {
   }
   }
 
 
   uninstallPlugin (props = this.props) {
   uninstallPlugin (props = this.props) {
-    const uppy = props.uppy
+    const { uppy } = props
 
 
     uppy.removePlugin(this.plugin)
     uppy.removePlugin(this.plugin)
   }
   }
@@ -50,6 +56,7 @@ class DragDrop extends React.Component {
       ref: (container) => {
       ref: (container) => {
         this.container = container
         this.container = container
       },
       },
+      ...this.validProps,
     })
     })
   }
   }
 }
 }

+ 1 - 1
packages/@uppy/react/src/ProgressBar.d.ts

@@ -1,7 +1,7 @@
 import { ToUppyProps } from './CommonTypes'
 import { ToUppyProps } from './CommonTypes'
 import ProgressBar = require('@uppy/progress-bar')
 import ProgressBar = require('@uppy/progress-bar')
 
 
-export type ProgressBarProps = ToUppyProps<ProgressBar.ProgressBarOptions>
+export type ProgressBarProps = ToUppyProps<ProgressBar.ProgressBarOptions> & React.BaseHTMLAttributes<HTMLDivElement>
 
 
 /**
 /**
  * React component that renders a progress bar at the top of the page.
  * React component that renders a progress bar at the top of the page.

+ 9 - 2
packages/@uppy/react/src/ProgressBar.js

@@ -2,6 +2,7 @@ const React = require('react')
 const PropTypes = require('prop-types')
 const PropTypes = require('prop-types')
 const ProgressBarPlugin = require('@uppy/progress-bar')
 const ProgressBarPlugin = require('@uppy/progress-bar')
 const uppyPropType = require('./propTypes').uppy
 const uppyPropType = require('./propTypes').uppy
+const getHTMLProps = require('./getHTMLProps')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -10,6 +11,11 @@ const h = React.createElement
  */
  */
 
 
 class ProgressBar extends React.Component {
 class ProgressBar extends React.Component {
+  constructor (props) {
+    super(props)
+    this.validProps = getHTMLProps(props)
+  }
+
   componentDidMount () {
   componentDidMount () {
     this.installPlugin()
     this.installPlugin()
   }
   }
@@ -26,7 +32,7 @@ class ProgressBar extends React.Component {
   }
   }
 
 
   installPlugin () {
   installPlugin () {
-    const uppy = this.props.uppy
+    const { uppy } = this.props
     const options = {
     const options = {
       id: 'react:ProgressBar',
       id: 'react:ProgressBar',
       ...this.props,
       ...this.props,
@@ -40,7 +46,7 @@ class ProgressBar extends React.Component {
   }
   }
 
 
   uninstallPlugin (props = this.props) {
   uninstallPlugin (props = this.props) {
-    const uppy = props.uppy
+    const { uppy } = props
 
 
     uppy.removePlugin(this.plugin)
     uppy.removePlugin(this.plugin)
   }
   }
@@ -50,6 +56,7 @@ class ProgressBar extends React.Component {
       ref: (container) => {
       ref: (container) => {
         this.container = container
         this.container = container
       },
       },
+      ...this.validProps,
     })
     })
   }
   }
 }
 }

+ 1 - 1
packages/@uppy/react/src/StatusBar.d.ts

@@ -1,7 +1,7 @@
 import { ToUppyProps } from './CommonTypes'
 import { ToUppyProps } from './CommonTypes'
 import StatusBar = require('@uppy/status-bar')
 import StatusBar = require('@uppy/status-bar')
 
 
-export type StatusBarProps = ToUppyProps<StatusBar.StatusBarOptions>
+export type StatusBarProps = ToUppyProps<StatusBar.StatusBarOptions>  & React.BaseHTMLAttributes<HTMLDivElement>
 
 
 /**
 /**
  * React component that renders a status bar containing upload progress and speed,
  * React component that renders a status bar containing upload progress and speed,

+ 9 - 2
packages/@uppy/react/src/StatusBar.js

@@ -2,6 +2,7 @@ const React = require('react')
 const PropTypes = require('prop-types')
 const PropTypes = require('prop-types')
 const StatusBarPlugin = require('@uppy/status-bar')
 const StatusBarPlugin = require('@uppy/status-bar')
 const uppyPropType = require('./propTypes').uppy
 const uppyPropType = require('./propTypes').uppy
+const getHTMLProps = require('./getHTMLProps')
 
 
 const h = React.createElement
 const h = React.createElement
 
 
@@ -11,6 +12,11 @@ const h = React.createElement
  */
  */
 
 
 class StatusBar extends React.Component {
 class StatusBar extends React.Component {
+  constructor (props) {
+    super(props)
+    this.validProps = getHTMLProps(props)
+  }
+
   componentDidMount () {
   componentDidMount () {
     this.installPlugin()
     this.installPlugin()
   }
   }
@@ -27,7 +33,7 @@ class StatusBar extends React.Component {
   }
   }
 
 
   installPlugin () {
   installPlugin () {
-    const uppy = this.props.uppy
+    const { uppy } = this.props
     const options = {
     const options = {
       id: 'react:StatusBar',
       id: 'react:StatusBar',
       ...this.props,
       ...this.props,
@@ -41,7 +47,7 @@ class StatusBar extends React.Component {
   }
   }
 
 
   uninstallPlugin (props = this.props) {
   uninstallPlugin (props = this.props) {
-    const uppy = props.uppy
+    const { uppy } = props
 
 
     uppy.removePlugin(this.plugin)
     uppy.removePlugin(this.plugin)
   }
   }
@@ -51,6 +57,7 @@ class StatusBar extends React.Component {
       ref: (container) => {
       ref: (container) => {
         this.container = container
         this.container = container
       },
       },
+      ...this.validProps,
     })
     })
   }
   }
 }
 }

+ 164 - 0
packages/@uppy/react/src/getHTMLProps.js

@@ -0,0 +1,164 @@
+// Attributes in the format of htmlAtrribute: reactAttribute
+const possibleStandardNames = [
+  'accept',
+  'acceptCharset',
+  'acceptCharset',
+  'accessKey',
+  'action',
+  'allowFullScreen',
+  'alt',
+  'as',
+  'async',
+  'autoCapitalize',
+  'autoComplete',
+  'autoCorrect',
+  'autoFocus',
+  'autoPlay',
+  'autoSave',
+  'capture',
+  'cellPadding',
+  'cellSpacing',
+  'challenge',
+  'charSet',
+  'checked',
+  'children',
+  'cite',
+  'className',
+  'classID',
+  'className',
+  'cols',
+  'colSpan',
+  'content',
+  'contentEditable',
+  'contextMenu',
+  'controls',
+  'controlsList',
+  'coords',
+  'crossOrigin',
+  'dangerouslySetInnerHTML',
+  'data',
+  'dateTime',
+  'default',
+  'defaultChecked',
+  'defaultValue',
+  'defer',
+  'dir',
+  'disabled',
+  'disablePictureInPicture',
+  'disableRemotePlayback',
+  'download',
+  'draggable',
+  'encType',
+  'enterKeyHint',
+  'htmlFor',
+  'form',
+  'formMethod',
+  'formAction',
+  'formEncType',
+  'formNoValidate',
+  'formTarget',
+  'frameBorder',
+  'headers',
+  // 'height',
+  'hidden',
+  'high',
+  'href',
+  'hrefLang',
+  'htmlFor',
+  'httpEquiv',
+  'httpEquiv',
+  'icon',
+  'id',
+  'innerHTML',
+  'inputMode',
+  'integrity',
+  'is',
+  'itemID',
+  'itemProp',
+  'itemRef',
+  'itemScope',
+  'itemType',
+  'keyParams',
+  'keyType',
+  'kind',
+  'label',
+  'lang',
+  'list',
+  'loop',
+  'low',
+  'manifest',
+  'marginWidth',
+  'marginHeight',
+  'max',
+  'maxLength',
+  'media',
+  'mediaGroup',
+  'method',
+  'min',
+  'minLength',
+  'multiple',
+  'muted',
+  'name',
+  'noModule',
+  'nonce',
+  'noValidate',
+  'open',
+  'optimum',
+  'pattern',
+  'placeholder',
+  'playsInline',
+  'poster',
+  'preload',
+  'profile',
+  'radioGroup',
+  'readOnly',
+  'referrerPolicy',
+  'rel',
+  'required',
+  'reversed',
+  'role',
+  'rows',
+  'rowSpan',
+  'sandbox',
+  'scope',
+  'scoped',
+  'scrolling',
+  'seamless',
+  'selected',
+  'shape',
+  'size',
+  'sizes',
+  'span',
+  'spellCheck',
+  'src',
+  'srcDoc',
+  'srcLang',
+  'srcSet',
+  'start',
+  'step',
+  'style',
+  'summary',
+  'tabIndex',
+  // 'target',
+  'title',
+  'type',
+  'useMap',
+  'value',
+  // 'width',
+  'wmode',
+  'wrap',
+]
+
+// A decent polyfill for Object.entries for good browser support
+const getEntries = (object) => {
+  // eslint-disable-next-line compat/compat
+  return Object.entries ? Object.entries(object) : Object.keys(object).map(key => [key, object[key]])
+}
+
+const getHTMLProps = (props) => {
+  // Gets all the React props
+  const reducer = (acc, [key, value]) => (possibleStandardNames.includes(key) ? { ...acc, [key]: value } : acc)
+  return getEntries(props).reduce(reducer, {})
+}
+
+module.exports = getHTMLProps