Skip to content

Commit

Permalink
@uppy/dashboard: auto discover and install plugins without target (#4343
Browse files Browse the repository at this point in the history
)

* Dashboard: auto discover plugins no matter when they were installed

* Remove target from RemoteSoruces, making it compatible with @uppy/react

* Update packages/@uppy/dashboard/src/Dashboard.jsx

Co-authored-by: Mikael Finstad <finstaden@gmail.com>

* Removed comments, added comments

* better comment

* Change type — otherwise gets listed on Dashboard sources

* Add RemoteSources to React test

* Add RemoteSources to React example

* Add tests

* Refactor for less iteration for each plugin, rename functions

* Prevent error when opts are undefined

* remove console.logs

* prettier

---------

Co-authored-by: Mikael Finstad <finstaden@gmail.com>
  • Loading branch information
arturi and mifi authored Oct 12, 2023
1 parent 933d9df commit 8b25208
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 24 deletions.
7 changes: 6 additions & 1 deletion e2e/clients/react/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ import Uppy from '@uppy/core'
import React, { useState } from 'react'
import { Dashboard, DashboardModal, DragDrop } from '@uppy/react'
import ThumbnailGenerator from '@uppy/thumbnail-generator'
import RemoteSources from '@uppy/remote-sources'

import '@uppy/core/dist/style.css'
import '@uppy/dashboard/dist/style.css'
import '@uppy/drag-drop/dist/style.css'

export default function App () {
const uppyDashboard = new Uppy({ id: 'dashboard' })
const RemoteSourcesOptions = {
companionUrl: 'http://companion.uppy.io',
sources: ['GoogleDrive', 'OneDrive', 'Unsplash', 'Zoom', 'Url'],
}
const uppyDashboard = new Uppy({ id: 'dashboard' }).use(RemoteSources, { ...RemoteSourcesOptions })
const uppyModal = new Uppy({ id: 'modal' })
const uppyDragDrop = new Uppy({ id: 'drag-drop' }).use(ThumbnailGenerator)
const [open, setOpen] = useState(false)
Expand Down
16 changes: 16 additions & 0 deletions e2e/cypress/integration/react.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ describe('@uppy/react', () => {
.each((element) => expect(element).attr('src').to.include('blob:'))
})

it('should render Dashboard with Remote Sources plugin pack', () => {
const sources = [
'My Device',
'Google Drive',
'OneDrive',
'Unsplash',
'Zoom',
'Link',
]
cy.get('#dashboard .uppy-DashboardTab-name').each((item, index, list) => {
expect(list).to.have.length(6)
// Returns the current element from the loop
expect(Cypress.$(item).text()).to.eq(sources[index])
})
})

it('should render Modal in React and show thumbnails', () => {
cy.get('#open').click()
cy.get('@modal-input').selectFile(
Expand Down
8 changes: 6 additions & 2 deletions examples/react-example/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import React from'react'
import Uppy from'@uppy/core'
import Tus from'@uppy/tus'
import GoogleDrive from'@uppy/google-drive'
import GoogleDrive from '@uppy/google-drive'
import Webcam from '@uppy/webcam'
import RemoteSources from '@uppy/remote-sources'
import { Dashboard, DashboardModal, DragDrop, ProgressBar, FileInput } from'@uppy/react'

import '@uppy/core/dist/style.css'
Expand All @@ -22,7 +24,9 @@ export default class App extends React.Component {

this.uppy = new Uppy({ id: 'uppy1', autoProceed: true, debug: true })
.use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' })
.use(GoogleDrive, { companionUrl: 'https://companion.uppy.io' })
.use(Webcam)
.use(RemoteSources, { companionUrl: 'https://companion.uppy.io', sources: ['GoogleDrive', 'Box', 'Dropbox', 'Facebook', 'Instagram', 'OneDrive', 'Unsplash', 'Zoom', 'Url'],
})

this.uppy2 = new Uppy({ id: 'uppy2', autoProceed: false, debug: true })
.use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' })
Expand Down
2 changes: 2 additions & 0 deletions packages/@uppy/core/src/Uppy.js
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,8 @@ class Uppy {
}
plugin.install()

this.emit('plugin-added', plugin)

return this
}

Expand Down
2 changes: 2 additions & 0 deletions packages/@uppy/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
"devDependencies": {
"@uppy/google-drive": "workspace:^",
"@uppy/status-bar": "workspace:^",
"@uppy/url": "workspace:^",
"@uppy/webcam": "workspace:^",
"resize-observer-polyfill": "^1.5.0",
"vitest": "^0.34.5"
},
Expand Down
47 changes: 32 additions & 15 deletions packages/@uppy/dashboard/src/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ export default class Dashboard extends UIPlugin {
this.startListeningToResize()
document.addEventListener('paste', this.handlePasteOnBody)

this.uppy.on('plugin-added', this.#addSupportedPluginIfNoTarget)
this.uppy.on('plugin-remove', this.removeTarget)
this.uppy.on('file-added', this.hideAllPanels)
this.uppy.on('dashboard:modal-closed', this.hideAllPanels)
Expand Down Expand Up @@ -780,8 +781,9 @@ export default class Dashboard extends UIPlugin {

this.stopListeningToResize()
document.removeEventListener('paste', this.handlePasteOnBody)

window.removeEventListener('popstate', this.handlePopState, false)

this.uppy.off('plugin-added', this.#addSupportedPluginIfNoTarget)
this.uppy.off('plugin-remove', this.removeTarget)
this.uppy.off('file-added', this.hideAllPanels)
this.uppy.off('dashboard:modal-closed', this.hideAllPanels)
Expand Down Expand Up @@ -1015,14 +1017,37 @@ export default class Dashboard extends UIPlugin {
})
}

discoverProviderPlugins = () => {
this.uppy.iteratePlugins((plugin) => {
if (plugin && !plugin.target && plugin.opts && plugin.opts.target === this.constructor) {
this.addTarget(plugin)
#addSpecifiedPluginsFromOptions = () => {
const plugins = this.opts.plugins || []

plugins.forEach((pluginID) => {
const plugin = this.uppy.getPlugin(pluginID)
if (plugin) {
plugin.mount(this, plugin)
} else {
this.uppy.log(`[Uppy] Dashboard could not find plugin '${pluginID}', make sure to uppy.use() the plugins you are specifying`, 'warning')
}
})
}

#autoDiscoverPlugins = () => {
this.uppy.iteratePlugins(this.#addSupportedPluginIfNoTarget)
}

#addSupportedPluginIfNoTarget = (plugin) => {
// Only these types belong on the Dashboard,
// we wouldn’t want to try and mount Compressor or Tus, for example.
const typesAllowed = ['acquirer', 'editor']
if (plugin && !plugin.opts?.target && typesAllowed.includes(plugin.type)) {
const pluginAlreadyAdded = this.getPluginState().targets.some(
installedPlugin => plugin.id === installedPlugin.id,
)
if (!pluginAlreadyAdded) {
plugin.mount(this, plugin)
}
}
}

install = () => {
// Set default state for Dashboard
this.setPluginState({
Expand Down Expand Up @@ -1055,15 +1080,6 @@ export default class Dashboard extends UIPlugin {
this.mount(target, this)
}

const plugins = this.opts.plugins || []

plugins.forEach((pluginID) => {
const plugin = this.uppy.getPlugin(pluginID)
if (plugin) {
plugin.mount(this, plugin)
}
})

if (!this.opts.disableStatusBar) {
this.uppy.use(StatusBar, {
id: `${this.id}:StatusBar`,
Expand Down Expand Up @@ -1111,7 +1127,8 @@ export default class Dashboard extends UIPlugin {
this.darkModeMediaQuery.addListener(this.handleSystemDarkModeChange)
}

this.discoverProviderPlugins()
this.#addSpecifiedPluginsFromOptions()
this.#autoDiscoverPlugins()
this.initEvents()
}

Expand Down
36 changes: 36 additions & 0 deletions packages/@uppy/dashboard/src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { afterAll, beforeAll, describe, it, expect } from 'vitest'
import Core from '@uppy/core'
import StatusBarPlugin from '@uppy/status-bar'
import GoogleDrivePlugin from '@uppy/google-drive'
import WebcamPlugin from '@uppy/webcam'
import Url from '@uppy/url'

import resizeObserverPolyfill from 'resize-observer-polyfill'
import DashboardPlugin from '../lib/index.js'

Expand Down Expand Up @@ -66,6 +69,39 @@ describe('Dashboard', () => {
core.close()
})

it('should automatically add plugins which have no target', () => {
const core = new Core()
core.use(Url, { companionUrl: 'https://companion.uppy.io' })
core.use(DashboardPlugin, { inline: false })
core.use(WebcamPlugin)

const dashboardPlugins = core.getState().plugins['Dashboard'].targets

// two built-in plugins + these ones below
expect(dashboardPlugins.length).toEqual(4)
expect(dashboardPlugins.some((plugin) => plugin.id === 'Url')).toEqual(true)
expect(dashboardPlugins.some((plugin) => plugin.id === 'Webcam')).toEqual(true)

core.close()
})

it('should not automatically add plugins which have a non-Dashboard target', () => {
const core = new Core()
WebcamPlugin.prototype.start = () => {}
core.use(Url, { companionUrl: 'https://companion.uppy.io' })
core.use(DashboardPlugin, { inline: false })
core.use(WebcamPlugin, { target: 'body' })

const dashboardPlugins = core.getState().plugins['Dashboard'].targets

// two built-in plugins + these ones below
expect(dashboardPlugins.length).toEqual(3)
expect(dashboardPlugins.some((plugin) => plugin.id === 'Url')).toEqual(true)
expect(dashboardPlugins.some((plugin) => plugin.id === 'Webcam')).toEqual(false)

core.close()
})

it('should change options on the fly', () => {
const core = new Core()
core.use(DashboardPlugin, {
Expand Down
4 changes: 2 additions & 2 deletions packages/@uppy/image-editor/src/ImageEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ export default class ImageEditor extends UIPlugin {
...opts,
actions: {
...defaultActions,
...opts.actions,
...opts?.actions,
},
cropperOptions: {
...defaultCropperOptions,
...opts.cropperOptions,
...opts?.cropperOptions,
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ export default class ProviderView extends View {
// and that will allow the user to start the upload, so we need to make sure we have
// finished all async operations before we add any file
// see https://github.com/transloadit/uppy/pull/4384
this.plugin.uppy.log('Adding remote provider files')
this.plugin.uppy.log('Adding files from a remote provider')
this.plugin.uppy.addFiles(newFiles.map((file) => this.getTagFile(file)))

this.plugin.setPluginState({ filterInput: '' })
Expand Down
4 changes: 1 addition & 3 deletions packages/@uppy/remote-sources/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { BasePlugin } from '@uppy/core'
import Dashboard from '@uppy/dashboard'
import Dropbox from '@uppy/dropbox'
import GoogleDrive from '@uppy/google-drive'
import Instagram from '@uppy/instagram'
Expand Down Expand Up @@ -34,11 +33,10 @@ export default class RemoteSources extends BasePlugin {
constructor (uppy, opts) {
super(uppy, opts)
this.id = this.opts.id || 'RemoteSources'
this.type = 'acquirer'
this.type = 'preset'

const defaultOptions = {
sources: Object.keys(availablePlugins),
target: Dashboard,
}
this.opts = { ...defaultOptions, ...opts }

Expand Down
2 changes: 2 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9867,7 +9867,9 @@ __metadata:
"@uppy/provider-views": "workspace:^"
"@uppy/status-bar": "workspace:^"
"@uppy/thumbnail-generator": "workspace:^"
"@uppy/url": "workspace:^"
"@uppy/utils": "workspace:^"
"@uppy/webcam": "workspace:^"
classnames: ^2.2.6
is-shallow-equal: ^1.0.1
lodash: ^4.17.21
Expand Down

0 comments on commit 8b25208

Please sign in to comment.