-
Notifications
You must be signed in to change notification settings - Fork 808
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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
[change] Class names being added to body during render of non-opened modals #436
[change] Class names being added to body during render of non-opened modals #436
Conversation
@ajfuller This PR does a lot of stuff, maybe we can separate the fix from this new structure. It's better to integrate small PRs. |
In order to avoid adding too much complexity on |
@diasbruno I can remove the Are there any specific changes you're concerned about? The majority of changes are consolidating the instance tracking and associated tests. I'm more than happy to change the implementation, but it was the only way I could find to make it pass the tests. |
We can simplify the structure by changing the value to be a <Modal isOpen={true} bodyOpenClassName={A} />
<Modal isOpen={false} bodyOpenClassName={A} />
<Modal isOpen={false} bodyOpenClassName={B} />
{ "A": 1, "B": 0 } The // register/increment
add(bodyOpenClassName);
// unregister/decrement
remove(bodyOpenClassName);
// check if a class name must be in `document.body`
active(bodyOpenClassName) -> Boolean It seems better to handle toggle the class name and aria in the We also could use It seems the way to go since we can't no longer rely on the reference of the instance neither the value of |
@diasbruno good call, on the counter. To make that work I've changed the code to be in the |
1 similar comment
This is really nice. Letting |
specs/Modal.spec.js
Outdated
@@ -166,6 +166,7 @@ describe('State', () => { | |||
expect( | |||
moverlay(modal).className.includes('myOverlayClass') | |||
).toBeTruthy(); | |||
unmountModal(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unmountModal
shouldn't be necessary. afterEach
should deal with clean up. I'll have a look on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'll remove, it was failing during one of my initial attempts, but it's passing now with the latest updates 馃憤
specs/Modal.spec.js
Outdated
renderModal({ isOpen: true }); | ||
expect(isBodyWithReactModalOpenClass()).toBeTruthy(); | ||
renderModal({ isOpen: false, bodyOpenClassName: 'testBodyClass' }); | ||
expect(isBodyWithReactModalOpenClass('testBodyClass')).toBeFalsy(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expectation should be always True
.
expect(!isBodyWithReactModalOpenClass('testBodyClass')).toBeTruthy();
specs/Modal.spec.js
Outdated
expect(isBodyWithReactModalOpenClass()).toBeTruthy(); | ||
renderModal({ isOpen: false, bodyOpenClassName: 'testBodyClass' }); | ||
renderModal({ isOpen: false }); | ||
expect(isBodyWithReactModalOpenClass('testBodyClass')).toBeFalsy(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect(!isBodyWithReactModalOpenClass('testBodyClass')).toBeTruthy();
specs/Modal.spec.js
Outdated
expect(isBodyWithReactModalOpenClass()).toBeTruthy(); | ||
unmountModal(); | ||
expect(isBodyWithReactModalOpenClass()).toBeTruthy(); | ||
unmountModal(); | ||
expect(!isBodyWithReactModalOpenClass()).toBeTruthy(); | ||
expect(isBodyWithReactModalOpenClass()).toBeFalsy(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect(!isBodyWithReactModalOpenClass()).toBeTruthy();
src/helpers/refCount.js
Outdated
|
||
export function totalCount() { | ||
const count = Object.keys(modals) | ||
.reduce((acc, curr) => acc + modals[curr], 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be inlined with return
.
src/components/ModalPortal.js
Outdated
// Focus only needs to be set once when the modal is being opened | ||
if (!this.props.isOpen && newProps.isOpen) { | ||
this.setFocusAfterRender(true); | ||
this.addModalInstance(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps, this.addModalInstance();
should be called from this.open();
.
src/components/ModalPortal.js
Outdated
this.open(); | ||
} else if (this.props.isOpen && !newProps.isOpen) { | ||
this.removeModalInstance(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps, this.removeModalInstance();
should be called from this.close();
.
src/components/ModalPortal.js
Outdated
@@ -84,6 +102,7 @@ export default class ModalPortal extends Component { | |||
} | |||
|
|||
componentWillUnmount() { | |||
this.removeModalInstance(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better let this.close()
call this.removeModalInstance();
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved the other ones, but in componentWillUnmount
we aren't calling the close method at all, so the tests will fail.
We could call close
method here, but I feel safer calling removeModalInstance
directly so we don't run into potential issues if a closeTimeoutMS
prop is set.
src/components/ModalPortal.js
Outdated
@@ -62,16 +69,27 @@ export default class ModalPortal extends Component { | |||
// Focus needs to be set when mounting and already open | |||
if (this.props.isOpen) { | |||
this.setFocusAfterRender(true); | |||
this.addModalInstance(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better let this.open()
call this.addModalInstance();
.
if (props.ariaHideApp) { | ||
ariaAppHider.toggle(props.isOpen, props.appElement); | ||
} | ||
|
||
this.portal = renderSubtreeIntoContainer(this, ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something that bugs me. Not sure what happens with this.portal
when a modal is open and close too many times. I'll make some tests.
When Modal is unmounted, can this property leaks?
This is going to bump minor version since we are going to introduce this rule for this class name, but it's backwards-compatible, so it will be released as |
1 similar comment
src/components/ModalPortal.js
Outdated
} | ||
} | ||
|
||
removeModalInstance() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removeModalInstance
can be beforeClose
.
src/components/ModalPortal.js
Outdated
@@ -99,12 +116,37 @@ export default class ModalPortal extends Component { | |||
this.content = content; | |||
} | |||
|
|||
addModalInstance() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can rename addModalInstance
to beforeOpen
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed 馃憤
I'm going to make the last release for |
1 similar comment
I guess it just needs to be rebase and squash all commits and we can ship it! |
src/components/ModalPortal.js
Outdated
refCount.add(bodyOpenClassName); | ||
// Add body class | ||
elementClass(document.body).add(bodyOpenClassName); | ||
// Add aria-hidden to appELement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ajfuller appELement
with a big L!
3b445c0
to
d38c83a
Compare
d38c83a
to
27579ca
Compare
Screwed up my rebase and accidentally closed, hopefully should be good now |
Just add |
5c6ed53
to
8a6ceb3
Compare
@diasbruno I've updated both PR and commit name to include [change] |
Thank you, @ajfuller. |
Great job. Released |
Changes proposed:
Modal reference tracking
There is an issue where class names are being added to document.body during render of non-opened modals. With only one type of
bodyOpenClassName
it was "working", but if you added a custombodyOpenClassName
it was causing that value to be added regardless ofisOpen
status.See the tests to replicate the behavior against current codebase.
This PR creates a new data structure for the modal references. Instead of an array of modals, we need to know which open modals are associated with each
bodyOpenClassName
.Proposed structure is:
aria-hidden
was being removed from theappElement
before all open modals were actually closed.I'm not a huge fan of the side-effects when manipulating the
modals
object, but it's consistent with the current code, and it works 馃拝Acceptance Checklist:
CONTRIBUTING.md
.