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

Fix modals not being able to open after being closed once #1393

Merged
merged 7 commits into from Jan 19, 2021
Merged

Fix modals not being able to open after being closed once #1393

merged 7 commits into from Jan 19, 2021

Conversation

SanderKnauff
Copy link
Contributor

@SanderKnauff SanderKnauff commented Jan 11, 2021

Currently, modals can only be opened once. After this, it gets stuck in a state on which the BsModal thinks it is open already, causing the show() method to do nothing at all.

While the issue I encountered did not exactly fit issue #1386 (I did not receive any errors on my side), it might resolve that issue as well. I however do not know how to test that issue.

@@ -473,7 +473,7 @@ export default class Modal extends Component {
if (!this._isOpen) {
return;
}
this.isOpen = false;
this._isOpen = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

If I didn't missed something, this was the only place, in which isOpen was actually set. So we wouldn't need @localCopy on it anymore. It would be just an alias to this.args.isOpen.

If the big could be resolved by not setting isOpen, would actually be inline with my findings in #1386 (comment).

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 refactored out the @localcopy. I've tried to set the value to this.args.open, however it seems like that breaks more than it fixes. Accepting open as an argument with a default value of true does not seem to break any tests. Any calls to this.isOpen without the underscore has been changed to open.

Copy link
Contributor

Choose a reason for hiding this comment

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

Glad to see that we could actually drop @localCopy decorator. Less complexity is always good. 😄

Your changes look very good. I'm just a little bit confused why you haven't stuck with isOpen name. The is prefix is a helpful indicator that a property is a boolean in my opinion. In this special case I also fear that a property open might be confused with the show method.

Would it be okay for you to change the name back to isOpen? Not the implementation. That one is totally fine. I'm only talking about the naming.

@jelhan
Copy link
Contributor

jelhan commented Jan 11, 2021

Thanks a lot for working on this. I think you are on the right track here

While the issue I encountered did not exactly fit issue #1386, it might resolve that issue as well. I however do not know how to test that issue.

Such a test would look very similar to the test you have. But instead closing the model by setting @open to false, you would close it with a click on backdrop (or close button). Additionally you would provide an action handler to @onHidden, which sets @open to false. So invokation would look like this:

this.set('open', false);
this.set('close', () => this.open = false);

await render(hbs`
  <BsModal
    @open={{this.open}}
    @onHidden={{action this.close}}
  />
`);

@SanderKnauff
Copy link
Contributor Author

Thanks a lot for working on this. I think you are on the right track here

While the issue I encountered did not exactly fit issue #1386, it might resolve that issue as well. I however do not know how to test that issue.

Such a test would look very similar to the test you have. But instead closing the model by setting @open to false, you would close it with a click on backdrop (or close button). Additionally you would provide an action handler to @onHidden, which sets @open to false. So invokation would look like this:

this.set('open', false);
this.set('close', () => this.open = false);

await render(hbs`
  <BsModal
    @open={{this.open}}
    @onHidden={{action this.close}}
  />
`);

@SanderKnauff
Copy link
Contributor Author

SanderKnauff commented Jan 12, 2021

(Apologies for closing, I misclicked)

@jelhan I've updated the test as well as per your suggestions. The test is a bit closer to the case that was presented but is still in the same spirit as it was.

As I've added more commits I'm wondering about the workflow. Should I squash these after everything has been cleared or will that be handled when merging?

Copy link
Contributor

@jelhan jelhan left a comment

Choose a reason for hiding this comment

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

I've updated the test as well as per your suggestions. The test is a bit closer to the case that was presented but is still in the same spirit as it was.

The new test looks very good. But I meant it as an addition not as a replacement of your original test. Both tests - the original one in your first commit and the current one - are testing two different cases. And it seems as we have missed test coverage for both.

I'm sorry for that confusion. Would it be okay for you to add the test again?

As I've added more commits I'm wondering about the workflow. Should I squash these after everything has been cleared or will that be handled when merging?

You don't need to care about squashing. We can squash when merging.

Comment on lines 132 to 137
/**
* @property open
* @private
*/
@arg
open = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason you renamed the variable from isOpen to open? The is prefix indicates that it's a boolean. I fear open can be confused with a method to open the modal. I would prefer to keep isOpen to be honest. Would it be okay for you to change back?

Copy link
Contributor

Choose a reason for hiding this comment

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

I haven't reviewed this PR yet, but I am a bit confused at this point: @open is the public argument, which we cannot rename. The JS docs above are wrong somehow, as there is @property open twice!

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 tried to rename isOpen to open to remove the dependency on the @localcopy and to clarify that it is an argument to the BsModal component. I must admit that I do not understand why there is both a isOpen and a _isOpen. The JSDoc seems indeed to be a bit odd, as isOpen had two JSDocs above it as if something between the docs was missing.

While I do agree that isOpen would be a better name for a boolean, it does not reflect the actual args of the component well, causing confusion. I've tried a few ways of getting it to be a proper alias of open, but none of them were succesful.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh. I missed that this has @arg decorator pointing to the argument. Sorry. Should have taken more time to review. Can not could how often I already got confused by @open argument and the two internal properties isOpen and _isOpen.

To be honest I thought this PR decouples @open from the internal state and manages the state updates through {{did-update}} modifier in the template only. I think I need to reserve some time to fully understand state flow in this component.

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'm not fully clear about the lifecycle of this component. I mostly noticed that the _isOpen state did not get put back to false, causing the modal to never reopen. It does seem to fix the odd behaviour people have been experiencing without breaking the existing test conditions.

@@ -473,7 +473,7 @@ export default class Modal extends Component {
if (!this._isOpen) {
return;
}
this.isOpen = false;
this._isOpen = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Glad to see that we could actually drop @localCopy decorator. Less complexity is always good. 😄

Your changes look very good. I'm just a little bit confused why you haven't stuck with isOpen name. The is prefix is a helpful indicator that a property is a boolean in my opinion. In this special case I also fear that a property open might be confused with the show method.

Would it be okay for you to change the name back to isOpen? Not the implementation. That one is totally fine. I'm only talking about the naming.

@SanderKnauff
Copy link
Contributor Author

I've added back the original test case as well now

Copy link
Contributor

@simonihmig simonihmig left a comment

Choose a reason for hiding this comment

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

Apart from the minor JSDoc issue (see inline comment) this seems good to me!

Tbh I was a bit puzzled that we were able to simply remove that @localCopy without any breaking changes, but as all tests are passing (including the newly added ones 👍), this seems to be the case.

Apparently I was also getting confused with those different states of the component. 🤦‍♂️
Initially that mutable isOpen copy of @open was used to be able to directly close the modal by user interaction (ESC, backdrop click) without mutating (and thus violating DDAU) the original @open (which was possible in the Ember.Component days, but luckily became impossible with Glimmer components). Whether you like that API or not (see discussion in #830), we can only change that as a breaking change API overhaul in a major version, not now. Anyways, we seem to already have mutable component state like showModal and inDom that is enough for this purpose, with requiring @localCopy...

Thanks a lot for this work here @SanderKnauff! 🙏

Comment on lines 132 to 135
/**
* @property open
* @private
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/**
* @property open
* @private
*/

This JSDoc block should be removed, as it is wrongly stating that @open is private, and is in direct conflict with the doc block (referring to the same @property open) just a few lines above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! I've removed it now.

@simonihmig
Copy link
Contributor

This looks good from my point of view. @jelhan I believe your feedback has been addressed, or is not relevant anymore (isOpen vs open), right? If so, I would be happy to merge this!

@simonihmig simonihmig added the bug label Jan 19, 2021
Copy link
Contributor

@jelhan jelhan left a comment

Choose a reason for hiding this comment

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

Thanks a lot @SanderKnauff for working on this. Removing @localCopy decorator simplifies the code a lot. The two tests will help us to catch regressions earlier next time.

I still feel that we can (and should) improve the state flow in this component. But that can be done in a follow-up pull request.

@jelhan jelhan merged commit 1e0fb6e into ember-bootstrap:master Jan 19, 2021
@simonihmig
Copy link
Contributor

Released: https://github.com/kaliber5/ember-bootstrap/releases/tag/v4.6.1 🎉

@tbartelmess
Copy link

Thank you!

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

Successfully merging this pull request may close these issues.

None yet

4 participants