Skip to content

Commit

Permalink
Read and transform svg icons from list (#3872)
Browse files Browse the repository at this point in the history
* Read and transform svg icons from list

* Apply review comments

* Automatically add new icons to the icons story in Storybook (#3873)

* Automatically add new icons to the icons story in Storybook

* Add available codicons to Storybook (#3874)
  • Loading branch information
sroy3 authored May 12, 2023
1 parent 169a197 commit 46cba7e
Show file tree
Hide file tree
Showing 35 changed files with 372 additions and 473 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ jobs:
- run: yarn run install-frozen-lockfile

- run: yarn svgr

- run: yarn run lint

- uses: paambaati/[email protected]
Expand Down
22 changes: 22 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,25 @@ There are some discrepancies between the Storybook environment and the
environment of a real VS Code extension, custom themes being a big one. Always
make sure to try out changed components in the full dev environment before
merging!

## Adding and using icons (SVGs)

Before adding one or more icons, check the available icons in
`webview/src/shared/component/icons`. You can also start Storybook and verify
the icons' story to see all the icons at once. Try to pick one that's already
available before adding one.

If none of the icons fit the purpose, you can select one from
[VS Code codicon](https://microsoft.github.io/vscode-codicons/dist/codicon.html).
Once you know which icon you want to add, copy its name and add it to
`webview/icons/codicons.mjs` in the `codicons` constant. If, by some unfortunate
circumstance, none of the codicon meets your needs, you can add a custom SVG to
the `webview/icons` directory.

If you have added any icon (custom or from codicon), you will have to run
`yarn svgr` or `yarn install` to ensure the icon is now available in the
codebase.

To use an icon, import it from `webview/src/shared/components/icons` (from the
index file, not the file itself) and use it with the `<Icon />` component
filling its `icon` prop with your imported icon.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"build": "yarn turbo run package",
"install-frozen-lockfile": "./scripts/install-frozen-lockfile.sh",
"dev-server": "yarn turbo run dev --parallel",
"postinstall": "husky install && git submodule init && git submodule update",
"postinstall": "husky install && git submodule init && git submodule update && yarn svgr",
"storybook": "yarn workspace dvc-vscode-webview storybook",
"build-storybook": "yarn turbo run build-storybook --filter=dvc-vscode-webview",
"setup:venv": "yarn turbo run lint:build && yarn workspace dvc run setup-venv",
Expand Down
28 changes: 28 additions & 0 deletions webview/icons/codicons.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const codicons = [
'add',
'arrow-up',
'arrow-down',
'beaker',
'check',
'chevron-down',
'chevron-right',
'close',
'copy',
'ellipsis',
'error',
'filter',
'git-commit',
'git-merge',
'graph-line',
'graph-scatter',
'gripper',
'info',
'list-filter',
'pass-filled',
'pinned',
'refresh',
'sort-precedence',
'star-empty',
'star-full',
'trash'
]
82 changes: 82 additions & 0 deletions webview/icons/generate.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { transform } from '@svgr/core'
import {
appendFile,
mkdir,
readdir,
readFile,
rm,
writeFile
} from 'node:fs/promises'
import path from 'path'
import { codicons } from './codicons.mjs'

const iconsPath = 'src/shared/components/icons/'

try {
await rm(iconsPath, { recursive: true, force: true })
await mkdir(iconsPath)
} catch (err) {
console.error(err.message)
}

const customIcons = []
try {
const files = await readdir('icons')
for (const file of files) {
if (path.extname(file) === '.svg') {
customIcons.push(`icons/${file}`)
}
}
} catch (err) {
console.error(err.message)
}

const codiconsPath = '../node_modules/@vscode/codicons/src/icons'

const allIcons = [
...customIcons,
...codicons.map(codicon => `${codiconsPath}/${codicon}.svg`)
]

function toPascalCase(text) {
return text.replace(/(^\w|-\w)/g, clearAndUpper)
}

function clearAndUpper(text) {
return text.replace(/-/, '').toUpperCase()
}

const components = []
await Promise.all(
allIcons.map(async icon => {
try {
const iconContent = await readFile(icon, { encoding: 'utf8' })
const componentName = toPascalCase(path.basename(icon).split('.')[0])
const svgComponent = await transform(
iconContent,
{
typescript: true,
plugins: ['@svgr/plugin-jsx', '@svgr/plugin-prettier']
},
{ componentName }
)
await writeFile(`${iconsPath}${componentName}.tsx`, svgComponent)
components.push(componentName)
} catch (err) {
console.error(err.message)
}
})
)

await appendFile(
`${iconsPath}index.ts`,
components
.sort()
.map(
componentName =>
`export { default as ${componentName} } from './${componentName}'\n`
)
.join('')
)

console.log(`Icons were generated to ${iconsPath}`)
11 changes: 4 additions & 7 deletions webview/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"test": "jest --collect-coverage",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build --webpack-stats-json",
"svgr": "svgr --out-dir src/shared/components/icons --ignore-existing ../node_modules/@vscode/codicons/src/icons && svgr -d src/shared/components/icons icons/"
"svgr": "node --experimental-modules icons/generate.mjs"
},
"main": "./index.js",
"peerDependencies": {
Expand Down Expand Up @@ -48,7 +48,9 @@
"@storybook/react": "7.0.8",
"@storybook/react-webpack5": "7.0.8",
"@storybook/testing-library": "0.1.0",
"@svgr/cli": "7.0.0",
"@svgr/core": "^8.0.0",
"@svgr/plugin-jsx": "^8.0.1",
"@svgr/plugin-prettier": "^8.0.1",
"@swc/core": "1.3.56",
"@swc/jest": "0.2.26",
"@testing-library/jest-dom": "5.16.5",
Expand Down Expand Up @@ -81,10 +83,5 @@
"webpack": "5.82.0",
"webpack-cli": "5.0.2",
"webpack-dev-server": "4.13.3"
},
"svgr": {
"typescript": true,
"dimensions": false,
"ignoreExisting": true
}
}
11 changes: 5 additions & 6 deletions webview/src/shared/components/icons/Add.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import * as React from 'react'
import { SVGProps } from 'react'

const SvgAdd = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const Add = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
{...props}
>
<path d="M14 7v1H8v6H7V8H1V7h6V1h1v6h6z" fill="currentColor" />
<path d="M14 7v1H8v6H7V8H1V7h6V1h1v6h6z" />
</svg>
)

export default SvgAdd
export default Add
6 changes: 3 additions & 3 deletions webview/src/shared/components/icons/ArrowDown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'
const SvgArrowDown = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const ArrowDown = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -16,4 +16,4 @@ const SvgArrowDown = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
)
export default SvgArrowDown
export default ArrowDown
6 changes: 3 additions & 3 deletions webview/src/shared/components/icons/ArrowUp.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'
const SvgArrowUp = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const ArrowUp = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -16,4 +16,4 @@ const SvgArrowUp = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
)
export default SvgArrowUp
export default ArrowUp
6 changes: 3 additions & 3 deletions webview/src/shared/components/icons/Beaker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'
const SvgBeaker = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const Beaker = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -12,4 +12,4 @@ const SvgBeaker = (props: SVGProps<SVGSVGElement>) => (
<path d="M13.893 13.558L10 6.006v-4h1v-1H9.994V1l-.456.005H5V2h1v3.952l-3.894 7.609A1 1 0 0 0 3 15.006h10a1 1 0 0 0 .893-1.448zm-7-7.15L7 6.193V2.036l2-.024v4.237l.11.215 1.827 3.542H5.049l1.844-3.598zM3 14.017l1.54-3.011h6.916l1.547 3L3 14.017z" />
</svg>
)
export default SvgBeaker
export default Beaker
6 changes: 3 additions & 3 deletions webview/src/shared/components/icons/Check.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'
const SvgCheck = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const Check = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -16,4 +16,4 @@ const SvgCheck = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
)
export default SvgCheck
export default Check
6 changes: 3 additions & 3 deletions webview/src/shared/components/icons/ChevronDown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'
const SvgChevronDown = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const ChevronDown = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -16,4 +16,4 @@ const SvgChevronDown = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
)
export default SvgChevronDown
export default ChevronDown
6 changes: 3 additions & 3 deletions webview/src/shared/components/icons/ChevronRight.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'
const SvgChevronRight = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const ChevronRight = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -16,4 +16,4 @@ const SvgChevronRight = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
)
export default SvgChevronRight
export default ChevronRight
39 changes: 19 additions & 20 deletions webview/src/shared/components/icons/Clock.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import * as React from 'react'

function SvgClock(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14 8A6 6 0 112 8a6 6 0 0112 0zm1 0A7 7 0 111 8a7 7 0 0114 0zM7 4v5.5h3v-1H8V4H7z"
fill="currentColor"
/>
</svg>
)
}

export default SvgClock
import type { SVGProps } from 'react'
const Clock = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14 8C14 11.3137 11.3137 14 8 14C4.68629 14 2 11.3137 2 8C2 4.68629 4.68629 2 8 2C11.3137 2 14 4.68629 14 8ZM15 8C15 11.866 11.866 15 8 15C4.13401 15 1 11.866 1 8C1 4.13401 4.13401 1 8 1C11.866 1 15 4.13401 15 8ZM7 4V9V9.5H7.5H10V8.5H8V4H7Z"
fill="currentColor"
/>
</svg>
)
export default Clock
8 changes: 3 additions & 5 deletions webview/src/shared/components/icons/Close.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'

const SvgClose = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const Close = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -17,5 +16,4 @@ const SvgClose = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
)

export default SvgClose
export default Close
6 changes: 3 additions & 3 deletions webview/src/shared/components/icons/Copy.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'
const SvgCopy = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const Copy = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -21,4 +21,4 @@ const SvgCopy = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
)
export default SvgCopy
export default Copy
8 changes: 3 additions & 5 deletions webview/src/shared/components/icons/Ellipsis.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react'
import { SVGProps } from 'react'

const SvgEllipsis = (props: SVGProps<SVGSVGElement>) => (
import type { SVGProps } from 'react'
const Ellipsis = (props: SVGProps<SVGSVGElement>) => (
<svg
width={16}
height={16}
Expand All @@ -13,5 +12,4 @@ const SvgEllipsis = (props: SVGProps<SVGSVGElement>) => (
<path d="M4 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm5 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm5 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" />
</svg>
)

export default SvgEllipsis
export default Ellipsis
Loading

0 comments on commit 46cba7e

Please sign in to comment.