microsoft / vscode-webview-ui-toolkit

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

React button not showing icon #508

Open StephaneAdeso opened 11 months ago

StephaneAdeso commented 11 months ago

I am developing an extension for vscode with React and using the WebUi Toolkit library for components. I want to add a "save" icon to my button.

I have followed the documentation to add my button in react and add an icon. So the resulting code is:

import { VSCodeButton } from '@vscode/webview-ui-toolkit/react';    

<VSCodeButton >
    Save
    <span slot="start" className="codicon codicon-save"></span>
</VSCodeButton>

But the icon is not showing. How to add the icon to the button?

image

hawkticehurst commented 11 months ago

Hey @StephaneAdeso! Out of curiosity what does your content security policy look like in your extension? This looks like a case where the icon is being blocked from rendering.

As an example, your CSP should probably look something like this: https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/frameworks/component-gallery-react/src/panels/ComponentGalleryPanel.ts#L122

In particular, the font-src CSP rule should allow the codicons.ttf (required to render Codicons) file to be rendered.

StephaneAdeso commented 11 months ago

Thank you very much for answering. After several days without finding a solution, I finally decided to use an icon library. I must also say that you are right. I did not have any content security policy established. As soon as I have some time I'll try it. Thank you so much.

hawkticehurst commented 11 months ago

Of course! Please let me know how it goes so I can know if this issue is resolved and I can close it :)

StephaneAdeso commented 11 months ago

Sorry, i was unable to find a correct CSP configuration that works. All variations i tried throw errors so i removed it.

hawkticehurst commented 11 months ago

Hmm okay, in that case, could you create/provide a link to a GitHub repo with a small reproduction of the issue so I can see if I can figure out what the problem might be?

maninak commented 9 months ago

I'm using vue with the web components and I have the same issue.

My CSP is the following:

      <meta
        http-equiv="Content-Security-Policy"
        content="
          default-src 'none';
          object-src 'none';
          base-uri 'none';
          style-src ${allowedSource} 'unsafe-inline';
          img-src ${allowedSource} https: data:;
          script-src 'strict-dynamic' 'nonce-${nonce}' 'unsafe-inline' https:;
        "
      >

But I don't think it's a CSP issue because I get no CSP-related issues logged in the webview's dev tools' console and even if I delete the whole CSP I still have the icons not showing.

Repro steps

  1. $ git clone git@github.com:cytechmobile/radicle-vscode-extension.git
  2. $ git checkout 4ee3787d591a1612242b43b663bd7167fbee9fad (or browse source code)
  3. F5 (no need to install node modules)
  4. Ctrl + Shift + P (to open Cmd Palette) and select command "Open dat webview"

you should be seeing this

https://github.com/microsoft/vscode-webview-ui-toolkit/assets/7542054/146d0118-aea1-4536-8773-6426f38f5d1f

The related code can be found under src/webviews/src/components/Counter.vue and looks like this

    <vscode-button appearance="icon" @click="showInfoNotifWithCount">
      <span class="codicon codicon-megaphone"></span>
    </vscode-button>
r3m0t commented 9 months ago

You are missing the font-src directive, so default-src 'none' is taking precedence.

maninak commented 9 months ago

@r3m0t if you could share a diff so that I can better understand where I'm missing it that would help a lot, thank you!

I Ctrl + F for "font-src" in the docs and don't find anything.

That's what's shown in the docs image

hawkticehurst commented 9 months ago

Hey @maninak! Yes, as @r3m0t said this looks like a case of not including the font-src CSP directive (thank you for chiming in @r3m0t btw 😊).

You would add the font-src CSP to the content attribute in the meta tag you wrote out above. Something like this:

<meta 
  http-equiv="Content-Security-Policy" 
  content="
    default-src 'none'; 
    script-src 'nonce-${nonce}';
    style-src ${webview.cspSource}; 
    font-src ${webview.cspSource};   // <-- here!
">

The above example is specifically pulled from our component-gallery sample extension. Feel free to look there for more context.

Also, the content security policy docs live in Webview API VS Code documentation since CSP is a topic that applies to webview extension development in general, not just in the Webview UI Toolkit.

Hope that helps, but please let me know if there are any other questions you have!

maninak commented 9 months ago

Thank you both for the quick respons and especially @hawkticehurst for the detailed hints.

I've changed my CSP to be


      <meta
        http-equiv="Content-Security-Policy"
        content="
          default-src 'none';
          object-src 'none';
          base-uri 'none';
          style-src ${allowedSource} 'unsafe-inline';
          img-src ${allowedSource} https: data:;
          script-src 'strict-dynamic' 'nonce-${nonce}' 'unsafe-inline' https:;
          font-src ${allowedSource};
        "
      >

The above seemed to have no effect. Noteworthy is that the chromium devtools report no CSP warnings or errors! Neither did they before nor do they now after adding the font-src directive.

r3m0t commented 9 months ago

What does the Network tab show after filtering for Font?

Is the codicon.css stylesheet referenced in your project?

maninak commented 9 months ago

Hey @r3m0t, after reading your comment I didn't know to which codicon.css you were referring to. There's no mention of it on the Readme nor the Getting Started Guide.

After checking the link @hawkticehurst shared above I found that in that sample there's an explicit import for codicon.css which also imports codicon.ttf and both are local in the repo.

I copy-pasted those into my vue repo's assets/ folder. The vue repo is nested in my vscode extension which meant that the actual path is src/webviews/src/assets/codicon.css. Then I imported the css like so inside my App.vue.

<style>
@import './assets/codicon.css';
</style>

In codicon.css the font is loaded like so

 @font-face {
  font-family: "codicon";
  font-display: block;
  src: url("./codicon.ttf") format("truetype");
}

Here's is also a screencap of the filetree showing that those files coexist and colocate inside the dist dir that vite produces

See image

This has definitely moved me closer but now I'm seeing this in the console and the network tab image image

In my webview options I have localResourceRoots set with what seems to be a correct path to the location of the above assets:

  const webviewOptions: WebviewOptions = {
    enableScripts: true,
    localResourceRoots: [
      Uri.joinPath(getExtensionContext().extensionUri, 'dist'),
      Uri.joinPath(getExtensionContext().extensionUri, 'assets'),
      Uri.joinPath(getExtensionContext().extensionUri, 'src', 'webviews', 'dist'),
    ],
  }

It looks like it's a problem with how the ttf file's path resolves and honestly I have no clue how to fix it.

For reference, the index.css file gets loaded correctly and its path seems to be different (I guess vite patches behind the scenes) image

Shouldn't this very important info about copying and importing those files be in the readme/guide? Better yet, why isn't this an implementation detail handled by the UI lib itself and I have to copy and import those manually? Perhaps I am doing it wrong?

Thank you so much for your assistance! :pray:

maninak commented 9 months ago

OK, so after copying the css and ttf file under assets/ and importing it in my App.vue what was left was setting base: '' in the vue app's vite.config.ts which, for the sake of completion, now looks like this:

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
      'lib': fileURLToPath(new URL('../../lib', import.meta.url)),
      'utils': fileURLToPath(new URL('../utils', import.meta.url)),
    }
  },
  build: {
    rollupOptions: {
      external: ['vscode'],
      // produce predictable filenames without cache-busting SHA suffix
      output: {
        entryFileNames: `assets/[name].js`,
        chunkFileNames: `assets/[name].js`,
        assetFileNames: `assets/[name].[ext]`,
      },
    },
  },
  base: '', // <-- this fixed the problem with the resolved path to codicons.ttf :man_shrugging: 
})

Of course, this is more specific to my use case, but I'd argue that it is tangential to the scope of this issue, since I wouldn't have to deal with all of this if the UI library would make sure to import its necessary assets by itself and if the necessity for the font-src CSP directive was documented.