-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
chore(website): preserve RulesTable filters state in searchParams #6568
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
Conversation
Thanks for the PR, @hasparus! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. |
✅ Deploy Preview for typescript-eslint ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
Yeah 😞. #6508 |
|
||
test('Accessibility', async ({ page }) => { | ||
await new AxeBuilder({ page }).analyze(); | ||
}); |
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.
😍 thanks for adding this in!
(I'll continue reviewing soon, just wanted to express appreciation sooner)
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.
Yup, this looks great. Thanks for sending it in @hasparus!
Just one suggestion on refactoring RulesTable
<>useRulesFilters
a bit. What do you think?
I'm down to merge as-is if you don't like the suggestion, or make it myself if you don't have time.
@@ -122,78 +123,57 @@ export default function RulesTable({ | |||
}: { | |||
extensionRules?: boolean; | |||
}): JSX.Element { | |||
const [filters, changeFilter] = useRulesFilters( | |||
extensionRules ? 'extension-rules' : 'supported-rules', | |||
); |
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.
[Cleanup] I'm not a big fan of hardcoding IDs on boolean logic. It tends to break over time, and makes it harder to understand where the IDs come from. How about giving the RulesTable
component a mandatory id
prop that's then passed directly to useRulesFilters
?
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 agree about hardcoding is no boolean logic, but I feel that
<RulesTable id="extension-rules">
might be a bit confusing given that the heading above receives id
attribute from the markdown pipeline.
How about a prop ruleset: "supported-rules" | "extension-rules"
that would subsume the boolean flag and be usable as a search param key?
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.
Yeah I like that a lot!
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 added it in 642031f, but it seems there are indirect codecov changes that are failing the CI. Do you think it's connected to my 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.
We can ignore codecov for these website PRs. We don't have great unit test coverage on it anyway. 🤷
@JoshuaKGoldberg The deploy has finished in 3 minutes, but the status check's been hanging for 19 hours. Is there a way we can stop it or restart it? This seems like a bug in the Netlify integration. @Skn0tt sorry for pinging you here, but I couldn't find a contact button at https://app.netlify.com/sites/typescript-eslint/deploys/64131d041e22810009b38f2c. |
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 implementation looks mostly good to me, but as some extra reading, you may want to try useSyncExternalStore
(interesting read) ^^
const [state, setState] = useState(neutralFiltersState); | ||
|
||
useIsomorphicLayoutEffect(() => { | ||
const search = new URLSearchParams(window.location.search); |
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.
You can replace window.location
with useLocation()
so you get SSR safety for free
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 expected useLocation()
to trigger re-renders whenever the location changes, as the docs would suggest: https://reactrouter.com/en/main/hooks/use-location
So using location
from useLocation
here wouldn't be the same logic, right?
And I'm in a useLayoutEffect
which does not run on the server, so accessing window.location
works.
Could you tell me more what do you mean by "SSR safety for free"?
Notice that I had rewritten this logic to react-router
specific hooks, what I described in "Alternative solution" in the original post. It turned out to be buggy and harder to reason about than the less elegant, naive solution with setState
and useLayoutEffect
, so I reverted that change — 786557f.
The implementation looks mostly good to me, but as some extra reading, you may want to try useSyncExternalStore (interesting read) ^^
Nice article! Do you think I should rewrite this PR to use useHistorySelector
presented in the post?
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'm okay either way! I don't have strong opinions on this, and TBH my React is getting a bit rusty these days. Just thought you may be interested—since effects don't work well with SSR.
Could you tell me more what do you mean by "SSR safety for free"?
I mean you don't have to use useIsomorphicLayoutEffect
and read from a client-only API inside. useLocation
is available both server-side and client-side, so it's more robust.
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 mean you don't have to use useIsomorphicLayoutEffect and read from a client-only API inside. useLocation is available both server-side and client-side, so it's more robust.
Well, yes, that's true, but then we shouldn't use useState
, because a change to the location will already re-render the component. Would you agree?
Would you prefer if I went back to the state from 7c2e81a? and write end-to-end tests to ensure it works?
(I'll defer to @Josh-Cena - who is more knowledgeable than me on Docusaurus best practices anyway!) |
Oh sorry I missed this - it's been flaky for a while. We can ignore it. #6508 😞 |
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 looks great, if you fix those linting errors i think we are ready to merge
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.
🚀 excellent, thanks @hasparus!
Thanks for the review and bearing with me, guys 🙏 |
Sorry for getting back so late, I was on PTO the past weeks. Not sure what happened here, but it looks like things are alright now? Let me know if not, and i'll see how I can help :) |
PR Checklist
Overview
Howdy! 👋
I added syncing rules table filters state to URL search params, and I wrote two test cases for it.
Solution
There are two tables, so I serialised the state to two search params, one for each table
supported-rules
,extension-rules
.Each value is list of categories concatenated with dashes
-
.If
FilterMode
is"include"
, we include the category as issupported-rules=recommended-fixable
If
FilterMode
is"exclude"
, we prefix it withx
. (We can't use!
in the URL.)supported-rules=xrecommended-strict-xfixable
I grouped the state of filters into one objects so it's easier to stringify.
Non-solutions
I initially just added a
useEffect
to update search params after the state change, anduseIsomorphicLayoutEffect
, but the problem was that the initial state would be written to the URL before it was read. I ended up moving writing to the URL to thechangeFilter
function exported fromuseRuleFilters
hook.We need
useIsomorphicLayoutEffect
to read from the URL instead of just using the state initialiser, because initialising different state in SSG and post-hydration would give us a hydration mismatch.Alternative solution
I noticed Docusaurus uses React Router, so in 7c2e81a I removed
setState
and synchronization and move the entire state touseLocation
, what in theory sounded better, because we'd just keep it in one place. Unfortunately, this got me a bug where the state wasn't re-read post hydration (useLocation
doesn't rerender again?) and I needed a layout effect again, so I ended up just reverting this change.Example
Screen.Recording.2023-03-04.at.18.28.04.mov