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

feat(auto-init): Fire event on mdcAutoInit complete #1012

Merged
merged 10 commits into from
Sep 11, 2017
Merged

feat(auto-init): Fire event on mdcAutoInit complete #1012

merged 10 commits into from
Sep 11, 2017

Conversation

yshterev
Copy link
Contributor

FIx #954

@yeelan0319 The dialog component is extending MDCComponent so it has direct access to emit method. I am not sure how to access the emit method properly in mdcAutoInit function. I tried to import MDCComponent, but I guess it is not good.

Also I am not sure what the test should be.
Any help appreciated.

@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If your company signed a CLA, they designated a Point of Contact who decides which employees are authorized to participate. You may need to contact the Point of Contact for your company and ask to be added to the group of authorized contributors. If you don't know who your Point of Contact is, direct the project maintainer to go/cla#troubleshoot.
  • In order to pass this check, please resolve this problem and have the pull request author add another comment and the bot will run again.

@yshterev
Copy link
Contributor Author

I signed it!

@googlebot
Copy link

CLAs look good, thanks!

@codecov-io
Copy link

codecov-io commented Jul 25, 2017

Codecov Report

Merging #1012 into master will decrease coverage by 0.02%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #1012      +/-   ##
=========================================
- Coverage   99.93%   99.9%   -0.03%     
=========================================
  Files          68      69       +1     
  Lines        3276    3314      +38     
  Branches      403     409       +6     
=========================================
+ Hits         3274    3311      +37     
- Misses          2       3       +1
Impacted Files Coverage Δ
packages/mdc-auto-init/index.js 100% <100%> (ø) ⬆️
packages/mdc-select/foundation.js 100% <0%> (ø) ⬆️
packages/mdc-dialog/index.js 100% <0%> (ø) ⬆️
packages/mdc-form-field/constants.js 100% <0%> (ø) ⬆️
packages/mdc-dialog/foundation.js 100% <0%> (ø) ⬆️
packages/mdc-snackbar/foundation.js 100% <0%> (ø) ⬆️
packages/mdc-form-field/foundation.js 100% <0%> (ø) ⬆️
packages/mdc-radio/index.js 97.67% <0%> (ø) ⬆️
packages/mdc-form-field/index.js 100% <0%> (ø) ⬆️
packages/mdc-textfield/index.js 100% <0%> (ø) ⬆️
... and 4 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update defa7ac...5691dee. Read the comment docs.

@yeelan0319
Copy link
Contributor

@yshterev Thanks for filing the PR.

So here in the auto-init you don't really need to import MDCComponent - all the components themselves are instances of an derived MDCComponent and have this emit method.

For firing event
Take a look at mdc-auto-init index.js line 45 where an component instance is created here.
You can add component.emit('MDCAutoInit:End', {}, true); after line 51. It will make the component emit an MDCAutoInit:End event after it inits.
You can find a detailed explanation of the argument explanation in mdc-base document.

Test firing event
Take a look at unit test for mdc-tab-bar line 234. It need some modification but basically you can see a good reference how to test whether a event if fired.

Listen to fired event (FYI)
I also created a pen illustrate how the custom event could be listened.

Feel free to ping me back for further question and thanks again for being on it!

@yshterev
Copy link
Contributor Author

@yeelan0319 The FakeComponent in mdc-auto-init test was too fake. It did not have emit method, so I decided to use add the real mdc-base component. I think this way the test will be more accurate.

I am not sure my test logic is very good. It relies on the first argument passed to the handler func being the event object. Please review.

Object.defineProperty(node, ctorName, {
value: component,
writable: false,
enumerable: false,
configurable: true,
});
}

if (component !== null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

There might be multiple components initialize with mdc-auto-init method and my original thought is each of them should have their own init:end event.

If you are interested in just having one custom event about the whole auto-init end, maybe we shouldn't do that from a component level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably I got this wrong.

If each component emits its own init:end event when done, how would you know when all of components are initialzied? Or maybe we can use combination of both? Each component can emit own event when initialized, like ComponentNameInit:End and then 'MDCAutoInit:End' when all of them are initialized?

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 understand correctly the original issue was regarding having a way to tell when all components initialize, so I agree that should be the main goal of this PR.

Having one event fire per component init seems potentially excessive and is perhaps a separate bridge we should cross if a convincing use case arises?

Also, I'm not sure whether it makes sense to have the "all initialized" event emit from a particular component, since which component emits it could change just based on how an app changes during development. I'm open to thoughts/arguments otherwise here though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kfranqueiro When I submitted the issue I was not sure. The discussion in the issue makes it clear it is about having a way to tell when all components initialize, so I changed its name to be more clear.

I guess using the component to trigger the event was suggested by @yeelan0319 to keep it DRY and reuse the emit method.

I also think the PR should resolve the issue, meaning "all initialized" only until other use case arises.

Copy link
Contributor

Choose a reason for hiding this comment

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

@yshterev I looked at the comments on the issue and discussion here. I think it was slightly different though when we discuss it internally as we thought it would be done at component level.
So as now we understand the landscape of the question better, I think it should be proper to dispatch a custom event directly instead of using emit method from a single component. Could you update the PR accordingly? Thanks!

return this.root_;
}

getDefaultFoundation() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why we are adding Foundation method here? It would inherent from MDCComponent so you don't need to reimplement it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some tests don't pass. The error is Error: Subclasses must override getDefaultFoundation to return a properly configured foundation class

Copy link
Contributor

@yeelan0319 yeelan0319 Aug 23, 2017

Choose a reason for hiding this comment

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

I think once we directly implemented emit function, we no longer need the fake element here to inherit from MDCComponent. We can safely change it back to what it was and simply adding two new test cases.

mdcAutoInit(root);

td.verify(handler(td.matchers.isA(Object)));
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Test looks good!

### Events

#### MDCAutoInit:End
Triggered when initialization of all components is complete.
Copy link
Contributor

Choose a reason for hiding this comment

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

If that's the case, we probably shouldn't rely on component to emit event.

See my comment below

@lynnmercier
Copy link
Contributor

Hey @yshterev, is there anything unclear about the changes requested here?

@yshterev
Copy link
Contributor Author

@lynnjepsen Sorry for the delay. PR updated.

@yshterev
Copy link
Contributor Author

What should I do to pass the codecov test? Maybe rebase from master?

Copy link
Contributor

@yeelan0319 yeelan0319 left a comment

Choose a reason for hiding this comment

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

@yshterev Thanks for updating the PR. The codecov failed because line 37/38 in mdc-auto-init.js is not being covered, thus make the code coverage drop below the requirement. Please take a look at line 157 in test/unit/mdc-base/components.test.js to see how emit is tested.

return this.root_;
}

getDefaultFoundation() {
Copy link
Contributor

@yeelan0319 yeelan0319 Aug 23, 2017

Choose a reason for hiding this comment

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

I think once we directly implemented emit function, we no longer need the fake element here to inherit from MDCComponent. We can safely change it back to what it was and simply adding two new test cases.

@yeelan0319
Copy link
Contributor

@yshterev Any updates to the changes or let me know if you have more questions!

@yshterev
Copy link
Contributor Author

FakeComponent in test reverted. 2 new tests added for the emit function.

* @param {!Object} evtData
* @param {boolean=} shouldBubble
*/
export function emit(element, evtType, evtData, shouldBubble = false) {
Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO, I might not consider making emit a private helper function of mdc-auto-init instead of public. So far, I cannot see a strong argument why this method need to be public.

If testing is your biggest concern, how about cover both branch using the following methods?
The logic behind this is, we don't need explicitly export emit to test it but we mock the run-time environment (as you did in testing emit directly) and see if mdcAutoInit behave uniformly in both case.

test('#emit event from MDCComponent on init complete', () => {
  const root = setupTest();
  const handler = td.func('init event handler');
  let evt = null;

  td.when(handler(td.matchers.isA(Object))).thenDo((evt_) => {
    evt = evt_;
  });

  const type = 'MDCAutoInit:End';

  document.addEventListener(type, handler);
  mdcAutoInit(root);

  assert.isOk(evt !== null);
  assert.equal(evt.type, type);
});

test('#emit event from MDCComponent on init complete - custom events not supported', () => {
  const root = setupTest();
  const handler = td.func('init event handler');
  let evt = null;

  td.when(handler(td.matchers.isA(Object))).thenDo((evt_) => {
    evt = evt_;
  });

  const type = 'MDCAutoInit:End';

  document.addEventListener(type, handler);
  const {CustomEvent} = window;
  window.CustomEvent = undefined;
  try {
    mdcAutoInit(root);
  } finally {
    window.CustomEvent = CustomEvent;
  }

  assert.isOk(evt !== null);
  assert.equal(evt.type, type);
});

Let me know if this makes sense or your consideration for making emit public. Thanks!

Copy link
Contributor Author

@yshterev yshterev Sep 1, 2017

Choose a reason for hiding this comment

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

Sure. Your proposal is better. I exported emit just because of the test. Then the coverage did not pass again and I saw I was missing another test.
Do you think I should remove the element param from the emit function and just use document? I don't think element param makes sense anymore because tests always attach the listener to document.

Copy link
Contributor

Choose a reason for hiding this comment

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

@yshterev Sorry for the tardiness. Yes. I think that is totally a valid suggestion, right now we are only emitting event at document level, which means the element is not really being used. Feel free to proceed with this idea and let me know when it is ready to review.

@yshterev
Copy link
Contributor Author

@yeelan0319 Updated

Copy link
Contributor

@yeelan0319 yeelan0319 left a comment

Choose a reason for hiding this comment

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

@yshterev Thanks for the update. I am still a bit worried that we make emit a public function. I left some suggestions and see if you could agree with it.

* @param {!Object} evtData
* @param {boolean=} shouldBubble
*/
export function emit(evtType, evtData, shouldBubble = false) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we consider making this a private method?
My concern is there might not be a use case where this function will be called by external user (now).

We could do function emit_ to identify this is a private method and remove export. I also commented about revoke and test below.

What do you think?

@@ -49,6 +70,8 @@ export default function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
configurable: true,
});
}

emit('MDCAutoInit:End', {});
Copy link
Contributor

Choose a reason for hiding this comment

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

_emit('MDCAutoInit:End', {}); instead.

@@ -18,6 +18,7 @@ import bel from 'bel';
import td from 'testdouble';
import {assert} from 'chai';
import mdcAutoInit from '../../../packages/mdc-auto-init';
import {emit} from '../../../packages/mdc-auto-init';
Copy link
Contributor

Choose a reason for hiding this comment

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

We can proxy over mdcAutoInit to test private method emit is called, so we could remove this line.

@@ -95,3 +96,47 @@ test('#register warns when registered key is being overridden', () => {

td.verify(warn(contains('(mdc-auto-init) Overriding registration')));
});

test('#emit dispatches a custom event with supplied data', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider rewording '#emit dispatches a custom event with supplied data' > '#mdcAutoInit dispatches a MDCAutoInit:End event when initialization of all components is complete'

evt = evt_;
});

const data = {evtData: true};
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we could remove this line as the event doesn't come with data


assert.isOk(evt !== null);
assert.equal(evt.type, type);
assert.deepEqual(evt.detail, data);
Copy link
Contributor

Choose a reason for hiding this comment

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

If we remove data above, also here.

assert.deepEqual(evt.detail, data);
});

test('#emit dispatches an event with supplied data - custom events not supported', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing here. Consider rewording '#mdcAutoInit dispatches a MDCAutoInit:End event when initialization of all components is complete - custom events not supported'?

evt = evt_;
});

const data = {evtData: true};
Copy link
Contributor

Choose a reason for hiding this comment

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

Here

const {CustomEvent} = window;
window.CustomEvent = undefined;
try {
emit(type, data);
Copy link
Contributor

Choose a reason for hiding this comment

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

And here


assert.isOk(evt !== null);
assert.equal(evt.type, type);
assert.deepEqual(evt.detail, data);
Copy link
Contributor

Choose a reason for hiding this comment

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

And here

@yshterev
Copy link
Contributor Author

@yeelan0319 Sorry .. I was sleepy I guess. Updated.

Copy link
Contributor

@yeelan0319 yeelan0319 left a comment

Choose a reason for hiding this comment

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

Thanks @yshterev for keeping contributing on it, everything looks great 👍

@yeelan0319 yeelan0319 merged commit 08b5a32 into material-components:master Sep 11, 2017
@yshterev
Copy link
Contributor Author

@yeelan0319 My pleasure 👍

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.

6 participants