Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dashboard: disallow clicking on buttons and links in Dashboard disabled mode #4292

Merged
merged 8 commits into from
Feb 11, 2023
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 @@ -459,26 +460,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 @@ -834,12 +843,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 @@ -947,7 +956,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