-
Notifications
You must be signed in to change notification settings - Fork 530
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(rendering): always render with current state #5429
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit a15a451:
|
…re about isRefined)
this is because of the change in render-args, but i don't see how
// @MAJOR: use scheduleRender here | ||
// For now, widgets don't expect to be rendered at the start of `loading`, | ||
// so it would be a breaking change to add an extra render. We don't have | ||
// these guarantees about the render event, thus emitting it once more | ||
// isn't a breaking change. | ||
this.emit('render'); | ||
this.scheduleRender(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is part B of the implementation
packages/instantsearch.js/src/widgets/infinite-hits/__tests__/infinite-hits-integration-test.ts
Show resolved
Hide resolved
"disjunctiveFacets": [ | ||
"brand", | ||
], | ||
"disjunctiveFacetsRefinements": { | ||
"brand": [ | ||
"Apple", | ||
], | ||
}, | ||
"disjunctiveFacets": [], | ||
"disjunctiveFacetsRefinements": {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are now always rendering with the current state. The state from the results is wrong (merged) and thus useless. This PR changes that.
seems that react may skip the render if `loading` is visible <1 frame
getResults() { | ||
return derivedHelper && derivedHelper.lastResults; | ||
if (!derivedHelper) return null; | ||
if (!derivedHelper.lastResults) return null; | ||
|
||
// To make the UI optimistic, we will always render using the current state, | ||
// but the previous results. This means a change will be visible immediately, | ||
// regardless of the status of the network request. | ||
derivedHelper.lastResults._state = helper!.state; | ||
|
||
return derivedHelper.lastResults; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is part A of the implementation
b0e8d6a
to
0833320
Compare
expect(searchClient.search).toHaveBeenCalledTimes(0); | ||
expect(appleRefinement).not.toBeChecked(); | ||
|
||
userEvent.click(appleRefinement); | ||
|
||
await waitFor(() => { | ||
expect(appleRefinement).toBeChecked(); | ||
expect(searchClient.search).toHaveBeenCalledTimes(1); | ||
// possible bug in testing-library, this is checked in real life in the same situation | ||
// expect(appleRefinement).toBeChecked(); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test was never correctly passing:
- searchClient wasn't correct (returned no facets on result)
- and behaviour it pretended to fix was never fixed (if result._state said something was refined, it would show as refined, but first click would only set it to refined again, second click would toggle again, because local search parameters are used for
refine
)
And the behaviour changed in this PR. It's sufficiently covered under the expanded "renders refinements from local widget state" test now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
Co-authored-by: Dhaya <154633+dhayab@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to your existing tests, I think each flavor should have the same suite of user-oriented tests (testing the UI, not checking what methods are called) where we slow down the search request, click a refinement, and assess whether the checkbox is checked or not (before this PR it should not, with this PR it should).
Ideally, we do this for every impacted widget (all refinement widgets, pagination, etc.) This could be in a optimistic-ui
test suite.
Later on, when we do a common test suite as in Predict, we could factorize that into one.
packages/instantsearch.js/src/connectors/infinite-hits/connectInfiniteHits.ts
Show resolved
Hide resolved
packages/instantsearch.js/src/lib/__tests__/InstantSearch-test.tsx
Outdated
Show resolved
Hide resolved
packages/react-instantsearch-hooks/src/hooks/__tests__/useInstantSearch.test.tsx
Show resolved
Hide resolved
packages/vue-instantsearch/src/util/__tests__/createServerRootMixin.test.js
Show resolved
Hide resolved
Not used finally in the test (because timing still is complicated), but it would be possible later
ariaLabel={`${pageNumber + 1}`} | ||
ariaLabel={`Page ${pageNumber + 1}`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
other aria-labels aren't configurable, so changing this one to add text isn't a breaking change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can those changes be easily moved into their own PR? It adds a bit of noise and this one is already quite large.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests would fail or need to be skipped between the tests. Happy to revert in this PR if needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that's right, I saw you use those new aria label strings in tests. It's fine for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's added because they all three used different aria-label before this PR, so I didn't have a way to target the right items
@@ -88,6 +88,7 @@ | |||
<a | |||
:class="suit('link')" | |||
:href="state.createURL(page)" | |||
:aria-label="`Page ${page + 1}`" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
other aria-labels aren't configurable, so adding this one to add text isn't a breaking change
// Fixes https://github.com/algolia/react-instantsearch/issues/3530 | ||
test('renders initial refinement and allows to refine them', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mentioned in a different comment too, but this test wasn't actually asserting what it was meant to (toBeChecked at the bottom should be .not.toBeChecked if we actually fixed the issue, but we never did). The similar assertion about it being interactive is added to the other test earlier.
// To make the UI optimistic, we patch the state to display to the current | ||
// one instead of the one associated with the latest results. | ||
// This means user-driven UI changes (e.g., checked checkbox) are reflected | ||
// immediately instead of waiting for Algolia to respond, regardless of | ||
// the status of the network request. | ||
derivedHelper.lastResults._state = helper!.state; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
root of the implementation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great set up for the common test suite!
ariaLabel={`${pageNumber + 1}`} | ||
ariaLabel={`Page ${pageNumber + 1}`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can those changes be easily moved into their own PR? It adds a bit of noise and this one is already quite large.
// wait for initial results | ||
await act(async () => { | ||
await wait(margin + delay); | ||
await wait(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain or comment why we wait a second time after most of the interactions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't fully understand, but I believe it's related to jest timers being quite strict in ordering. If there's no two timers waited for, a timer added during the initial wait will still be triggered after.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comments for the hierarchical-menu
common suite apply to others when relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice PR ! Great job on abstracting the common test suites :D
also enables those relevant tests
helper update caused a tiny bit increase, and React InstantSearch connectors was sized tightly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gorgeous work on this topic, and I'm excited we have a common test suite now. Can't wait to move most our tests there!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really nice work on the common test suite 👏
Summary
This change enables optimistic rendering, as discussed in RFC 82 and also #4403.
This doesn't (yet) implement reverting the state on error, as it is done in #5438.
Result
FX-2196