-
Notifications
You must be signed in to change notification settings - Fork 39
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
Preactified popup #1742
Preactified popup #1742
Conversation
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.
Looking good! I left a few drive-by comments in case they're helpful.
Hey @birtles, how do you want to handle theming in the Cosmos fixtures? From a glance, it looks like the fixtures you've previously created for the settings page are affected by the browser theme, but not the 10ten theme, so this is new ground. I was thinking maybe add a Cosmos decorator that gives us a theme toggle for all fixtures in the EDIT: I went ahead and did this, but of course LMK if you'd prefer a different approach. |
That's exactly what I had in mind. Looks great! |
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.
Looking great! Thank you!
src/common/i18n.tsx
Outdated
() => ({ t, setLocale, locale: props.locale }), | ||
[t, setLocale, props.locale] | ||
); | ||
const langTag = useMemo(() => t('lang_tag'), [t, props.locale]) |
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.
Nit: GitHub is showing a bunch of prettier errors (missing semicolon in this case). I wonder if you can run prettier on your branch or set up your editor to run it automatically on save?
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, sorry about all the CI failure emails. What's weird is I thought I saw it running prettier as a pre-commit hook—but I guess that was just ESLint? Either way, yeah I can run it locally to avoid the GH errors.
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.
Looks like I only configured it to run on .ts
files (not .tsx
or .json
files etc.)
Lines 55 to 58 in 9a0ce77
"*.ts": [ | |
"prettier --write", | |
"eslint --fix" | |
], |
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 makes sense. I can change it as part of this PR.
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.
Okay, well I did run the lint_staged
task before pushing up my latest commit (also updated it to include .tsx
files), but I didn't think about the couple of style errors from previous commits that weren't in the staged files, so the pipeline still sent a failure message for that push...but hopefully the last one 😄.
@@ -0,0 +1,176 @@ | |||
import { KanjiReferencesTable } from './KanjiReferencesTable'; | |||
|
|||
// import '../../../css/popup.css'; |
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.
So this is super tricky, but it looks like we're using options.html
as the template file due to this line here:
10ten-ja-reader/webpack.config.cosmos.js
Line 51 in 9a0ce77
template: './src/options/options.html', |
That's why we don't need to import popup.css
ourselves.
Ideally we'd use a separate template for popup components vs options components but I don't know if there's a way to get Cosmos to use separate template files based on the fixture. It might be worth having a quick dig (or asking on their discord) but otherwise I guess this is fine as-is.
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 can definitely see if I can set up a separate template for options and popup fixtures.
FYI, the commented out line is there because the coloration on the table rows wasn't working, and I thought it was because the popup.css
styles weren't getting imported properly. Turns out that it was actually because theme.css
hadn't been imported, since that's where the CSS var for --cell-highlight-bg
gets defined. Just hadn't gotten to removing it yet :).
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.
FYI, looks like it's not possible to have more than one HTML template. The main React Cosmos maintainer Ovidiu recommended we import the CSS via JS, and use decorators if we need to vary it based on folder.
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 chasing up with Ovidiu about that! Ok, that seems fine. It's not strictly the CSS files that I'm interested in but rather the SVG assets in options.html
(which should be available to options components but not to popup componets). Anyway, using options.html
as-is seems fine for now. If it becomes a problem we can address it then.
src/content/popup/KanjiEntry.tsx
Outdated
{options.kanjiReferences && options.kanjiReferences.length && ( | ||
<KanjiReferencesTable | ||
entry={entry} | ||
kanjiReferences={options.kanjiReferences} | ||
/> | ||
)} |
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.
FWIW, I have very mixed feelings about this style as a way of doing conditional rendering, but I've acquiesced to it over time because it seems to be the accepted practice in the React world. Still, happy to change it depending on your feelings.
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, in general I think it's fine (as long as you ensure the value on the LHS is a bool, otherwise you sometimes get undefined
and things showing up in the output). In this case I think !!options.kanjiReferences?.length && ...
would do the trick.
</div> | ||
); | ||
} | ||
|
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.
Not sure what your preference is on helper components being declared above/below the main component. It doesn't particularly matter to me either way, and actually I tend to declare them above, and put the main component at the bottom, but I did the opposite here since that more closely mirrored what you had in the old manual rendering code in kanji.ts
.
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. Yeah, these days I lean towards doing the main thing at the top and adding the helpers below (i.e. leaning on JS's function hoisting).
I think of this as being "newspaper style" where you describe the main thing first then add details below. I find this is easier to read since you start reading at the top, get an overview, and then can continue reading for as long as you're interested.
function LegacyIcon({ | ||
iconSvg, | ||
opacity, | ||
}: { | ||
iconSvg: SVGElement; | ||
opacity: number; | ||
}) { | ||
const ref = useRef<HTMLDivElement>(null); | ||
useLayoutEffect(() => { | ||
iconSvg.classList.add('svgicon'); | ||
iconSvg.style.opacity = `${opacity}`; | ||
ref.current?.replaceWith(iconSvg); | ||
}); | ||
|
||
return <div ref={ref}></div>; | ||
} |
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 a temporary hack I have no intention of actually keeping. I just did it to get the icons rendering onscreen again with the new React setup, without having to change too much code at once.
However, it does raise the question of how you'd like to handle the icons in icons.ts
. Should I just convert each of those functions to a React component that returns an <svg>
?
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.
However, it does raise the question of how you'd like to handle the icons in
icons.ts
. Should I just convert each of those functions to a React component that returns an<svg>
?
Yes, I think converting those to individual React components makes the most sense.
src/content/popup/expandable.ts
Outdated
@@ -64,6 +64,9 @@ export function updateExpandable( | |||
`calc(${round(collapsedHeight, 2)}px + var(--expand-button-allowance))` | |||
: `${expandedHeight}px`; | |||
|
|||
// TODO: temporary solution in development bc this isn't playing well with React | |||
expandable.style.height = '600px'; |
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.
For some reason, the .expandable
element was collapsing to height: 0px
whenever I switched the kanji entry to React. I'm not totally sure why, but I'm guessing something to do with the React element not yet being rendered when the expandable does its height measurement. I'll dig into it more tomorrow so I can get rid of this.
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, this part is tricky (it took quite a bit of work to get the expandable behavior to behave correctly in all the different configurations) so I'd definitely prefer to deal with this in a separate PR where we can take the time to get it right.
@birtles I remember you had said something about converting just the kanji reference table to React and shipping that first, and we can still do that if you'd like, I'll just have to roll back some commits on this branch. However, I've went ahead and started on converting the |
Sorry for the delay, yes, I'd like to just ship the converted kanji table first. There are two risks I'm concerned about:
I'd also prefer to do the Preact and Tailwind conversion hand-in-hand (as separate patches/commits but at around the same time) just because it's easier to convert the styles for a component when you have its structure in your head. Which is to say, I don't want to go too far with the other Preact conversion in this PR. Would you mind narrowing this PR down to just the kanji references table in this PR? |
package.json
Outdated
@@ -56,6 +56,10 @@ | |||
"prettier --write", | |||
"eslint --fix" | |||
], | |||
"*.tsx": [ |
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 think we can just combine this with the above *.ts
pattern to make it *.{ts,tsx}
. Maybe even *.tsx?
works?
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 tried *.tsx?
previously and it wouldn't work, but *.{ts,tsx}
might do the trick.
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 noticed the docs have *.{ts,tsx}
(or actually I think it was *.{js,jsx}
) so at least that much should work. Not sure why *.tsx?
doesn't work but I suppose it's due to the glob library they're using.
Ah okay, I was thinking maybe you were planning to ship just the kanji table first purely because it was an easy first step, didn't realize these other reasons.
Yeah, no problem. I'll do that and move the other work I've done so far to another branch. |
As a side note, I wonder if webpack will tree-shake this away for us, so long as we don't use |
7ce4245
to
3d8069a
Compare
Alright, lets try this on for size now. I've cleaned the commit history to include just the table. I'll tack on the Tailwind conversion of the table styles as a separate commit when I get the chance. FYI, there is a warning about bundle size for the content script that I don't think was there before:
I'm guessing that's probably due to Preact being included. Is figuring out whether page load times are affected and if we need to lazy-load the content script something you wanted to do as part of this PR? If so, I'm happy to help, but might need a little guidance. The nuances of web extension development are new to me 🙂. Also, the knip.js run in failing on CI because |
From memory it doesn't get tree-shaken unfortunately (which makes sense since I think tree-shaking is generally pretty limited to things like independently exported functions/variables as opposed to class/component methods). |
Great, thank you! I think we can do the Tailwind conversion as a separate PR even (see comment below). So once you're happy with the Preact part, feel free to mark this PR as ready for review.
Normally I try to keep the main branch such that it's always shippable but I think it's fine in this case to merge this and then address the perf issue in a separate PR. I think what might make sense is to create a async import (annotated with We can work out the details in a separate PR but I think that would ultimately mean that for users who have the extension permanently loaded (and activated using Ctrl or somesuch) they only pay the cost of loading a more minimal content script on pages where they don't actually look anything up. So I think what I would want to do is:
I think we can just mark the export as |
3d8069a
to
c5a4d4c
Compare
Okay, sounds good! I'm happy with this PR whenever you are. If you have any remaining nits, feel free to commit them yourself—or leave them to me, whatever you prefer. Just putting it out there if you don't want to wait the 12hr time difference for me to wake up and get to my computer again 🙂. |
Great, thank you! I pushed some nits now but unfortunately I found a nasty problem. At the point where we go to calculate the height of the kanji entry (in I think we need to force the render to by synchronous. Preact doesn't properly implement |
I hadn't noticed the height discrepancy because I keep my main 10ten installation with the 'always expand window' option checked, but I see what you're talking about now. My bad for not catching that. I'll see if I can get a synchronous render working somehow. |
No problem! For what it's worth you can use I'll have a look at the sync render tomorrow if you don't get to it before then. Once we've converted the whole thing to Preact we should be able to replace the sync render with a |
Oh, I do do this, I just meant that I was using the stable, released version to compare against the dev version (the one running with
So I did try the trick you linked to above with setting If you beat me to the punch on figuring it out I won’t complain 😅. |
I had a look at this and found the same thing. I think it must be possible to force Preact to render synchronously somehow because the Preact render tests do it: It looks like their setup is more complex where they override That said, some tests don't appear to be using the |
I tried using their I also tried using * Update: I did some testing just now, and the synchrony (or lack thereof) is definitely the issue. If you add a // Collapse expandable containers
for (const expandable of contentContainer.querySelectorAll<HTMLElement>(
'.expandable'
)) {
if (expandable.classList.contains('foobar')) {
setTimeout(() => {
updateExpandable(expandable, {
...options,
showKeyboardShortcut: options.displayMode === 'static',
});
}, 0);
} else {
updateExpandable(expandable, {
...options,
showKeyboardShortcut: options.displayMode === 'static',
});
}
} then the issue disappears. |
Thanks for trying those ideas! I've been away from the office this past week due to family issues but hopefully I'll be back in a couple of days and can look into it more thoroughly then. Yes, you can confirm the issue in a few ways like printing out the outerHTML of the container after calling render and debugging the values updateExpandables gets. One alternative might be to pass in a callback and then call it from a useLayoutEffect hook and use that to trigger updateExpandables but I think the popup positioning code might also want the final layout size so if we can keep the render synchronous that would be ideal. |
No worries! I've been busy the last few days too like I said, but hopefully I'll have time to jump back into this tomorrow or Wednesday, if you haven't beaten me to it.
Yeah, I had considered the This is a wild thought, but we could maybe use Preact's SSR to get a synchronous render. That involves rendering to a string though, so we'd then have to use either |
Okay, I've finally fixed this! I still could not get it to render synchronously. However, passing down a callback that updates the FWIW, I think the reason the sync render isn't working as expected might be something to do with the Anyway, it works now. I'm happy with it if you're happy with it. |
Thanks for working on this! Unfortunately As a result, with the current PR we'll end up positioning the popup in the wrong place in some cases since we won't take into account the size of the references table: I'm sure we can work around that too but if at all possible I'd like to do a sync render simply because it's less risky. If we end up adding workarounds we might miss something else or cause new issues due to the different timing of the calls (which will be different to what we finally ship anyway).
Oh that's really interesting. I hadn't thought of that! |
Oh yeah, it's very likely the return (
<i18nContext.Provider value={value}>
{!isLoading && props.children}
</i18nContext.Provider>
); Until the layout effect in the provider runs and resolves its async import, The next step might be working out how to do a compile-time conditional import of |
I went ahead and split up the i18n provider so that it renders synchronously in extension contexts and it seems to work for me over in the I'm going to cherry pick the i18n refactoring patch and try to land that as a separate PR. If that lands hopefully you should be able to rebase this PR on top of it and drop the |
Ok, I've made the i18n provider sync over in #1759 so you should be able to rebase on top of that and drop the changes to explicitly call I added the |
e4873bc
to
b6ff138
Compare
Oh, nice catch. Seems kind of obvious now 😅.
Thanks! I've done the rebase. |
Great! Thank you! |
Well, with all the back and forth this one took I might not have saved you much time, but I’m glad this finally got merged. Thanks for the patience. I’m hoping to work on the lazy-loading of the popup code in a few day’s time—hopefully we can get that one sorted out with less than 70 comments on the PR :). |
Not at all! Thanks for your patience! Thanks to you noticing the provider was the problem I think we came up with something that should be pretty low risk to ship.
Yeah, I've been thinking about that. I'm not sure yet how it should work. I was actually going to have a fiddle with it this afternoon if you don't mind. |
Oh yeah, by all means. It'll be a lot easier for you to weigh the best options there anyway since this is your codebase. |
Just to quickly follow up here—the last few days have been very busy so I've only had an hour or so to work on this. I started refactoring the popup positioning code over in the |
Nice, looking forward to seeing what you come up with. |
Sorry, looks like it will be Saturday before I can work on this properly. |
This is nowhere near ready, just throwing it up as a draft for visibility, and as a place I can ask questions if I have them :).
Ignore the commit history, I plan to squash this down once it's ready to merge.