diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 000000000..0acbe04b1
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,32 @@
+codecov:
+ notify:
+ require_ci_to_pass: yes
+
+coverage:
+ precision: 2
+ round: down
+ range: "70...100"
+
+ status:
+ project: yes
+ patch: yes
+ changes: no
+
+parsers:
+ gcov:
+ branch_detection:
+ conditional: yes
+ loop: yes
+ method: no
+ macro: no
+
+comment:
+ layout: "header, diff"
+ behavior: default
+ require_changes: no
+
+ignore:
+ - "__mocks__"
+ - "__tests__"
+ - "config"
+ - "static"
diff --git a/README.md b/README.md
index 7f5b1082c..c37c312ff 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,9 @@ yarn install && yarn start
// Testing command
yarn test
+// Testing with debug (repl) command
+yarn test:debug
+
// Distribution command
yarn dist
```
diff --git a/__tests__/renderer/shared/components/Forms/Input/Input.test.js b/__tests__/renderer/shared/components/Forms/Input/Input.test.js
new file mode 100644
index 000000000..1511cd043
--- /dev/null
+++ b/__tests__/renderer/shared/components/Forms/Input/Input.test.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import Input from 'shared/components/Forms/Input/Input';
+
+const mountContainer = (props = {}) => {
+ return shallow();
+};
+
+describe('', () => {
+ it('renders an input', () => {
+ const props = { id: 'name', defaultValue: 'foo' };
+ const wrapper = mountContainer(props);
+ expect(wrapper.type()).toEqual('input');
+ expect(wrapper.props()).toEqual(expect.objectContaining(props));
+ });
+
+ it('applies a custom className', () => {
+ const wrapper = mountContainer({ className: 'passwordField' });
+ expect(wrapper.prop('className').split(' ')).toContain('passwordField');
+ });
+});
diff --git a/__tests__/renderer/shared/components/Forms/Input/index.test.js b/__tests__/renderer/shared/components/Forms/Input/index.test.js
index 066cf9a85..ebbc72e1a 100644
--- a/__tests__/renderer/shared/components/Forms/Input/index.test.js
+++ b/__tests__/renderer/shared/components/Forms/Input/index.test.js
@@ -1,22 +1,16 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
-import Input from 'shared/components/Forms/Input';
+import InputContainer from 'shared/components/Forms/Input';
+import Input from 'shared/components/Forms/Input/Input';
const mountContainer = (props = {}) => {
- return shallow();
+ return mount();
};
describe('', () => {
- it('renders an input', () => {
- const props = { id: 'name', defaultValue: 'foo' };
- const wrapper = mountContainer(props);
- expect(wrapper.type()).toEqual('input');
- expect(wrapper.props()).toEqual(expect.objectContaining(props));
- });
-
- it('applies a custom className', () => {
- const wrapper = mountContainer({ className: 'passwordField' });
- expect(wrapper.prop('className').split(' ')).toContain('passwordField');
+ it('forwards the ref to the component', () => {
+ const wrapper = mountContainer({ ref: React.createRef() });
+ expect(wrapper).toForwardRefTo(Input);
});
});
diff --git a/__tests__/renderer/shared/components/Forms/LabeledInput/LabeledInput.test.js b/__tests__/renderer/shared/components/Forms/LabeledInput/LabeledInput.test.js
new file mode 100644
index 000000000..b73226fc0
--- /dev/null
+++ b/__tests__/renderer/shared/components/Forms/LabeledInput/LabeledInput.test.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import { mount } from 'enzyme';
+
+import LabeledInput from 'shared/components/Forms/LabeledInput/LabeledInput';
+import Label from 'shared/components/Forms/Label';
+import Input from 'shared/components/Forms/Input';
+
+const mountContainer = (props = {}) => {
+ return mount();
+};
+
+describe('', () => {
+ it('renders a label', () => {
+ const wrapper = mountContainer({ id: 'name', label: 'Name' });
+ const label = wrapper.find(Label);
+ expect(label.exists()).toBe(true);
+ expect(label.props()).toEqual(expect.objectContaining({ label: 'Name', htmlFor: 'name' }));
+ });
+
+ it('renders an input', () => {
+ const inputProps = { id: 'pw', type: 'password', defaultValue: 'foo' };
+ const wrapper = mountContainer({ label: 'Password', ...inputProps });
+ const input = wrapper.find(Input);
+ expect(input.exists()).toBe(true);
+ expect(input.props()).toEqual(expect.objectContaining(inputProps));
+ });
+});
diff --git a/__tests__/renderer/shared/components/Forms/LabeledInput/index.test.js b/__tests__/renderer/shared/components/Forms/LabeledInput/index.test.js
index a0b5155b2..f71116052 100644
--- a/__tests__/renderer/shared/components/Forms/LabeledInput/index.test.js
+++ b/__tests__/renderer/shared/components/Forms/LabeledInput/index.test.js
@@ -1,27 +1,16 @@
import React from 'react';
import { mount } from 'enzyme';
-import LabeledInput from 'shared/components/Forms/LabeledInput';
-import Label from 'shared/components/Forms/Label';
-import Input from 'shared/components/Forms/Input';
+import LabeledInputContainer from 'shared/components/Forms/LabeledInput';
+import LabeledInput from 'shared/components/Forms/LabeledInput/LabeledInput';
const mountContainer = (props = {}) => {
- return mount();
+ return mount();
};
describe('', () => {
- it('renders a label', () => {
- const wrapper = mountContainer({ id: 'name', label: 'Name' });
- const label = wrapper.find(Label);
- expect(label.exists()).toBe(true);
- expect(label.props()).toEqual(expect.objectContaining({ label: 'Name', htmlFor: 'name' }));
- });
-
- it('renders an input', () => {
- const inputProps = { id: 'pw', type: 'password', defaultValue: 'foo' };
- const wrapper = mountContainer({ label: 'Password', ...inputProps });
- const input = wrapper.find(Input);
- expect(input.exists()).toBe(true);
- expect(input.props()).toEqual(expect.objectContaining(inputProps));
+ it('forwards the ref to the component', () => {
+ const wrapper = mountContainer({ ref: React.createRef() });
+ expect(wrapper).toForwardRefTo(LabeledInput);
});
});
diff --git a/__tests__/renderer/shared/components/Forms/LabeledSelect/LabeledSelect.test.js b/__tests__/renderer/shared/components/Forms/LabeledSelect/LabeledSelect.test.js
new file mode 100644
index 000000000..4ac267c7a
--- /dev/null
+++ b/__tests__/renderer/shared/components/Forms/LabeledSelect/LabeledSelect.test.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import { mount } from 'enzyme';
+
+import LabeledSelect from 'shared/components/Forms/LabeledSelect';
+import Label from 'shared/components/Forms/Label';
+import Select from 'shared/components/Forms/Select';
+
+const mountContainer = (props = {}) => {
+ return mount();
+};
+
+describe('', () => {
+ it('renders a label', () => {
+ const wrapper = mountContainer({ id: 'currency', label: 'Currency' });
+ const label = wrapper.find(Label);
+ expect(label.exists()).toBe(true);
+ expect(label.props()).toEqual(expect.objectContaining({ label: 'Currency', htmlFor: 'currency' }));
+ });
+
+ it('renders a select', () => {
+ const children = (
+
+
+
+
+ );
+ const wrapper = mountContainer({ id: 'currency', label: 'Currency', children });
+ const select = wrapper.find(Select);
+ expect(select.exists()).toBe(true);
+ expect(select.props()).toEqual(expect.objectContaining({ id: 'currency', children }));
+ });
+});
diff --git a/__tests__/renderer/shared/components/Forms/LabeledSelect/index.test.js b/__tests__/renderer/shared/components/Forms/LabeledSelect/index.test.js
index 4ac267c7a..7b82dd97a 100644
--- a/__tests__/renderer/shared/components/Forms/LabeledSelect/index.test.js
+++ b/__tests__/renderer/shared/components/Forms/LabeledSelect/index.test.js
@@ -1,32 +1,16 @@
import React from 'react';
import { mount } from 'enzyme';
-import LabeledSelect from 'shared/components/Forms/LabeledSelect';
-import Label from 'shared/components/Forms/Label';
-import Select from 'shared/components/Forms/Select';
+import LabeledSelectContainer from 'shared/components/Forms/LabeledSelect';
+import LabeledSelect from 'shared/components/Forms/LabeledSelect/LabeledSelect';
const mountContainer = (props = {}) => {
- return mount();
+ return mount();
};
describe('', () => {
- it('renders a label', () => {
- const wrapper = mountContainer({ id: 'currency', label: 'Currency' });
- const label = wrapper.find(Label);
- expect(label.exists()).toBe(true);
- expect(label.props()).toEqual(expect.objectContaining({ label: 'Currency', htmlFor: 'currency' }));
- });
-
- it('renders a select', () => {
- const children = (
-
-
-
-
- );
- const wrapper = mountContainer({ id: 'currency', label: 'Currency', children });
- const select = wrapper.find(Select);
- expect(select.exists()).toBe(true);
- expect(select.props()).toEqual(expect.objectContaining({ id: 'currency', children }));
+ it('forwards the ref to the component', () => {
+ const wrapper = mountContainer({ ref: React.createRef() });
+ expect(wrapper).toForwardRefTo(LabeledSelect);
});
});
diff --git a/__tests__/renderer/shared/components/Forms/Select/Select.test.js b/__tests__/renderer/shared/components/Forms/Select/Select.test.js
new file mode 100644
index 000000000..866de26b1
--- /dev/null
+++ b/__tests__/renderer/shared/components/Forms/Select/Select.test.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import Select from 'shared/components/Forms/Select/Select';
+
+const mountContainer = (props = {}) => {
+ return shallow();
+};
+
+describe('', () => {
+ it('renders a select', () => {
+ const children = (
+
+
+
+
+ );
+ const props = { id: 'name', defaultValue: 'foo', children };
+ const wrapper = mountContainer(props);
+ expect(wrapper.type()).toEqual('select');
+ expect(wrapper.props()).toEqual(expect.objectContaining(props));
+ });
+
+ it('applies a custom className', () => {
+ const wrapper = mountContainer({ className: 'currency' });
+ expect(wrapper.prop('className').split(' ')).toContain('currency');
+ });
+});
diff --git a/__tests__/renderer/shared/components/Forms/Select/index.test.js b/__tests__/renderer/shared/components/Forms/Select/index.test.js
index a8c47019d..bf7082fc9 100644
--- a/__tests__/renderer/shared/components/Forms/Select/index.test.js
+++ b/__tests__/renderer/shared/components/Forms/Select/index.test.js
@@ -1,28 +1,16 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
-import Select from 'shared/components/Forms/Select';
+import SelectContainer from 'shared/components/Forms/Select';
+import Select from 'shared/components/Forms/Select/Select';
const mountContainer = (props = {}) => {
- return shallow();
+ return mount();
};
describe('', () => {
- it('renders a select', () => {
- const children = (
-
-
-
-
- );
- const props = { id: 'name', defaultValue: 'foo', children };
- const wrapper = mountContainer(props);
- expect(wrapper.type()).toEqual('select');
- expect(wrapper.props()).toEqual(expect.objectContaining(props));
- });
-
- it('applies a custom className', () => {
- const wrapper = mountContainer({ className: 'currency' });
- expect(wrapper.prop('className').split(' ')).toContain('currency');
+ it('forwards the ref to the component', () => {
+ const wrapper = mountContainer({ ref: React.createRef() });
+ expect(wrapper).toForwardRefTo(Select);
});
});
diff --git a/__tests__/setupFramework.js b/__tests__/setupFramework.js
index 934d1289c..77dc81a26 100644
--- a/__tests__/setupFramework.js
+++ b/__tests__/setupFramework.js
@@ -1,3 +1,5 @@
+import { getDisplayName } from 'recompose';
+
expect.extend({
toContainObject(received, argument) {
const pass = this.equals(
@@ -11,6 +13,16 @@ expect.extend({
? () => `expected ${this.utils.printReceived(received)} not to contain object ${this.utils.printExpected(argument)}`
: () => `expected ${this.utils.printReceived(received)} to contain object ${this.utils.printExpected(argument)}`;
+ return { pass, message };
+ },
+
+ toForwardRefTo(received, argument) {
+ const pass = received.find('ForwardRef').find(argument).exists();
+
+ const message = pass
+ ? () => `expected to not forward ref to ${this.utils.printExpected(getDisplayName(argument))}`
+ : () => `expected to forward ref to ${this.utils.printExpected(getDisplayName(argument))}`;
+
return { pass, message };
}
});
diff --git a/package.json b/package.json
index 6c87e35db..cd9e1198b 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"pack": "yarn dist --dir -c.compression=store -c.mac.identity=null",
"pretest": "yarn run lint && yarn run stylelint",
"test": "jest",
+ "test:debug": "node inspect ./node_modules/.bin/jest --runInBand",
"stylelint": "stylelint src/**/*.scss",
"lint": "eslint --env browser,node,server --ext .jsx,.js --color .",
"lint:fix": "yarn run lint --fix",
diff --git a/src/renderer/shared/components/Forms/Button/Button.js b/src/renderer/shared/components/Forms/Button/Button.js
index 0ec09f5e6..de44cea8e 100644
--- a/src/renderer/shared/components/Forms/Button/Button.js
+++ b/src/renderer/shared/components/Forms/Button/Button.js
@@ -1,39 +1,30 @@
import React from 'react';
import classNames from 'classnames';
-import { string } from 'prop-types';
+import { string, func } from 'prop-types';
+import { omit } from 'lodash';
import styles from './Button.scss';
export default class Button extends React.PureComponent {
+ static propTypes = {
+ className: string,
+ type: string,
+ forwardedRef: func
+ };
+
+ static defaultProps = {
+ className: null,
+ type: 'button',
+ forwardedRef: null
+ };
+
render() {
return ( // eslint-disable-next-line react/button-has-type
);
}
-
- registerRef = (el) => {
- this.button = el;
- }
-
- focus = () => {
- this.button.focus();
- }
-
- blur = () => {
- this.button.blur();
- }
}
-
-Button.propTypes = {
- className: string,
- type: string
-};
-
-Button.defaultProps = {
- className: null,
- type: 'button'
-};
diff --git a/src/renderer/shared/components/Forms/Button/index.js b/src/renderer/shared/components/Forms/Button/index.js
index efe8c800c..5601a4629 100644
--- a/src/renderer/shared/components/Forms/Button/index.js
+++ b/src/renderer/shared/components/Forms/Button/index.js
@@ -1 +1,5 @@
-export { default } from './Button';
+import withForwardedRef from 'shared/hocs/withForwardedRef';
+
+import Button from './Button';
+
+export default withForwardedRef()(Button);
diff --git a/src/renderer/shared/components/Forms/Input/Input.js b/src/renderer/shared/components/Forms/Input/Input.js
index a3f06cf6b..ae596be30 100644
--- a/src/renderer/shared/components/Forms/Input/Input.js
+++ b/src/renderer/shared/components/Forms/Input/Input.js
@@ -1,24 +1,27 @@
import React from 'react';
import classNames from 'classnames';
-import { string } from 'prop-types';
+import { string, func } from 'prop-types';
import styles from './Input.scss';
export default class Input extends React.PureComponent {
static propTypes = {
- className: string
+ className: string,
+ forwardedRef: func
};
static defaultProps = {
- className: null
+ className: null,
+ forwardedRef: null
};
render() {
- const { className, ...passDownProps } = this.props;
+ const { className, forwardedRef, ...passDownProps } = this.props;
return (
);
diff --git a/src/renderer/shared/components/Forms/Input/index.js b/src/renderer/shared/components/Forms/Input/index.js
index a2e60496d..ddb2908b3 100644
--- a/src/renderer/shared/components/Forms/Input/index.js
+++ b/src/renderer/shared/components/Forms/Input/index.js
@@ -1 +1,5 @@
-export { default } from './Input';
+import withForwardedRef from 'shared/hocs/withForwardedRef';
+
+import Input from './Input';
+
+export default withForwardedRef()(Input);
diff --git a/src/renderer/shared/components/Forms/LabeledInput/LabeledInput.js b/src/renderer/shared/components/Forms/LabeledInput/LabeledInput.js
index 932062d5b..3da446847 100644
--- a/src/renderer/shared/components/Forms/LabeledInput/LabeledInput.js
+++ b/src/renderer/shared/components/Forms/LabeledInput/LabeledInput.js
@@ -6,22 +6,24 @@ import Label from '../Label';
import Input from '../Input';
import styles from './LabeledInput.scss';
-export default function LabeledInput(props) {
- const { id, label, labelClass, ...passDownProps } = props;
+export default class LabeledInput extends React.PureComponent {
+ static propTypes = {
+ id: string.isRequired,
+ label: node.isRequired,
+ labelClass: string
+ };
- return (
-
- );
-}
+ static defaultProps = {
+ labelClass: null
+ };
-LabeledInput.propTypes = {
- id: string.isRequired,
- label: node.isRequired,
- labelClass: string
-};
+ render() {
+ const { id, label, labelClass, ...passDownProps } = this.props;
-LabeledInput.defaultProps = {
- labelClass: null
-};
+ return (
+
+ );
+ }
+}
diff --git a/src/renderer/shared/components/Forms/LabeledInput/index.js b/src/renderer/shared/components/Forms/LabeledInput/index.js
index a06c57e42..242c0c91d 100644
--- a/src/renderer/shared/components/Forms/LabeledInput/index.js
+++ b/src/renderer/shared/components/Forms/LabeledInput/index.js
@@ -1 +1,5 @@
-export { default } from './LabeledInput';
+import withForwardedRef from 'shared/hocs/withForwardedRef';
+
+import LabeledInput from './LabeledInput';
+
+export default withForwardedRef()(LabeledInput);
diff --git a/src/renderer/shared/components/Forms/LabeledSelect/LabeledSelect.js b/src/renderer/shared/components/Forms/LabeledSelect/LabeledSelect.js
index e7caca2a0..3d62795e2 100644
--- a/src/renderer/shared/components/Forms/LabeledSelect/LabeledSelect.js
+++ b/src/renderer/shared/components/Forms/LabeledSelect/LabeledSelect.js
@@ -6,22 +6,24 @@ import Label from '../Label';
import Select from '../Select';
import styles from './LabeledSelect.scss';
-export default function LabeledSelect(props) {
- const { id, label, labelClass, ...passDownProps } = props;
+export default class LabeledSelect extends React.PureComponent {
+ static propTypes = {
+ id: string.isRequired,
+ label: node.isRequired,
+ labelClass: string
+ };
- return (
-
- );
-}
+ static defaultProps = {
+ labelClass: null
+ };
-LabeledSelect.propTypes = {
- id: string.isRequired,
- label: node.isRequired,
- labelClass: string
-};
+ render() {
+ const { id, label, labelClass, ...passDownProps } = this.props;
-LabeledSelect.defaultProps = {
- labelClass: null
-};
+ return (
+
+ );
+ }
+}
diff --git a/src/renderer/shared/components/Forms/LabeledSelect/index.js b/src/renderer/shared/components/Forms/LabeledSelect/index.js
index edfe4c5de..49526475f 100644
--- a/src/renderer/shared/components/Forms/LabeledSelect/index.js
+++ b/src/renderer/shared/components/Forms/LabeledSelect/index.js
@@ -1 +1,5 @@
-export { default } from './LabeledSelect';
+import withForwardedRef from 'shared/hocs/withForwardedRef';
+
+import LabeledSelect from './LabeledSelect';
+
+export default withForwardedRef()(LabeledSelect);
diff --git a/src/renderer/shared/components/Forms/PrimaryButton/PrimaryButton.js b/src/renderer/shared/components/Forms/PrimaryButton/PrimaryButton.js
index f7c159721..5a527d160 100644
--- a/src/renderer/shared/components/Forms/PrimaryButton/PrimaryButton.js
+++ b/src/renderer/shared/components/Forms/PrimaryButton/PrimaryButton.js
@@ -1,38 +1,29 @@
import React from 'react';
import classNames from 'classnames';
-import { string } from 'prop-types';
+import { string, func } from 'prop-types';
+import { omit } from 'lodash';
import Button from '../Button';
import styles from './PrimaryButton.scss';
export default class PrimaryButton extends React.PureComponent {
+ static propTypes = {
+ className: string,
+ forwardedRef: func
+ };
+
+ static defaultProps = {
+ className: null,
+ forwardedRef: null
+ };
+
render() {
return (
);
}
-
- registerRef = (el) => {
- this.button = el;
- }
-
- focus = () => {
- this.button.focus();
- }
-
- blur = () => {
- this.button.blur();
- }
}
-
-PrimaryButton.propTypes = {
- className: string
-};
-
-PrimaryButton.defaultProps = {
- className: null
-};
diff --git a/src/renderer/shared/components/Forms/PrimaryButton/index.js b/src/renderer/shared/components/Forms/PrimaryButton/index.js
index c8be43ad2..1878042b3 100644
--- a/src/renderer/shared/components/Forms/PrimaryButton/index.js
+++ b/src/renderer/shared/components/Forms/PrimaryButton/index.js
@@ -1 +1,5 @@
-export { default } from './PrimaryButton';
+import withForwardedRef from 'shared/hocs/withForwardedRef';
+
+import PrimaryButton from './PrimaryButton';
+
+export default withForwardedRef()(PrimaryButton);
diff --git a/src/renderer/shared/components/Forms/Select/Select.js b/src/renderer/shared/components/Forms/Select/Select.js
index b8768c3b4..57e17ce7d 100644
--- a/src/renderer/shared/components/Forms/Select/Select.js
+++ b/src/renderer/shared/components/Forms/Select/Select.js
@@ -1,24 +1,28 @@
import React from 'react';
import classNames from 'classnames';
-import { string } from 'prop-types';
+import { string, func } from 'prop-types';
import styles from './Select.scss';
export default class Select extends React.PureComponent {
static propTypes = {
- className: string
+ className: string,
+ forwardedRef: func
};
static defaultProps = {
- className: null
+ className: null,
+ forwardedRef: null
};
render() {
- const { className, ...passDownProps } = this.props;
+ const { className, forwardedRef, ...passDownProps } = this.props;
return (
+
);
diff --git a/src/renderer/shared/components/Forms/Select/index.js b/src/renderer/shared/components/Forms/Select/index.js
index 22ae81932..877939c0e 100644
--- a/src/renderer/shared/components/Forms/Select/index.js
+++ b/src/renderer/shared/components/Forms/Select/index.js
@@ -1 +1,5 @@
-export { default } from './Select';
+import withForwardedRef from 'shared/hocs/withForwardedRef';
+
+import Select from './Select';
+
+export default withForwardedRef()(Select);
diff --git a/src/renderer/shared/hocs/withForwardedRef.js b/src/renderer/shared/hocs/withForwardedRef.js
new file mode 100644
index 000000000..8c5bb3a49
--- /dev/null
+++ b/src/renderer/shared/hocs/withForwardedRef.js
@@ -0,0 +1,10 @@
+import React from 'react';
+
+const withForwardedRef = (propName = 'forwardedRef') => (Component) => {
+ return React.forwardRef((props, ref) => {
+ const refProps = { [propName]: ref };
+ return ;
+ });
+};
+
+export default withForwardedRef;