Feature selector (#4074)
* WIP * ParityBar verification * import from index.js * i18n expansion & tests * Features component * Adapt language selector to use features * Add features to settings view * typo * Convert logging * Fix earlier merge issues resulting in test failures * Lint failure fixes (new rules) * Fix additional listing rules * Re-add FormattedMessage (missing after merge), fix tests * Fix loader overrides * grumble: split item rendering (& test) * grumble: allow enable/disable while testing (default on) * grumble: move LanguageSelector below Features * grumble: don't pass visiblity prop (& update tests) * grumble: missing observable (onClick misbehaving) * grumble: don't reset to defaults per session * Fix to single store instance
This commit is contained in:
parent
5b2dd8deb2
commit
155bbc328f
27
js/src/i18n/constants.js
Normal file
27
js/src/i18n/constants.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const DEFAULT_LOCALE = 'en';
|
||||||
|
const DEFAULT_LOCALES = process.env.NODE_ENV === 'production'
|
||||||
|
? ['en']
|
||||||
|
: ['en', 'de'];
|
||||||
|
const LS_STORE_KEY = '_parity::locale';
|
||||||
|
|
||||||
|
export {
|
||||||
|
DEFAULT_LOCALE,
|
||||||
|
DEFAULT_LOCALES,
|
||||||
|
LS_STORE_KEY
|
||||||
|
};
|
30
js/src/i18n/languages.spec.js
Normal file
30
js/src/i18n/languages.spec.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { DEFAULT_LOCALE } from './constants';
|
||||||
|
import languages from './languages';
|
||||||
|
|
||||||
|
const keys = Object.keys(languages);
|
||||||
|
|
||||||
|
describe('i18n/languages', () => {
|
||||||
|
it('has a language list', () => {
|
||||||
|
expect(keys.length > 1).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes DEFAULT_LOCALE as a language', () => {
|
||||||
|
expect(keys.includes(DEFAULT_LOCALE)).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
@ -21,39 +21,32 @@ import de from 'react-intl/locale-data/de';
|
|||||||
import en from 'react-intl/locale-data/en';
|
import en from 'react-intl/locale-data/en';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
|
import { DEFAULT_LOCALE, DEFAULT_LOCALES, LS_STORE_KEY } from './constants';
|
||||||
import languages from './languages';
|
import languages from './languages';
|
||||||
import deMessages from './de';
|
import deMessages from './de';
|
||||||
import enMessages from './en';
|
import enMessages from './en';
|
||||||
|
|
||||||
const LS_STORE_KEY = '_parity::locale';
|
|
||||||
|
|
||||||
let instance = null;
|
let instance = null;
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
|
||||||
|
|
||||||
const DEFAULT = 'en';
|
|
||||||
const LANGUAGES = flatten({ languages });
|
const LANGUAGES = flatten({ languages });
|
||||||
const MESSAGES = {
|
const MESSAGES = {
|
||||||
de: Object.assign(flatten(deMessages), LANGUAGES),
|
de: Object.assign(flatten(deMessages), LANGUAGES),
|
||||||
en: Object.assign(flatten(enMessages), LANGUAGES)
|
en: Object.assign(flatten(enMessages), LANGUAGES)
|
||||||
};
|
};
|
||||||
const LOCALES = isProduction
|
|
||||||
? ['en']
|
|
||||||
: ['en', 'de'];
|
|
||||||
|
|
||||||
addLocaleData([...de, ...en]);
|
addLocaleData([...de, ...en]);
|
||||||
|
|
||||||
export default class Store {
|
export default class Store {
|
||||||
@observable locale = DEFAULT;
|
@observable locale = DEFAULT_LOCALE;
|
||||||
@observable locales = LOCALES;
|
@observable locales = DEFAULT_LOCALES;
|
||||||
@observable messages = MESSAGES[DEFAULT];
|
@observable messages = MESSAGES[DEFAULT_LOCALE];
|
||||||
@observable isDevelopment = !isProduction;
|
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
const savedLocale = store.get(LS_STORE_KEY);
|
const savedLocale = store.get(LS_STORE_KEY);
|
||||||
|
|
||||||
this.locale = (savedLocale && LOCALES.includes(savedLocale))
|
this.locale = (savedLocale && DEFAULT_LOCALES.includes(savedLocale))
|
||||||
? savedLocale
|
? savedLocale
|
||||||
: DEFAULT;
|
: DEFAULT_LOCALE;
|
||||||
this.messages = MESSAGES[this.locale];
|
this.messages = MESSAGES[this.locale];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
73
js/src/i18n/store.spec.js
Normal file
73
js/src/i18n/store.spec.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import store from 'store';
|
||||||
|
|
||||||
|
import { DEFAULT_LOCALE, DEFAULT_LOCALES, LS_STORE_KEY } from './constants';
|
||||||
|
import { LocaleStore } from './';
|
||||||
|
|
||||||
|
let localeStore;
|
||||||
|
|
||||||
|
describe('i18n/Store', () => {
|
||||||
|
before(() => {
|
||||||
|
localeStore = LocaleStore.get();
|
||||||
|
store.set(LS_STORE_KEY, 'testing');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a default instance', () => {
|
||||||
|
expect(localeStore).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the default locale to default (invalid localStorage)', () => {
|
||||||
|
expect(localeStore.locale).to.equal(DEFAULT_LOCALE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the locale from localStorage (valid localStorage)', () => {
|
||||||
|
const testLocale = DEFAULT_LOCALES[DEFAULT_LOCALES.length - 1];
|
||||||
|
|
||||||
|
store.set(LS_STORE_KEY, testLocale);
|
||||||
|
|
||||||
|
const testStore = new LocaleStore();
|
||||||
|
|
||||||
|
expect(testStore.locale).to.equal(testLocale);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('lists the locales', () => {
|
||||||
|
expect(localeStore.locales.length > 1).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('lists locals including default', () => {
|
||||||
|
expect(localeStore.locales.includes(DEFAULT_LOCALE)).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@action', () => {
|
||||||
|
describe('setLocale', () => {
|
||||||
|
const testLocale = DEFAULT_LOCALES[DEFAULT_LOCALES.length - 1];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localeStore.setLocale(testLocale);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the locale as passed', () => {
|
||||||
|
expect(localeStore.locale).to.equal(testLocale);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the locale in localStorage', () => {
|
||||||
|
expect(store.get(LS_STORE_KEY)).to.equal(testLocale);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
21
js/src/ui/Features/constants.js
Normal file
21
js/src/ui/Features/constants.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const LS_STORE_KEY = '_parity::features';
|
||||||
|
|
||||||
|
export {
|
||||||
|
LS_STORE_KEY
|
||||||
|
};
|
61
js/src/ui/Features/defaults.js
Normal file
61
js/src/ui/Features/defaults.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const MODES = {
|
||||||
|
DEVELOPMENT: 1000, // only in dev mode, disabled by default, can be toggled
|
||||||
|
TESTING: 1011, // feature is available in dev mode
|
||||||
|
PRODUCTION: 1022 // feature is available
|
||||||
|
};
|
||||||
|
|
||||||
|
const FEATURES = {
|
||||||
|
LANGUAGE: 'LANGUAGE',
|
||||||
|
LOGLEVELS: 'LOGLEVELS'
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULTS = {
|
||||||
|
[FEATURES.LANGUAGE]: {
|
||||||
|
mode: MODES.TESTING,
|
||||||
|
name: 'Language Selection',
|
||||||
|
description: 'Allows changing the default interface language'
|
||||||
|
},
|
||||||
|
[FEATURES.LOGLEVELS]: {
|
||||||
|
mode: MODES.TESTING,
|
||||||
|
name: 'Logging Level Selection',
|
||||||
|
description: 'Allows changing of the log levels for various components'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
Object
|
||||||
|
.keys(MODES)
|
||||||
|
.forEach((mode) => {
|
||||||
|
const key = `.${mode}`;
|
||||||
|
|
||||||
|
FEATURES[key] = key;
|
||||||
|
DEFAULTS[key] = {
|
||||||
|
mode: MODES[mode],
|
||||||
|
name: key,
|
||||||
|
description: key
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DEFAULTS;
|
||||||
|
|
||||||
|
export {
|
||||||
|
FEATURES,
|
||||||
|
MODES
|
||||||
|
};
|
67
js/src/ui/Features/defaults.spec.js
Normal file
67
js/src/ui/Features/defaults.spec.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import defaults, { FEATURES, MODES } from './defaults';
|
||||||
|
|
||||||
|
const features = Object.values(FEATURES);
|
||||||
|
const modes = Object.values(MODES);
|
||||||
|
|
||||||
|
describe('ui/Features/Defaults', () => {
|
||||||
|
describe('feature codes', () => {
|
||||||
|
Object.keys(FEATURES).forEach((key) => {
|
||||||
|
describe(key, () => {
|
||||||
|
let value;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
value = FEATURES[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exists as an default', () => {
|
||||||
|
expect(defaults[value]).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a single unique code', () => {
|
||||||
|
expect(features.filter((code) => code === value).length).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('defaults', () => {
|
||||||
|
Object.keys(defaults).forEach((key) => {
|
||||||
|
describe(key, () => {
|
||||||
|
let value;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
value = defaults[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exists as an exposed feature', () => {
|
||||||
|
expect(features.includes(key)).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a valid mode', () => {
|
||||||
|
expect(modes.includes(value.mode)).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a name and description', () => {
|
||||||
|
expect(value.description).to.be.ok;
|
||||||
|
expect(value.name).to.be.ok;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
20
js/src/ui/Features/features.css
Normal file
20
js/src/ui/Features/features.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity is free software: you can redistribute it and/or modify
|
||||||
|
/* it under the terms of the GNU General Public License as published by
|
||||||
|
/* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
/* (at your option) any later version.
|
||||||
|
/*
|
||||||
|
/* Parity is distributed in the hope that it will be useful,
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
/* GNU General Public License for more details.
|
||||||
|
/*
|
||||||
|
/* You should have received a copy of the GNU General Public License
|
||||||
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.description {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
69
js/src/ui/Features/features.js
Normal file
69
js/src/ui/Features/features.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { Checkbox } from 'material-ui';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { List, ListItem } from 'material-ui/List';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import defaults, { MODES } from './defaults';
|
||||||
|
import Store from './store';
|
||||||
|
import styles from './features.css';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class Features extends Component {
|
||||||
|
store = Store.get();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List>
|
||||||
|
{
|
||||||
|
Object
|
||||||
|
.keys(defaults)
|
||||||
|
.filter((key) => defaults[key].mode !== MODES.PRODUCTION)
|
||||||
|
.map(this.renderItem)
|
||||||
|
}
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderItem = (key) => {
|
||||||
|
const feature = defaults[key];
|
||||||
|
const onCheck = () => this.store.toggleActive(key);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
key={ `feature_${key}` }
|
||||||
|
leftCheckbox={
|
||||||
|
<Checkbox
|
||||||
|
checked={ this.store.active[key] }
|
||||||
|
onCheck={ onCheck }
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
primaryText={ feature.name }
|
||||||
|
secondaryText={
|
||||||
|
<div className={ styles.description }>
|
||||||
|
{ feature.description }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
89
js/src/ui/Features/features.spec.js
Normal file
89
js/src/ui/Features/features.spec.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import defaults, { MODES } from './defaults';
|
||||||
|
|
||||||
|
import Features from './';
|
||||||
|
|
||||||
|
let component;
|
||||||
|
let instance;
|
||||||
|
|
||||||
|
function render (props = { visible: true }) {
|
||||||
|
component = shallow(
|
||||||
|
<Features { ...props } />
|
||||||
|
);
|
||||||
|
instance = component.instance();
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('views/Settings/Features', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders defaults', () => {
|
||||||
|
expect(component).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('visibility', () => {
|
||||||
|
let oldEnv;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
oldEnv = process.env.NODE_ENV;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env.NODE_ENV = oldEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders null when NODE_ENV === production', () => {
|
||||||
|
process.env.NODE_ENV = 'production';
|
||||||
|
render();
|
||||||
|
expect(component.get(0)).to.be.null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders component when NODE_ENV !== production', () => {
|
||||||
|
process.env.NODE_ENV = 'development';
|
||||||
|
render();
|
||||||
|
expect(component.get(0)).not.to.be.null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('instance methods', () => {
|
||||||
|
describe('renderItem', () => {
|
||||||
|
const keys = Object.keys(defaults).filter((key) => defaults[key].mode !== MODES.PRODUCTION);
|
||||||
|
const key = keys[0];
|
||||||
|
|
||||||
|
let item;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
item = instance.renderItem(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders an item', () => {
|
||||||
|
expect(item).not.to.be.null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the correct name', () => {
|
||||||
|
expect(item.props.primaryText).to.equal(defaults[key].name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
25
js/src/ui/Features/index.js
Normal file
25
js/src/ui/Features/index.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { FEATURES } from './defaults';
|
||||||
|
import FeaturesStore from './store';
|
||||||
|
|
||||||
|
export default from './features';
|
||||||
|
|
||||||
|
export {
|
||||||
|
FEATURES,
|
||||||
|
FeaturesStore
|
||||||
|
};
|
73
js/src/ui/Features/store.js
Normal file
73
js/src/ui/Features/store.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { action, observable } from 'mobx';
|
||||||
|
import store from 'store';
|
||||||
|
|
||||||
|
import { LS_STORE_KEY } from './constants';
|
||||||
|
import defaults, { FEATURES, MODES } from './defaults';
|
||||||
|
|
||||||
|
const isProductionMode = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
let instance = null;
|
||||||
|
|
||||||
|
export default class Store {
|
||||||
|
@observable active = {};
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
this.loadActiveFeatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setActiveFeatures = (features = {}, isProduction) => {
|
||||||
|
this.active = Object.assign({}, this.getDefaultActive(isProduction), features);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action toggleActive = (featureKey) => {
|
||||||
|
this.active = Object.assign({}, this.active, { [featureKey]: !this.active[featureKey] });
|
||||||
|
this.saveActiveFeatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultActive (isProduction = isProductionMode) {
|
||||||
|
const modesTest = [MODES.PRODUCTION];
|
||||||
|
|
||||||
|
if (!isProduction) {
|
||||||
|
modesTest.push(MODES.TESTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object
|
||||||
|
.keys(FEATURES)
|
||||||
|
.reduce((visibility, feature) => {
|
||||||
|
visibility[feature] = modesTest.includes(defaults[feature].mode);
|
||||||
|
return visibility;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadActiveFeatures () {
|
||||||
|
this.setActiveFeatures(store.get(LS_STORE_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
saveActiveFeatures () {
|
||||||
|
store.set(LS_STORE_KEY, this.active);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get () {
|
||||||
|
if (!instance) {
|
||||||
|
instance = new Store();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
96
js/src/ui/Features/store.spec.js
Normal file
96
js/src/ui/Features/store.spec.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import lstore from 'store';
|
||||||
|
|
||||||
|
import { LS_STORE_KEY } from './constants';
|
||||||
|
import defaults, { MODES } from './defaults';
|
||||||
|
|
||||||
|
import Store from './store';
|
||||||
|
|
||||||
|
let store;
|
||||||
|
|
||||||
|
function createStore () {
|
||||||
|
store = new Store();
|
||||||
|
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ui/Features/Store', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
lstore.set(LS_STORE_KEY, { 'testingFromStorage': true });
|
||||||
|
createStore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads with values from localStorage', () => {
|
||||||
|
expect(store.active.testingFromStorage).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@action', () => {
|
||||||
|
describe('setActiveFeatures', () => {
|
||||||
|
it('sets the active features', () => {
|
||||||
|
store.setActiveFeatures({ 'testing': true });
|
||||||
|
expect(store.active.testing).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('overrides the defaults', () => {
|
||||||
|
store.setActiveFeatures({ '.PRODUCTION': false });
|
||||||
|
expect(store.active['.PRODUCTION']).to.be.false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toggleActive', () => {
|
||||||
|
it('changes the state of a feature', () => {
|
||||||
|
expect(store.active['.PRODUCTION']).to.be.true;
|
||||||
|
store.toggleActive('.PRODUCTION');
|
||||||
|
expect(store.active['.PRODUCTION']).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves the updated state to localStorage', () => {
|
||||||
|
store.toggleActive('.PRODUCTION');
|
||||||
|
expect(lstore.get(LS_STORE_KEY)).to.deep.equal(store.active);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('operations', () => {
|
||||||
|
describe('getDefaultActive', () => {
|
||||||
|
it('returns features where mode === TESTING|PRODUCTION (non-production)', () => {
|
||||||
|
const visibility = store.getDefaultActive(false);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
Object
|
||||||
|
.keys(visibility)
|
||||||
|
.filter((key) => visibility[key])
|
||||||
|
.filter((key) => ![MODES.TESTING, MODES.PRODUCTION].includes(defaults[key].mode))
|
||||||
|
.length
|
||||||
|
).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns features where mode === PRODUCTION (production)', () => {
|
||||||
|
const visibility = store.getDefaultActive(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
Object
|
||||||
|
.keys(visibility)
|
||||||
|
.filter((key) => visibility[key])
|
||||||
|
.filter((key) => ![MODES.PRODUCTION].includes(defaults[key].mode))
|
||||||
|
.length
|
||||||
|
).to.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -14,20 +14,23 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import { MenuItem } from 'material-ui';
|
import { MenuItem } from 'material-ui';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { LocaleStore } from '~/i18n';
|
||||||
|
import { FeaturesStore, FEATURES } from '../Features';
|
||||||
|
|
||||||
import Select from '../Form/Select';
|
import Select from '../Form/Select';
|
||||||
import { LocaleStore } from '../../i18n';
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export default class LanguageSelector extends Component {
|
export default class LanguageSelector extends Component {
|
||||||
|
features = FeaturesStore.get();
|
||||||
store = LocaleStore.get();
|
store = LocaleStore.get();
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
if (!this.store.isDevelopment) {
|
if (!this.features.active[FEATURES.LANGUAGE]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +73,6 @@ export default class LanguageSelector extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onChange = (event, index, locale) => {
|
onChange = (event, index, locale) => {
|
||||||
this.store.setLocale(locale);
|
this.store.setLocale(locale || event.target.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
69
js/src/ui/LanguageSelector/langugeSelector.spec.js
Normal file
69
js/src/ui/LanguageSelector/langugeSelector.spec.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import React from 'react';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import { LocaleStore } from '~/i18n';
|
||||||
|
|
||||||
|
import LanguageSelector from './';
|
||||||
|
|
||||||
|
let component;
|
||||||
|
|
||||||
|
function render (props = {}) {
|
||||||
|
component = shallow(
|
||||||
|
<LanguageSelector { ...props } />
|
||||||
|
);
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('LanguageSelector', () => {
|
||||||
|
it('renders defaults', () => {
|
||||||
|
expect(render()).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Select', () => {
|
||||||
|
let select;
|
||||||
|
let localeStore;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localeStore = LocaleStore.get();
|
||||||
|
sinon.stub(localeStore, 'setLocale');
|
||||||
|
|
||||||
|
render();
|
||||||
|
select = component.find('Select');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
localeStore.setLocale.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the Select', () => {
|
||||||
|
expect(select).to.have.length(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has locale items', () => {
|
||||||
|
expect(select.find('MenuItem').length > 0).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls localeStore.setLocale when changed', () => {
|
||||||
|
select.simulate('change', { target: { value: 'de' } });
|
||||||
|
expect(localeStore.setLocale).to.have.been.calledWith('de');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -31,6 +31,7 @@ import CopyToClipboard from './CopyToClipboard';
|
|||||||
import CurrencySymbol from './CurrencySymbol';
|
import CurrencySymbol from './CurrencySymbol';
|
||||||
import Editor from './Editor';
|
import Editor from './Editor';
|
||||||
import Errors from './Errors';
|
import Errors from './Errors';
|
||||||
|
import Features, { FEATURES, FeaturesStore } from './Features';
|
||||||
import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select, RadioButtons } from './Form';
|
import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select, RadioButtons } from './Form';
|
||||||
import GasPriceEditor from './GasPriceEditor';
|
import GasPriceEditor from './GasPriceEditor';
|
||||||
import GasPriceSelector from './GasPriceSelector';
|
import GasPriceSelector from './GasPriceSelector';
|
||||||
@ -74,6 +75,9 @@ export {
|
|||||||
CurrencySymbol,
|
CurrencySymbol,
|
||||||
Editor,
|
Editor,
|
||||||
Errors,
|
Errors,
|
||||||
|
FEATURES,
|
||||||
|
Features,
|
||||||
|
FeaturesStore,
|
||||||
Form,
|
Form,
|
||||||
FormWrap,
|
FormWrap,
|
||||||
GasPriceEditor,
|
GasPriceEditor,
|
||||||
|
@ -16,17 +16,18 @@
|
|||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
|
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
||||||
import { CancelIcon, FingerprintIcon } from '~/ui/Icons';
|
import { CancelIcon, FingerprintIcon } from '~/ui/Icons';
|
||||||
import { Badge, Button, ContainerTitle, ParityBackground } from '~/ui';
|
import { Badge, Button, ContainerTitle, ParityBackground } from '~/ui';
|
||||||
import { Embedded as Signer } from '../Signer';
|
import { Embedded as Signer } from '../Signer';
|
||||||
import DappsStore from '~/views/Dapps/dappsStore';
|
import DappsStore from '~/views/Dapps/dappsStore';
|
||||||
|
|
||||||
import imagesEthcoreBlock from '!url-loader!../../../assets/images/parity-logo-white-no-text.svg';
|
|
||||||
import styles from './parityBar.css';
|
import styles from './parityBar.css';
|
||||||
|
|
||||||
const LS_STORE_KEY = '_parity::parityBar';
|
const LS_STORE_KEY = '_parity::parityBar';
|
||||||
@ -112,10 +113,6 @@ class ParityBar extends Component {
|
|||||||
render () {
|
render () {
|
||||||
const { moving, opened, position } = this.state;
|
const { moving, opened, position } = this.state;
|
||||||
|
|
||||||
const content = opened
|
|
||||||
? this.renderExpanded()
|
|
||||||
: this.renderBar();
|
|
||||||
|
|
||||||
const containerClassNames = opened
|
const containerClassNames = opened
|
||||||
? [ styles.overlay ]
|
? [ styles.overlay ]
|
||||||
: [ styles.bar ];
|
: [ styles.bar ];
|
||||||
@ -124,11 +121,12 @@ class ParityBar extends Component {
|
|||||||
containerClassNames.push(styles.moving);
|
containerClassNames.push(styles.moving);
|
||||||
}
|
}
|
||||||
|
|
||||||
const parityBgClassName = opened
|
const parityBgClassNames = [
|
||||||
? styles.expanded
|
opened
|
||||||
: styles.corner;
|
? styles.expanded
|
||||||
|
: styles.corner,
|
||||||
const parityBgClassNames = [ parityBgClassName, styles.parityBg ];
|
styles.parityBg
|
||||||
|
];
|
||||||
|
|
||||||
if (moving) {
|
if (moving) {
|
||||||
parityBgClassNames.push(styles.moving);
|
parityBgClassNames.push(styles.moving);
|
||||||
@ -169,7 +167,11 @@ class ParityBar extends Component {
|
|||||||
ref='container'
|
ref='container'
|
||||||
style={ parityBgStyle }
|
style={ parityBgStyle }
|
||||||
>
|
>
|
||||||
{ content }
|
{
|
||||||
|
opened
|
||||||
|
? this.renderExpanded()
|
||||||
|
: this.renderBar()
|
||||||
|
}
|
||||||
</ParityBackground>
|
</ParityBackground>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -182,34 +184,38 @@ class ParityBar extends Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parityIcon = (
|
|
||||||
<img
|
|
||||||
src={ imagesEthcoreBlock }
|
|
||||||
className={ styles.parityIcon }
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const parityButton = (
|
|
||||||
<Button
|
|
||||||
className={ styles.parityButton }
|
|
||||||
icon={ parityIcon }
|
|
||||||
label={ this.renderLabel('Parity') }
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={ styles.cornercolor }
|
className={ styles.cornercolor }
|
||||||
ref={ this.onRef }
|
ref={ this.onRef }
|
||||||
>
|
>
|
||||||
{ this.renderLink(parityButton) }
|
{
|
||||||
|
this.renderLink(
|
||||||
|
<Button
|
||||||
|
className={ styles.parityButton }
|
||||||
|
icon={
|
||||||
|
<img
|
||||||
|
className={ styles.parityIcon }
|
||||||
|
src={ imagesEthcoreBlock }
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
this.renderLabel(
|
||||||
|
<FormattedMessage
|
||||||
|
id='parityBar.label.parity'
|
||||||
|
defaultMessage='Parity'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
<Button
|
<Button
|
||||||
className={ styles.button }
|
className={ styles.button }
|
||||||
icon={ <FingerprintIcon /> }
|
icon={ <FingerprintIcon /> }
|
||||||
label={ this.renderSignerLabel() }
|
label={ this.renderSignerLabel() }
|
||||||
onClick={ this.toggleDisplay }
|
onClick={ this.toggleDisplay }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{ this.renderDrag() }
|
{ this.renderDrag() }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -307,7 +313,13 @@ class ParityBar extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.renderLabel('Signer', bubble);
|
return this.renderLabel(
|
||||||
|
<FormattedMessage
|
||||||
|
id='parityBar.label.signer'
|
||||||
|
defaultMessage='Signer'
|
||||||
|
/>,
|
||||||
|
bubble
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHorizontal (x) {
|
getHorizontal (x) {
|
||||||
|
157
js/src/views/ParityBar/parityBar.spec.js
Normal file
157
js/src/views/ParityBar/parityBar.spec.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import React from 'react';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import ParityBar from './';
|
||||||
|
|
||||||
|
let component;
|
||||||
|
let instance;
|
||||||
|
let store;
|
||||||
|
|
||||||
|
function createRedux (state = {}) {
|
||||||
|
store = {
|
||||||
|
dispatch: sinon.stub(),
|
||||||
|
subscribe: sinon.stub(),
|
||||||
|
getState: () => Object.assign({ signer: { pending: [] } }, state)
|
||||||
|
};
|
||||||
|
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render (props = {}, state = {}) {
|
||||||
|
component = shallow(
|
||||||
|
<ParityBar { ...props } />,
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
store: createRedux(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).find('ParityBar').shallow({ context: { api: {} } });
|
||||||
|
instance = component.instance();
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('views/ParityBar', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
render({ dapp: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders defaults', () => {
|
||||||
|
expect(component).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes the ParityBackground', () => {
|
||||||
|
expect(component.find('Connect(ParityBackground)')).to.have.length(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('renderBar', () => {
|
||||||
|
let bar;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
bar = shallow(instance.renderBar());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders nothing when not overlaying a dapp', () => {
|
||||||
|
render({ dapp: false });
|
||||||
|
expect(instance.renderBar()).to.be.null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders when overlaying a dapp', () => {
|
||||||
|
expect(bar.find('div')).not.to.have.length(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the Parity button', () => {
|
||||||
|
const label = shallow(bar.find('Button').first().props().label);
|
||||||
|
|
||||||
|
expect(label.find('FormattedMessage').props().id).to.equal('parityBar.label.parity');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the Signer button', () => {
|
||||||
|
const label = shallow(bar.find('Button').last().props().label);
|
||||||
|
|
||||||
|
expect(label.find('FormattedMessage').props().id).to.equal('parityBar.label.signer');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('renderExpanded', () => {
|
||||||
|
let expanded;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
expanded = shallow(instance.renderExpanded());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes the Signer', () => {
|
||||||
|
expect(expanded.find('Connect(Embedded)')).to.have.length(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('renderLabel', () => {
|
||||||
|
it('renders the label name', () => {
|
||||||
|
expect(shallow(instance.renderLabel('testing', null)).text()).to.equal('testing');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders name and bubble', () => {
|
||||||
|
expect(shallow(instance.renderLabel('testing', '(bubble)')).text()).to.equal('testing(bubble)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('renderSignerLabel', () => {
|
||||||
|
let label;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
label = shallow(instance.renderSignerLabel());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the signer label', () => {
|
||||||
|
expect(label.find('FormattedMessage').props().id).to.equal('parityBar.label.signer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render a badge when no pending requests', () => {
|
||||||
|
expect(label.find('Badge')).to.have.length(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders a badge when pending requests', () => {
|
||||||
|
render({}, { signer: { pending: ['123', '456'] } });
|
||||||
|
expect(shallow(instance.renderSignerLabel()).find('Badge').props().value).to.equal(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('opened state', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
sinon.spy(instance, 'renderBar');
|
||||||
|
sinon.spy(instance, 'renderExpanded');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
instance.renderBar.restore();
|
||||||
|
instance.renderExpanded.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the bar on with opened === false', () => {
|
||||||
|
expect(component.find('Link[to="/apps"]')).to.have.length(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders expanded with opened === true', () => {
|
||||||
|
expect(instance.renderExpanded).not.to.have.been.called;
|
||||||
|
instance.setState({ opened: true });
|
||||||
|
expect(instance.renderExpanded).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -14,67 +14,28 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { MenuItem } from 'material-ui';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { MenuItem } from 'material-ui';
|
|
||||||
import LogLevel from 'loglevel';
|
|
||||||
|
|
||||||
import { LOG_KEYS } from '~/config';
|
|
||||||
import { Select, Container, LanguageSelector } from '~/ui';
|
import { Select, Container, LanguageSelector } from '~/ui';
|
||||||
|
import Features, { FeaturesStore, FEATURES } from '~/ui/Features';
|
||||||
|
|
||||||
|
import Store, { LOGLEVEL_OPTIONS } from './store';
|
||||||
import layout from '../layout.css';
|
import layout from '../layout.css';
|
||||||
|
|
||||||
|
@observer
|
||||||
export default class Parity extends Component {
|
export default class Parity extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: PropTypes.object.isRequired
|
api: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
store = new Store(this.context.api);
|
||||||
loglevels: {},
|
features = FeaturesStore.get();
|
||||||
mode: 'active',
|
|
||||||
selectValues: []
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
this.loadMode();
|
return this.store.loadMode();
|
||||||
this.loadLogLevels();
|
|
||||||
this.setSelectValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadLogLevels () {
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextState = { ...this.state.logLevels };
|
|
||||||
|
|
||||||
Object.keys(LOG_KEYS).map((logKey) => {
|
|
||||||
const log = LOG_KEYS[logKey];
|
|
||||||
|
|
||||||
const logger = LogLevel.getLogger(log.key);
|
|
||||||
const level = logger.getLevel();
|
|
||||||
|
|
||||||
nextState[logKey] = { level, log };
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({ logLevels: nextState });
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectValues () {
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectValues = Object.keys(LogLevel.levels).map((levelName) => {
|
|
||||||
const value = LogLevel.levels[levelName];
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: levelName,
|
|
||||||
value
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({ selectValues });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -94,18 +55,30 @@ export default class Parity extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={ layout.details }>
|
<div className={ layout.details }>
|
||||||
<LanguageSelector />
|
|
||||||
{ this.renderModes() }
|
{ this.renderModes() }
|
||||||
|
<Features />
|
||||||
|
<LanguageSelector />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ this.renderLogsConfig() }
|
{ this.renderLogsConfig() }
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderItem (mode, label) {
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
key={ mode }
|
||||||
|
label={ label }
|
||||||
|
value={ mode }
|
||||||
|
>
|
||||||
|
{ label }
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderLogsConfig () {
|
renderLogsConfig () {
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (!this.features.active[FEATURES.LOGLEVELS]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,129 +100,89 @@ export default class Parity extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderLogsLevels () {
|
renderLogsLevels () {
|
||||||
if (process.env.NODE_ENV === 'production') {
|
const { logLevels } = this.store;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { logLevels, selectValues } = this.state;
|
return Object
|
||||||
|
.keys(logLevels)
|
||||||
|
.map((key) => {
|
||||||
|
const { level, log } = logLevels[key];
|
||||||
|
const { path, desc } = log;
|
||||||
|
|
||||||
return Object.keys(logLevels).map((logKey) => {
|
const onChange = (_, index) => {
|
||||||
const { level, log } = logLevels[logKey];
|
this.store.updateLoggerLevel(path, Object.values(LOGLEVEL_OPTIONS)[index].value);
|
||||||
const { key, desc } = log;
|
};
|
||||||
|
|
||||||
const onChange = (_, index) => {
|
return (
|
||||||
const nextLevel = Object.values(selectValues)[index].value;
|
<div key={ key }>
|
||||||
|
<p>{ desc }</p>
|
||||||
LogLevel.getLogger(key).setLevel(nextLevel);
|
<Select
|
||||||
this.loadLogLevels();
|
onChange={ onChange }
|
||||||
};
|
value={ level }
|
||||||
|
values={ LOGLEVEL_OPTIONS }
|
||||||
return (
|
/>
|
||||||
<div key={ logKey }>
|
</div>
|
||||||
<p>{ desc }</p>
|
);
|
||||||
<Select
|
});
|
||||||
onChange={ onChange }
|
|
||||||
value={ level }
|
|
||||||
values={ selectValues }
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderModes () {
|
renderModes () {
|
||||||
const { mode } = this.state;
|
const { mode } = this.store;
|
||||||
|
|
||||||
const renderItem = (mode, label) => {
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
key={ mode }
|
|
||||||
value={ mode }
|
|
||||||
label={ label }
|
|
||||||
>
|
|
||||||
{ label }
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
label={
|
id='parityModeSelect'
|
||||||
<FormattedMessage
|
|
||||||
id='settings.parity.modes.label'
|
|
||||||
defaultMessage='mode of operation'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
hint={
|
hint={
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='settings.parity.modes.hint'
|
id='settings.parity.modes.hint'
|
||||||
defaultMessage='the syning mode for the Parity node'
|
defaultMessage='the syning mode for the Parity node'
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
value={ mode }
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='settings.parity.modes.label'
|
||||||
|
defaultMessage='mode of operation'
|
||||||
|
/>
|
||||||
|
}
|
||||||
onChange={ this.onChangeMode }
|
onChange={ this.onChangeMode }
|
||||||
|
value={ mode }
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
renderItem('active',
|
this.renderItem('active', (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='settings.parity.modes.mode_active'
|
id='settings.parity.modes.mode_active'
|
||||||
defaultMessage='Parity continuously syncs the chain'
|
defaultMessage='Parity continuously syncs the chain'
|
||||||
/>
|
/>
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
renderItem('passive',
|
this.renderItem('passive', (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='settings.parity.modes.mode_passive'
|
id='settings.parity.modes.mode_passive'
|
||||||
defaultMessage='Parity syncs initially, then sleeps and wakes regularly to resync'
|
defaultMessage='Parity syncs initially, then sleeps and wakes regularly to resync'
|
||||||
/>
|
/>
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
renderItem('dark',
|
this.renderItem('dark', (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='settings.parity.modes.mode_dark'
|
id='settings.parity.modes.mode_dark'
|
||||||
defaultMessage='Parity syncs only when the RPC is active'
|
defaultMessage='Parity syncs only when the RPC is active'
|
||||||
/>
|
/>
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
renderItem('offline',
|
this.renderItem('offline', (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='settings.parity.modes.mode_offline'
|
id='settings.parity.modes.mode_offline'
|
||||||
defaultMessage="Parity doesn't sync"
|
defaultMessage="Parity doesn't sync"
|
||||||
/>
|
/>
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeMode = (event, index, mode) => {
|
onChangeMode = (event, index, mode) => {
|
||||||
const { api } = this.context;
|
this.store.changeMode(mode || event.target.value);
|
||||||
|
|
||||||
api.parity
|
|
||||||
.setMode(mode)
|
|
||||||
.then((result) => {
|
|
||||||
if (result) {
|
|
||||||
this.setState({ mode });
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.warn('onChangeMode', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadMode () {
|
|
||||||
const { api } = this.context;
|
|
||||||
|
|
||||||
api.parity
|
|
||||||
.mode()
|
|
||||||
.then((mode) => {
|
|
||||||
this.setState({ mode });
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.warn('loadMode', error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
98
js/src/views/Settings/Parity/parity.spec.js
Normal file
98
js/src/views/Settings/Parity/parity.spec.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import React from 'react';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import { createApi } from './parity.test.js';
|
||||||
|
import Parity from './';
|
||||||
|
|
||||||
|
let component;
|
||||||
|
let instance;
|
||||||
|
|
||||||
|
function render (props = {}) {
|
||||||
|
component = shallow(
|
||||||
|
<Parity { ...props } />,
|
||||||
|
{ context: { api: createApi() } }
|
||||||
|
);
|
||||||
|
instance = component.instance();
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('views/Settings/Parity', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
render();
|
||||||
|
sinon.spy(instance.store, 'loadMode');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
instance.store.loadMode.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders defaults', () => {
|
||||||
|
expect(component).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('componentWillMount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return instance.componentWillMount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the mode in the store', () => {
|
||||||
|
expect(instance.store.loadMode).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('components', () => {
|
||||||
|
it('renders a Container component', () => {
|
||||||
|
expect(component.find('Container')).to.have.length(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders a LanguageSelector component', () => {
|
||||||
|
expect(component.find('LanguageSelector')).to.have.length(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders a Features component', () => {
|
||||||
|
expect(component.find('LanguageSelector')).to.have.length(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Parity features', () => {
|
||||||
|
describe('mode selector', () => {
|
||||||
|
let select;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
select = component.find('Select[id="parityModeSelect"]');
|
||||||
|
sinon.spy(instance.store, 'changeMode');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
instance.store.changeMode.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders a mode selector', () => {
|
||||||
|
expect(select).to.have.length(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changes the mode on the store when changed', () => {
|
||||||
|
select.simulate('change', { target: { value: 'dark' } });
|
||||||
|
expect(instance.store.changeMode).to.have.been.calledWith('dark');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
30
js/src/views/Settings/Parity/parity.test.js
Normal file
30
js/src/views/Settings/Parity/parity.test.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
function createApi () {
|
||||||
|
return {
|
||||||
|
parity: {
|
||||||
|
mode: sinon.stub().resolves('passive'),
|
||||||
|
setMode: sinon.stub().resolves(true)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
createApi
|
||||||
|
};
|
105
js/src/views/Settings/Parity/store.js
Normal file
105
js/src/views/Settings/Parity/store.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import LogLevel from 'loglevel';
|
||||||
|
import { action, observable } from 'mobx';
|
||||||
|
|
||||||
|
import { LOG_KEYS } from '~/config';
|
||||||
|
|
||||||
|
const DEFAULT_MODE = 'active';
|
||||||
|
const LOGLEVEL_OPTIONS = Object
|
||||||
|
.keys(LogLevel.levels)
|
||||||
|
.map((name) => {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
value: LogLevel.levels[name]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default class Store {
|
||||||
|
@observable logLevels = {};
|
||||||
|
@observable mode = DEFAULT_MODE;
|
||||||
|
|
||||||
|
constructor (api) {
|
||||||
|
this._api = api;
|
||||||
|
|
||||||
|
this.loadLogLevels();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setLogLevels = (logLevels) => {
|
||||||
|
this.logLevels = logLevels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setLogLevelsSelect = (logLevelsSelect) => {
|
||||||
|
this.logLevelsSelect = logLevelsSelect;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setMode = (mode) => {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeMode (mode) {
|
||||||
|
return this._api.parity
|
||||||
|
.setMode(mode)
|
||||||
|
.then((result) => {
|
||||||
|
if (result) {
|
||||||
|
this.setMode(mode);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('changeMode', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLogLevels () {
|
||||||
|
this.setLogLevels(
|
||||||
|
Object
|
||||||
|
.keys(LOG_KEYS)
|
||||||
|
.reduce((state, logKey) => {
|
||||||
|
const log = LOG_KEYS[logKey];
|
||||||
|
const logger = LogLevel.getLogger(log.key);
|
||||||
|
const level = logger.getLevel();
|
||||||
|
|
||||||
|
state[logKey] = {
|
||||||
|
level,
|
||||||
|
log
|
||||||
|
};
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}, this.logLevels)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLoggerLevel (path, level) {
|
||||||
|
LogLevel.getLogger(path).setLevel(level);
|
||||||
|
this.loadLogLevels();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMode () {
|
||||||
|
return this._api.parity
|
||||||
|
.mode()
|
||||||
|
.then((mode) => {
|
||||||
|
this.setMode(mode);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('loadMode', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
LOGLEVEL_OPTIONS
|
||||||
|
};
|
84
js/src/views/Settings/Parity/store.spec.js
Normal file
84
js/src/views/Settings/Parity/store.spec.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import { createApi } from './parity.test.js';
|
||||||
|
import Store from './store';
|
||||||
|
|
||||||
|
let api;
|
||||||
|
let store;
|
||||||
|
|
||||||
|
function createStore () {
|
||||||
|
api = createApi();
|
||||||
|
store = new Store(api);
|
||||||
|
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('views/Settings/Parity/Store', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
createStore();
|
||||||
|
sinon.spy(store, 'setMode');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
store.setMode.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults to mode === active', () => {
|
||||||
|
expect(store.mode).to.equal('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@action', () => {
|
||||||
|
describe('setMode', () => {
|
||||||
|
it('sets the mode', () => {
|
||||||
|
store.setMode('offline');
|
||||||
|
expect(store.mode).to.equal('offline');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('operations', () => {
|
||||||
|
describe('changeMode', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return store.changeMode('offline');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls parity.setMode', () => {
|
||||||
|
expect(api.parity.setMode).to.have.been.calledWith('offline');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the mode as provided', () => {
|
||||||
|
expect(store.setMode).to.have.been.calledWith('offline');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadMode', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return store.loadMode();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls parity.mode', () => {
|
||||||
|
expect(api.parity.mode).to.have.been.called;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the mode as retrieved', () => {
|
||||||
|
expect(store.setMode).to.have.been.calledWith('passive');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -117,8 +117,13 @@ module.exports = {
|
|||||||
test: /\.(woff(2)|ttf|eot|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.(woff(2)|ttf|eot|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
use: [ 'file-loader?name=fonts/[name][hash:10].[ext]' ]
|
use: [ 'file-loader?name=fonts/[name][hash:10].[ext]' ]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /parity-logo-white-no-text\.svg/,
|
||||||
|
use: [ 'url-loader' ]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.svg(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.svg(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
|
exclude: [ /parity-logo-white-no-text\.svg/ ],
|
||||||
use: [ 'file-loader?name=assets/[name].[hash:10].[ext]' ]
|
use: [ 'file-loader?name=assets/[name].[hash:10].[ext]' ]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user