Giter Club home page Giter Club logo

Comments (23)

tanato-cit avatar tanato-cit commented on July 30, 2024 15

The two provided solutions didn't seemed great to me,

Setting the initialItemCount fixed in the code when not in SSR is quite dangerous, as it'll basically render all elements thus removing the purpose of Virtualization and also can lead to multiple renders.

The second solution to override the initialItemCount on the component code that uses Virtuoso according to the environment that's running (test vs production) doesn't seem great either as the component code will be messy.

A better solution in my opinion is to override the Virtuoso implementation by creating a mocked version with a HOC that set this attribute, like this: (in this example I'm setting the initialItemCount to the same value as totalCount so it render all elements)

import { ElementType, Component as mockComponent } from 'react'

// ...

jest.mock('react-virtuoso', () => {
  const { Virtuoso } = jest.requireActual('react-virtuoso')

  const mockVirtuoso = (WrappedVirtuoso: ElementType) =>
    class extends mockComponent<{ totalCount: number }, unknown> {
      render() {
        return <WrappedVirtuoso initialItemCount={this.props?.totalCount} {...this.props} />
      }
    }
  return { Virtuoso: mockVirtuoso(Virtuoso) }
})

from react-virtuoso.

petyosi avatar petyosi commented on July 30, 2024 10

For everyone finding this issue - there's a new recommended approach, check this improved approach: https://virtuoso.dev/mocking-in-tests/.

from react-virtuoso.

petyosi avatar petyosi commented on July 30, 2024 8

Hi,

I am not familiar with the testing-library internals, but my guess is that it is executed in a node environment, bypassing the hooks, effects, etc. If this is so, you can probably make it work by setting the initialItemCount prop, available in v0.10 (released yesterday). Check the demo: https://stackblitz.com/edit/react-virtuoso-server-side-rendering?file=example.js
It is also mentioned in the API reference: https://virtuoso.dev/virtuoso-api-reference/

Please let me know if this works for you since it is a new feature.

from react-virtuoso.

MaxboDev avatar MaxboDev commented on July 30, 2024 5

I'm still having problems with this. I managed to get it working by setting initialItemCount to the size of the list but then I had to test some functionality which changed the size of the list and react-virtuoso wouldn't render the updated list even if I updated the initialItemCount.

My test environment isn't anything unusual, its the jest with jsdom setup offered out of the box by create-react-app. Is there a suggested way of getting this to work in that test environment?

Currently I've had to mock out react-virtuoso in my tests, but I'd really rather be running the real code in my tests. Any help would be super appreciated. Thanks!

from react-virtuoso.

NordlingDev avatar NordlingDev commented on July 30, 2024 5

@M4sterShake - One way to bypass this is to assign a key to the Virtuoso component if the node environment is test. This will of course render a new virtuoso instance if the items count changes, but in a test environment I don't think it's such a big case to deal with.

So, if you set initialItemCount to be the length of the data, set it to key as well:

const totalCount = data.length;

<Virtuoso
  totalCount={totalCount}
  {...someProps}
  {...(process.env.NODE_ENV === 'test'
    ? {
      initialItemCount: totalCount,
      key: totalCount
    }
    : {}
  )}
/>

from react-virtuoso.

stazrad avatar stazrad commented on July 30, 2024 4

SOLVED- same problem working with @testing-library/react...the data is correct but the list item is never rendered to the "dom" (or whatever equivalent this testing library is using to generate a "document"). It seems that itemContent is never fired at all when mounting in the test environment.

solved by setting initialItemCount to data.length

from react-virtuoso.

philmeta0 avatar philmeta0 commented on July 30, 2024 4

Here's an alternative approach that mocks out the Virtuoso component to render the entire list without any wrapper in our test suite. We just added this to our setupTests.tsx file.

jest.mock('react-virtuoso', () => {
  function Virtuoso(props: VirtuosoProps<unknown, unknown>) {
    return <>{props.data?.map((value, index) => props.itemContent?.(index, value, undefined))}</>;
  }

  return { ...jest.requireActual('react-virtuoso'), Virtuoso };
});

from react-virtuoso.

brapifra avatar brapifra commented on July 30, 2024 1

That works! 🎉 Ty

from react-virtuoso.

stassiek02 avatar stassiek02 commented on July 30, 2024 1

@aimad-majdou Did you import mockComponent like this?
import { ElementType, Component as mockComponent } from 'react'

from react-virtuoso.

mstykow avatar mstykow commented on July 30, 2024 1

If you're getting "Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?" do this:

jest.mock('react-virtuoso', () => {
  const Virtuoso = forwardRef((props: VirtuosoProps<unknown, unknown>, _) => {
    return <>{props.data?.map((value, index) => props.itemContent?.(index, value, undefined))}</>;
  })

  return { ...jest.requireActual('react-virtuoso'), Virtuoso };
});

from react-virtuoso.

petyosi avatar petyosi commented on July 30, 2024 1

@jonbri that's logical given how initialTopMostItemIndex works - it 1) scrolls to a specific location 2) waits for the scrollTop to be reported to render.

https://github.com/petyosi/react-virtuoso/blob/master/src/initialTopMostItemIndexSystem.ts#L31-L48
https://github.com/petyosi/react-virtuoso/blob/master/src/listStateSystem.ts#L225-L232

If you're up for it, I am happy to accept a PR.

from react-virtuoso.

alvarogfn avatar alvarogfn commented on July 30, 2024 1

If anyone is having the same problem as @jonbri, and can't modify the code directly, my temporary solution was:

jest.mock('react-virtuoso', () => ({
  ...jest.requireActual<typeof VirtuosoModule>('react-virtuoso'),
  Virtuoso:
    // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
    forwardRef<VirtuosoHandle, VirtuosoProps<any, any>>(({ initialTopMostItemIndex, ...props }, ref) => {
      const { Virtuoso } = jest.requireActual<typeof VirtuosoModule>('react-virtuoso')
      return React.createElement(Virtuoso, { ref, ...props })
    })
}))

from react-virtuoso.

Zache avatar Zache commented on July 30, 2024

This works for me also, only with the added problem that with it initialItemCount set React re-renders the component endlessly. The profiler says because of hooks changed. I'm dealing with this by only setting initialItemCount during tests, but it would be nice to not have to do that.

from react-virtuoso.

sallyhill avatar sallyhill commented on July 30, 2024

Hi there, is there a way to programmatically scroll in unit tests that don't define refs? Enzyme testing strips out refs on components, so scrollToIndex and anything attached to that Virtuoso ref don't seem to be accessible. Given I'm using window scrolling, I'm not sure how else to trigger the visible rendered items to shift. I've tried window.scrollTo and scrollToElement on nodes. Sorry this maybe more an enzyme or jsdom question, but it might be helped if someone has a better understanding of where window scroll listeners are being attached so I can work out how to trigger the right ones.

from react-virtuoso.

wjureczka avatar wjureczka commented on July 30, 2024

Hi, is there any solution for that? initialItemCount seems not to work in my case.
Scenario is:

  1. Component has an empty array from prop so no list items are rendered - good
  2. That arrays changes and has one element, but no list item is rendered - bad

from react-virtuoso.

kyle-sawatsky avatar kyle-sawatsky commented on July 30, 2024

@wjureczka I tried that solution as well in a jest test. Oddly I swear the first time I ran the test after putting in initialItemCount it worked and then subsequently it never worked again.

I get it because the js-dom environment doesn't really give anything true dimensions. So the checks that Virtuoso makes to figure out how much to render never result in anything.

from react-virtuoso.

gustavo-depaula avatar gustavo-depaula commented on July 30, 2024

@wjureczka I have the same problem

from react-virtuoso.

OzzieOrca avatar OzzieOrca commented on July 30, 2024

It seems like initialItemCount has to be manually set for tests to work. If it's set to 0, I can get the EmptyPlaceholder to show. If it's set to a value higher than the length of the data, it throws errors trying to render undefined items. Since I'm loading my data from GraphQL, the length initially is 0 but afterwards it is set to a number. But If I set initialItemCount to the length of the data, it doesn't update on subsequent render.

Is there a better way to test this component? Guess I might end up mocking it.

from react-virtuoso.

petyosi avatar petyosi commented on July 30, 2024

@OzzieOrca Honestly, I'm not sure what you are trying to test here. Can you describe the overall idea?

from react-virtuoso.

NuclleaR avatar NuclleaR commented on July 30, 2024

Hi, is there any solution for that? initialItemCount seems not to work in my case. Scenario is:

  1. Component has an empty array from prop so no list items are rendered - good
  2. That arrays changes and has one element, but no list item is rendered - bad

Any ideas how to check this case?

from react-virtuoso.

StephenTHendrix avatar StephenTHendrix commented on July 30, 2024

The example mocking Virtuoso above is excellent!

from react-virtuoso.

tanato-cit avatar tanato-cit commented on July 30, 2024

I'm still having problems with this. I managed to get it working by setting initialItemCount to the size of the list but then I had to test some functionality which changed the size of the list and react-virtuoso wouldn't render the updated list even if I updated the initialItemCount.

My test environment isn't anything unusual, its the jest with jsdom setup offered out of the box by create-react-app. Is there a suggested way of getting this to work in that test environment?

Currently I've had to mock out react-virtuoso in my tests, but I'd really rather be running the real code in my tests. Any help would be super appreciated. Thanks!

You can mock AND still run the real code, as in the example above, it just override one property

from react-virtuoso.

jonbri avatar jonbri commented on July 30, 2024

Thanks for the above solution @petyosi , it's mostly working for me.
Something that seems to make it break however is when the initialTopMostItemIndex prop is used.

Here is my unit test, forked from the example given in the doc:

import { createElement } from 'react';
import { render, screen } from '@testing-library/react';
import { Virtuoso, VirtuosoMockContext } from 'react-virtuoso';

describe('Virtuoso', () => {
  const totalCount = 100;
  type Item = { id: string; value: string };
  const data: Item[] = [...Array(totalCount).keys()].map((i) => ({
    id: i.toString(),
    value: `Item ${i}`,
  }));

  it('correctly renders items starting at index', () => {
    render(<Virtuoso data={data} initialTopMostItemIndex={50} />, {
      wrapper: ({ children }) => (
        <VirtuosoMockContext.Provider value={{ viewportHeight: 300, itemHeight: 100 }}>
          {children}
        </VirtuosoMockContext.Provider>
      ),
    });

    screen.getByText('Item 50'); // this fails
  });
});

Here are the results:

  ● Virtuoso › correctly renders items starting at index

    TestingLibraryElementError: Unable to find an element with the text: Item 50. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

    Ignored nodes: comments, <script />, <style />
    <body>
      <div>
        <div
          data-test-id="virtuoso-scroller"
          data-virtuoso-scroller="true"
          style="height: 100%; outline: none; overflow-y: auto; position: relative;"
          tabindex="0"
        >
          <div
            data-viewport-type="element"
            style="width: 100%; height: 100%; position: absolute; top: 0px;"
          >
            <div
              data-test-id="virtuoso-item-list"
              style="box-sizing: border-box; padding-top: 0px; padding-bottom: 10000px; margin-top: 0px;"
            />
          </div>
        </div>
      </div>
    </body>

      20 |     });
      21 |
    > 22 |     screen.getByText('Item 50'); // this fails
         |            ^
      23 |   });
      24 | });
      25 |

      at Object.getElementError (../node_modules/@testing-library/dom/dist/config.js:38:19)
      at ../node_modules/@testing-library/dom/dist/query-helpers.js:90:38
      at ../node_modules/@testing-library/dom/dist/query-helpers.js:62:17
      at ../node_modules/@testing-library/dom/dist/query-helpers.js:111:19
      at Object.<anonymous> (components/List/__tests__/Virtuoso.spec.tsx:22:12)

For some reason, it's not rendering any items when the initialTopMostItemIndex prop is used.
If I simply remove initialTopMostItemIndex the items are rendered per-usual.

I tried adding different combinations of initialItemCount (initialItemCount={totalCount}) and key (key={totalCount}) but they didn't seem to have any affect.

Has anyone else come across this?

from react-virtuoso.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.