瀏覽代碼

@uppy/react: propagate prop mutation (#3208)

* @uppy/react: propagate prop mutation

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

* fixup! @uppy/react: propagate prop mutation

* fixup! @uppy/react: propagate prop mutation

* Apply changes to other Components

* Update TODOs
Antoine du Hamel 3 年之前
父節點
當前提交
6b68876c68

+ 7 - 5
packages/@uppy/react/src/Dashboard.js

@@ -2,6 +2,7 @@ const React = require('react')
 const DashboardPlugin = require('@uppy/dashboard')
 const basePropTypes = require('./propTypes').dashboard
 const getHTMLProps = require('./getHTMLProps')
+const nonHtmlPropsHaveChanged = require('./nonHtmlPropsHaveChanged')
 
 const h = React.createElement
 
@@ -11,11 +12,6 @@ const h = React.createElement
  */
 
 class Dashboard extends React.Component {
-  constructor (props) {
-    super(props)
-    this.validProps = getHTMLProps(props)
-  }
-
   componentDidMount () {
     this.installPlugin()
   }
@@ -24,6 +20,10 @@ class Dashboard extends React.Component {
     if (prevProps.uppy !== this.props.uppy) {
       this.uninstallPlugin(prevProps)
       this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this, prevProps)) {
+      const options = { ...this.props, target: this.container }
+      delete options.uppy
+      this.plugin.setOptions(options)
     }
   }
 
@@ -51,6 +51,8 @@ class Dashboard extends React.Component {
   }
 
   render () {
+    // TODO: stop exposing `validProps` as a public property and rename it to `htmlProps`
+    this.validProps = getHTMLProps(this.props)
     return h('div', {
       ref: (container) => {
         this.container = container

+ 18 - 0
packages/@uppy/react/src/Dashboard.test.js

@@ -32,4 +32,22 @@ describe('react <Dashboard />', () => {
     expect(oninstall).toHaveBeenCalled()
     expect(onuninstall).toHaveBeenCalled()
   })
+
+  it('react on HTMLDivElement props update', async () => {
+    const uppy = new Uppy()
+    const dash = mount((
+      <Dashboard
+        uppy={uppy}
+        hidden
+      />
+    ))
+
+    expect(dash.getDOMNode().hidden).toBeTruthy()
+
+    dash.setProps({ hidden: false })
+
+    expect(dash.getDOMNode().hidden).toBeFalsy()
+
+    dash.unmount()
+  })
 })

+ 7 - 5
packages/@uppy/react/src/DashboardModal.js

@@ -3,6 +3,7 @@ const PropTypes = require('prop-types')
 const DashboardPlugin = require('@uppy/dashboard')
 const basePropTypes = require('./propTypes').dashboard
 const getHTMLProps = require('./getHTMLProps')
+const nonHtmlPropsHaveChanged = require('./nonHtmlPropsHaveChanged')
 
 const h = React.createElement
 
@@ -12,11 +13,6 @@ const h = React.createElement
  */
 
 class DashboardModal extends React.Component {
-  constructor (props) {
-    super(props)
-    this.validProps = getHTMLProps(props)
-  }
-
   componentDidMount () {
     this.installPlugin()
   }
@@ -25,6 +21,10 @@ class DashboardModal extends React.Component {
     if (prevProps.uppy !== this.props.uppy) {
       this.uninstallPlugin(prevProps)
       this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this, prevProps)) {
+      const options = { ...this.props, onRequestCloseModal: this.props.onRequestClose }
+      delete options.uppy
+      this.plugin.setOptions(options)
     }
     if (prevProps.open && !this.props.open) {
       this.plugin.closeModal()
@@ -65,6 +65,8 @@ class DashboardModal extends React.Component {
   }
 
   render () {
+    // TODO: stop exposing `validProps` as a public property and rename it to `htmlProps`
+    this.validProps = getHTMLProps(this.props)
     return h('div', {
       ref: (container) => {
         this.container = container

+ 37 - 0
packages/@uppy/react/src/DashboardModal.test.js

@@ -78,4 +78,41 @@ describe('react <DashboardModal />', () => {
 
     dash.unmount()
   })
+
+  it('react on HTMLDivElement props update', async () => {
+    const uppy = new Uppy()
+    const dash = mount((
+      <DashboardModal
+        uppy={uppy}
+        hidden
+      />
+    ))
+
+    expect(dash.getDOMNode().hidden).toBeTruthy()
+
+    dash.setProps({ hidden: false })
+
+    expect(dash.getDOMNode().hidden).toBeFalsy()
+
+    dash.unmount()
+  })
+
+  it('react on @uppy/dashboard props update', async () => {
+    const uppy = new Uppy()
+    const dash = mount((
+      <DashboardModal
+        uppy={uppy}
+        theme="dark"
+      />
+    ))
+
+    const { plugin } = dash.instance()
+
+    expect(plugin.opts.theme).toBe('dark')
+
+    dash.setProps({ theme: 'light' })
+    expect(plugin.opts.theme).toBe('light')
+
+    dash.unmount()
+  })
 })

+ 7 - 5
packages/@uppy/react/src/DragDrop.js

@@ -2,6 +2,7 @@ const React = require('react')
 const DragDropPlugin = require('@uppy/drag-drop')
 const propTypes = require('./propTypes')
 const getHTMLProps = require('./getHTMLProps')
+const nonHtmlPropsHaveChanged = require('./nonHtmlPropsHaveChanged')
 
 const h = React.createElement
 
@@ -11,11 +12,6 @@ const h = React.createElement
  */
 
 class DragDrop extends React.Component {
-  constructor (props) {
-    super(props)
-    this.validProps = getHTMLProps(props)
-  }
-
   componentDidMount () {
     this.installPlugin()
   }
@@ -24,6 +20,10 @@ class DragDrop extends React.Component {
     if (prevProps.uppy !== this.props.uppy) {
       this.uninstallPlugin(prevProps)
       this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this, prevProps)) {
+      const options = { ...this.props, target: this.container }
+      delete options.uppy
+      this.plugin.setOptions(options)
     }
   }
 
@@ -52,6 +52,8 @@ class DragDrop extends React.Component {
   }
 
   render () {
+    // TODO: stop exposing `validProps` as a public property and rename it to `htmlProps`
+    this.validProps = getHTMLProps(this.props)
     return h('div', {
       ref: (container) => {
         this.container = container

+ 18 - 0
packages/@uppy/react/src/DragDrop.test.js

@@ -32,4 +32,22 @@ describe('react <DragDrop />', () => {
     expect(oninstall).toHaveBeenCalled()
     expect(onuninstall).toHaveBeenCalled()
   })
+
+  it('react on HTMLDivElement props update', async () => {
+    const uppy = new Uppy()
+    const dash = mount((
+      <DragDrop
+        uppy={uppy}
+        hidden
+      />
+    ))
+
+    expect(dash.getDOMNode().hidden).toBeTruthy()
+
+    dash.setProps({ hidden: false })
+
+    expect(dash.getDOMNode().hidden).toBeFalsy()
+
+    dash.unmount()
+  })
 })

+ 7 - 5
packages/@uppy/react/src/ProgressBar.js

@@ -3,6 +3,7 @@ const PropTypes = require('prop-types')
 const ProgressBarPlugin = require('@uppy/progress-bar')
 const uppyPropType = require('./propTypes').uppy
 const getHTMLProps = require('./getHTMLProps')
+const nonHtmlPropsHaveChanged = require('./nonHtmlPropsHaveChanged')
 
 const h = React.createElement
 
@@ -11,11 +12,6 @@ const h = React.createElement
  */
 
 class ProgressBar extends React.Component {
-  constructor (props) {
-    super(props)
-    this.validProps = getHTMLProps(props)
-  }
-
   componentDidMount () {
     this.installPlugin()
   }
@@ -24,6 +20,10 @@ class ProgressBar extends React.Component {
     if (prevProps.uppy !== this.props.uppy) {
       this.uninstallPlugin(prevProps)
       this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this, prevProps)) {
+      const options = { ...this.props, target: this.container }
+      delete options.uppy
+      this.plugin.setOptions(options)
     }
   }
 
@@ -52,6 +52,8 @@ class ProgressBar extends React.Component {
   }
 
   render () {
+    // TODO: stop exposing `validProps` as a public property and rename it to `htmlProps`
+    this.validProps = getHTMLProps(this.props)
     return h('div', {
       ref: (container) => {
         this.container = container

+ 20 - 0
packages/@uppy/react/src/ProgressBar.test.js

@@ -32,4 +32,24 @@ describe('react <ProgressBar />', () => {
     expect(oninstall).toHaveBeenCalled()
     expect(onuninstall).toHaveBeenCalled()
   })
+
+  it('react on HTMLDivElement props update', async () => {
+    const uppy = new Uppy()
+    const dash = mount((
+      <ProgressBar
+        uppy={uppy}
+        onInstall={Function.prototype}
+        onUninstall={Function.prototype}
+        hidden
+      />
+    ))
+
+    expect(dash.getDOMNode().hidden).toBeTruthy()
+
+    dash.setProps({ hidden: false })
+
+    expect(dash.getDOMNode().hidden).toBeFalsy()
+
+    dash.unmount()
+  })
 })

+ 7 - 5
packages/@uppy/react/src/StatusBar.js

@@ -3,6 +3,7 @@ const PropTypes = require('prop-types')
 const StatusBarPlugin = require('@uppy/status-bar')
 const uppyPropType = require('./propTypes').uppy
 const getHTMLProps = require('./getHTMLProps')
+const nonHtmlPropsHaveChanged = require('./nonHtmlPropsHaveChanged')
 
 const h = React.createElement
 
@@ -12,11 +13,6 @@ const h = React.createElement
  */
 
 class StatusBar extends React.Component {
-  constructor (props) {
-    super(props)
-    this.validProps = getHTMLProps(props)
-  }
-
   componentDidMount () {
     this.installPlugin()
   }
@@ -25,6 +21,10 @@ class StatusBar extends React.Component {
     if (prevProps.uppy !== this.props.uppy) {
       this.uninstallPlugin(prevProps)
       this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this, prevProps)) {
+      const options = { ...this.props, target: this.container }
+      delete options.uppy
+      this.plugin.setOptions(options)
     }
   }
 
@@ -53,6 +53,8 @@ class StatusBar extends React.Component {
   }
 
   render () {
+    // TODO: stop exposing `validProps` as a public property and rename it to `htmlProps`
+    this.validProps = getHTMLProps(this.props)
     return h('div', {
       ref: (container) => {
         this.container = container

+ 20 - 0
packages/@uppy/react/src/StatusBar.test.js

@@ -32,4 +32,24 @@ describe('react <StatusBar />', () => {
     expect(oninstall).toHaveBeenCalled()
     expect(onuninstall).toHaveBeenCalled()
   })
+
+  it('react on HTMLDivElement props update', async () => {
+    const uppy = new Uppy()
+    const dash = mount((
+      <StatusBar
+        uppy={uppy}
+        onInstall={Function.prototype}
+        onUninstall={Function.prototype}
+        hidden
+      />
+    ))
+
+    expect(dash.getDOMNode().hidden).toBeTruthy()
+
+    dash.setProps({ hidden: false })
+
+    expect(dash.getDOMNode().hidden).toBeFalsy()
+
+    dash.unmount()
+  })
 })

+ 10 - 0
packages/@uppy/react/src/nonHtmlPropsHaveChanged.js

@@ -0,0 +1,10 @@
+'use strict'
+
+// TODO: replace with `Object.hasOwn` when dropping support for older browsers.
+const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key)
+
+module.exports = function nonHtmlPropsHaveChanged (component, prevProps) {
+  return Object.keys(component.props)
+    // TODO: replace `validProps` with an exported `Symbol('htmlProps')`.
+    .some(key => !hasOwn(component.validProps, key) && component.props[key] !== prevProps[key])
+}