Skip to content

Commit

Permalink
Feature: [HOP-48] Create svg-icons (#93)
Browse files Browse the repository at this point in the history
Co-authored-by: alexandre.asselin <[email protected]>
Co-authored-by: Alexandre Asselin <[email protected]>
  • Loading branch information
3 people authored Nov 21, 2023
1 parent ee14db8 commit 59d57e2
Show file tree
Hide file tree
Showing 463 changed files with 5,103 additions and 2,537 deletions.
5 changes: 5 additions & 0 deletions .changeset/few-actors-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hopper-ui/svg-icons": major
---

Initial release of the package
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:

- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: ".nvmrc"

- uses: pnpm/action-setup@v2
name: Install pnpm
Expand All @@ -38,15 +40,15 @@ jobs:
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Setup pnpm cache
uses: actions/cache@v3
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm i --frozen-lockfile

Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lts/*
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"doc:start": "pnpm --filter=docs dev",
"doc:storybook": "pnpm --filter=docs storybook",
"doc:build": "pnpm --filter=docs build",
"test": "jest",
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
"build": "pnpm -r build ",
"build:tokens": "pnpm --filter=\"@hopper-ui/tokens\" build",
"build:pkg": "pnpm -r --filter \"{packages/**}\" build ",
Expand All @@ -36,7 +36,7 @@
"devDependencies": {
"@changesets/cli": "2.26.2",
"@hopper-ui/tokens": "workspace:*",
"@netlify/plugin-nextjs": "4.41.1",
"@netlify/plugin-nextjs": "4.41.2",
"@storybook/addon-essentials": "7.5.3",
"@storybook/addon-interactions": "7.5.3",
"@storybook/addon-links": "7.5.3",
Expand All @@ -45,14 +45,15 @@
"@storybook/react-vite": "7.5.3",
"@storybook/react": "7.5.3",
"@storybook/testing-library": "0.2.2",
"@types/jest": "29.5.8",
"@types/node": "20.9.0",
"@types/jest": "29.5.9",
"@types/node": "20.9.3",
"@workleap/eslint-plugin": "3.0.0",
"@workleap/stylelint-configs": "2.0.0",
"@workleap/typescript-configs": "3.0.2",
"cross-env": "7.0.3",
"eslint-plugin-storybook": "0.6.15",
"jest": "29.7.0",
"prettier": "2.8.8",
"prettier": "3.1.0",
"prop-types": "15.8.1",
"react-dom": "18.2.0",
"react": "18.2.0",
Expand Down
1 change: 0 additions & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
"react-test-renderer": "18.2.0",
"react": "18.2.0",
"ts-jest": "29.1.1",
"ts-node": "10.9.1",
"tsup": "7.2.0",
"typescript": "5.2.2"
},
Expand Down
1 change: 0 additions & 1 deletion packages/styled-system/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
"react-test-renderer": "18.2.0",
"react": "18.2.0",
"ts-jest": "29.1.1",
"ts-node": "10.9.1",
"tsup": "7.2.0",
"type-fest": "4.7.1",
"typescript": "5.2.2"
Expand Down
5 changes: 5 additions & 0 deletions packages/svg-icons/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"$schema": "https://json.schemastore.org/eslintrc",
"root": true,
"extends": "plugin:@workleap/typescript-library"
}
40 changes: 40 additions & 0 deletions packages/svg-icons/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# @hopper-ui/svg-icons

A set of icons handcrafted by Workleap.

[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](../../LICENSE)
[![npm version](https://img.shields.io/npm/v/@hopper-ui/svg-icons)](https://www.npmjs.com/package/@hopper-ui/svg-icons)

## Installation

Install the following packages:

**With pnpm**

```shell
pnpm add @hopper-ui/svg-icons
```

**With yarn**

```shell
yarn add -D @hopper-ui/svg-icons
```

**With npm**

```shell
npm install -D @hopper-ui/svg-icons
```

## Usage

View the [user's documentation](https://hopper.workleap.design/).

## 🤝 Contributing

View the [contributor's documentation](https://github.com/gsoft-inc/wl-hopper/blob/main/CONTRIBUTING.md).

## License

Copyright © 2023, Workleap. This code is licensed under the Apache License, Version 2.0. You may obtain a copy of this license at https://github.com/gsoft-inc/workleap-license/blob/master/LICENSE.
12 changes: 12 additions & 0 deletions packages/svg-icons/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Config } from "jest";
import { swcConfig } from "./swc.jest.ts";

const config: Config = {
testEnvironment: "node",
transform: {
"^.+\\.(js|ts|tsx)$": ["@swc/jest", swcConfig as Record<string, unknown>]
},
extensionsToTreatAsEsm: [".ts"]
};

export default config;
50 changes: 50 additions & 0 deletions packages/svg-icons/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@hopper-ui/svg-icons",
"author": "Workleap",
"version": "0.0.0",
"description": "A set of icons handcrafted by Workleap",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/gsoft-inc/wl-hopper.git",
"directory": "packages/svg-icons"
},
"publishConfig": {
"access": "public",
"provenance": true
},
"type": "module",
"sideEffects": "*.svg",
"files": [
"/dist",
"CHANGELOG.md",
"README.md"
],
"exports": {
"./icons/*.svg": "./dist/icons/*.svg"
},
"scripts": {
"build": "tsx scripts/build.ts"
},
"devDependencies": {
"@swc/core": "1.3.96",
"@swc/helpers": "0.5.3",
"@swc/jest": "0.2.29",
"@types/jest": "29.5.9",
"@types/node": "^20.9.3",
"@workleap/eslint-plugin": "3.0.0",
"@workleap/swc-configs": "2.1.2",
"@workleap/typescript-configs": "3.0.2",
"hast-util-select": "6.0.2",
"jest": "29.7.0",
"rehype-parse": "9.0.0",
"svgo": "^3.0.4",
"tsx": "4.1.4",
"typescript": "5.3.2",
"unified": "11.0.4"

},
"engines": {
"node": ">=18.0.0"
}
}
31 changes: 31 additions & 0 deletions packages/svg-icons/scripts/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import path from "path";
import { IconSizes, IconsDistDirectory, IconsSourceDirectory } from "./constants.ts";
import { generateIcons } from "./generate-icons.ts";

/**
* Converts a file path to a file name.
* @example fileNameConverter("C:\\Dev\\wl-hopper\\packages\\svgs\\src\\icons\\16px\\Add.svg") // add-16.svg
*/
function fileNameConverter(filePath: string) {
const dirName = path.dirname(filePath);
const size = path.basename(dirName).replace("px", "");

if ((IconSizes as readonly number[]).includes(Number(size)) === false) {
throw new Error(`Invalid icon size: ${size}`);
}

const fileName = path.basename(filePath, ".svg");

const kebabCaseName = fileName
.replace(/([a-z])([A-Z])/g, "$1-$2")
.replace(/[\s_]+/g, "-")
.toLowerCase();

return `${kebabCaseName}-${size}.svg`;
}

console.log("⚙️ Optimizing icons...\n");

generateIcons(IconsSourceDirectory, IconsDistDirectory, fileNameConverter);

console.log("✨ The icons have been optimized!\n");
5 changes: 5 additions & 0 deletions packages/svg-icons/scripts/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const IconsSourceDirectory = "src/icons";
export const IconsDistDirectory = "dist/icons";
export const IconSizes = [16, 24, 32] as const;
export const NeutralIconColor = "#3C3C3C"; // --hop-neutral-icon
export const PrimaryIconColor = "#3B57FF"; // --hop-primary-icon
62 changes: 62 additions & 0 deletions packages/svg-icons/scripts/generate-icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import fs from "fs";
import path from "path";
import { optimize } from "svgo";
import config from "./svgo-config.ts";

function ensureDirSync(dir: string) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}

export function generateIcons(srcDir: string, outputDir: string, fileNameConverter?: (filePath: string) => string) {
ensureDirSync(outputDir);

// This line requires Node.js 20.5.2 or higher to execute properly
// https://github.com/nodejs/node/issues/48858
const files = fs.readdirSync(srcDir, { recursive: true, withFileTypes: true });

const svgFiles = files.filter(file => file.isFile() && file.name.endsWith(".svg"));

const iconFiles = svgFiles.map(file => {
const srcPath = path.resolve(file.path, file.name);
const dstPath = path.resolve(outputDir, fileNameConverter ? fileNameConverter(srcPath) : file.name);

return {
srcPath: srcPath,
dstPath
};
});

// If it's possible to rename a file with the same name, then we need to validate that there are no duplicates
if (fileNameConverter) {
validateNoNameDuplicate(iconFiles.map(x => x.dstPath));
}

iconFiles.forEach(iconFile => {
const contents = fs.readFileSync(iconFile.srcPath, "utf8");
const { data } = optimize(contents, {
path: iconFile.srcPath,
...config
});

fs.writeFileSync(iconFile.dstPath, Buffer.from(data));
});
}

function validateNoNameDuplicate(names: string[]) {
const duplicateNames: string[] = [];
const nameSet = new Set<string>();

for (const name of names) {
if (nameSet.has(name)) {
duplicateNames.push(name);
} else {
nameSet.add(name);
}
}

if (duplicateNames.length > 0) {
throw new Error(`Duplicate icon names detected: ${duplicateNames.join(", ")}`);
}
}
61 changes: 61 additions & 0 deletions packages/svg-icons/scripts/svgo-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { Config } from "svgo";
import { NeutralIconColor } from "./constants.ts";

const config: Config = {
multipass: true,
plugins: [
{
name: "preset-default",
params: {
overrides: {
/**
* viewBox is needed in order to produce 20px by 20px containers
* with smaller icons inside.
*/
removeViewBox: false,
/**
* Some of our icons have multiple fill colors. We want to keep them, but replace the main icon color
* with the currentColor value. This allows us to change the color of the icon with the color CSS property.
*/
convertColors: {
currentColor: NeutralIconColor
}

}
}
},
/**
* Converts presentation attributes in element styles to the equivalent XML attribute.
* Presentation attributes can be used in both attributes and styles, but in most cases it'll take fewer bytes to use attributes
*/
{ name: "convertStyleToAttrs" },
/**
* Remove all <style> elements from the document.
*/
{ name: "removeStyleElement" },
/**
* Sorts attributes in all elements in the document. This does not reduce the size of the SVG, but improves readability
*/
{ name: "sortAttrs" },
/**
* SVGs can be interactive through JavaScript.
* However, unless the SVG is coming from a trusted source, it's strongly advised to strip off JavaScript to avoid XSS attacks.
*
* Since we're not using JavaScript in our SVGs, we can safely remove the script element.
*/
{ name: "removeScriptElement" },
/**
* Removes the clip-rule attribute from all elements.
* In the unit tests, we make sure that there is no clipPath in our svgs. Yet for some reason
* clip-rule are in almost all of our svgs. So to save some extra bytes, we remove them.
*/
{
name: "removeAttrs",
params: {
attrs: "clip-rule"
}
}
]
};

export default config;
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/Add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/AddCalendar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/AddUser.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/AngleDown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/AngleLeft.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/AngleRight.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/AngleUp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/Archive.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/svg-icons/src/icons/16px/ArrowDown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 59d57e2

Please sign in to comment.