From 28248dc5cc13566509378c050b70f4ce4839a228 Mon Sep 17 00:00:00 2001
From: Helene Kassandra <39946146+HeleneKassandra@users.noreply.github.com>
Date: Tue, 5 Dec 2023 18:49:40 +0100
Subject: [PATCH 1/4] feat(ffe-icons): henter og lagrer alle relevante
varianter av material symbols i svg BREAKING CHANGE: sletter alle gamle
svg'er
---
packages/ffe-icons/bin/build.js | 106 ++++++++----------
packages/ffe-icons/bin/deleteSvg.js | 49 ++++++++
packages/ffe-icons/bin/downloadSvgs.js | 78 +++++++++++++
packages/ffe-icons/bin/getIconNames.js | 18 +++
packages/ffe-icons/bin/utils.js | 53 +++++++++
.../icons/300/filled/sm/battery_90.svg | 1 +
.../icons/300/open/sm/battery_90.svg | 1 +
packages/ffe-icons/less/ffe-icons.less | 2 +
packages/ffe-icons/less/icons.less | 26 +++++
packages/ffe-icons/less/theme.less | 14 +++
packages/ffe-icons/package.json | 6 +-
11 files changed, 294 insertions(+), 60 deletions(-)
create mode 100644 packages/ffe-icons/bin/deleteSvg.js
create mode 100644 packages/ffe-icons/bin/downloadSvgs.js
create mode 100644 packages/ffe-icons/bin/getIconNames.js
create mode 100644 packages/ffe-icons/bin/utils.js
create mode 100644 packages/ffe-icons/icons/300/filled/sm/battery_90.svg
create mode 100644 packages/ffe-icons/icons/300/open/sm/battery_90.svg
create mode 100644 packages/ffe-icons/less/ffe-icons.less
create mode 100644 packages/ffe-icons/less/icons.less
create mode 100644 packages/ffe-icons/less/theme.less
diff --git a/packages/ffe-icons/bin/build.js b/packages/ffe-icons/bin/build.js
index 5c14b1b217..7f742632a0 100755
--- a/packages/ffe-icons/bin/build.js
+++ b/packages/ffe-icons/bin/build.js
@@ -1,60 +1,48 @@
-#!/usr/bin/env node
-'use strict'; // eslint-disable-line strict
-
-const fs = require('fs');
const path = require('path');
-const svgstore = require('svgstore');
-const mkdirp = require('mkdirp');
-
-const ICONS_PATH = path.join(__dirname, '..', 'icons');
-
-// convenience to avoid having file extension in config
-const appendSvgExtension = icons =>
- icons.map(name => (name.endsWith('.svg') ? name : `${name}.svg`));
-
-const options = require('yargs')
- .config('opts')
- .options({
- icons: {
- default: '**/*.svg',
- type: 'array',
- coerce: appendSvgExtension,
- },
- projectIcons: {
- type: 'array',
- coerce: appendSvgExtension,
- },
- dest: {
- default: 'dist',
- normalize: true,
- coerce: path.resolve,
- },
- }).argv;
-
-const matchesIcon = icons =>
- icons.includes('*.svg') || icons.includes('**/*.svg')
- ? () => true
- : fileName => icons.includes(path.basename(fileName));
-
-const sprite = svgstore();
-
-fs.readdirSync(ICONS_PATH)
- .filter(fileName => fileName.match(/\.svg$/))
- .filter(matchesIcon(options.icons))
- .forEach(fileName => {
- const iconPath = path.join(ICONS_PATH, fileName);
- const iconName = path.basename(fileName, '.svg');
- sprite.add(iconName, fs.readFileSync(iconPath), 'utf-8');
- });
-
-if (options.projectIcons) {
- options.projectIcons.forEach(fileName => {
- const iconPath = path.join(fileName);
- const iconName = path.basename(fileName, '.svg');
- sprite.add(iconName, fs.readFileSync(iconPath), 'utf-8');
- });
-}
-
-mkdirp.sync(options.dest);
-
-fs.writeFileSync(path.join(options.dest, 'ffe-icons.svg'), sprite.toString());
+const fs = require('fs');
+const { makedirs } = require('./utils');
+const { getIconNames } = require('./getIconNames');
+const { getDownloads, downloadAll } = require('./downloadSvgs');
+const {
+ createListOfRemovedIcons,
+ deleteRemovedIconsFiles,
+} = require('./deleteSvg');
+
+(async () => {
+ const weights = [300, 500];
+ const sizes = [
+ { name: 'sm', opsz: 20 },
+ { name: 'md', opsz: 24 },
+ { name: 'lg', opsz: 40 },
+ { name: 'xl', opsz: 48 },
+ ];
+ const fill = [0, 1];
+
+ const iconNames = await getIconNames();
+ const listOfRemovedIcons = await createListOfRemovedIcons(iconNames);
+ let downloads = [];
+
+ for (const weight of weights) {
+ for (const fillValue of fill) {
+ const type = fillValue === 0 ? 'filled' : 'open';
+ for (const size of sizes) {
+ const dirPath = path.resolve(
+ __dirname,
+ `../icons/${weight}/${type}/${size.name}`,
+ );
+ if (!fs.existsSync(dirPath)) {
+ await makedirs(dirPath);
+ }
+ if (listOfRemovedIcons.length > 0) {
+ await deleteRemovedIconsFiles(listOfRemovedIcons, dirPath);
+ }
+ downloads = downloads.concat(
+ getDownloads(iconNames, weight, fillValue, size, dirPath),
+ );
+ }
+ }
+ }
+ console.log('Downloading SVG files...');
+ await downloadAll(downloads);
+ console.log('All done!');
+})();
diff --git a/packages/ffe-icons/bin/deleteSvg.js b/packages/ffe-icons/bin/deleteSvg.js
new file mode 100644
index 0000000000..c2b1fe8981
--- /dev/null
+++ b/packages/ffe-icons/bin/deleteSvg.js
@@ -0,0 +1,49 @@
+const fs = require('fs/promises');
+const path = require('path');
+
+/* Function: createListOfRemovedIcons
+ Creates and returns an array of all the filenames of svg-files that exist,
+ but are no longer mentioned in the Material Symbols Codepoints.
+
+ Since we know all the different subfolder / variations of the icons contain the same iconnames,
+ we only need to check 1 folder.
+*/
+const createListOfRemovedIcons = async iconNames => {
+ const directory = path.resolve(__dirname, '../icons/300/filled/lg');
+ try {
+ await fs.access(directory);
+ const filesInDir = await fs.readdir(directory);
+ const removedIcons = filesInDir.filter(
+ fileName => !iconNames.includes(fileName.replace('.svg', '')),
+ );
+ return removedIcons;
+ } catch (err) {
+ console.log('Directory does not exist in check for removed icons');
+ return [];
+ }
+};
+
+/* Function: deleteSvgFile
+ Does the actual deleting of the file
+ */
+const deleteSvgFile = async fileName => {
+ try {
+ await fs.unlink(fileName);
+ console.log(`Deleted file ${fileName}`);
+ } catch (err) {
+ console.error(`Failed to delete file ${fileName}: ${err}`);
+ }
+};
+
+/* Function: deleteRemovedIconsFiles
+ Loop through the list of fileNames that should be deleted in a specific directory
+ and call the delete function.
+*/
+const deleteRemovedIconsFiles = async (listOfRemovedIcons, directory) => {
+ for (const fileName of listOfRemovedIcons) {
+ const filePath = path.join(directory, fileName);
+ await deleteSvgFile(filePath);
+ }
+};
+
+module.exports = { createListOfRemovedIcons, deleteRemovedIconsFiles };
diff --git a/packages/ffe-icons/bin/downloadSvgs.js b/packages/ffe-icons/bin/downloadSvgs.js
new file mode 100644
index 0000000000..5ebcb3af37
--- /dev/null
+++ b/packages/ffe-icons/bin/downloadSvgs.js
@@ -0,0 +1,78 @@
+const { apply, fileExists } = require('./utils');
+const path = require('path');
+const fs = require('fs/promises');
+
+/* Function: generateDownloadUrl
+ Takes the icon name, fill, weight and size, and generate the url to download the svg from.
+*/
+const generateDownloadUrl = (iconName, fill, weight, size) => {
+ let style = `wght${weight}${fill === 0 ? '' : 'fill1'}`;
+ if (fill === 0 && weight === 400) {
+ style = 'default'; // Can technically be removed since we're not supporting the standard value of 400 font weight. But keeping it in to make sure nothing break if we decide to add it later
+ }
+ return `https://fonts.gstatic.com/s/i/short-term/release/materialsymbolsrounded/${iconName}/${style}/${size}px.svg`;
+};
+
+/* Function: getDownloads
+ Returns an array for each variation of the icons with the download url, filename and filepath.
+ The array is later used to know what to download and where to save the files.
+*/
+const getDownloads = (iconNames, weight, fill, size, dirPath) => {
+ const downloads = [];
+ if (!iconNames || !weight || !size || fill === undefined) {
+ throw new Error('iconNames, weight, fill or size is not provided');
+ }
+ for (const icon of iconNames) {
+ const safeIconName = icon; // Fix for icons that has a number as the first character - which is not valid const name
+ downloads.push({
+ url: generateDownloadUrl(safeIconName, fill, weight, size.opsz),
+ fileName: `${icon}.svg`,
+ filePath: dirPath,
+ });
+ }
+ return downloads;
+};
+
+/* Function: download
+ Does the actual downloading of the file
+ */
+const download = async downloadElement => {
+ const { url, fileName, filePath } = downloadElement;
+ const fileLocation = path.resolve(__dirname, `${filePath}/${fileName}`);
+ try {
+ console.log(`Downloading ${fileLocation}`);
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ const data = await response.text();
+ await fs.writeFile(fileLocation, data);
+ } catch (error) {
+ console.error(
+ `Failed to download file from ${url} to ${fileLocation}. Error: ${error.message}`,
+ );
+ }
+};
+
+/* Function: downloadAll
+ Takes all the downloads, and apply the download function to each of them.
+ Also let you set if you want to ignore existing files, this is on by default.
+*/
+const downloadAll = async (downloads, { ignoreExisting = true } = {}) => {
+ let allDownloads = [];
+ if (ignoreExisting) {
+ for (const file of downloads) {
+ const fileAlreadyExists = await fileExists(
+ `${file.filePath}/${file.fileName}`,
+ );
+ if (!fileAlreadyExists) {
+ allDownloads.push(file);
+ }
+ }
+ } else {
+ allDownloads = downloads;
+ }
+ await apply(download, allDownloads);
+};
+
+module.exports = { getDownloads, downloadAll };
diff --git a/packages/ffe-icons/bin/getIconNames.js b/packages/ffe-icons/bin/getIconNames.js
new file mode 100644
index 0000000000..be2a0ed82a
--- /dev/null
+++ b/packages/ffe-icons/bin/getIconNames.js
@@ -0,0 +1,18 @@
+/* Fetches all the icon names by using the codepoints in the font */
+const getIconNames = async () => {
+ let iconNames = [];
+ const url =
+ 'https://raw.githubusercontent.com/google/material-design-icons/master/variablefont/MaterialSymbolsRounded%5BFILL%2CGRAD%2Copsz%2Cwght%5D.codepoints';
+ const response = await fetch(url);
+ const data = await response.text();
+
+ const lines = data.split('\n');
+ const names = lines
+ .filter(line => line.trim() !== '')
+ .map(line => `${line.split(' ')[0]}`);
+ iconNames = [...iconNames, ...names];
+
+ return iconNames;
+};
+
+module.exports = { getIconNames };
diff --git a/packages/ffe-icons/bin/utils.js b/packages/ffe-icons/bin/utils.js
new file mode 100644
index 0000000000..ffcd7c688c
--- /dev/null
+++ b/packages/ffe-icons/bin/utils.js
@@ -0,0 +1,53 @@
+const fs = require('fs/promises');
+
+/* Create directory */
+const makedirs = async dir => {
+ try {
+ await fs.mkdir(dir, { recursive: true });
+ console.log(`Created ${dir}`);
+ } catch (err) {
+ throw new Error(`Failed to create directory ${dir}: ${err}`);
+ }
+};
+
+/* Function: apply
+ Utility function to run an async function on an array of arguments in parallel with a concurrency limit.
+ Used to download multiple files at once, but with a concurrency limit to avoid overloading the server.
+ Arguments:
+ - func: async function to run (download)
+ - args: array of arguments to pass to the function (array of download objects)
+*/
+const apply = async (func, args) => {
+ const concurrency = 8;
+ const results = [];
+ let i = 0;
+ const next = async () => {
+ const j = i++;
+ if (j >= args.length) {
+ return;
+ }
+ try {
+ results[j] = await func(args[j]);
+ } catch (error) {
+ console.error(error);
+ }
+ await next();
+ };
+ await Promise.all(Array.from({ length: concurrency }, next));
+ return results;
+};
+
+/* Function: fileExists
+ Checks if a file exists at the given path
+*/
+const fileExists = async filePath => {
+ try {
+ await fs.access(filePath);
+ return true;
+ } catch (error) {
+ // The file doesn't exist or there was an error accessing it
+ return false;
+ }
+};
+
+module.exports = { makedirs, apply, fileExists };
diff --git a/packages/ffe-icons/icons/300/filled/sm/battery_90.svg b/packages/ffe-icons/icons/300/filled/sm/battery_90.svg
new file mode 100644
index 0000000000..2f88955380
--- /dev/null
+++ b/packages/ffe-icons/icons/300/filled/sm/battery_90.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ffe-icons/icons/300/open/sm/battery_90.svg b/packages/ffe-icons/icons/300/open/sm/battery_90.svg
new file mode 100644
index 0000000000..2f88955380
--- /dev/null
+++ b/packages/ffe-icons/icons/300/open/sm/battery_90.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ffe-icons/less/ffe-icons.less b/packages/ffe-icons/less/ffe-icons.less
new file mode 100644
index 0000000000..660478faf5
--- /dev/null
+++ b/packages/ffe-icons/less/ffe-icons.less
@@ -0,0 +1,2 @@
+@import 'theme';
+@import 'icons';
diff --git a/packages/ffe-icons/less/icons.less b/packages/ffe-icons/less/icons.less
new file mode 100644
index 0000000000..ac8e5d1253
--- /dev/null
+++ b/packages/ffe-icons/less/icons.less
@@ -0,0 +1,26 @@
+.ffe-icons {
+ color: var(--ffe-v-icons-default-color);
+ mask-repeat: no-repeat;
+ background-color: currentColor;
+ display: inline-block;
+
+ &--sm {
+ height: var(--ffe-v-icons-size-sm);
+ width: var(--ffe-v-icons-size-sm);
+ }
+
+ &--md {
+ height: var(--ffe-v-icons-size-md);
+ width: var(--ffe-v-icons-size-md);
+ }
+
+ &--lg {
+ height: var(--ffe-v-icons-size-lg);
+ width: var(--ffe-v-icons-size-lg);
+ }
+
+ &--xl {
+ height: var(--ffe-v-icons-size-xl);
+ width: var(--ffe-v-icons-size-xl);
+ }
+}
diff --git a/packages/ffe-icons/less/theme.less b/packages/ffe-icons/less/theme.less
new file mode 100644
index 0000000000..074ed403d1
--- /dev/null
+++ b/packages/ffe-icons/less/theme.less
@@ -0,0 +1,14 @@
+:root,
+:host {
+ --ffe-v-icons-size-sm: 20px;
+ --ffe-v-icons-size-md: 24px;
+ --ffe-v-icons-size-lg: 40px;
+ --ffe-v-icons-size-xl: 48px;
+ --ffe-v-icons-default-color: var(--ffe-farge-vann);
+
+ .native & {
+ @media (prefers-color-scheme: dark) {
+ --ffe-v-icons-default-color: var(--ffe-farge-vann-70);
+ }
+ }
+}
diff --git a/packages/ffe-icons/package.json b/packages/ffe-icons/package.json
index 256ee3dca3..73723771ec 100644
--- a/packages/ffe-icons/package.json
+++ b/packages/ffe-icons/package.json
@@ -7,12 +7,16 @@
"bin": {
"ffe-icons": "bin/build.js"
},
+ "files": [
+ "less"
+ ],
"repository": {
"type": "git",
"url": "ssh://git@github.com:SpareBank1/designsystem.git"
},
"scripts": {
- "build": "node bin/build.js",
+ "build:": "",
+ "build:icons": "node bin/build.js",
"lint": "eslint bin",
"test": "npm run lint"
},
From 16bd6da5183267b9f5491e9ee513f99caeb30222 Mon Sep 17 00:00:00 2001
From: Helene Kassandra <39946146+HeleneKassandra@users.noreply.github.com>
Date: Sat, 9 Dec 2023 00:57:53 +0100
Subject: [PATCH 2/4] chore: legg til workflow for oppdatering av ffe-icons
svger
---
.github/workflows/update-ffe-icons.yml | 33 ++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 .github/workflows/update-ffe-icons.yml
diff --git a/.github/workflows/update-ffe-icons.yml b/.github/workflows/update-ffe-icons.yml
new file mode 100644
index 0000000000..ee618a0427
--- /dev/null
+++ b/.github/workflows/update-ffe-icons.yml
@@ -0,0 +1,33 @@
+name: update ffe-icons svgs
+
+on:
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v2
+ with:
+ node-version: 18
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build icons
+ run: npm run build:icons
+ working-directory: packages/ffe-icons
+
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@v3
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ commit-message: 'fix(ffe-icons): oppdater ikon SVGer'
+ title: 'feat(ffe-icons): oppdater ikon SVGer'
+ body: "Oppdaterer SVG'ene i ffe-icons til å passe med de som er tilgjengelige i Material Symbols"
+ branch: 'auto-update-icons'
From 795b666007b86b28e06d0554db2a5fde745f3d5e Mon Sep 17 00:00:00 2001
From: Helene Kassandra <39946146+HeleneKassandra@users.noreply.github.com>
Date: Mon, 18 Dec 2023 15:26:52 +0100
Subject: [PATCH 3/4] feat(component-overview): legg til icon eksempler
---
component-overview/examples/icons/Icon-ariahidden.jsx | 7 +++++++
component-overview/examples/icons/Icon.jsx | 6 ++++++
.../messages/message-box/InfoMessage-customIcon.jsx | 4 ++--
component-overview/examples/typography/LinkIcon.jsx | 7 ++-----
component-overview/src/style.less | 1 +
5 files changed, 18 insertions(+), 7 deletions(-)
create mode 100644 component-overview/examples/icons/Icon-ariahidden.jsx
create mode 100644 component-overview/examples/icons/Icon.jsx
diff --git a/component-overview/examples/icons/Icon-ariahidden.jsx b/component-overview/examples/icons/Icon-ariahidden.jsx
new file mode 100644
index 0000000000..583af51882
--- /dev/null
+++ b/component-overview/examples/icons/Icon-ariahidden.jsx
@@ -0,0 +1,7 @@
+import { Icon } from '@sb1/ffe-icons-react';
+import batteryIcon from '@sb1/ffe-icons/icons/300/filled/sm/battery_90.svg';
+() => {
+
+ return ;
+
+}
diff --git a/component-overview/examples/icons/Icon.jsx b/component-overview/examples/icons/Icon.jsx
new file mode 100644
index 0000000000..576b6070e1
--- /dev/null
+++ b/component-overview/examples/icons/Icon.jsx
@@ -0,0 +1,6 @@
+import { Icon } from '@sb1/ffe-icons-react';
+import batteryIcon from '@sb1/ffe-icons/icons/300/filled/sm/battery_90.svg';
+
+() => {
+ return ;
+}
diff --git a/component-overview/examples/messages/message-box/InfoMessage-customIcon.jsx b/component-overview/examples/messages/message-box/InfoMessage-customIcon.jsx
index 7bfa92ad78..fb67817474 100644
--- a/component-overview/examples/messages/message-box/InfoMessage-customIcon.jsx
+++ b/component-overview/examples/messages/message-box/InfoMessage-customIcon.jsx
@@ -1,7 +1,7 @@
import { InfoMessage } from '@sb1/ffe-message-box-react';
import { Paragraph } from '@sb1/ffe-core-react';
-import { HandlevognIkon } from '@sb1/ffe-icons-react';
+import Symbol from '@sb1/ffe-symbols-react';
-}>
+}>
Du har ingenting i handlevognen din.
diff --git a/component-overview/examples/typography/LinkIcon.jsx b/component-overview/examples/typography/LinkIcon.jsx
index b284de1020..7b6292fbee 100644
--- a/component-overview/examples/typography/LinkIcon.jsx
+++ b/component-overview/examples/typography/LinkIcon.jsx
@@ -1,9 +1,6 @@
import { LinkIcon } from '@sb1/ffe-core-react';
-import { SnakkebobleIkon } from '@sb1/ffe-icons-react';
+import Symbol from '@sb1/ffe-symbols-react';
-
+
diff --git a/component-overview/src/style.less b/component-overview/src/style.less
index d6a2f490ea..7d38b16dd5 100644
--- a/component-overview/src/style.less
+++ b/component-overview/src/style.less
@@ -19,3 +19,4 @@
@import '@sb1/ffe-system-message/less/ffe-system-message';
@import '@sb1/ffe-tables/less/tables';
@import '@sb1/ffe-tabs/less/tabs';
+@import '@sb1/ffe-icons/less/ffe-icons';
From 22bd2aedfd0899f2a3ee3a0c7b59ffc93de2553c Mon Sep 17 00:00:00 2001
From: Helene Kassandra <39946146+HeleneKassandra@users.noreply.github.com>
Date: Mon, 18 Dec 2023 15:28:21 +0100
Subject: [PATCH 4/4] feat(ffe-icons-react): legg til ikon wrapper komponent
---
packages/ffe-icons-react/package.json | 5 +--
.../scripts/lib/readIconFiles.js | 5 ++-
packages/ffe-icons-react/src/Icon.js | 35 +++++++++++++++++++
packages/ffe-icons-react/src/index.d.ts | 12 +++++++
packages/ffe-icons-react/src/index.js | 3 ++
5 files changed, 57 insertions(+), 3 deletions(-)
create mode 100644 packages/ffe-icons-react/src/Icon.js
create mode 100644 packages/ffe-icons-react/src/index.d.ts
create mode 100644 packages/ffe-icons-react/src/index.js
diff --git a/packages/ffe-icons-react/package.json b/packages/ffe-icons-react/package.json
index 5b1b44e025..ed2ff6b0fc 100644
--- a/packages/ffe-icons-react/package.json
+++ b/packages/ffe-icons-react/package.json
@@ -7,7 +7,8 @@
"files": [
"lib",
"types",
- "es"
+ "es",
+ "src"
],
"main": "lib/index.js",
"types": "types/index.d.ts",
@@ -18,7 +19,7 @@
"url": "ssh://git@github.com:SpareBank1/designsystem.git"
},
"scripts": {
- "build": "node scripts/build.js && ffe-buildtool babel --copy-typedef=gen-src/index.d.ts gen-src",
+ "build": "ffe-buildtool babel",
"clean": "rimraf gen-src lib es types",
"lint": "eslint scripts",
"test": "eslint gen-src"
diff --git a/packages/ffe-icons-react/scripts/lib/readIconFiles.js b/packages/ffe-icons-react/scripts/lib/readIconFiles.js
index 4b73179a22..c31734888b 100644
--- a/packages/ffe-icons-react/scripts/lib/readIconFiles.js
+++ b/packages/ffe-icons-react/scripts/lib/readIconFiles.js
@@ -6,10 +6,13 @@ const caseUtil = require('case');
module.exports = files =>
files.reduce((acc, filePath) => {
const fileName = path.basename(filePath, '.svg');
+ const safeFileName = !isNaN(fileName.charAt(0))
+ ? `ffe${fileName}`
+ : fileName;
acc.push({
fileName,
- iconName: caseUtil.pascal(fileName),
+ iconName: caseUtil.pascal(safeFileName),
svg: svg2jsxStr(fs.readFileSync(filePath, 'utf-8')),
});
diff --git a/packages/ffe-icons-react/src/Icon.js b/packages/ffe-icons-react/src/Icon.js
new file mode 100644
index 0000000000..8f0bfcf396
--- /dev/null
+++ b/packages/ffe-icons-react/src/Icon.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import classNames from 'classnames';
+import { string, oneOf } from 'prop-types';
+
+const Icon = props => {
+ const { filePath, className, ariaLabel, size, ...rest } = props;
+
+ return (
+
+ );
+};
+
+Icon.defaultProps = {
+ size: 'md',
+};
+
+Icon.propTypes = {
+ /** The path to the svg-file */
+ filePath: string.isRequired,
+ /** Additional classnames */
+ className: string,
+ /** Aria label, if ariaLabel is hull it'll sett aria-hidden to true */
+ ariaLabel: string,
+ /** Size of the container around the icon */
+ size: oneOf(['sm', 'md', 'lg', 'xl']),
+};
+
+export default Icon;
diff --git a/packages/ffe-icons-react/src/index.d.ts b/packages/ffe-icons-react/src/index.d.ts
new file mode 100644
index 0000000000..bf90a77cb3
--- /dev/null
+++ b/packages/ffe-icons-react/src/index.d.ts
@@ -0,0 +1,12 @@
+import * as React from 'react';
+
+export interface IconProps {
+ filePath: string;
+ className?: string;
+ ariaLabel?: string;
+ size: 'sm' | 'md' | 'lg' | 'xl';
+}
+
+declare class Icon extends React.Component {}
+
+export default Icon;
diff --git a/packages/ffe-icons-react/src/index.js b/packages/ffe-icons-react/src/index.js
new file mode 100644
index 0000000000..91a68dc445
--- /dev/null
+++ b/packages/ffe-icons-react/src/index.js
@@ -0,0 +1,3 @@
+import Icon from './Icon';
+
+export { Icon };