Skip to content

Commit

Permalink
Dashboard: disallow clicking on buttons and links in Dashboard disabl…
Browse files Browse the repository at this point in the history
…ed mode (#4292)

* Disallow clicking on buttons and links in Dashboard disabled mode

* Add disabled attr to all interactive elements instead of tabindex=-1

* Test and apply Antoine’s suggestions

* Account for links, use aria-disabled and pointer-events

* Rename disableAllFocusableElements --> disableInteractiveElements

* Add role="button" just in case for the future
  • Loading branch information
arturi authored Feb 11, 2023
1 parent 7c9e166 commit a602f1e
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 22 deletions.
51 changes: 30 additions & 21 deletions packages/@uppy/dashboard/src/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import toArray from '@uppy/utils/lib/toArray'
import getDroppedFiles from '@uppy/utils/lib/getDroppedFiles'
import { nanoid } from 'nanoid/non-secure'
import memoizeOne from 'memoize-one'
import FOCUSABLE_ELEMENTS from '@uppy/utils/lib/FOCUSABLE_ELEMENTS'
import * as trapFocus from './utils/trapFocus.js'
import createSuperFocus from './utils/createSuperFocus.js'
import DashboardUI from './components/Dashboard.jsx'
Expand Down Expand Up @@ -44,6 +43,8 @@ function defaultPickerIcon () {
export default class Dashboard extends UIPlugin {
static VERSION = packageJson.version

#disabledNodes = null

constructor (uppy, opts) {
super(uppy, opts)
this.id = this.opts.id || 'Dashboard'
Expand Down Expand Up @@ -456,26 +457,34 @@ export default class Dashboard extends UIPlugin {
}
}

disableAllFocusableElements = (disable) => {
const focusableNodes = toArray(this.el.querySelectorAll(FOCUSABLE_ELEMENTS))
disableInteractiveElements = (disable) => {
const NODES_TO_DISABLE = [
'a[href]',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'button:not([disabled])',
'[role="button"]:not([disabled])',
]

const nodesToDisable = this.#disabledNodes ?? toArray(this.el.querySelectorAll(NODES_TO_DISABLE))
.filter(node => !node.classList.contains('uppy-Dashboard-close'))

for (const node of nodesToDisable) {
// Links can’t have `disabled` attr, so we use `aria-disabled` for a11y
if (node.tagName === 'A') {
node.setAttribute('aria-disabled', disable)
} else {
node.disabled = disable
}
}

if (disable) {
focusableNodes.forEach((node) => {
// save previous tabindex in a data-attribute, to restore when enabling
const currentTabIndex = node.getAttribute('tabindex')
if (currentTabIndex) {
node.dataset.inertTabindex = currentTabIndex // eslint-disable-line no-param-reassign
}
node.setAttribute('tabindex', '-1')
})
this.#disabledNodes = nodesToDisable
} else {
focusableNodes.forEach((node) => {
if ('inertTabindex' in node.dataset) {
node.setAttribute('tabindex', node.dataset.inertTabindex)
} else {
node.removeAttribute('tabindex')
}
})
this.#disabledNodes = null
}

this.dashboardIsDisabled = disable
}

Expand Down Expand Up @@ -831,12 +840,12 @@ export default class Dashboard extends UIPlugin {

afterUpdate = () => {
if (this.opts.disabled && !this.dashboardIsDisabled) {
this.disableAllFocusableElements(true)
this.disableInteractiveElements(true)
return
}

if (!this.opts.disabled && this.dashboardIsDisabled) {
this.disableAllFocusableElements(false)
this.disableInteractiveElements(false)
}

this.superFocusOnEachUpdate()
Expand Down Expand Up @@ -944,7 +953,7 @@ export default class Dashboard extends UIPlugin {
activePickerPanel: pluginState.activePickerPanel,
showFileEditor: pluginState.showFileEditor,
saveFileEditor: this.saveFileEditor,
disableAllFocusableElements: this.disableAllFocusableElements,
disableInteractiveElements: this.disableInteractiveElements,
animateOpenClose: this.opts.animateOpenClose,
isClosing: pluginState.isClosing,
progressindicators,
Expand Down
10 changes: 9 additions & 1 deletion packages/@uppy/dashboard/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,22 @@
opacity: 0.6;
filter: grayscale(100%);
user-select: none;
pointer-events: none;
cursor: not-allowed;
}
}

.uppy-Dashboard--isDisabled .uppy-ProviderIconBg {
fill: #9f9f9f;
}

// Disallow clicking on all interactive elements
.uppy-Dashboard--isDisabled {
[disabled], [aria-disabled] {
pointer-events: none;
cursor: not-allowed;
}
}

.uppy-Dashboard--modal .uppy-Dashboard-inner {
position: fixed;
top: 35px;
Expand Down

0 comments on commit a602f1e

Please sign in to comment.