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

[Popover] Refactor popover transition - separation of concerns #7720

Merged
merged 17 commits into from
Aug 10, 2017

Conversation

rosskevin
Copy link
Member

@rosskevin rosskevin commented Aug 9, 2017

squash+merge

For simplification and external reuse of the Popover transition (now called Grow) with react-autosuggest, I have refactored Popover. Popover is currently used exclusively by Menu.

As it stands, here are the purposes:

  • internal/Popover = positioning + Modal + Grow + Paper (used by Menu)
  • transitions/Grow = only the effect exhibited by popovers - grow with a bit of fade in.

There are also various small fixes I encountered as I refactored.

I should also mention that this is possibly the lead-in to deleting the positioning code in Popover in lieu of using react-popper, but I'll wait to take that on after I've used it a bit with react-autosuggest.


  • PR has tests / docs demo, and is linted.
  • Commit and PR titles begin with [ComponentName], and are in imperative form: "[Component] Fix leaky abstraction".
  • Description explains the issue / use-case resolved, and auto-closes the related issue(s) (http://tr.im/vFqem).

@rosskevin
Copy link
Member Author

rosskevin commented Aug 9, 2017

NOTE: I have seen cross-contamination of tests when run sequentially. I could use some help with that.

Individually, tests are passing and the Grow.spec.js in fact never uses the value woof, so I suspect that the virtual DOM has something left over or there is some sort of race condition from a previous test.

I have changed all references to woof to isolate the cross-contamination:

  1101 passing (3s)
  3 failing

  1) <Grow /> event callbacks should fire event callbacks:
     Error: Warning: Material-UI: argument "duration" must be a number but found woofCollapse
      at Console.console.error (test/utils/consoleError.js:8:5)
      at warning (node_modules/warning/warning.js:51:17)
      at Object.create (src/styles/transitions.js:66:5)
      at Grow._this.handleEnter (src/transitions/Grow.js:92:19)
      at node_modules/enzyme/build/ShallowWrapper.js:844:23
      at ReactDefaultBatchingStrategyTransaction.perform (node_modules/react-test-renderer/lib/shallow/Transaction.js:143:20)
      at Object.batchedUpdates (node_modules/react-test-renderer/lib/shallow/ReactDefaultBatchingStrategy.js:62:26)
      at Object.batchedUpdates (node_modules/react-test-renderer/lib/shallow/ReactUpdates.js:97:27)
      at ReactShallowRenderer.unstable_batchedUpdates (node_modules/react-test-renderer/lib/shallow/ReactShallowRenderer.js:130:25)
      at performBatchedUpdates (node_modules/enzyme/build/ShallowWrapper.js:103:21)
      at node_modules/enzyme/build/ShallowWrapper.js:843:13
      at withSetStateAllowed (node_modules/enzyme/build/Utils.js:284:3)
      at ShallowWrapper.simulate (node_modules/enzyme/build/ShallowWrapper.js:840:42)
      at src/transitions/Grow.spec.js:29:17
      at Array.forEach (native)
      at Context.<anonymous> (src/transitions/Grow.spec.js:27:14)

  2) <Grow /> transition lifecycle handleEnter(element) "before all" hook:
     Error: Warning: Material-UI: argument "duration" must be a number but found woofCollapse
      at Console.console.error (test/utils/consoleError.js:8:5)
      at warning (node_modules/warning/warning.js:51:17)
      at Object.create (src/styles/transitions.js:66:5)
      at Grow._this.handleEnter (src/transitions/Grow.js:92:19)
      at Context.<anonymous> (src/transitions/Grow.spec.js:55:28)

  3) <Grow /> transition lifecycle handleExit(element) "before all" hook:
     Error: Warning: Material-UI: argument "duration" must be a number but found woofCollapse
      at Console.console.error (test/utils/consoleError.js:8:5)
      at warning (node_modules/warning/warning.js:51:17)
      at Object.create (src/styles/transitions.js:66:5)
      at Grow._this.handleExit (src/transitions/Grow.js:120:19)
      at Context.<anonymous> (src/transitions/Grow.spec.js:104:28)

@rosskevin
Copy link
Member Author

This appears to be stubbed and perhaps never unstubbed:

theme.transitions.getAutoHeightDuration = stub().returns('woofCollapse');

@rosskevin
Copy link
Member Author

rosskevin commented Aug 10, 2017

@oliviertassinari this recently changed from a styleManagerMock to an assignment on theme.
Was:

         styleManagerMock = wrapper.context('styleManager');
          styleManagerMock.theme.transitions.getAutoHeightDuration = stub().returns('woof');
          wrapper.setContext({ styleManager: styleManagerMock });
          wrapper.setProps({ transitionDuration: 'auto' });
          instance = wrapper.instance();

Now:

          theme = instance.props.theme;
          theme.transitions.getAutoHeightDuration = stub().returns('woofCollapseStub');
          wrapper.setProps({ transitionDuration: 'auto' });
          instance = wrapper.instance();

Either we need to regenerate theme so it is pristine on each shallow | mount, or this test needs to go back to using a mock. What are your thoughts?

@rosskevin
Copy link
Member Author

I added an explicit restore of the stubbed method.

Copy link
Member

@oliviertassinari oliviertassinari left a comment

Choose a reason for hiding this comment

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

I like the idea of this PR. It's a quick review, I need to have a deeper look this evening :).

@@ -200,7 +200,7 @@ type AllProps = DefaultProps & Props;
/**
* @ignore - internal component.
*/
class Popover extends Component<DefaultProps, AllProps, void> {
class Popover extends PureComponent<DefaultProps, AllProps, void> {
Copy link
Member

Choose a reason for hiding this comment

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

The component takes a children, let's keep Component.

/**
* @ignore - internal component.
*/
class Grow extends PureComponent<DefaultProps, AllProps, void> {
Copy link
Member

Choose a reason for hiding this comment

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

The component takes a children, PureComponent is harming the performance, let's use to Component.

@rosskevin
Copy link
Member Author

I've confirmed the Grow to work as expected when used externally.

@FezVrasta
Copy link

Feel free to ping me if you need help or more info about react-popper and Popper.js in general.

@rosskevin
Copy link
Member Author

Thanks @FezVrasta - hope you are well.

Copy link
Member

@oliviertassinari oliviertassinari left a comment

Choose a reason for hiding this comment

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

I had a deep look into it. That looks great 😄 .

@@ -479,7 +411,7 @@ class Popover extends Component<DefaultProps, AllProps, void> {
<EventListener target="window" onResize={this.handleResize} />
{children}
</Paper>
</Transition>
</Grow>
</Modal>
);
}
Copy link
Member

Choose a reason for hiding this comment

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

You can remove all the occurrences of withTheme and theme in this file.

Copy link
Member Author

Choose a reason for hiding this comment

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

still need it in #handleEnter for theme.transitions

Copy link
Member

Choose a reason for hiding this comment

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

I can't find that.

Copy link
Member Author

@rosskevin rosskevin Aug 10, 2017

Choose a reason for hiding this comment

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

Sorry, I was looking at the wrong file, you are right.

@@ -449,24 +381,24 @@ class Popover extends Component<DefaultProps, AllProps, void> {
...other
} = this.props;

// FIXME: props API consistency problem? - `...other` not spread over the root
Copy link
Member

Choose a reason for hiding this comment

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

I agree, let's move the ...other to the root. What's preventing us from doing it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nothing yet, just want to save that for the next wave of the refactor.

Copy link
Member

Choose a reason for hiding this comment

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

OK

}

type DefaultProps = {
theme: Object,
Copy link
Member

Choose a reason for hiding this comment

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

This property is injected by withTheme(), I don't think that we need it.

Copy link
Member Author

Choose a reason for hiding this comment

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

necessary for flow resolution. We could add it to AllProps instead, but that would differ from the pattern in other files.

Copy link
Member

Choose a reason for hiding this comment

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

Won't making the property required allow us to remove it here? It's the same for the classes.

Copy link
Member Author

Choose a reason for hiding this comment

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

It has to be required somewhere, and it is not required outside this component. If it is not required outside the component, we cannot require it in export type Props, as it would prohibit using the type externally to compose components.

/**
* @ignore
*/
theme?: Object,
Copy link
Member

Choose a reason for hiding this comment

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

I think that it should be required.

Copy link
Member Author

@rosskevin rosskevin Aug 10, 2017

Choose a reason for hiding this comment

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

It's not required externally (maybe type), but it is internally, that's why it's optional in Props and required in DefaultProps. The intersection of these mean internally the component can expect it to be non-null.

Copy link
Member

Choose a reason for hiding this comment

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

Isn't the same thing with the classes? I have put them required in the code, but in the documentation I have change them to be optional. Anyway here it's not documented.

Copy link
Member Author

@rosskevin rosskevin Aug 10, 2017

Choose a reason for hiding this comment

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

Same as classes, see above for explanation. It's all about externalizing Props for reuse. If theme isn't allowed to be passed in (by the user), we should remove it from Props.

import Transition from '../internal/Transition';
import type { TransitionCallback } from '../internal/Transition';

export function getScale(value: number) {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should add // Only exported for the tests.

@oliviertassinari
Copy link
Member

I should also mention that this is possibly the lead-in to deleting the positioning code in Popover in lieu of using react-popper

I'm not that sure about it. The Popover has a getContentAnchorEl property to handle a specific positioning requirement of the material specification.
aout-10-2017 20-00-59

@rosskevin
Copy link
Member Author

I'm not that sure about it. The Popover has a getContentAnchorEl property to handle a specific positioning requirement of the material specification.

This can be done with popper, even the custom positioning with a modifier. I'm not too keen to take that on yet. I've got a lot on my plate, but it is certainly viable and I think it would cut code and rely on a good external project.

@oliviertassinari oliviertassinari merged commit 640e56e into mui:v1-beta Aug 10, 2017
@oliviertassinari
Copy link
Member

@rosskevin Great, it's much better with the concerns separated :).

sebald pushed a commit to PTW-Freiburg/material-ui that referenced this pull request Aug 15, 2017
* upstream/v1-beta: (24 commits)
  v1.0.0-beta.5
  Update CHANGELOG.md
  [CHANGELOG] Prepare v1.0.0-beta.5
  chore(package): update size-limit to version 0.9.0 (mui#7757)
  [docs] Fix yarn docs:build script (mui#7745)
  [docs] Update the readme
  [Radio] Fix accessibility issue (mui#7742)
  [Tabs][BottomNavigation] Use value over index property (mui#7741)
  [core] Remove createStyleSheet (mui#7740)
  [core] Start simplifying styling API (mui#7730)
  [LinearProgress] Use transform instead width (mui#7732)
  Fix Typo (mui#7736)
  Documentation Fix - Fixing up the README documentation (mui#7733)
  [ButtonBase] Expose internal component (mui#7727)
  [Popover] Refactor popover transition - separation of concerns (mui#7720)
  Button documentation fix (mui#7726)
  Update supported-components.md (mui#7722)
  chore(package): update webpack to version 3.5.3 (mui#7723)
  [flow] flow type transitions Slide, Fade, Collapse (fixes)
  Create CODE_OF_CONDUCT.md
  ...
@rosskevin rosskevin deleted the refactor-popover-transition branch August 16, 2017 21:05
@zannager zannager added the component: Popover The React component. label Mar 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: Popover The React component.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants