Skip to content

Commit

Permalink
Add icon components (#487)
Browse files Browse the repository at this point in the history
This adds a script that can generate .gts components from the .svg icons
in the public folder. This setup saves us a lot of manual work and it
easily allows us to manage the icons we bundle.
  • Loading branch information
Windvis authored Mar 25, 2024
1 parent c60e976 commit 0791d7a
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ jobs:
cache: npm
- name: Install Dependencies
run: npm ci
- name: Build icons
run: npm run build:icons
- name: Lint
run: npm run lint
- name: Run Tests
Expand All @@ -44,6 +46,8 @@ jobs:
cache: npm
- name: Install Dependencies
run: npm install --no-shrinkwrap
- name: Build icons
run: npm run build:icons
- name: Run Tests
run: npm run test:ember

Expand Down Expand Up @@ -73,5 +77,7 @@ jobs:
cache: npm
- name: Install Dependencies
run: npm ci
- name: Build icons
run: npm run build:icons
- name: Run Tests
run: ./node_modules/.bin/ember try:one ${{ matrix.try-scenario }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/build-storybook.log
/.storybook/preview-head.html
/declarations/
/addon/components/icons/

# dependencies
/node_modules/
Expand Down
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# build scripts
/lib/generate-icon-components.mjs

# compiled output
/dist/
/tmp/
Expand Down
59 changes: 59 additions & 0 deletions lib/generate-icon-components.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { pascalCase } from "change-case";
import { existsSync } from "node:fs";
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
import { basename, dirname, extname, join } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));

const ICON_FOLDER = join(__dirname, "..", "public", "icons");
const COMPONENT_ICON_FOLDER = join(
__dirname,
"..",
"addon",
"components",
"icons",
);

const files = await readdir(ICON_FOLDER);
const icons = files
.filter((file) => extname(file) === ".svg")
.map((svg) => basename(svg, ".svg"));

await prepareOutputDir();

const promises = icons.map((svg) => {
return generateComponent(svg);
});
await Promise.all(promises);

async function generateComponent(iconName) {
const componentName = pascalCase(iconName, {
mergeAmbiguousCharacters: true,
});

const iconContent = (await readFile(join(ICON_FOLDER, iconName + ".svg")))
.toString()
.replace(">", " ...attributes>"); // We assume the first closing bracket belongs to the svg element

const componentContent = `// THIS FILE IS GENERATED. ANY CHANGES TO THIS FILE WILL BE LOST.
import type { TOC } from '@ember/component/template-only';
export interface ${componentName}IconSignature {
Element: SVGSVGElement;
}
export const ${componentName}Icon: TOC<${componentName}IconSignature> = <template>${iconContent}</template>;`;

await writeFile(
join(COMPONENT_ICON_FOLDER, iconName + ".gts"),
componentContent,
);
}

async function prepareOutputDir() {
if (existsSync(COMPONENT_ICON_FOLDER)) {
await rm(COMPONENT_ICON_FOLDER, { recursive: true });
}
await mkdir(COMPONENT_ICON_FOLDER);
}
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"test": "tests"
},
"scripts": {
"build": "run-s icons build-ember build-storybook",
"build": "run-s build-ember build-storybook",
"build:icons": "concurrently \"node lib/generate-icon-components.mjs\" \"svg-symbols ./public/icons > ./public/appuniversum-symbolset.svg\"",
"build:types": "glint -d",
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
"lint:css": "stylelint --allow-empty-input \"**/*.css\"",
Expand All @@ -45,20 +46,18 @@
"lint:js": "eslint . --cache",
"lint:js:fix": "eslint . --fix",
"lint:types": "glint",
"prepack": "glint -d",
"prepack": "run-s build:icons build:types",
"postpack": "rimraf declarations",
"start": "run-p ember storybook",
"ember": "ember serve",
"ember": "npm run build:icons && ember serve",
"test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
"test:ember": "ember test",
"test:ember-compatibility": "ember try:each",
"icons": "svg-symbols ./public/icons > ./public/appuniversum-symbolset.svg",
"prepare": "svg-symbols ./public/icons > ./public/appuniversum-symbolset.svg",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
"build-ember": "ember build --environment=production",
"build-ember": "npm run build:icons && ember build --environment=production",
"build-ember-chromatic": "ember build --environment=chromatic",
"prep-chromatic": "run-s icons build-ember-chromatic",
"prep-chromatic": "run-s build:icons build-ember-chromatic",
"release": "release-it",
"chromatic": "chromatic --exit-zero-on-changes"
},
Expand Down Expand Up @@ -135,6 +134,7 @@
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"broccoli-asset-rev": "^3.0.0",
"change-case": "^5.4.3",
"chromatic": "^6.5.4",
"concurrently": "^8.2.2",
"ember-auto-import": "^2.7.0",
Expand Down
14 changes: 14 additions & 0 deletions tests/integration/components/icon-components-test.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { AddIcon } from '@appuniversum/ember-appuniversum/components/icons/add';

module('Integration | Icon components', function (hooks) {
setupRenderingTest(hooks);

test('the icon components accept attributes', async function (assert) {
await render(<template><AddIcon data-test-icon /></template>);

assert.dom('[data-test-icon]').exists();
});
});

0 comments on commit 0791d7a

Please sign in to comment.