-
Notifications
You must be signed in to change notification settings - Fork 144
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
Examples of how to use the framework with react/vue etc #283
Comments
Just came across this. For usage in React, you might want to take a look at what Front Matter are doing. Maybe relevant to you? |
@xsteadybcgo Thanks for reaching out! Creating a comprehensive React sample extension/docs is very high on our priority list (we already have an open issue for it in our sample extension repo). I plan to start on the sample by end of this week or the beginning of next week at the latest. Sample extensions/docs for other popular frameworks (Vue, Svelte, etc.) will also eventually be created, but requests for a React sample have been one of the biggest asks since the toolkit entered public preview so that will come first. A Temporary Solution As an immediate unblocker (while you wait for the samples), our recommendation is the use the fast-react-wrapper. It's a utility package made by the FAST team (FAST is the framework we use to build the toolkit) that enables automatically wrapping Web Components in a React component. We'll be using it in the sample extension and will have documentation covering how to use it, but hopefully, you can get a bit of a head start. |
Oh, I'm also going to close this issue since it's a duplicate of the one in the sample repo, but feel free to continue the discussion in this thread and I'll keep an eye on it in case there are any further comments/questions. 🙂 |
@hawkticehurst It's actually not me who is using I installed the package in one of my extensions and tried using it. My extension is building the webview using HTML directly. so I'm not using any frameworks like React, Angular, etc. I had some issues getting the components to render in the webview but that was because I'm using the The other issue I have is how the js file is loaded (as shown in your examples). This adds a lot of files into the final extension package and its file size grows. The issue is well described in #74 |
@dzhavat Oh whoops, my bad! I remembered that @estruyf was the main dev, but thought that maybe you were also a contributor to the project. 😅 But in that case, @estruyf feel free to chime in with your thoughts if you have any. Also thanks for mentioning Also good to hear that feedback on file size. As part of the sample extension work I mentioned above, there's also a backlog item to demonstrate how to use various build system tooling (i.e. webpack, rollup, vite, snowpack, etc.) in extensions so that we can hopefully take advantage of ESM/tree shaking in the toolkit and resolve #74. As a temporary solution (if you haven't discovered it yet), we also ship a |
Yes, I noticed it. The file size is actually not that bad even with the extra files from |
@dzhavat @hawkticehurst I'm indeed using I had some issues with it, and created my own fork from it, as the PR I created wasn't yet accepted. As I want to make the switch with Front Matter to this library, I'm open to giving the |
Just tested the The |
Hey @estruyf, I'm a maintainer of FAST. We'd be happy to help, advise, or take feedback here. Do you have a small repro that we could look at perhaps? Alternatively, can you share what version you're working on and a summary of the issues you're running into as far as complexity? |
+1 to this, I would also love to know any more details about the complexity and any thoughts on how it might be improved 🙂 Also, could you clarify "it seems that it has only been created for Fast Web Components"? The toolkit is built using FAST, so just curious if you ran into some FAST-related issues while trying to set things up that we should potentially address? |
@chrisdholt @hawkticehurst I do not have a sample repo at hand, but here are my thoughts about it. Let us start with the code sample from the documentation: https://www.fast.design/docs/integrations/react/ import {
provideFASTDesignSystem,
fastCard,
fastButton
} from '@microsoft/fast-components';
import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import React from 'react';
const { wrap } = provideReactWrapper(
React,
provideFASTDesignSystem()
);
export const FastCard = wrap(fastCard());
export const FastButton = wrap(fastButton()); All I need is a simple wrapper for a web component, here I need to first configure a provider, and then I can start wrapping the components. It is not something major, as it is only required once, but what if you have multiple web components? That probably requires multiple providers, as some would not need the The second thing about it is that the The import {wrapWc} from 'wc-react';
import '@vscode/webview-ui-toolkit/dist/esm/checkbox';
const VsCheckbox = wrapWc(`vscode-checkbox`); That is actually all, which is much easier IMO than specifying the wrapper. To make it easy to use, I would suggest doing something similar. import { fastWrap } from '@microsoft/fast-react-wrapper';
import '@vscode/webview-ui-toolkit/dist/esm/checkbox';
// Normal wrapping
const VsCheckbox = fastWrap(`vscode-checkbox`);
// Fast design provider
const VsCheckbox = fastWrap(`vscode-checkbox`, provideFASTDesignSystem()); |
@chrisdholt I would also recommend adding React 17 and 18 to the peer dependencies. As to test it out, I had to downgrade my version as it is currently using It would be better to add the following:
|
It looks like we need to improve our documentation since there are a few misunderstandings here. The first thing I'd point out is that you only need to create the provider once. That step is there to allow you to use whatever version of React you want. It's not internally linked to a specific React implementation. Once you create the provider, you can wrap as many components as you want. For example: const { wrap } = provideReactWrapper(React);
export const ReactComponent1 = wrap(Component1);
export const ReactComponent2 = wrap(Component2);
export const ReactComponent3 = wrap(Component3);
export const ReactComponent4 = wrap(Component4); The second thing to realize is that the wrapper can take different types of input depending on how your component is written. If the component is a FAST of FluentUI Web Component, you can just pass the component registration function: export const FluentCard = wrap(fluentCard()); If the component was written with Lit, you can pass the component class and the element name: export const MyReactComponent = wrap(MyComponent, { name: 'my-component' }); If the component is defined with some other library which doesn't define properties on the component prototype (making them impossible to discover), then you can also provide a list of properties: export const MyReactComponent = wrap(
MyComponent,
{
name: 'my-component',
properties: [
'list',
'properties',
'here'
]
}
); In any of the above cases, if you need to use non-standard events from the React side, you will need to provide the list of events. This is because there is no way to discover those through metadata and the wrapper needs to know so that they are handled correctly with real dom rather than the virtualized dom system. For example, for the menu-item: export const FastMenuItem = wrap(
fastMenuItem(),
{
events: {
onExpandedChange: 'expanded-change'
}
}
) This will also provide strong typing for the events on the React component. Full documentation is here: https://www.fast.design/docs/integrations/react We'll look at expanding this to help clarify these different scenarios. |
I'll quickly chime in to say that as of This is something that will be heavily documented once the React sample/docs are created/available and hopefully resolve a lot of this confusion. |
Also, the design system is only needed for the FAST or Fluent components. One provider is needed only. You can register non-FAST components with the same wrapper. It doesn't matter. That just provides a design system to the wrapper so that it can automatically register the appropriate components with the design system at the same time. |
One further note, if we only wrap based on element name, there will be no type checking available on the React components. We pass the type so that we can both extract metadata so that properties are handled correctly by React, but also so that we can provide the proper TS types to get compile-time checking on the wrappers. Passing the component also creates a static link, which ensures things aren't unnecessarily tree-shaken by some tools. |
@EisenbergEffect 👍 that is a great explanation. That makes it easy to understand. Would indeed be great that it gets added to the documentation. Proper TS types is indeed good, although in many cases it means you need to maintain it yourself. As when you're not the owner of the library, you need to provide all these properties yourself. Not a bad thing at all, but something to keep in mind when updating a library and doing changes in two places. I'll try it again tomorrow with the proposed approaches. Thanks 🙏 |
@estruyf I've added a todo to my list to make a second pass on the documentation based on this discussion. Thank you for the feedback! As far as typing goes, we tried to make it so there wasn't much maintenance for the "golden path" of using components based on FAST. In that scenario we have metadata from FAST and we know the types coming from FAST, so we can project those to React automatically. The next best path is Lit and similar libraries, because we know how those work and while we can't determine everything automatically, we can project some of the details into types automatically. The last case is the least optimal, listing all the properties, and shouldn't normally be needed. We enable it just in case. I'm hopeful that we'll be able to continue to improve the wrapper so that we can maybe handle the event typing automatically at least for FAST-based components at some point. |
I can see that this issue has lead to a good discussion. I personally don't use React in my extensions (hand-written HTML and JS for now) but am still very much interested in the toolkit, so I'm keeping an eye on it :) |
@dzhavat Nice! Well please always feel free to chime in/open issues if you find anything that could be improved about the vanilla HTML/JS toolkit experience 🙂 |
As promised, I went back to my tests and implemented the wrapper as suggested above. All works fine with the web components I'm using, except for the web components from this project. I first updated the library to the latest version. As I was using an older version, and that was causing the issue of using the wrapper because the components were not using the Test 1 🚨Once updated to
This results in a lot of errors during the build:
Test 2 🚨I tried to wrap it as follows:
Build runs fine, but no checkboxes are shown. Also tried it with the previous wrapper I was using, but the same happens. Test 3 ✅Back to the older version
This works fine, but also all web components get registered. Test 4 ✅To load a single web component, I did the following with versions
This is not a clean approach to use but allows you to load an individual component. Option 1 is the preferred one, but not sure how to make it work. |
The following is what should work for React with the latest versions of the libraries. If it doesn't, then we definitely have a bug. It seems that the issue could be something not quite right with the TS types, but not necessarily a runtime issue. import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import { vsCodeCheckbox, provideVSCodeDesignSystem } from '@vscode/webview-ui-toolkit';
const { wrap } = provideReactWrapper(
React,
provideVSCodeDesignSystem()
);
export const VsCheckbox = wrap(vsCodeCheckbox()); For others using Vue or other frameworks, you don't need the wrapper, just the design system setup and component registration. That looks like this: provideVSCodeDesignSystem()
.register(
vsCodeCheckbox(),
// just list the components here
); |
Alright the update has been published ( |
Ok, with this new release, this code should work: import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import { vsCodeCheckbox, provideVSCodeDesignSystem } from '@vscode/webview-ui-toolkit';
const { wrap } = provideReactWrapper(
React,
provideVSCodeDesignSystem()
);
export const VsCheckbox = wrap(vsCodeCheckbox()); It's quite possible that we've had a collection of bugs across the various libraries that all intersected in this use case. So, if the above does not work, we can keep digging in. I intent to try this out myself and debug. I just may not get to it until tomorrow...or next week. |
Great job! We are looking forward to the addition of this to the official documentation |
Export looks good, although I still got the build issues with the above sample:
Now the following works:
although this still results in rendering no checkboxes Seems that the web component isn't correctly registered on the page: |
Hmmm how odd, unless @EisenbergEffect or @chrisdholt have some initial thoughts for today I think this will end up being something that all of us will dive deeper into next week once I start up on the React sample work and @EisenbergEffect has some more time to look into the wrapper. In the meantime, thank you so much for continuing to test this out and report back for your findings! It's been immensely helpful!! 🙂🙏 |
Realistically, I think it will be next week before I get a chance to dig in here, unfortunately. The code for the first way is the right way, but maybe we messed up our TS types somewhere? I'll work it out and report back. |
I was giving this a shot for the Hex Editor, and though I was wrapping as described earlier import { provideReactWrapper } from "@microsoft/fast-react-wrapper";
import { TextField, provideVSCodeDesignSystem } from "@vscode/webview-ui-toolkit";
import React from "react";
const { wrap } = provideReactWrapper(React, provideVSCodeDesignSystem());
export const VsTextField = wrap(TextField, { name: "vscode-text-field" }); ...it seems to mostly no-op The hex editor is using esbuild, if it matters. |
@connor4312 yeah, I haven't had much success with that method of wrapping components either. Instead, the following has worked for me: import { provideReactWrapper } from "@microsoft/fast-react-wrapper";
import { provideVSCodeDesignSystem, vsCodeTextField } from "@vscode/webview-ui-toolkit";
import React from "react";
const { wrap } = provideReactWrapper(React, provideVSCodeDesignSystem());
export const VsTextField = wrap(vsCodeTextField()); The big caveat here, however, is there is a known issue where wrapped toolkit components have incomplete component attribute type annotations resulting in TS intellisense/build errors. So if you're using the text field without any attributes you should hopefully be fine, otherwise, you'll have to wait a bit while this issue gets resolved. |
With all that said, there's been some internal discussion, and based on what we've seen from the community experience thus far we've decided to experiment with having the toolkit take on the responsibility of wrapping the toolkit components. We would ideally publish the wrapped components ourselves so that all of you might be able to consume them with an import syntax along these lines: import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; And hopefully, this will alleviate what has seemingly been a bit of a painful process to get React toolkit components working. More to come on this soon. |
@hawkticehurst that is great news! Looking forward to the upcoming changes. Again, happy to give it a try once available for testing. |
FYI: Here's the issue for publishing the toolkit components as a set of wrapped React components for those who want to follow its progress. |
Chiming in with another update to say that an (almost complete) first draft of the React sample extension and documentation is now viewable here! I'd appreciate feedback from anyone who has the time and interest to check it out. Also please note that the PR description has a couple of important notes/tips that should be read carefully. As a final heads up I'll be working on this PR a bit more tomorrow, but will be out of the office for the rest of the week due to the holiday weekend here in the US. |
Update timeAs of toolkit package There's still a bit of work left to do on shipping a set of React components, but for the truly eager (who don't want to wait any longer) you should be able to wrap your own components without too much issue now. A quick guide to wrapping toolkit componentsTo those ends, I thought a quick guide might be helpful in directing people on how to get set up.
// File: toolkit.tsx
import React from "react";
import { provideReactWrapper } from "@microsoft/fast-react-wrapper";
import {
provideVSCodeDesignSystem,
vsCodeBadge,
vsCodeButton,
vsCodeCheckbox,
vsCodeDataGrid,
vsCodeDataGridCell,
vsCodeDataGridRow,
vsCodeDivider,
vsCodeDropdown,
vsCodeLink,
vsCodeOption,
vsCodePanels,
vsCodePanelTab,
vsCodePanelView,
vsCodeProgressRing,
vsCodeRadio,
vsCodeRadioGroup,
vsCodeTag,
vsCodeTextArea,
vsCodeTextField,
} from "@vscode/webview-ui-toolkit";
const { wrap } = provideReactWrapper(React, provideVSCodeDesignSystem());
export const VSCodeBadge = wrap(vsCodeBadge());
export const VSCodeButton = wrap(vsCodeButton());
export const VSCodeCheckbox = wrap(vsCodeCheckbox());
export const VSCodeDataGrid = wrap(vsCodeDataGrid());
export const VSCodeDataGridCell = wrap(vsCodeDataGridCell());
export const VSCodeDataGridRow = wrap(vsCodeDataGridRow());
export const VSCodeDivider = wrap(vsCodeDivider());
export const VSCodeDropdown = wrap(vsCodeDropdown());
export const VSCodeLink = wrap(vsCodeLink());
export const VSCodeOption = wrap(vsCodeOption());
export const VSCodePanels = wrap(vsCodePanels());
export const VSCodePanelTab = wrap(vsCodePanelTab());
export const VSCodePanelView = wrap(vsCodePanelView());
export const VSCodeProgressRing = wrap(vsCodeProgressRing());
export const VSCodeRadio = wrap(vsCodeRadio());
export const VSCodeRadioGroup = wrap(vsCodeRadioGroup());
export const VSCodeTag = wrap(vsCodeTag());
export const VSCodeTextArea = wrap(vsCodeTextArea());
export const VSCodeTextField = wrap(vsCodeTextField());
// File: App.tsx (as an example)
import { VSCodeButton } from "./toolkit";
function App() {
return (
<h1>Hello world!</h1>
<VSCodeButton>Click me!</VSCodeButton>
);
}
export default App; Some caveatsTwo things to keep in mind if you decide to go this route are: Importing attribute enumsAt this time there is a slightly less convenient way of needing to define a handful of component attributes with enums instead of string values. There is an upstream issue open that will hopefully address this but, for the time being, if you use any of the following component attributes you will need to import an enum to use them.
Here's an example of all of them in use: // File: App.tsx (as an example)
import {
DataGridCellTypes,
DataGridRowTypes,
DividerRole,
DropdownPosition,
GenerateHeaderOptions,
RadioGroupOrientation,
TextAreaResize,
TextFieldType,
} from "@vscode/webview-ui-toolkit";
import {
VSCodeDataGrid,
VSCodeDataGridCell,
VSCodeDataGridRow,
VSCodeDivider,
VSCodeDropdown,
VSCodeOption,
VSCodeRadio,
VSCodeRadioGroup,
VSCodeTextArea,
VSCodeTextField,
} from "./toolkit";
function App() {
return (
<main>
<VSCodeDataGrid generate-header={GenerateHeaderOptions.none}>
<VSCodeDataGridRow row-type={DataGridRowTypes.header}>
<VSCodeDataGridCell cell-type={DataGridCellTypes.columnHeader} grid-column="1">
Header 1
</VSCodeDataGridCell>
<VSCodeDataGridCell cell-type={DataGridCellTypes.columnHeader} grid-column="2">
Header 2
</VSCodeDataGridCell>
</VSCodeDataGridRow>
<VSCodeDataGridRow>
<VSCodeDataGridCell grid-column="1">Cell Data</VSCodeDataGridCell>
<VSCodeDataGridCell grid-column="2">Cell Data</VSCodeDataGridCell>
</VSCodeDataGridRow>
</VSCodeDataGrid>
<VSCodeDivider role={DividerRole.separator}></VSCodeDivider>
<VSCodeDropdown position={DropdownPosition.below} open>
<VSCodeOption>Item 1</VSCodeOption>
<VSCodeOption>Item 2</VSCodeOption>
<VSCodeOption>Item 3</VSCodeOption>
</VSCodeDropdown>
<VSCodeRadioGroup orientation={RadioGroupOrientation.vertical}>
<label slot="label">Radio Group Label</label>
<VSCodeRadio>Radio Label</VSCodeRadio>
<VSCodeRadio>Radio Label</VSCodeRadio>
</VSCodeRadioGroup>
<VSCodeTextArea resize={TextAreaResize.both}>Text Area Label</VSCodeTextArea>
<VSCodeTextField type={TextFieldType.password}>Text Field Label</VSCodeTextField>
</main>
);
}
export default App; Custom event declarationAs discussed earlier this thread in the cases where custom component events are needed, you may need to update the This is something we're still working on for the React components that will be shipped by the toolkit, so I don't have a full picture of what custom events might be needed in these components. Just keep this in mind as you use the components and please do let me know if you run into issues! Hope that all helps and good luck to those who do try this out! If you run into any issues or questions regarding anything in this update feel free to ask here or open new issues. |
🎊 Toolkit package Using the new components is as simple as importing the toolkit package into your project and just adding '/react' to the end of the import path. For example: import { VSCodeButton } from '@vscode/webview-ui-toolkit/react'; Most of the original toolkit documentation should map 1:1 with the new React components, but dedicated documentation and sample extensions will be coming in the next few days. This work also wraps up one of the last big milestones as we work towards a 1.0 release. From here on it's just catching up on some important backlog issues, adding more documentation, and final polishing! |
It should be easy, but has anyone ever integrated these web components into an Angular application/webview? |
Hey @lppedd, have you seen our Angular extension sample? It demonstrates how to correctly configure an Angular app to use web components. One of the most important things (if I recall correctly) is to set the @NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA], // <-- make sure to configure this!
})
export class AppModule {} A big caveat If you're trying to use Angular 15+, I believe there's an unresolved upstream issue that is causing toolkit components to not work. Angular 15+ adopted a newer version of TypeScript that likely included a breaking change that conflicts with the version of TypeScript that FAST (the web component framework we use to build the toolkit) uses. |
@hawkticehurst I think we've got this solved for your version if you want to give it a go and see if the issue is resolved which I'm betting would be a big help :) |
@hawkticehurst hey, thanks! Now that you mentioned it, I can see the app component is using a web component in its template. Didn't even notice it the first time, sorry. I'll give it a go on Angular 17 anyway, let's see. |
Oh yay! Thanks for the heads up @chrisdholt, I'll definitely give it a shot! Also to clarify, you're saying to just upgrade to the latest stable version of FAST Foundation ( |
Nice, please do let me know how it goes! |
Yeah, there were a couple issues IIRC so I'm not 100%, but I know one issue with typings has been fixed there. |
Awesome, I'll give it a shot in the next few days and report back |
Feature/component description
I can't wait to put it into my vs code extension development, my webview uses react and I want to use these components in a jsx way instead of a template string, I didn't find an answer in the documentation for this way
Use case
It seems to be not possible.
The text was updated successfully, but these errors were encountered: