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

[material-ui] Error in Tests [TypeError: Cannot read property 'scrollTop' of null] #17119

Closed
Technologeek opened this issue Aug 23, 2019 · 19 comments
Labels
docs Improvements or additions to the documentation test

Comments

@Technologeek
Copy link

  • [ x] The issue is present in the latest release.
  • [x ] I have searched the issues of this repository and believe that this is not a duplicate.

Current Behavior 馃槸

Hi, I'm using the Tabs component and passing all the props that I require using my own wrapper component. Everything is working fine except when I add scrollable prop to the component.

const returnScrollable = () => {
  const [value, setValue] = useState(0);
  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <>
      <Tabs
        tabItems={tabItemsScrollable} //array of tabs
        onChange={handleChange}
        value={value}
        variant="scrollable"
        scrollButtons="auto"
      />
    </>
  );
};

This works as it's supposed to be without any errors on the console but when I run tests through Jest of updating the snap-shots using storybook, I'm getting an error.

Error: Uncaught [TypeError: Cannot read property 'offsetHeight' of null]
Error: Uncaught [TypeError: Cannot read property 'scrollTop' of null]

The above error occurred in the <ScrollbarSize> component:
        in ScrollbarSize (created by ForwardRef(Tabs))
        in div (created by ForwardRef(Tabs))
        in ForwardRef(Tabs) (created by WithStyles(ForwardRef(Tabs)))
        in WithStyles(ForwardRef(Tabs)) (created by SBTabs)
        in header (created by ForwardRef(Paper))
        in ForwardRef(Paper) (created by WithStyles(ForwardRef(Paper)))
        in WithStyles(ForwardRef(Paper)) (created by ForwardRef(AppBar))
        in ForwardRef(AppBar) (created by WithStyles(ForwardRef(AppBar)))
        in WithStyles(ForwardRef(AppBar)) (created by SBTabs)
        in div (created by SBTabs)
        in SBTabs (created by WithStyles(SBTabs))
        in WithStyles(SBTabs) (created by BaseStoryScrollable)
        in BaseStoryScrollable

Expected Behavior 馃

The tests should pass just like it does when the scrollable prop is not passed on the Tabs component.

Steps to Reproduce 馃暪

Steps:

  1. Create a component using Tabs and try Jest snap-shot testing it.

Context 馃敠

Your Environment 馃寧

Tech Version
Material-UI v4.3.1
React 16.9.0
Browser Chrome
TypeScript NA
etc.
@oliviertassinari oliviertassinari added test docs Improvements or additions to the documentation labels Aug 23, 2019
@oliviertassinari
Copy link
Member

oliviertassinari commented Aug 23, 2019

@Technologeek Thanks for opening this issue, this is related to #17070 and how we should improve our testing guide. I think that it's a topic we have already discussed in #16523. The solution we went for is to test all our components with:

import ReactTestRenderer from 'react-test-renderer';

ReactTestRenderer.act(() => {
  ReactTestRenderer.create(<Foo />, {
    createNodeMock: node => {
      return document.createElement(node.type);
    },
  });
});

+80% of our components are passing this test. We will work on raising it to 100%. The ones that don't are the components using react-transition-groups or mounting a Portal. It's not the case of the tabs.

I assume that you are using @storybook/addon-storyshots: https://storybook.js.org/docs/testing/structural-testing/, https://github.com/storybookjs/storybook/tree/master/addons/storyshots/storyshots-core.

Looking at the sources, it leads me to: https://github.com/storybookjs/storybook/blob/v5.2.0-beta.39/addons/storyshots/storyshots-core/src/frameworks/react/renderTree.js.

Alright, it seems that part of the solution is documented in: https://github.com/storybookjs/storybook/tree/master/addons/storyshots/storyshots-core#using-createnodemock-to-mock-refs.

Could you try the following?

import ReactTestRenderer from 'react-test-renderer';
import initStoryshots, { snapshotWithOptions } from '@storybook/addon-storyshots';

initStoryshots({
  test: snapshotWithOptions({
    renderer: storyElement =>
      ReactTestRenderer.act(() => {
        ReactTestRenderer.create(storyElement, {
          createNodeMock: node => document.createElement(node.type),
        });
      }),
  }),
});

or

import initStoryshots, { snapshotWithOptions } from '@storybook/addon-storyshots'

initStoryshots({
  test: snapshotWithOptions({
    createNodeMock: node => document.createElement(node.type),
  }),
})

@Technologeek
Copy link
Author

@oliviertassinari Thanks a lot for your quick reply! As far as my use-case is concerned, I'm not getting any console-errors or warnings and the component seems to pass all other tests. I could apply your suggestion but it is not needed at the moment. We have multiple such third party elements which fail tests due to ForwardRefs but the component works great without any console errors. For now, I prefer to just ignore that story. If it helps anybody, this is how you ignore a story..

//In your storyshots.test.js file

import initStoryshots from "@storybook/addon-storyshots";

initStoryshots({
  storyKindRegex: /^((?!.*?NameOfYourStory).)*$/
});

Thanks again for your help!

@carloscuesta
Copy link
Contributor

Hey @oliviertassinari just wanted to say that the solution you provided with act and createNodeMock worked and helped out with our tests

Thanks 馃檹

@oliviertassinari
Copy link
Member

@carloscuesta Thanks for the feedback! Now, we need to update the testing guide: #17070. Any help would be appreciated.

@sibelius

This comment has been minimized.

@eps1lon
Copy link
Member

eps1lon commented May 4, 2020

this is also happening when using the unstable_createMuiTheme

If you use this theme you need to make sure that children of transition components forward their refs to the outermost DOM node. The original plan was to add documentation for this but there were other StrictMode issues that weren't fixable so I didn't pursue this beyond internal usage.

Would greatly appreciate it if someone would add docs for unstable_createMuiTheme with caveats to https://material-ui.com/customization/theming:

  • unstable_ means use at your own risk and not in production
  • seriously don't use this in production. It only removes deprecation warnings.
  • children of transition components need to forward their refs (maybe with example for Drawer)

@sibelius
Copy link

sibelius commented May 4, 2020

thanks for this explanation, now I know how to fix this

I'm using it only in Dev to test how it goes

@uazure
Copy link

uazure commented May 13, 2020

import initStoryshots, { snapshotWithOptions } from '@storybook/addon-storyshots'

initStoryshots({
  test: snapshotWithOptions({
    createNodeMock: node => document.createElement(node.type),
  }),
})

This works fine for me. Thank you!

@christopher-francisco
Copy link

import initStoryshots, { snapshotWithOptions } from '@storybook/addon-storyshots'

initStoryshots({
  test: snapshotWithOptions({
    createNodeMock: node => document.createElement(node.type),
  }),
})

This works fine for me. Thank you!

I'm not really sure I understand what the internal problem is, but the code above didn't work for me.

It's crashing on the Zoom component:

  const transitionDuration = {
    enter: theme.transitions.duration.enteringScreen,
    exit: theme.transitions.duration.leavingScreen,
  };

    <Zoom in={true} timeout={transitionDuration} unmountOnExit>

@mkermani144

This comment has been minimized.

@siriwatknp

This comment has been minimized.

@oliviertassinari
Copy link
Member

@siriwatknp This issue is about the lack of ref in the test environment (react-test-renderer). I think that the issue you are talking about is related but different. I'm not sure we should discuss it here. Maybe it should happen in a new issue with a reproduction?

@siriwatknp
Copy link
Member

siriwatknp commented Jul 7, 2021

@siriwatknp This issue is about the lack of ref in the test environment (react-test-renderer). I think that the issue you are talking about is related but different. I'm not sure we should discuss it here. Maybe it should happen in a new issue with a reproduction?

Okay, will create a new issue with reproduction: #27154.

@Poky85
Copy link

Poky85 commented Jun 28, 2022

For me, issue occurs even in browser. It's very rare so I'm not able to reproduce. Probably happens after the Tabs are unmounted, which means that not all calls to updateScrollButtonState() are cleared properly. Also updateScrollButtonState is provided as callback in imperative handler so with calling action.updateScrollButtons() after lifecycle ended you can easily shoot yourself in the foot.

TypeError
Cannot destructure property 'scrollTop' of 'H.current' as it is null.

https://sentry.io/share/issue/8026280039644d4f9a4784928818290f/

Could we check that tabsRef.current !== null in the beginning of updateScrollButtonState() function? So that calling updateScrollButtonState() would be safe.

@W01fw00d
Copy link

W01fw00d commented Oct 4, 2022

For me, issue occurs even in browser. It's very rare so I'm not able to reproduce. Probably happens after the Tabs are unmounted, which means that not all calls to updateScrollButtonState() are cleared properly. Also updateScrollButtonState is provided as callback in imperative handler so with calling action.updateScrollButtons() after lifecycle ended you can easily shoot yourself in the foot.

TypeError
Cannot destructure property 'scrollTop' of 'H.current' as it is null.

https://sentry.io/share/issue/8026280039644d4f9a4784928818290f/

Could we check that tabsRef.current !== null in the beginning of updateScrollButtonState() function? So that calling updateScrollButtonState() would be safe.

Yes, I could detect it too on Sentry. It happens on Chrome Browser at least:

Cannot destructure property 'scrollTop' of 'be.current' as it is null.

I suspect the Tabs component too.

@siriwatknp
Copy link
Member

Could we check that tabsRef.current !== null in the beginning of updateScrollButtonState() function? So that calling updateScrollButtonState() would be safe.

Feel free to submit a PR.

@bendras
Copy link

bendras commented Oct 18, 2022

Observed. 鈽濓笍
attaching context while debugging in browser:

TypeError: Cannot destructure property 'scrollTop' of 'tabsRef.current' as it is null.
    at http://localhost:8001/js/vendors-node_modules_mui_icons-material_CheckBox_js-node_modules_mui_icons-material_CheckBoxO-6c59ee.570e64915171c9efc4ec.js:1224:9
    at http://localhost:8001/js/app.645260a71ad0be185387.js:18951:19
    at ResizeObserver.<anonymous> (http://localhost:8001/js/vendors-node_modules_mui_icons-material_CheckBox_js-node_modules_mui_icons-material_CheckBoxO-6c59ee.570e64915171c9efc4ec.js:1254:7)
    at later (http://localhost:8001/js/app.645260a71ad0be185387.js:18162:12)

image

WORKAROUND

set <Tabs /> component property scrollButtons={false}.
It disables the following:

  const updateScrollButtonState = useEventCallback(() => {
    if (scrollable && scrollButtons !== false) {
      const {
        scrollTop,
        scrollHeight,
        clientHeight,
        scrollWidth,
        clientWidth
      } = tabsRef.current;
      let showStartScroll;
      let showEndScroll;

      if (vertical) {
        showStartScroll = scrollTop > 1;
        showEndScroll = scrollTop < scrollHeight - clientHeight - 1;
      } else {
        const scrollLeft = getNormalizedScrollLeft(tabsRef.current, theme.direction); // use 1 for the potential rounding error with browser zooms.

        showStartScroll = isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1;
        showEndScroll = !isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1;
      }

      if (showStartScroll !== displayScroll.start || showEndScroll !== displayScroll.end) {
        setDisplayScroll({
          start: showStartScroll,
          end: showEndScroll
        });
      }
    }
  });

cfl777 added a commit to cfl777/osapiens-challenge that referenced this issue Sep 5, 2023
Due to multiple issues that occur on the MUI transition in this instance, the grow transition is disabled.
This isn't the optimal solution, further investigation is needed.

Attempted to apply the solutions mentioned on bug reports. Added a React.Fragment wrap, which resolved only some of the issues. Specifically the "Uncaught TypeError: Cannot read properties of null (reading 'scrollTop').

Might be missing something small. See
mui/material-ui#27154
mui/material-ui#18119
mui/material-ui#17119
cfl777 added a commit to cfl777/osapiens-challenge that referenced this issue Sep 5, 2023
Due to multiple issues that occur on the MUI transition in this instance, the grow transition is disabled.
This isn't the optimal solution, further investigation is needed.

Attempted to apply the solutions mentioned on bug reports. Added a React.Fragment wrap, which resolved only some of the issues. Specifically the "Uncaught TypeError: Cannot read properties of null (reading 'scrollTop').

Might be missing something small. See
mui/material-ui#27154
mui/material-ui#18119
mui/material-ui#17119
@ZeeshanTamboli
Copy link
Member

With the switch to the Intersection Observer API for handling scroll button visibility since @mui/material version 5.14.2, I believe the error should no longer occur.

@samuelsycamore samuelsycamore changed the title Error in Tests [TypeError: Cannot read property 'scrollTop' of null] [material-ui] Error in Tests [TypeError: Cannot read property 'scrollTop' of null] Nov 28, 2023
@samuelsycamore
Copy link
Member

Closing this as completed since the error should no longer occur 馃槄 but we can always reopen it if new reports come in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to the documentation test
Projects
None yet
Development

No branches or pull requests