Skip to content

Documentation of componentOutputs looks not enough. #368

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

Closed
lacolaco opened this issue Feb 14, 2023 · 8 comments · Fixed by #372
Closed

Documentation of componentOutputs looks not enough. #368

lacolaco opened this issue Feb 14, 2023 · 8 comments · Fixed by #372

Comments

@lacolaco
Copy link
Contributor

Summary

When render(ComponentType) includes componentOutputs in its option, TypeScript inference seems broken.

Repro

BTW, I'm preparing a bugfix for this. This is a spec to reproduce the bug.

describe('componentOutputs', () => {
  it('invokes given callback when an output is emitted', async () => {
    @Component({ template: `` })
    class TestFixtureComponent {
      @Output() event = new EventEmitter<void>();
      emitEvent() {
        this.event.emit();
      }
    }

    const spy = jest.fn();
    const { fixture } = await render(TestFixtureComponent, {
      componentOutputs: { event: spy },
    });

    fixture.componentInstance.emitEvent();

    expect(spy).toHaveBeenCalled();
  });
});
           projects/testing-library/tests/render.spec.ts:170:31 - error TS2769: No overload matches this call.
             Overload 1 of 2, '(component: Type<TestFixtureComponent>, renderOptions?: RenderComponentOptions<TestFixtureComponent, typeof import("/Users/lacolaco/works/angular-testing-library/node_modules/@testing-library/dom/types/queries")> | undefined): Promise<...>', gave the following error.
               Type 'Mock<any, any>' is missing the following properties from type 'EventEmitter<void>': emit, subscribe, closed, currentObservers, and 16 more.
             Overload 2 of 2, '(template: string, renderOptions?: RenderTemplateOptions<WrapperComponent, {}, typeof import("/Users/lacolaco/works/angular-testing-library/node_modules/@testing-library/dom/types/queries")> | undefined): Promise<...>', gave the following error.
               Argument of type 'typeof TestFixtureComponent' is not assignable to parameter of type 'string'.
       
           170     const { fixture } = await render(TestFixtureComponent, {
                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           171       componentOutputs: { event: spy },
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           172     });
               ~~~~~~

image

@lacolaco
Copy link
Contributor Author

I'm reading the source code and found my understanding of this API is wrong.

@lacolaco
Copy link
Contributor Author

Completely works well.

describe('componentOutputs', () => {
  it('invokes given callback when an output is emitted', async () => {
    @Component({ template: `` })
    class TestFixtureComponent {
      @Output() event = new EventEmitter<void>();
      emitEvent() {
        this.event.emit();
      }
    }

    const mockEmitter = new EventEmitter<void>();
    const spy = jest.spyOn(mockEmitter, 'emit');
    const { fixture } = await render(TestFixtureComponent, {
      componentOutputs: { event: mockEmitter },
    });

    fixture.componentInstance.emitEvent();

    expect(spy).toHaveBeenCalled();
  });
});

@lacolaco
Copy link
Contributor Author

But, I have no idea why this is needed. What is the difference between componentProperties?

@lacolaco
Copy link
Contributor Author

After diving deeply, I found the answer.

  • componentInputs calls componentRef.setInput, so it can trigger OnChanges and OnPush safely.
  • componentOutputs just set the new value to the field. no other side-effects.
  • componentProperties looks ... a deprecated pattern. It is complex to trigger change detection after assigning.

I feel the current documentation is not enough to tell the proper API usage to the users. At least, I'd like to improve the documentation of componentOutputs.

@lacolaco lacolaco changed the title Type of render with componentOutputs is not correctly inferred Documentation of componentOutputs looks not enough. Feb 14, 2023
@timdeschryver
Copy link
Member

Hey @lacolaco you're 100% right.
componentProperties was what we had before, but we can now have fine-grained control with componentInputs. To still be able to set outputs, we've added componentOutputs.

Do you think these changes are worth it to deprecate componentProperties in favor of the new properties? My opinion is that the new properties are favored, but on the other hand componentProperties makes it easy to just set properties and render the component.
It also allows to set public properties of the component that are not inputs or outputs.
I didn't have a case for this yet, but I assume it could happen in some codebases.

@lacolaco
Copy link
Contributor Author

@timdeschryver Thanks for clarifying. I can totally agree with you. I think it is not easy to drop componentProperties because as you say it could happen in edge cases to mutate class non-input/output fields.

@timdeschryver
Copy link
Member

We'll see how it goes and we can re-evaluate later.
Perhaps a ESLint rule would be ideal in this case 🤔
Feel free to clarify this behavior in the docs if you want.

@lacolaco
Copy link
Contributor Author

lacolaco commented Mar 1, 2023

@timdeschryver I've submitted a PR! #372

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 a pull request may close this issue.

2 participants