Skip to content
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

Closed
xsteadybcgo opened this issue Nov 1, 2021 · 57 comments
Closed

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

xsteadybcgo opened this issue Nov 1, 2021 · 57 comments
Labels
enhancement New feature or request

Comments

@xsteadybcgo
Copy link

xsteadybcgo commented Nov 1, 2021

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.

@xsteadybcgo xsteadybcgo added the enhancement New feature or request label Nov 1, 2021
@xsteadybcgo xsteadybcgo changed the title Can you add examples of how to use the framework with react/vue etc Examples of how to use the framework with react/vue etc Nov 1, 2021
@dzhavat
Copy link
Contributor

dzhavat commented Nov 1, 2021

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
Copy link
Member

hawkticehurst commented Nov 1, 2021

@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
Copy link
Member

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
Copy link
Member

hawkticehurst commented Nov 1, 2021

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
Copy link
Contributor

dzhavat commented Nov 1, 2021

@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
Copy link
Member

hawkticehurst commented Nov 1, 2021

@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
Copy link
Contributor

dzhavat commented Nov 2, 2021

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
Copy link

estruyf commented Nov 3, 2021

@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
Copy link

estruyf commented Nov 3, 2021

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
Copy link
Member

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
Copy link
Member

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
Copy link

estruyf commented Nov 3, 2021

@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
Copy link

estruyf commented Nov 3, 2021

@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
Copy link

estruyf commented Nov 3, 2021

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
Copy link

EisenbergEffect commented Nov 3, 2021

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
Copy link
Member

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
Copy link

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
Copy link

EisenbergEffect commented Nov 3, 2021

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
Copy link

estruyf commented Nov 3, 2021

@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
Copy link

@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
Copy link
Contributor

dzhavat commented Nov 3, 2021

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
Copy link
Member

@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
Copy link

estruyf commented Nov 4, 2021

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
Copy link

estruyf commented Nov 4, 2021

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
Copy link

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

@hawkticehurst
Copy link
Member

hawkticehurst commented Nov 4, 2021

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

@EisenbergEffect
Copy link

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
Copy link
Author

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

@estruyf
Copy link

estruyf commented Nov 5, 2021

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
Copy link
Member

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
Copy link

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
Copy link
Member

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
Copy link
Member

hawkticehurst commented Nov 16, 2021

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
Copy link
Member

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
Copy link

estruyf commented Nov 16, 2021

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

@hawkticehurst
Copy link
Member

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
Copy link
Member

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
Copy link
Member

hawkticehurst commented Jan 5, 2022

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.

  • Data grid cell-type attr ==> import DataGridCellTypes enum
  • Data grid row-type attr ==> import DataGridRowTypes enum
  • Data grid generate-header attr ==> import GenerateHeaderOptions enum
  • Divider role attr ==> import DividerRole enum
  • Dropdown position attr ==> import DropdownPosition enum
  • Radio group orientation attr ==> import RadioGroupOrientation enum
  • Text area resize attr ==> import RadioGroupOrientation enum
  • Text field type attr ==> import TextFieldType enum

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
Copy link
Member

🎊 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
Copy link

lppedd commented Nov 29, 2023

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

@hawkticehurst
Copy link
Member

hawkticehurst commented Nov 30, 2023

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 schema option in app.module.ts. By default custom elements (aka web components) don't work in Angular without this setting configured.

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

@chrisdholt
Copy link
Member

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

@lppedd
Copy link

lppedd commented Nov 30, 2023

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

@hawkticehurst
Copy link
Member

hawkticehurst commented Nov 30, 2023

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 (v2.49) correct?

@hawkticehurst
Copy link
Member

I'll give it a go on Angular 17 anyway, let's see.

Nice, please do let me know how it goes!

@chrisdholt
Copy link
Member

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 (v2.49) correct?

Yeah, there were a couple issues IIRC so I'm not 100%, but I know one issue with typings has been fixed there.

@hawkticehurst
Copy link
Member

Awesome, I'll give it a shot in the next few days and report back

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants