From 155bbc328fe9da66272bc2a091438fb80eb34d62 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 24 Jan 2017 17:20:10 +0100 Subject: [PATCH] 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 --- js/src/i18n/constants.js | 27 +++ js/src/i18n/languages.spec.js | 30 +++ js/src/i18n/store.js | 19 +- js/src/i18n/store.spec.js | 73 +++++++ js/src/ui/Features/constants.js | 21 ++ js/src/ui/Features/defaults.js | 61 ++++++ js/src/ui/Features/defaults.spec.js | 67 +++++++ js/src/ui/Features/features.css | 20 ++ js/src/ui/Features/features.js | 69 +++++++ js/src/ui/Features/features.spec.js | 89 +++++++++ js/src/ui/Features/index.js | 25 +++ js/src/ui/Features/store.js | 73 +++++++ js/src/ui/Features/store.spec.js | 96 +++++++++ .../ui/LanguageSelector/languageSelector.js | 13 +- .../LanguageSelector/langugeSelector.spec.js | 69 +++++++ js/src/ui/index.js | 4 + js/src/views/ParityBar/parityBar.js | 70 ++++--- js/src/views/ParityBar/parityBar.spec.js | 157 +++++++++++++++ js/src/views/Settings/Parity/parity.js | 189 ++++++------------ js/src/views/Settings/Parity/parity.spec.js | 98 +++++++++ js/src/views/Settings/Parity/parity.test.js | 30 +++ js/src/views/Settings/Parity/store.js | 105 ++++++++++ js/src/views/Settings/Parity/store.spec.js | 84 ++++++++ js/webpack/app.js | 5 + 24 files changed, 1319 insertions(+), 175 deletions(-) create mode 100644 js/src/i18n/constants.js create mode 100644 js/src/i18n/languages.spec.js create mode 100644 js/src/i18n/store.spec.js create mode 100644 js/src/ui/Features/constants.js create mode 100644 js/src/ui/Features/defaults.js create mode 100644 js/src/ui/Features/defaults.spec.js create mode 100644 js/src/ui/Features/features.css create mode 100644 js/src/ui/Features/features.js create mode 100644 js/src/ui/Features/features.spec.js create mode 100644 js/src/ui/Features/index.js create mode 100644 js/src/ui/Features/store.js create mode 100644 js/src/ui/Features/store.spec.js create mode 100644 js/src/ui/LanguageSelector/langugeSelector.spec.js create mode 100644 js/src/views/ParityBar/parityBar.spec.js create mode 100644 js/src/views/Settings/Parity/parity.spec.js create mode 100644 js/src/views/Settings/Parity/parity.test.js create mode 100644 js/src/views/Settings/Parity/store.js create mode 100644 js/src/views/Settings/Parity/store.spec.js diff --git a/js/src/i18n/constants.js b/js/src/i18n/constants.js new file mode 100644 index 000000000..7b863be62 --- /dev/null +++ b/js/src/i18n/constants.js @@ -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 . + +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 +}; diff --git a/js/src/i18n/languages.spec.js b/js/src/i18n/languages.spec.js new file mode 100644 index 000000000..2f33bf3b6 --- /dev/null +++ b/js/src/i18n/languages.spec.js @@ -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 . + +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; + }); +}); diff --git a/js/src/i18n/store.js b/js/src/i18n/store.js index 497e55c72..f5a5f93ed 100644 --- a/js/src/i18n/store.js +++ b/js/src/i18n/store.js @@ -21,39 +21,32 @@ import de from 'react-intl/locale-data/de'; import en from 'react-intl/locale-data/en'; import store from 'store'; +import { DEFAULT_LOCALE, DEFAULT_LOCALES, LS_STORE_KEY } from './constants'; import languages from './languages'; import deMessages from './de'; import enMessages from './en'; -const LS_STORE_KEY = '_parity::locale'; - let instance = null; -const isProduction = process.env.NODE_ENV === 'production'; -const DEFAULT = 'en'; const LANGUAGES = flatten({ languages }); const MESSAGES = { de: Object.assign(flatten(deMessages), LANGUAGES), en: Object.assign(flatten(enMessages), LANGUAGES) }; -const LOCALES = isProduction - ? ['en'] - : ['en', 'de']; addLocaleData([...de, ...en]); export default class Store { - @observable locale = DEFAULT; - @observable locales = LOCALES; - @observable messages = MESSAGES[DEFAULT]; - @observable isDevelopment = !isProduction; + @observable locale = DEFAULT_LOCALE; + @observable locales = DEFAULT_LOCALES; + @observable messages = MESSAGES[DEFAULT_LOCALE]; constructor () { const savedLocale = store.get(LS_STORE_KEY); - this.locale = (savedLocale && LOCALES.includes(savedLocale)) + this.locale = (savedLocale && DEFAULT_LOCALES.includes(savedLocale)) ? savedLocale - : DEFAULT; + : DEFAULT_LOCALE; this.messages = MESSAGES[this.locale]; } diff --git a/js/src/i18n/store.spec.js b/js/src/i18n/store.spec.js new file mode 100644 index 000000000..d6dbc4fba --- /dev/null +++ b/js/src/i18n/store.spec.js @@ -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 . + +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); + }); + }); + }); +}); diff --git a/js/src/ui/Features/constants.js b/js/src/ui/Features/constants.js new file mode 100644 index 000000000..4bcf16c99 --- /dev/null +++ b/js/src/ui/Features/constants.js @@ -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 . + +const LS_STORE_KEY = '_parity::features'; + +export { + LS_STORE_KEY +}; diff --git a/js/src/ui/Features/defaults.js b/js/src/ui/Features/defaults.js new file mode 100644 index 000000000..60122e2fa --- /dev/null +++ b/js/src/ui/Features/defaults.js @@ -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 . + +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 +}; diff --git a/js/src/ui/Features/defaults.spec.js b/js/src/ui/Features/defaults.spec.js new file mode 100644 index 000000000..df7f4253a --- /dev/null +++ b/js/src/ui/Features/defaults.spec.js @@ -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 . + +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; + }); + }); + }); + }); +}); diff --git a/js/src/ui/Features/features.css b/js/src/ui/Features/features.css new file mode 100644 index 000000000..c5d370153 --- /dev/null +++ b/js/src/ui/Features/features.css @@ -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 . +*/ + +.description { + opacity: 0.75; +} diff --git a/js/src/ui/Features/features.js b/js/src/ui/Features/features.js new file mode 100644 index 000000000..bae1ffcd8 --- /dev/null +++ b/js/src/ui/Features/features.js @@ -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 . + +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 ( + + { + Object + .keys(defaults) + .filter((key) => defaults[key].mode !== MODES.PRODUCTION) + .map(this.renderItem) + } + + ); + } + + renderItem = (key) => { + const feature = defaults[key]; + const onCheck = () => this.store.toggleActive(key); + + return ( + + } + primaryText={ feature.name } + secondaryText={ +
+ { feature.description } +
+ } + /> + ); + } +} diff --git a/js/src/ui/Features/features.spec.js b/js/src/ui/Features/features.spec.js new file mode 100644 index 000000000..412505507 --- /dev/null +++ b/js/src/ui/Features/features.spec.js @@ -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 . + +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( + + ); + 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); + }); + }); + }); +}); diff --git a/js/src/ui/Features/index.js b/js/src/ui/Features/index.js new file mode 100644 index 000000000..97b2b25c7 --- /dev/null +++ b/js/src/ui/Features/index.js @@ -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 . + +import { FEATURES } from './defaults'; +import FeaturesStore from './store'; + +export default from './features'; + +export { + FEATURES, + FeaturesStore +}; diff --git a/js/src/ui/Features/store.js b/js/src/ui/Features/store.js new file mode 100644 index 000000000..263e6375c --- /dev/null +++ b/js/src/ui/Features/store.js @@ -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 . + +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; + } +} diff --git a/js/src/ui/Features/store.spec.js b/js/src/ui/Features/store.spec.js new file mode 100644 index 000000000..53b05c6d6 --- /dev/null +++ b/js/src/ui/Features/store.spec.js @@ -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 . + +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); + }); + }); + }); +}); diff --git a/js/src/ui/LanguageSelector/languageSelector.js b/js/src/ui/LanguageSelector/languageSelector.js index ddaa086d7..70cf2dafa 100644 --- a/js/src/ui/LanguageSelector/languageSelector.js +++ b/js/src/ui/LanguageSelector/languageSelector.js @@ -14,20 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { Component } from 'react'; -import { FormattedMessage } from 'react-intl'; import { MenuItem } from 'material-ui'; 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 { LocaleStore } from '../../i18n'; @observer export default class LanguageSelector extends Component { + features = FeaturesStore.get(); store = LocaleStore.get(); render () { - if (!this.store.isDevelopment) { + if (!this.features.active[FEATURES.LANGUAGE]) { return null; } @@ -70,6 +73,6 @@ export default class LanguageSelector extends Component { } onChange = (event, index, locale) => { - this.store.setLocale(locale); + this.store.setLocale(locale || event.target.value); } } diff --git a/js/src/ui/LanguageSelector/langugeSelector.spec.js b/js/src/ui/LanguageSelector/langugeSelector.spec.js new file mode 100644 index 000000000..285f6557d --- /dev/null +++ b/js/src/ui/LanguageSelector/langugeSelector.spec.js @@ -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 . + +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( + + ); + + 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'); + }); + }); +}); diff --git a/js/src/ui/index.js b/js/src/ui/index.js index 33001b84b..d66663d8e 100644 --- a/js/src/ui/index.js +++ b/js/src/ui/index.js @@ -31,6 +31,7 @@ import CopyToClipboard from './CopyToClipboard'; import CurrencySymbol from './CurrencySymbol'; import Editor from './Editor'; 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 GasPriceEditor from './GasPriceEditor'; import GasPriceSelector from './GasPriceSelector'; @@ -74,6 +75,9 @@ export { CurrencySymbol, Editor, Errors, + FEATURES, + Features, + FeaturesStore, Form, FormWrap, GasPriceEditor, diff --git a/js/src/views/ParityBar/parityBar.js b/js/src/views/ParityBar/parityBar.js index 3cc342e12..eec3577c9 100644 --- a/js/src/views/ParityBar/parityBar.js +++ b/js/src/views/ParityBar/parityBar.js @@ -16,17 +16,18 @@ import React, { Component, PropTypes } from 'react'; import ReactDOM from 'react-dom'; +import { FormattedMessage } from 'react-intl'; import { Link } from 'react-router'; import { connect } from 'react-redux'; import { throttle } from 'lodash'; import store from 'store'; +import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg'; import { CancelIcon, FingerprintIcon } from '~/ui/Icons'; import { Badge, Button, ContainerTitle, ParityBackground } from '~/ui'; import { Embedded as Signer } from '../Signer'; import DappsStore from '~/views/Dapps/dappsStore'; -import imagesEthcoreBlock from '!url-loader!../../../assets/images/parity-logo-white-no-text.svg'; import styles from './parityBar.css'; const LS_STORE_KEY = '_parity::parityBar'; @@ -112,10 +113,6 @@ class ParityBar extends Component { render () { const { moving, opened, position } = this.state; - const content = opened - ? this.renderExpanded() - : this.renderBar(); - const containerClassNames = opened ? [ styles.overlay ] : [ styles.bar ]; @@ -124,11 +121,12 @@ class ParityBar extends Component { containerClassNames.push(styles.moving); } - const parityBgClassName = opened - ? styles.expanded - : styles.corner; - - const parityBgClassNames = [ parityBgClassName, styles.parityBg ]; + const parityBgClassNames = [ + opened + ? styles.expanded + : styles.corner, + styles.parityBg + ]; if (moving) { parityBgClassNames.push(styles.moving); @@ -169,7 +167,11 @@ class ParityBar extends Component { ref='container' style={ parityBgStyle } > - { content } + { + opened + ? this.renderExpanded() + : this.renderBar() + } ); @@ -182,34 +184,38 @@ class ParityBar extends Component { return null; } - const parityIcon = ( - - ); - - const parityButton = ( -