diff --git a/lib/gui/app/components/svg-icon/svg-icon.jsx b/lib/gui/app/components/svg-icon/svg-icon.jsx
deleted file mode 100644
index 50c99bd9..00000000
--- a/lib/gui/app/components/svg-icon/svg-icon.jsx
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2018 balena.io
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict'
-
-/**
- * @module Etcher.Components.SVGIcon
- */
-
-const _ = require('lodash')
-const react = require('react')
-const propTypes = require('prop-types')
-const path = require('path')
-const fs = require('fs')
-const analytics = require('../../modules/analytics')
-const domParser = new window.DOMParser()
-
-const DEFAULT_SIZE = '40px'
-
-/**
- * @summary Try to parse SVG contents and return it data encoded
- *
- * @param {String} contents - SVG XML contents
- * @returns {String|null}
- *
- * @example
- * const encodedSVG = tryParseSVGContents('')
- *
- * img.src = encodedSVG
- */
-const tryParseSVGContents = (contents) => {
- const doc = domParser.parseFromString(contents, 'image/svg+xml')
- const parserError = doc.querySelector('parsererror')
- const svg = doc.querySelector('svg')
-
- if (!parserError && svg) {
- return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`
- }
-
- return null
-}
-
-/* eslint-disable jsdoc/require-example */
-
-/**
- * @summary SVG element that takes both filepaths and file contents
- * @type {Object}
- * @public
- */
-class SVGIcon extends react.Component {
- /**
- * @summary Render the SVG
- * @returns {react.Element}
- */
- render () {
- // __dirname behaves strangely inside a Webpack bundle,
- // so we need to provide different base directories
- // depending on whether __dirname is absolute or not,
- // which helps detecting a Webpack bundle.
- // We use global.__dirname inside a Webpack bundle since
- // that's the only way to get the "real" __dirname.
- const baseDirectory = path.isAbsolute(__dirname)
- ? path.join(__dirname, '..')
- // eslint-disable-next-line no-underscore-dangle
- : global.__dirname
-
- let svgData = ''
-
- _.find(this.props.contents, (content) => {
- const attempt = tryParseSVGContents(content)
-
- if (attempt) {
- svgData = attempt
- return true
- }
-
- return false
- })
-
- if (!svgData) {
- _.find(this.props.paths, (relativePath) => {
- // This means the path to the icon should be
- // relative to *this directory*.
- // TODO: There might be a way to compute the path
- // relatively to the `index.html`.
- const imagePath = path.join(baseDirectory, 'assets', relativePath)
-
- const contents = _.attempt(() => {
- return fs.readFileSync(imagePath, {
- encoding: 'utf8'
- })
- })
-
- if (_.isError(contents)) {
- analytics.logException(contents)
- return false
- }
-
- const parsed = _.attempt(tryParseSVGContents, contents)
-
- if (parsed) {
- svgData = parsed
- return true
- }
-
- return false
- })
- }
-
- const width = this.props.width || DEFAULT_SIZE
- const height = this.props.height || DEFAULT_SIZE
-
- return react.createElement('img', {
- className: 'svg-icon',
- style: {
- width,
- height
- },
- src: svgData,
- disabled: this.props.disabled
- })
- }
-}
-
-SVGIcon.propTypes = {
-
- /**
- * @summary Paths to SVG files to be tried in succession if any fails
- */
- paths: propTypes.array,
-
- /**
- * @summary List of embedded SVG contents to be tried in succession if any fails
- */
- contents: propTypes.array,
-
- /**
- * @summary SVG image width unit
- */
- width: propTypes.string,
-
- /**
- * @summary SVG image height unit
- */
- height: propTypes.string,
-
- /**
- * @summary Should the element visually appear grayed out and disabled?
- */
- disabled: propTypes.bool
-
-}
-
-module.exports = SVGIcon
diff --git a/lib/gui/app/components/svg-icon/svg-icon.tsx b/lib/gui/app/components/svg-icon/svg-icon.tsx
new file mode 100644
index 00000000..61efcebe
--- /dev/null
+++ b/lib/gui/app/components/svg-icon/svg-icon.tsx
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2018 balena.io
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as fs from 'fs';
+import * as _ from 'lodash';
+import * as path from 'path';
+import * as React from 'react';
+
+import * as analytics from '../../modules/analytics';
+
+const domParser = new window.DOMParser();
+
+const DEFAULT_SIZE = '40px';
+
+/**
+ * @summary Try to parse SVG contents and return it data encoded
+ *
+ * @param {String} contents - SVG XML contents
+ * @returns {String|null}
+ *
+ * @example
+ * const encodedSVG = tryParseSVGContents('')
+ *
+ * img.src = encodedSVG
+ */
+function tryParseSVGContents(contents: string) {
+ const doc = domParser.parseFromString(contents, 'image/svg+xml');
+ const parserError = doc.querySelector('parsererror');
+ const svg = doc.querySelector('svg');
+
+ if (!parserError && svg) {
+ return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`;
+ }
+
+ return null;
+}
+
+interface SVGIconProps {
+ // Paths to SVG files to be tried in succession if any fails
+ paths: string[];
+ // List of embedded SVG contents to be tried in succession if any fails
+ contents?: string[];
+ // SVG image width unit
+ width?: string;
+ // SVG image height unit
+ height?: string;
+ // Should the element visually appear grayed out and disabled?
+ disabled?: boolean;
+}
+
+/**
+ * @summary SVG element that takes both filepaths and file contents
+ */
+export class SVGIcon extends React.Component {
+ public render() {
+ // __dirname behaves strangely inside a Webpack bundle,
+ // so we need to provide different base directories
+ // depending on whether __dirname is absolute or not,
+ // which helps detecting a Webpack bundle.
+ // We use global.__dirname inside a Webpack bundle since
+ // that's the only way to get the "real" __dirname.
+ let baseDirectory: string;
+ if (path.isAbsolute(__dirname)) {
+ baseDirectory = path.join(__dirname, '..');
+ } else {
+ // @ts-ignore
+ baseDirectory = global.__dirname;
+ }
+
+ let svgData = '';
+
+ _.find(this.props.contents, content => {
+ const attempt = tryParseSVGContents(content);
+
+ if (attempt) {
+ svgData = attempt;
+ return true;
+ }
+
+ return false;
+ });
+
+ if (!svgData) {
+ _.find(this.props.paths, relativePath => {
+ // This means the path to the icon should be
+ // relative to *this directory*.
+ // TODO: There might be a way to compute the path
+ // relatively to the `index.html`.
+ const imagePath = path.join(baseDirectory, 'assets', relativePath);
+
+ const contents = _.attempt(() => {
+ return fs.readFileSync(imagePath, {
+ encoding: 'utf8',
+ });
+ });
+
+ if (_.isError(contents)) {
+ analytics.logException(contents);
+ return false;
+ }
+
+ const parsed = tryParseSVGContents(contents);
+
+ if (parsed) {
+ svgData = parsed;
+ return true;
+ }
+
+ return false;
+ });
+ }
+
+ const width = this.props.width || DEFAULT_SIZE;
+ const height = this.props.height || DEFAULT_SIZE;
+
+ return (
+
+ );
+ }
+}
diff --git a/lib/gui/app/pages/main/DriveSelector.tsx b/lib/gui/app/pages/main/DriveSelector.tsx
index a8cff995..132f475a 100644
--- a/lib/gui/app/pages/main/DriveSelector.tsx
+++ b/lib/gui/app/pages/main/DriveSelector.tsx
@@ -20,7 +20,7 @@ import styled from 'styled-components';
import * as driveConstraints from '../../../../shared/drive-constraints';
import * as DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.jsx';
import * as TargetSelector from '../../components/drive-selector/target-selector.jsx';
-import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
+import { SVGIcon } from '../../components/svg-icon/svg-icon';
import * as selectionState from '../../models/selection-state';
import * as settings from '../../models/settings';
import { observe, store } from '../../models/store';
@@ -105,7 +105,7 @@ export const DriveSelector = ({
)}
-
+
diff --git a/lib/gui/app/pages/main/Flash.tsx b/lib/gui/app/pages/main/Flash.tsx
index c77e591e..d83bb075 100644
--- a/lib/gui/app/pages/main/Flash.tsx
+++ b/lib/gui/app/pages/main/Flash.tsx
@@ -22,7 +22,7 @@ import * as constraints from '../../../../shared/drive-constraints';
import * as messages from '../../../../shared/messages';
import * as DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.jsx';
import * as ProgressButton from '../../components/progress-button/progress-button.jsx';
-import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
+import { SVGIcon } from '../../components/svg-icon/svg-icon';
import * as availableDrives from '../../models/available-drives';
import * as flashState from '../../models/flash-state';
import * as selection from '../../models/selection-state';
@@ -222,7 +222,7 @@ export const Flash = ({ shouldFlashStepBeDisabled, goToSuccess }: any) => {
-
diff --git a/lib/gui/app/pages/main/MainPage.tsx b/lib/gui/app/pages/main/MainPage.tsx
index ea7cc8f4..392c6cb9 100644
--- a/lib/gui/app/pages/main/MainPage.tsx
+++ b/lib/gui/app/pages/main/MainPage.tsx
@@ -26,7 +26,7 @@ import * as ImageSelector from '../../components/image-selector/image-selector';
import * as ReducedFlashingInfos from '../../components/reduced-flashing-infos/reduced-flashing-infos';
import * as SafeWebview from '../../components/safe-webview/safe-webview';
import { SettingsModal } from '../../components/settings/settings';
-import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
+import { SVGIcon } from '../../components/svg-icon/svg-icon';
import * as flashState from '../../models/flash-state';
import * as selectionState from '../../models/selection-state';
import * as settings from '../../models/settings';
@@ -139,11 +139,11 @@ export class MainPage extends React.Component<
}
tabIndex={100}
>
-
+ />