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鈥檒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

Merged

Conversation

fuller
Copy link
Contributor

@fuller fuller commented Jun 23, 2017

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 custom bodyOpenClassName it was causing that value to be added regardless of isOpen 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:

modals = {
    "bodyClassName1": [ modal1Ref, modal3Ref ],
    "bodyClassName2": [ modal2Ref ],
    "bodyClassName3": [ modal4Ref ],
}
  • Additionally fixed an issue where aria-hidden was being removed from the appElement before all open modals were actually closed.
  • Also refactored some of the demo app so I could manually see this functionality working

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:

  • All commits have been squashed to one.
  • The commit message follows the guidelines in CONTRIBUTING.md.
  • Documentation (README.md) and examples have been updated as needed.
  • If this is a code change, a spec testing the functionality has been added.
  • If the commit message has [changed] or [removed], there is an upgrade path above.

@coveralls
Copy link

coveralls commented Jun 23, 2017

Coverage Status

Coverage increased (+0.6%) to 85.593% when pulling 8f30171 on ajfuller:fix/bodyOpenClassName-adding-classes into 8f3898a on reactjs:master.

@diasbruno
Copy link
Collaborator

@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.

@diasbruno
Copy link
Collaborator

In order to avoid adding too much complexity on react-modal, I think we need to add some rules to use this property since it can be really problematic.

@fuller
Copy link
Contributor Author

fuller commented Jun 23, 2017

@diasbruno I can remove the examples/basic/app.js changes for now. I left them in because the changes helped validate the broken scenarios were actually working now.

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.

@diasbruno
Copy link
Collaborator

We can simplify the structure by changing the value to be a counter. This way we can just increment and decrement the counter for each class name.

<Modal isOpen={true} bodyOpenClassName={A} />
<Modal isOpen={false} bodyOpenClassName={A} />
<Modal isOpen={false} bodyOpenClassName={B} />

{ "A": 1, "B": 0 }

The refCount api can be updated to something like:

// 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 Modal class, otherwise it'll be hidden inside the refCount. Probably we need to improve both methods renderPortal and removePortal.

We also could use componentWillReceiveProps to throw an exception if bodyOpenClassName changes. Perhaps, it would be better to treat this as a violation.

It seems the way to go since we can't no longer rely on the reference of the instance neither the value of bodyOpenClassName (if it can change).

@fuller
Copy link
Contributor Author

fuller commented Jun 24, 2017

@diasbruno good call, on the counter. To make that work I've changed the code to be in theModalPortal component instead. That way we're only running refCount.add() when the modal is actually opening and refCount.remove() when the modal is closing, not just whenever renderPortal is being called.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 84.814% when pulling a78049b on ajfuller:fix/bodyOpenClassName-adding-classes into 8f3898a on reactjs:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 84.814% when pulling a78049b on ajfuller:fix/bodyOpenClassName-adding-classes into 8f3898a on reactjs:master.

@diasbruno
Copy link
Collaborator

This is really nice. Letting Modal be just a proxy class.

@@ -166,6 +166,7 @@ describe('State', () => {
expect(
moverlay(modal).className.includes('myOverlayClass')
).toBeTruthy();
unmountModal();
Copy link
Collaborator

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.

Copy link
Contributor Author

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 馃憤

renderModal({ isOpen: true });
expect(isBodyWithReactModalOpenClass()).toBeTruthy();
renderModal({ isOpen: false, bodyOpenClassName: 'testBodyClass' });
expect(isBodyWithReactModalOpenClass('testBodyClass')).toBeFalsy();
Copy link
Collaborator

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();

expect(isBodyWithReactModalOpenClass()).toBeTruthy();
renderModal({ isOpen: false, bodyOpenClassName: 'testBodyClass' });
renderModal({ isOpen: false });
expect(isBodyWithReactModalOpenClass('testBodyClass')).toBeFalsy();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expect(!isBodyWithReactModalOpenClass('testBodyClass')).toBeTruthy();

expect(isBodyWithReactModalOpenClass()).toBeTruthy();
unmountModal();
expect(isBodyWithReactModalOpenClass()).toBeTruthy();
unmountModal();
expect(!isBodyWithReactModalOpenClass()).toBeTruthy();
expect(isBodyWithReactModalOpenClass()).toBeFalsy();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expect(!isBodyWithReactModalOpenClass()).toBeTruthy();


export function totalCount() {
const count = Object.keys(modals)
.reduce((acc, curr) => acc + modals[curr], 0);
Copy link
Collaborator

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.

// Focus only needs to be set once when the modal is being opened
if (!this.props.isOpen && newProps.isOpen) {
this.setFocusAfterRender(true);
this.addModalInstance();
Copy link
Collaborator

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();.

this.open();
} else if (this.props.isOpen && !newProps.isOpen) {
this.removeModalInstance();
Copy link
Collaborator

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();.

@@ -84,6 +102,7 @@ export default class ModalPortal extends Component {
}

componentWillUnmount() {
this.removeModalInstance();
Copy link
Collaborator

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();.

Copy link
Contributor Author

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.

@@ -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();
Copy link
Collaborator

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, (
Copy link
Collaborator

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?

@diasbruno
Copy link
Collaborator

diasbruno commented Jun 25, 2017

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 v2.1.x.

@coveralls
Copy link

coveralls commented Jun 25, 2017

Coverage Status

Coverage decreased (-0.01%) to 84.943% when pulling 480e6dd on ajfuller:fix/bodyOpenClassName-adding-classes into 8f3898a on reactjs:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.01%) to 84.943% when pulling 7873156 on ajfuller:fix/bodyOpenClassName-adding-classes into 8f3898a on reactjs:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.01%) to 84.943% when pulling 7873156 on ajfuller:fix/bodyOpenClassName-adding-classes into 8f3898a on reactjs:master.

}
}

removeModalInstance() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeModalInstance can be beforeClose.

@@ -99,12 +116,37 @@ export default class ModalPortal extends Component {
this.content = content;
}

addModalInstance() {
Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed 馃憤

@coveralls
Copy link

coveralls commented Jun 25, 2017

Coverage Status

Coverage decreased (-0.01%) to 84.943% when pulling 0ff3caf on ajfuller:fix/bodyOpenClassName-adding-classes into 8f3898a on reactjs:master.

@diasbruno
Copy link
Collaborator

I'm going to make the last release for v2.0 after merging #441. And later release this patch for version v2.1.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 85.559% when pulling 24caa29 on ajfuller:fix/bodyOpenClassName-adding-classes into 27579ca on reactjs:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 85.559% when pulling 24caa29 on ajfuller:fix/bodyOpenClassName-adding-classes into 27579ca on reactjs:master.

@diasbruno
Copy link
Collaborator

I guess it just needs to be rebase and squash all commits and we can ship it!

refCount.add(bodyOpenClassName);
// Add body class
elementClass(document.body).add(bodyOpenClassName);
// Add aria-hidden to appELement
Copy link
Contributor

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!

@fuller fuller force-pushed the fix/bodyOpenClassName-adding-classes branch 2 times, most recently from 3b445c0 to d38c83a Compare June 27, 2017 00:24
@fuller fuller closed this Jun 27, 2017
@fuller fuller force-pushed the fix/bodyOpenClassName-adding-classes branch from d38c83a to 27579ca Compare June 27, 2017 00:42
@fuller
Copy link
Contributor Author

fuller commented Jun 27, 2017

Screwed up my rebase and accidentally closed, hopefully should be good now

@fuller fuller reopened this Jun 27, 2017
@diasbruno
Copy link
Collaborator

diasbruno commented Jun 27, 2017

Just add [change] on the message and we can merge. Both issue and commit message, please.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 85.559% when pulling 5c6ed53 on ajfuller:fix/bodyOpenClassName-adding-classes into 27579ca on reactjs:master.

@coveralls
Copy link

coveralls commented Jun 27, 2017

Coverage Status

Coverage increased (+0.05%) to 85.559% when pulling 5c6ed53 on ajfuller:fix/bodyOpenClassName-adding-classes into 27579ca on reactjs:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 85.559% when pulling 5c6ed53 on ajfuller:fix/bodyOpenClassName-adding-classes into 27579ca on reactjs:master.

@fuller fuller changed the title [fixed] Class names being added to body during render of non-opened modals [change] Class names being added to body during render of non-opened modals Jun 27, 2017
@fuller fuller force-pushed the fix/bodyOpenClassName-adding-classes branch from 5c6ed53 to 8a6ceb3 Compare June 27, 2017 01:01
@fuller
Copy link
Contributor Author

fuller commented Jun 27, 2017

@diasbruno I've updated both PR and commit name to include [change]

@coveralls
Copy link

coveralls commented Jun 27, 2017

Coverage Status

Coverage increased (+0.05%) to 85.559% when pulling 8a6ceb3 on ajfuller:fix/bodyOpenClassName-adding-classes into 27579ca on reactjs:master.

@diasbruno
Copy link
Collaborator

Thank you, @ajfuller.

@diasbruno diasbruno merged commit 1baebf4 into reactjs:master Jun 27, 2017
@diasbruno
Copy link
Collaborator

Great job. Released v2.1.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants