diff --git a/packages/dnb-ui-lib/src/components/modal/ModalContent.js b/packages/dnb-ui-lib/src/components/modal/ModalContent.js
index 21d168fefd9..ac84f987cba 100644
--- a/packages/dnb-ui-lib/src/components/modal/ModalContent.js
+++ b/packages/dnb-ui-lib/src/components/modal/ModalContent.js
@@ -27,8 +27,9 @@ import Context from '../../shared/Context'
export default class ModalContent extends React.PureComponent {
static propTypes = {
modal_content: PropTypes.node.isRequired,
- hide: PropTypes.bool,
mode: PropTypes.string,
+ hide: PropTypes.bool,
+ root_id: PropTypes.string,
labelled_by: PropTypes.string,
content_id: PropTypes.string,
title: PropTypes.node,
@@ -73,6 +74,7 @@ export default class ModalContent extends React.PureComponent {
static defaultProps = {
mode: null,
hide: null,
+ root_id: null,
labelled_by: null,
content_id: null,
title: null,
@@ -102,9 +104,11 @@ export default class ModalContent extends React.PureComponent {
super(props)
this._contentRef = React.createRef()
this._id = makeUniqueId()
- this._ii = new InteractionInvalidation().setBypassSelector(
- '.dnb-modal__content'
- )
+ this._ii = new InteractionInvalidation()
+ this._ii.setBypassSelector([
+ '.dnb-modal__content',
+ `#dnb-modal-${props.root_id || 'root'}`
+ ])
}
componentDidMount() {
@@ -126,7 +130,7 @@ export default class ModalContent extends React.PureComponent {
try {
this._contentRef.current.focus() // in case the button is disabled
const focusElement = this._contentRef.current.querySelector(
- '.dnb-h--xx-large:first-of-type, .dnb-h--large:first-of-type, .dnb-modal__close-button'
+ 'h1:first-of-type, h2:first-of-type, .dnb-modal__close-button'
)
if (focusElement) {
focusElement.focus()
diff --git a/packages/dnb-ui-lib/src/components/modal/__tests__/Modal.test.js b/packages/dnb-ui-lib/src/components/modal/__tests__/Modal.test.js
index ebbaf813b3d..22b1e181d46 100644
--- a/packages/dnb-ui-lib/src/components/modal/__tests__/Modal.test.js
+++ b/packages/dnb-ui-lib/src/components/modal/__tests__/Modal.test.js
@@ -29,6 +29,11 @@ props.close_title = 'close_title'
props.direct_dom_return = true
props.no_animation = true
+beforeAll(() => {
+ const button = document.createElement('BUTTON')
+ document.body.appendChild(button)
+})
+
describe('Modal component', () => {
const Comp = mount()
Comp.setState({
@@ -37,6 +42,46 @@ describe('Modal component', () => {
it('have to match snapshot', () => {
expect(toJson(Comp)).toMatchSnapshot()
})
+ it('should have aria-hidden and tabindex on other elements', () => {
+ const Comp = mount(
+
+
+
+ )
+
+ // Check the global button
+ Comp.find('Modal').find('button.dnb-modal__trigger').simulate('click')
+ expect(document.querySelector('button') instanceof HTMLElement).toBe(
+ true
+ )
+ expect(
+ document.querySelector('button').hasAttribute('aria-hidden')
+ ).toBe(true)
+ expect(document.querySelector('button').getAttribute('tabindex')).toBe(
+ '-1'
+ )
+ Comp.update()
+ expect(
+ Comp.find('.dnb-modal__content')
+ .instance()
+ .hasAttribute('aria-hidden')
+ ).toBe(false)
+ expect(
+ Comp.find('.dnb-modal__content')
+ .find('button')
+ .instance()
+ .hasAttribute('aria-hidden')
+ ).toBe(false)
+
+ // And close it again
+ Comp.find('button.dnb-modal__close-button').simulate('click')
+ expect(
+ document.querySelector('button').hasAttribute('aria-hidden')
+ ).toBe(false)
+ expect(document.querySelector('button').hasAttribute('tabindex')).toBe(
+ false
+ )
+ })
it('has to have the correct title', () => {
expect(Comp.find('h1').text()).toBe(props.title)
})
diff --git a/packages/dnb-ui-lib/src/shared/component-helper.js b/packages/dnb-ui-lib/src/shared/component-helper.js
index 86f5fcd95c3..c8f1a55d2a7 100644
--- a/packages/dnb-ui-lib/src/shared/component-helper.js
+++ b/packages/dnb-ui-lib/src/shared/component-helper.js
@@ -678,23 +678,29 @@ export const convertJsxToString = (elements, separator = undefined) => {
export class InteractionInvalidation {
constructor() {
- this.bypassSelector = '.not-specified'
+ this.bypassElement = null
+ this.bypassSelectors = []
return this
}
- setBypassSelector(bypassSelector = null) {
- if (bypassSelector instanceof HTMLElement) {
- this.bypassElement = bypassSelector
- } else {
- this.bypassElement = null
- this.bypassSelector = bypassSelector || '.not-specified'
+ setBypassElement(bypassElement) {
+ if (bypassElement instanceof HTMLElement) {
+ this.bypassElement = bypassElement
}
return this
}
- activate(TargetElement = null) {
+ setBypassSelector(bypassSelector) {
+ if (!Array.isArray(bypassSelector)) {
+ bypassSelector = [bypassSelector]
+ }
+ this.bypassSelectors = bypassSelector
+ return this
+ }
+
+ activate(targetElement = null) {
if (!this.nodesToInvalidate) {
- this._runInvalidaiton(TargetElement)
+ this._runInvalidaiton(targetElement)
}
}
@@ -703,7 +709,7 @@ export class InteractionInvalidation {
this.nodesToInvalidate = null
}
- _runInvalidaiton(TargetElement) {
+ _runInvalidaiton(targetElement) {
if (
typeof document === 'undefined'
// || isTouchDevice() // as for now, we do the same on touch devices
@@ -711,7 +717,7 @@ export class InteractionInvalidation {
return // stop here
}
- this._setNodesToInvalidate(TargetElement)
+ this._setNodesToInvalidate(targetElement)
if (Array.isArray(this.nodesToInvalidate)) {
this.nodesToInvalidate.forEach((node) => {
@@ -731,13 +737,14 @@ export class InteractionInvalidation {
) {
node._orig_ariahidden = node.getAttribute('aria-hidden')
}
- if (
- node &&
- typeof node._orig_style === 'undefined' &&
- node.hasAttribute('style')
- ) {
- node._orig_style = node.getAttribute('style')
- }
+ // Skip the outline for now - or does it give a value?
+ // if (
+ // node &&
+ // typeof node._orig_outline === 'undefined' &&
+ // node.style.outline
+ // ) {
+ // node._orig_outline = node.style.outline
+ // }
node.setAttribute('tabindex', '-1')
node.setAttribute('aria-hidden', 'true')
@@ -745,7 +752,7 @@ export class InteractionInvalidation {
// tabindex=-1 does not prevent the mouse from focusing the node (which
// would show a focus outline around the element). prevent this by disabling
// outline styles while the modal is open
- node.style.outline = 'none'
+ // node.style.outline = 'none'
} catch (e) {
//
}
@@ -775,39 +782,47 @@ export class InteractionInvalidation {
} else {
node.removeAttribute('aria-hidden')
}
- if (node && typeof node._orig_style !== 'undefined') {
- node.setAttribute('style', node._orig_style)
- node._orig_style = null
- delete node._orig_style
- } else {
- node.removeAttribute('style')
- }
+
+ // Skip the outline for now - or does it give a value?
+ // if (node && typeof node._orig_outline !== 'undefined') {
+ // node.style.outline = node._orig_outline
+ // delete node._orig_outline
+ // } else if(node.style) {
+ // node.style.outline = null
+ // }
} catch (e) {
//
}
})
}
- _setNodesToInvalidate(TargetElement = null) {
+ _setNodesToInvalidate(targetElement = null) {
if (typeof document === 'undefined') {
return // stop here
}
- if (typeof TargetElement === 'string') {
- TargetElement = document.querySelector(TargetElement)
+ if (typeof targetElement === 'string') {
+ targetElement = document.querySelector(targetElement)
}
- const skipTheseNodes = Array.from(
- (this.bypassElement || document).querySelectorAll(
- this.bypassSelector ? `${this.bypassSelector} *` : '*'
- )
- )
+ const skipTheseNodes =
+ this.bypassSelectors && this.bypassSelectors.length > 0
+ ? Array.from(
+ (this.bypassElement || document).querySelectorAll(
+ this.bypassSelectors
+ ? this.bypassSelectors.map((s) => `${s} *`).join(', ')
+ : '*'
+ )
+ )
+ : []
// by only finding elements that do not have tabindex="-1" we ensure we don't
// corrupt the previous state of the element if a modal was already open
this.nodesToInvalidate = Array.from(
- (TargetElement || document).querySelectorAll(
- `body *:not(${this.bypassSelector}):not(script)`
+ (targetElement || document).querySelectorAll(
+ `body *${this.bypassSelectors
+ .map((s) => `:not(${s})`)
+ .join('')}:not(script):not(style):not(path)`
)
).filter((node) => !skipTheseNodes.includes(node))
}
diff --git a/packages/dnb-ui-lib/stories/components/Modal.js b/packages/dnb-ui-lib/stories/components/Modal.js
index e462c18007a..e700471fa53 100644
--- a/packages/dnb-ui-lib/stories/components/Modal.js
+++ b/packages/dnb-ui-lib/stories/components/Modal.js
@@ -185,8 +185,12 @@ export const DrawerSandbox = () => (
// class="inner_class"
>
- Modal.Inner
- {/* */}
+ Focus me with Tab key
+