microsoft / vscode-webview-ui-toolkit

A component library for building webview-based extensions in Visual Studio Code.
MIT License
2.02k stars 144 forks source link

Examples of how to use the framework with react/vue etc #283

Closed xsteadybcgo closed 3 years ago

xsteadybcgo commented 3 years ago

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

import { vsCodeBadge as VsCodeBadge } from '@vscode/webview-ui-toolkit/dist/dts/badge';

function Demo() {
  return <VsCodeBadge> demo </VsCodeBadge>
}

It seems to be not possible.

dzhavat commented 3 years ago

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?

hawkticehurst commented 3 years ago

@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.

hawkticehurst commented 3 years ago

Also thanks for chiming in @dzhavat!

As I just mentioned above, our recommendation is to use fast-react-wrapper, but I see you're using wc-react and I'm curious to know what your experience has been with that package?

hawkticehurst commented 3 years ago

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. 🙂

dzhavat commented 3 years ago

@hawkticehurst It's actually not me who is using wc-react. I only mentioned it because I noticed that the Front Matter extension are using it. Maybe @estruyf can share his experience with the vscode-webview-ui-toolkit package.

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 localResourceRoots setting that is passed to createWebviewPanel in order to limit the paths from which the webview can load local files. So I had to "allow" the webview-ui-toolkit folder. So that was one difference between the examples you've given and my extension.

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

hawkticehurst commented 3 years ago

@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 localResourceRoots, I had included that detail as part of the Getting Started Guide at one point but decided to remove it to keep what was starting to feel like a pretty involved getting started guide a bit leaner. I think I'll put it on my list to add that information into the sample extension as a sort of documentation addendum.

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 toolkit.min.js that may help a bit with the file size dilemma.

dzhavat commented 3 years ago

As a temporary solution (if you haven't discovered it yet), we also ship a toolkit.min.js that may help a bit with the file size dilemma.

Yes, I noticed it. The file size is actually not that bad even with the extra files from node_modules but I really would like to avoid including more files than strictly necessary. So I will most likely use the toolkit.min.js in order to use some of the components in my extension(s) but will also keep an eye on #74 :)

estruyf commented 3 years ago

@dzhavat @hawkticehurst I'm indeed using wc-react which is developed by someone from the Microsoft Graph teams that worked on the Microsoft Graph Toolkit. These are web components created with lit, very similar to fast.

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 fast-react-wrapper a try. I'm assuming it will just work similarly to wc-react as it only wraps a web component into a React element and allows you to pass properties.

estruyf commented 3 years ago

Just tested the fast-react-wrapper library, but it seems that it has only been created for Fast Web Components. Plus it is not so straightforward to get it working.

The wc-react or reactify-wc libraries allow you to just load the web component and wrap it. It is not as complicated as the fast-react-wrapper.

chrisdholt commented 3 years ago

Just tested the fast-react-wrapper library, but it seems that it has only been created for Fast Web Components. Plus it is not so straightforward to get it working.

The wc-react or reactify-wc libraries allow you to just load the web component and wrap it. It is not as complicated as the fast-react-wrapper.

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?

hawkticehurst commented 3 years ago

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?

estruyf commented 3 years ago

@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 provideFASTDesignSystem() method.

The second thing about it is that the fastCard and fastButton WCs are functions, which allows them to wrap these by invoking their function, but the ones from this library @vscode/webview-ui-toolkit are classes. Which cannot be invoked the same way.

The wc-react and reactify-wc only requires to load the web component and wrap them. This looks as follows:

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());
estruyf commented 3 years ago

@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 "react": "^16.9.0".

It would be better to add the following:

"peerDependencies": {
    "react": "^16 || ^17 || ^18"
}
estruyf commented 3 years ago

Ok, start to understand how the fastCard function works

Screenshot 2021-11-03 at 20 22 45

For the badge that is provided above, it would be the vsCodeBadge that needs to be used in order to wrap the web component. That might do the trick, but that still leaves other web components behind, as they are not using the compose method.

EisenbergEffect commented 3 years ago

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.

hawkticehurst commented 3 years ago

That might do the trick, but that still leaves other web components behind, as they are not using the compose method.

I'll quickly chime in to say that as of v0.8.3 we updated the toolkit to FAST v2 which means that all components are now created using the compose method and are exported as functions (scroll to the bottom of any component index.ts file).

This is something that will be heavily documented once the React sample/docs are created/available and hopefully resolve a lot of this confusion.

EisenbergEffect commented 3 years ago

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.

EisenbergEffect commented 3 years ago

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.

estruyf commented 3 years ago

@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 🙏

EisenbergEffect commented 3 years ago

@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.

dzhavat commented 3 years ago

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 :)

hawkticehurst commented 3 years ago

@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 🙂

estruyf commented 3 years ago

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 compose method.

Test 1 🚨

Once updated to 0.8.3, I did the following:

import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import { vsCodeCheckbox } from '@vscode/webview-ui-toolkit';

const { wrap } = provideReactWrapper(React);

export const VsCheckbox = wrap(vsCodeCheckbox());

This results in a lot of errors during the build:

ERROR in /Users/eliostruyf/nodejs/vscode/vscode-front-matter/src/panelWebView/components/VscodeComponents.ts
    ./src/panelWebView/components/VscodeComponents.ts
    [tsl] ERROR in /Users/eliostruyf/nodejs/vscode/vscode-front-matter/src/panelWebView/components/VscodeComponents.ts(29,32)
          TS2769: No overload matches this call.
      Overload 1 of 2, '(registry: FoundationElementRegistry<FoundationElementDefinition, any>, config?: ReactWrapperConfig<unknown> | undefined): Constructable<...>', gave the following error.
        Argument of type 'FoundationElementRegistry<CheckboxOptions, Constructable<FoundationElement>>' is not assignable to parameter of type 'FoundationElementRegistry<FoundationElementDefinition, any>'.
          The types returned by 'new type(...)' are incompatible between these types.
            Type 'import("/Users/eliostruyf/nodejs/vscode/vscode-front-matter/node_modules/@microsoft/fast-foundation/dist/fast-foundation").FoundationElement' is not assignable to type 'import("/Users/eliostruyf/nodejs/vscode/vscode-front-matter/node_modules/@microsoft/fast-react-wrapper/node_modules/@microsoft/fast-foundation/dist/fast-foundation").FoundationElement'.
              Types have separate declarations of a private property '_presentation'.
      Overload 2 of 2, '(type: Constructable<HTMLElement>, config?: ReactWrapperConfig<unknown> | undefined): Constructable<any>', gave the following error.
        Argument of type 'FoundationElementRegistry<CheckboxOptions, Constructable<FoundationElement>>' is not assignable to parameter of type 'Constructable<HTMLElement>'.
          Type 'FoundationElementRegistry<CheckboxOptions, Constructable<FoundationElement>>' provides no match for the signature 'new (...args: any[]): HTMLElement'.

Test 2 🚨

I tried to wrap it as follows:

import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import { Checkbox } from '@vscode/webview-ui-toolkit/dist/esm/checkbox';

const { wrap } = provideReactWrapper(React);

export const VsCheckbox = wrap(Checkbox, { name: `vscode-checkbox` });

Build runs fine, but no checkboxes are shown. Also tried it with the previous wrapper I was using, but the same happens.

Screenshot 2021-11-04 at 09 31 25

Test 3 ✅

Back to the older version 0.8.1 I was originally using, and wrapping it as follows:

import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit';

const { wrap } = provideReactWrapper(React);

export const VsCheckbox = wrap(VSCodeCheckbox, { name: `vscode-checkbox` });

This works fine, but also all web components get registered.

Test 4 ✅

To load a single web component, I did the following with versions 0.8.1 and 0.8.2.

import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import '@vscode/webview-ui-toolkit/dist/esm/checkbox';

const { wrap } = provideReactWrapper(React);

class Dummy {}
export const VsCheckbox = wrap(Dummy as any, { name: `vscode-checkbox` });

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.

estruyf commented 3 years ago

Another thing I just noticed, when I use the FAST wrapper, I get the following errors in the console:

Screenshot 2021-11-04 at 11 22 35

EisenbergEffect commented 3 years ago

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
  );
EisenbergEffect commented 3 years ago

The runtime error above doesn't look like a FAST-specific issue. FAST doesn't use JSON at all internally. That might be a bug in the specific component or something wrong with the data being passed to it.

estruyf commented 3 years ago

@EisenbergEffect seems to happen in the wrapping. Could it be some serialization of properties?

estruyf commented 3 years ago

@EisenbergEffect tested the approach you proposed, this is the result:

Screenshot 2021-11-04 at 16 45 56

Import needs to happen as follows:

Screenshot 2021-11-04 at 16 45 42

Build is full of errors:

Screenshot 2021-11-04 at 16 47 11

Edit: The following line should be added to the index barrel file:

export * from './vscode-design-system';

Edit 2: When fixing the import, the next issue arises:

image

EisenbergEffect commented 3 years ago

Actually, it looks like the design system provider hasn't been exported from here https://github.com/microsoft/vscode-webview-ui-toolkit/blob/main/src/index.ts That looks to be the source of these errors. I didn't see that missing when I was making the recommendation. @chrisdholt Do you know the status of the API update? Looks like a few things aren't exported yet.

hawkticehurst commented 3 years ago

@EisenbergEffect we actually export all the components + design system provider from a different file (src/index-rollup.ts), so I believe it should be available.

https://github.com/microsoft/vscode-webview-ui-toolkit/blob/main/src/index-rollup.ts

hawkticehurst commented 3 years ago

Altho now that I look closer, it looks like it's exported as FASTDesignSystem and registers all components (versus just exporting the individual design system provider function) 🧐

EisenbergEffect commented 3 years ago

That index-rollup.ts is not part of the ES module code that customers will get through an NPM install. That's only for single-file script tag scenarios. We'll need to add the exports directly to the index.ts so that customers using NPM can access them. I think it was just an oversight. See here for the same file in FAST Components: https://github.com/microsoft/fast/blob/master/packages/web-components/fast-components/src/index.ts#L4-L5

hawkticehurst commented 3 years ago

Ah! You're 1000% right, sometimes forget that my npm run build script is comprised of rollup and tsc 😅

Anywho, yeah that does look to be an oversight, I can quickly make that change now and publish a new version of the package. Also while I'm at it, does that FASTDesignSystem in index-rollup look like an oversight too? Should that be VSCodeDesignSystem?

EisenbergEffect commented 3 years ago

Yes, that looks like a copy/paste error. That should be renamed.

hawkticehurst commented 3 years ago

Great, opened a PR and added you and @chrisdholt as reviewers in case you have a free moment to give a sanity check. Have to step away to do something else for 10 minutes, but after that, I'll go ahead and merge/publish regardless.

hawkticehurst commented 3 years ago

Alright the update has been published (v0.8.4)!

EisenbergEffect commented 3 years ago

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.

xsteadybcgo commented 3 years ago

Great job! We are looking forward to the addition of this to the official documentation

estruyf commented 3 years ago

Export looks good, although I still got the build issues with the above sample:

ERROR in /Users/eliostruyf/nodejs/vscode/vscode-front-matter/src/panelWebView/components/VscodeComponents.ts
    [tsl] ERROR in /Users/eliostruyf/nodejs/vscode/vscode-front-matter/src/panelWebView/components/VscodeComponents.ts(54,32)
          TS2769: No overload matches this call.
      Overload 1 of 2, '(registry: FoundationElementRegistry<FoundationElementDefinition, any>, config?: ReactWrapperConfig<unknown> | undefined): Constructable<...>', gave the following error.
        Argument of type 'FoundationElementRegistry<CheckboxOptions, Constructable<FoundationElement>>' is not assignable to parameter of type 'FoundationElementRegistry<FoundationElementDefinition, any>'.
          The types returned by 'new type(...)' are incompatible between these types.
            Type 'import("/Users/eliostruyf/nodejs/vscode/vscode-front-matter/node_modules/@microsoft/fast-foundation/dist/fast-foundation").FoundationElement' is not assignable to type 'import("/Users/eliostruyf/nodejs/vscode/vscode-front-matter/node_modules/@microsoft/fast-react-wrapper/node_modules/@microsoft/fast-foundation/dist/fast-foundation").FoundationElement'.
              Types have separate declarations of a private property '_presentation'.
      Overload 2 of 2, '(type: Constructable<HTMLElement>, config?: ReactWrapperConfig<unknown> | undefined): Constructable<any>', gave the following error.
        Argument of type 'FoundationElementRegistry<CheckboxOptions, Constructable<FoundationElement>>' is not assignable to parameter of type 'Constructable<HTMLElement>'.
          Type 'FoundationElementRegistry<CheckboxOptions, Constructable<FoundationElement>>' provides no match for the signature 'new (...args: any[]): HTMLElement'.

Now the following works:

import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import { Checkbox, provideVSCodeDesignSystem } from '@vscode/webview-ui-toolkit';

const { wrap } = provideReactWrapper(React, provideVSCodeDesignSystem());

export const VsCheckbox = wrap(Checkbox, { name: `vscode-checkbox` });

although this still results in rendering no checkboxes

image

Seems that the web component isn't correctly registered on the page:

image

hawkticehurst commented 3 years ago

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!! 🙂🙏

EisenbergEffect commented 3 years ago

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.

connor4312 commented 3 years ago

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.

hawkticehurst commented 3 years ago

I was giving this a shot for the Hex Editor, and though I was wrapping as described earlier

@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.

hawkticehurst commented 3 years ago

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.

estruyf commented 3 years ago

@hawkticehurst that is great news! Looking forward to the upcoming changes. Again, happy to give it a try once available for testing.

hawkticehurst commented 3 years ago

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.

hawkticehurst commented 3 years ago

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.

hawkticehurst commented 2 years ago

Update time

As of toolkit package v0.8.5 (released today), we should finally be unblocked in the ability to properly wrap toolkit components as React components! 🎉

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 components

To those ends, I thought a quick guide might be helpful in directing people on how to get set up.

  1. Update the toolkit package to v0.8.5
  2. npm install @microsoft/fast-react-wrapper
  3. Create a toolkit.tsx file which will contain all the wrapped toolkit components (alternatively you can individually wrap the components in the files in which they will be used, but I think this method is easier/cleaner to maintain).
// 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());
  1. From here you should be able to import and use the React components as you desire
// 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 caveats

Two things to keep in mind if you decide to go this route are:

Importing attribute enums

At 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 declaration

As discussed earlier this thread in the cases where custom component events are needed, you may need to update the toolkit.tsx component wrap declarations to include those events.

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.

hawkticehurst commented 2 years ago

🎊 Toolkit package v0.9.0 just dropped today and now ships a set of React components!

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!

lppedd commented 1 year ago

It should be easy, but has anyone ever integrated these web components into an Angular application/webview?