-
Notifications
You must be signed in to change notification settings - Fork 553
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
Remove & inline markdown-toolbar-element
dependency
#2339
Conversation
🦋 Changeset detectedLatest commit: 151c010 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
size-limit report 📦
|
…eact into inline-formatting-tools
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.
Work looks great, but there appears to be some issues with act()
in the tests that are failing with the changes.
I think my definition for |
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.
Changes appear to have fixed the broken CI build
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 think we want to do this for several reasons. Chiefly being that we should be leaning into web components more within Primer React as we do with Primer View Components, because they offer us useful tools within the browser environment.
Moving away from sharing dependencies will cause inconsistencies across the two design systems. You mention sharing logic in behaviours, I think that’s definitely the plan - but I also think as behaviours get more complex they’ll start to take the shape of web components more and more, so we’ll have to address it there too.
The issues you mention around specifics of markdown toolbar sound imminently fixable. We should address those so we can continue to use this component.
Issues around compiler tool chains sound interesting. Could you detail the specific issues you’re having here? It sounds like something that needs to be addressed in our tool chain.
I have to disagree with you there. I think the web components pattern works well with PVC, but the same cannot be said for React. While web components technically can be used within React, they come with so many downsides that it's just not worth it. Specifically looking at this
While some of these problems are fixable, most are not because they stem from a fundamental incompatibility between web components and React components. They just don't work well together because they align with two very different styles of web architecture. I think sharing the logic code is great and we should definitely do that as much as possible. But React components should be allowed to handle their own rendering the way they want, using React patterns and React components internally. The logic code should be purely logic code - it shouldn't come with rendered baggage. As we as a company start to move in a React-first direction, it just doesn't make sense to be trying to make web components fit here. We should try to make it as easy as possible to share logic between our different component systems, and web components don't do that.
I don't think this is necessarily the case. As behaviors get more complex, tying them into rendered components feels like an antipattern. Having a simple behavior tied to a component is one thing, but complex behaviors should be as extensible and customizeable as possible. In my ideal world, the PVC and PRC implementations of the Markdown editor would share no rendered components at all, but would share as much of the underlying logic as they possibly can (ie, formatting tools, file handling, suggestions filtering, etc). This way the React implementation can be rendered purely using React, gaining all of the advantages that React provides without any hacky workarounds to use the shared logic. |
Yeah there’s work to do here to get React to utilise native CSS which is being worked on. The hope is certainly that these will end up using the same themes and same base components.
The
The Markdown toolbar component doesn’t do any client side rendering IIRC, the tags can be rendered by any server. The client side bundle needs specific client side code just like react does with react-dom. This should be reasonably straightforward to deliver for Primer as we do on GitHub. If there are issues around delivering this we should get them fixed; this is all standard JavaScript so everything should be interoperable and if it fails to be we should remediate that.
There’s lots of ways to solve this! We should explore them! Just like other behaviours, shortcut binding should be handled consistently between platforms we have.
Right. Let’s fix that to make sure it is reusable. We own both repositories so we can get this done in a reasonable time frame.
The components are written in typescript and have declared types: https://github.com/github/markdown-toolbar-element/blob/e48fbc5b8aec1f0451325b616d45f1324cdbce36/src/index.ts#L2. Perhaps there’s an error in the publish or perhaps typescript is misconfigured? We should look into this because this is definitely imminently solvable.
It’s great that we’re in agreement here! Our web components including markdown toolbar render very little and are very nicely composable. Web components are just standard JavaScript so should work fine with any framework.
We aren’t moving in a React-first direction and even if we were web components are still a great fit for sharing logic between different component systems which is exactly why we’re looking more into doing exactly that.
We’re in agreement here! Components should be extensible and customisable. This is about how we deliver patterns and technology choices matter very little here. Again I think any specific issues with the markdown toolbar element should be resolved, and we should work to ensure we can share as much as possible between our two platforms to ensure consistency. I think it’d benefit us greatly here to talk about just the specific issues with markdown toolbar element so we can resolve those and move forward productively! |
Thanks @keithamus for your thoughts here and I appreciate your perspective. Just for posterity sake, this code approach was discussed more broadly in this Slack Convo with @mattcosta7 and @dgreif. Disclaimer: The following is my POV and what I would want from the perspective of a React developer. I agree with @iansan5653 stated points on this and he did an incredible job breaking down why he choose this solution. Web Components and React Components work together for simple tasks from my experience, but requiring them to work together and blocking this Pull Request seems like the wrong approach to me. My team has attempted to use some web components features in the past, but we found that having more control over the components in React were easier to use, customize and test than using the Web Component primitives. We have also removed all (or most) of the web components in our project. Offering extensibility in the React code for features we will be using feels like the right approach and the approach I believe most React developers will prefer. I think having to access imperative DOM APIs to interact with Web Component and needing refs to interact with the DOM nodes directly is not a great experience and not an experience that I would be happy using. Blocking this PR will also continue requiring some repos to have a dependency on I feel strongly that we should have reasonable expectations on what we require for developers to share their components upstream to PRC. The ask here, to me, seems like a high bar for @iansan5653 to assess and re-work (if we decide this is the best approach) as someone that is not a core contributor to the Primer React or Web Component repositories. Teams that are in need of this component requested this port from @iansan5653's team. Which he has graciously done with this work. I think to continue fostering teams to share their React components for other teams to enjoy, we need to find a balance of making the barrier to entry much easier. I've personally not seen great experiences or examples of Web Components and React paradigms working well together and I believe that is 100% ok. I will argue that having shared React components that are accessible, easy to build and quickly shareable in this repository or others supersedes requiring them to work with both React and Web Component. |
Mostly just adding some commentary to a few points in the comments above since I was tagged here
Conceptually, I agree with this, however the support for web components in react is just not really that good yet. Most of these shortcomings can be worked around, but not very idiomatically. React 19+ should fix this, assuming React releases it on that release. React 17 is still the most commonly used version at github, with the same flaws as React 18 from these tests. I guess the important question is - do we ship this knowing we unblock some use-cases now or hold off, keeping those trying to use these features blocked until the underlying issues are resolved?
Agreed there, but also not sure it's worth blocking progress here for the time being, until someone can pick that work up?
I don't think, given the issues we can't yet address (theming, for one) that sounds like a path forward here - without the workarounds mentioned which don't seem worth it to me on the surface
I think the majority of issues here lie in the fact that primer bundles a commonjs build that attempts to require its dependencies, some of which are esm only builds - leading to runtime errors that can't be rectified just in the toolchains. Primer/react could modify its build system to bundle the dependencies it relies on or stop shipping any commonjs (preferable I think), but I'm not aware of what the deprecations plan would ideally look like there. The other issues around calling
While this does seem like a goal that's understood as necessary, I'm curious what the timeframe is for this. Weeks to months sounds likelier than days to weeks (I don't believe there's ongoing work towards this yet, but maybe it is).
except the many cases where they don't work with react well out of the box, or where they require very non-idiomatic escape hatches to work with. Maybe we need to implement a react friendly wrapper, and still hope to not run into the issues that need code that just isn't react friendly.
It looks like it does define types, but it doesn't define I think @Lukeghenco sums up a lot of my thoughts here well with the sentiments of:
We should probably try to provide this level of support for web components used in react, however I think that will require a good deal of effort to do well, while providing an idiomatic way to interact with them (that's also typesafe)
I think we might be at a good place to consider this a reasonable path, for now, even if we accept that we might need to re-work some of this later. That seems to be what the 'drafts' in prc are for. quick iteration, learning and eventual refining
It can happen, but agreed it is not the norm, and not something that just works. This is a failing of react more than anything, but a reality we need to accept some for the time being. We're also limited by our other react technical choices (theming for one) and until those underlying issues are handled, some latitude to work around them while maintaining productivity seems reasonable |
Thanks @mattcosta7 for your thoughtful responses to both me and @keithamus concerns here. My biggest concern here is this being a blocked PR for reasons I worry it shouldn't be blocked for. I think the solution is a good enough solution for now and I think it is very likely to assume this components internal workings will change a lot in the next weeks/months. As for this comment:
I am glad that React is working to improve the interoperability experience with web components though, since they are here to stay as good or bad as that might be depending on your POV of them being standardized as a web standard. I think that React improving this is a win for everyone in web development though. |
Can we please focus on the specific issues we're currently facing, so they can be remediated? Definitely don't want to block anyone but I cannot see what this is fundamentally solving. |
@keithamus there are two fundamental issues here: First is the immediate issue, that
Only (3), (4), and (5) are actually viable options here, since (1) and (2) are not working. So we either need to make breaking changes to There is another less immediate issue at hand here, which is that the react/src/drafts/MarkdownEditor/_FormattingTools.tsx Lines 26 to 74 in 9eaa37e
This is defeating the entire purpose of a web component, which is that it renders content to the DOM - it would be much cleaner to have a Why we are doing this is a bit more complicated to explain but it comes down to needing to just use React components in React components. We initially did try (for a long time) to render the toolbar as a toolbar but ran into a myriad of issues that prevented this from working, all of which stem from the fact that the toolbar and its buttons are too different from the corresponding React components. It's far easier to just render a set of So that's why I opened this PR. Option (4) not only avoids breaking changes upstream, it also cleans up this hacky code. I think (5) is the obvious best solution, but I don't have the capacity to implement it at the moment and this fix is somewhat urgent. |
Thanks for outlining this @iansan5653. We're actively discussing this and we'll get back shortly. I think solution 3 should be workable without making breaking changes to either codebase, as the code will run safely client side and server side can be stubbed out if we need to run server side. The concerns we have with solution 4 is that it may introduce more technical debt with duplicate code which is why we're hesitant to merge. |
I'm interested to see what this might look like as a non-breaking change. From my admittedly limited understanding of SSR, I'm not sure if this is as simple as "don't register unless the |
Custom Elements don't need to be registered server side so the first one is applicable. |
From an SSR perspective, adding a simple |
I think the best approach may be as I described in #2353 - which is to have a client side bundle that can pull in these dependencies:
|
This is an interesting idea, but also sounds a lot like the conditional require that is causing issues already. Would the PRC consumer be responsible for the conditional loading? |
To me that would be the most sensible idea. It’s highly likely that consumers of PRC are already building a client side bundle with client side only code such as polyfills. Also likely they aren’t bundling for server side as it’s not super compelling to do so. |
I don't think it's a common approach to making consumers bundle separately for those environments, where possible not to, and might lead to some friction in doing so. A separate import of like
It does sound like those provide a bit of a rough way to interact with, vs having the import in primer code directly, handling environments gracefully and making this an experience consumers don't need to consider I'm also wonder whether there's potential namespacing issues (because web-component namespacing isn't a thing) and this isn't just consumed internally. What do consumers who have a I think in this case it's a bit more complicated because the elements area hidden implementation detail. consumers of primer/react don't even know these exist - will they even understand the errors if they run into them?
is that true? I'm not sure how many common ssr frameworks handle this, since it's been a long time since I've had to look at it, there's definitely at least some build that might be used in both client and server. Most react libraries, at least historically, take the 'verify if window exists' approach and specifically move window apis into |
I've opened an issue in github/markdown-toolbar-element#68 where we can discuss strategies for resolving this problem with the element itself. |
Just wanted to follow-up on this PR after some of the discussions on Slack today to see if we're aligned on next steps 👀 It seems like there might be a chance that the underlying In terms of next steps then, what do folks think of:
|
Hi! This pull request has been marked as stale because it has been open with no activity for 60 days. You can comment on the pull request or remove the stale label to keep it open. If you do nothing, this pull request will be closed in 7 days. |
In the
MarkdownEditor
, we are usingmarkdown-toolbar-element
to power basic formatting tools (bold, italics, etc). However, this is a custom element and not a React component, so this strategy comes with some complications.The way we are using the element is by rendering it with
display: none
and then calling.click()
on the underlying elements. This is really hacky but it's necessary because themarkdown-toolbar-element
package doesn't expose the underlying logic at all.In addition to the hackiness, the dependency is problematic because it performs side effects (registering custom elements) on import, so we have to conditionally
require
the dependency in auseEffect
callback to avoid calling browser APIs during server-side rendering. This mix ofrequire
andimport
confuses transpilers and occasionally breaks things in unexpected and hard-to-debug ways (for example, see this Slack thread).To get around this issue, I tried using
require()
inside auseEffect
instead (pull request). This fails due to a known bug in the JavaScript engine that causes a segmentation fault.So to resolve both the hackiness of this approach and the issues with SSR/unit tests, I've decided to remove the dependency altogether and inline the logic instead. The downside to this is that we are duplicating the logic in that dependency when the whole point of the dependency was to help remove duplication, but there are many upsides.
While inlining this logic, I've tried to make as few changes as possible in order to reduce risk. This means that I left some refactoring opportunities on the table for future work - I've only made the following changes:
insertText
function in favor of using our much more robustsyntheticallyChangeInput
strategy. This required changing the logic inside formatting functions a bit to avoid directly passing around / mutating theHTMLTextareaElement
instance.expandSelectionToLine
with thegetSelectedLineRange
util, since this was an easy and simple win for removing duplicationAn even better solution would be to extract all the logic that's shared between the PVC and PRC Markdown editors into a new common behavior dependency. This could include formatting, list editing, file handling, etc... But this is not something that I have the capacity for at the moment, and I don't want to delay this fix as it's actively blocking work on downstream teams.
Screenshots
There are no visual changes.
Merge checklist
[ ] Added/updated tests[ ] Added/updated documentationTake a look at the What we look for in reviews section of the contributing guidelines for more information on how we review PRs.