From 317ab64e96641f6c44f6eb85fd3c16e1a86d6b0a Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 11 Mar 2020 17:19:03 -0400 Subject: [PATCH 1/4] Replace themes toggle with radio buttons --- _locales/en/messages.json | 13 +++- .../panel/icon-information-tooltip-blue.svg | 6 ++ .../components/BuildingBlocks/RadioButton.jsx | 46 +++++++++++ .../BuildingBlocks/RadioButtonGroup.jsx | 74 ++++++++++++++++++ app/panel/components/BuildingBlocks/index.js | 4 + app/panel/components/Subscription.jsx | 12 +-- .../Subscription/SubscriptionInfo.jsx | 2 +- .../Subscription/SubscriptionThemes.jsx | 78 +++++++++++++------ app/scss/panel.scss | 2 +- app/scss/partials/_radio_button.scss | 43 ++++++++++ app/scss/partials/_settings.scss | 12 ++- app/scss/partials/_subscribe.scss | 13 ++-- 12 files changed, 258 insertions(+), 47 deletions(-) create mode 100644 app/images/panel/icon-information-tooltip-blue.svg create mode 100644 app/panel/components/BuildingBlocks/RadioButton.jsx create mode 100644 app/panel/components/BuildingBlocks/RadioButtonGroup.jsx create mode 100644 app/scss/partials/_radio_button.scss diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 4a850e047..b43070477 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1822,8 +1822,17 @@ "subscribe_pitch_sign_in": { "message": "Already subscribed? Sign in" }, - "subscription_midnight_theme": { - "message": "Midnight Theme" + "subscription_default_theme": { + "message": "Default" + }, + "subscription_dark_blue_theme": { + "message": "Dark Blue Theme" + }, + "subscription_palm_theme": { + "message": "Palm Theme" + }, + "subscription_leaf_theme": { + "message": "Leaf Theme" }, "subscription_status": { "message": "Status" diff --git a/app/images/panel/icon-information-tooltip-blue.svg b/app/images/panel/icon-information-tooltip-blue.svg new file mode 100644 index 000000000..2b277cf22 --- /dev/null +++ b/app/images/panel/icon-information-tooltip-blue.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/panel/components/BuildingBlocks/RadioButton.jsx b/app/panel/components/BuildingBlocks/RadioButton.jsx new file mode 100644 index 000000000..d8205a983 --- /dev/null +++ b/app/panel/components/BuildingBlocks/RadioButton.jsx @@ -0,0 +1,46 @@ +/** + * Radio Button Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +/* eslint jsx-a11y/label-has-associated-control: 0 */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import ClassNames from 'classnames'; + +/** + * @class Implements a single radio button to be used inside the RadioButtonGroup component + * @memberof PanelBuildingBlocks + */ + +const RadioButton = (props) => { + const OuterCircleClassNames = ClassNames('RadioButton__outerCircle', { + checked: props.checked, + }); + const InnerCircleClassNames = ClassNames('RadioButton__innerCircle', { + checked: props.checked, + }); + return ( + + + + + + ); +}; + +// PropTypes ensure we pass required props of the correct type +RadioButton.propTypes = { + handleClick: PropTypes.func.isRequired, +}; + +export default RadioButton; diff --git a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx new file mode 100644 index 000000000..b49d84497 --- /dev/null +++ b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx @@ -0,0 +1,74 @@ +/** + * Radio Button Group Component + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +/* eslint jsx-a11y/label-has-associated-control: 0 */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import RadioButton from './RadioButton'; + +/** + * @class Implements a radio button group + * @memberof PanelBuildingBlocks + */ +class RadioButtonGroup extends React.Component { + constructor(props) { + super(props); + this.state = { + buttons: [] + }; + } + + componentDidMount() { + const buttons = new Array(this.props.items.length).fill(false); + buttons[this.props.selectedIndex] = true; + this.setState({ buttons }); + } + + handleClick(indexClicked) { + const { buttons } = this.state; + const updatedButtons = buttons.map((button, index) => (index === indexClicked)); + this.setState({ buttons: updatedButtons }); + this.props.handleItemClick(indexClicked); + } + + /** + * React's required render function. Returns JSX + * @return {JSX} JSX for rendering the Toggle Slider used throughout the extension + */ + render() { + const { buttons } = this.state; + return ( + this.props.items.map((item, index) => ( +
+ + {t(item.text)} + +
+ this.handleClick(index)} /> +
+
+ )) + ); + } +} + +// PropTypes ensure we pass required props of the correct type +RadioButtonGroup.propTypes = { + items: PropTypes.arrayOf(PropTypes.object).isRequired, // Number of objects in array is the number of radio buttons + handleItemClick: PropTypes.func.isRequired, + selectedIndex: PropTypes.number.isRequired +}; + + +export default RadioButtonGroup; diff --git a/app/panel/components/BuildingBlocks/index.js b/app/panel/components/BuildingBlocks/index.js index edc925cbc..8f73d6052 100644 --- a/app/panel/components/BuildingBlocks/index.js +++ b/app/panel/components/BuildingBlocks/index.js @@ -21,6 +21,8 @@ import GhosteryFeature from './GhosteryFeature'; import NotScanned from './NotScanned'; import PauseButton from './PauseButton'; import ToggleSlider from './ToggleSlider'; +import RadioButtonGroup from './RadioButtonGroup'; +import RadioButton from './RadioButton'; import ModalExitButton from './ModalExitButton'; export { @@ -31,5 +33,7 @@ export { NotScanned, PauseButton, ToggleSlider, + RadioButtonGroup, + RadioButton, ModalExitButton }; diff --git a/app/panel/components/Subscription.jsx b/app/panel/components/Subscription.jsx index 004e5d9ab..d39afad65 100644 --- a/app/panel/components/Subscription.jsx +++ b/app/panel/components/Subscription.jsx @@ -28,7 +28,9 @@ import PrioritySupport from './Subscription/PrioritySupport'; class Subscription extends React.Component { constructor(props) { super(props); - this.state = { isChecked: (props.current_theme !== 'default') }; + this.state = { + theme: this.props.current_theme + }; } /** @@ -75,10 +77,8 @@ class Subscription extends React.Component { return { loading: true }; } - toggleThemes = () => { - const newChecked = !this.state.isChecked; - this.setState({ isChecked: newChecked }); - const updated_theme = newChecked ? 'midnight-theme' : 'default'; + changeTheme = (updated_theme) => { + this.setState({ theme: updated_theme }); this.props.actions.getTheme(updated_theme).then(() => { sendMessage('ping', 'theme_change'); }); @@ -86,7 +86,7 @@ class Subscription extends React.Component { SubscriptionInfoComponent = () => (); - SubscriptionThemesComponent = () => (); + SubscriptionThemesComponent = () => (); PrioritySupportComponent = () => (); diff --git a/app/panel/components/Subscription/SubscriptionInfo.jsx b/app/panel/components/Subscription/SubscriptionInfo.jsx index 93c1161a4..8d115b17b 100644 --- a/app/panel/components/Subscription/SubscriptionInfo.jsx +++ b/app/panel/components/Subscription/SubscriptionInfo.jsx @@ -74,7 +74,7 @@ const SubscriptionInfo = (props) => {
    -
  • {t('subscription_midnight_theme')}
  • +
  • {t('subscription_dark_blue_theme')}
  • {t('historical_stats')}
  • {t('priority_support')}
diff --git a/app/panel/components/Subscription/SubscriptionThemes.jsx b/app/panel/components/Subscription/SubscriptionThemes.jsx index 3a00f0282..4703300a0 100644 --- a/app/panel/components/Subscription/SubscriptionThemes.jsx +++ b/app/panel/components/Subscription/SubscriptionThemes.jsx @@ -12,36 +12,68 @@ */ import React from 'react'; -import { ToggleSlider } from '../BuildingBlocks'; +import PropTypes from 'prop-types'; +import { RadioButtonGroup } from '../BuildingBlocks'; /** * @class Implement Themes subview as a React component. * The view opens from the left-side menu of the main Subscription view. - * It allows to switch between available Ghostery themes. Right now it handles just one theme. Hence - slider. + * It allows to switch between available Ghostery themes. * @memberOf SettingsComponents - */ -const SubscriptionThemes = props => ( -
-
-
-

{ t('subscription_themes_title') }

-
- - - {t('subscription_midnight_theme')} - - -
- -
+*/ +const SubscriptionThemes = (props) => { + const themes = [ + { + name: 'default', + text: 'subscription_default_theme', + }, + { + name: 'midnight-theme', + text: 'subscription_dark_blue_theme', + }, + { + name: 'palm', + text: 'subscription_palm_theme', + }, + { + name: 'leaf', + text: 'subscription_leaf_theme', + } + ]; + + const getSelectedIndex = () => { + const index = themes.findIndex(theme => theme.name === props.theme); + return index; + }; + + const handleThemeClick = (index) => { + const theme = themes[index]; + props.changeTheme(theme.name); + }; + + return ( +
+
+
+

{t('subscription_themes_title')}

+ + +
-
-); + ); +}; + +// PropTypes ensure we pass required props of the correct type +SubscriptionThemes.propTypes = { + changeTheme: PropTypes.func.isRequired, + theme: PropTypes.string.isRequired, +}; + export default SubscriptionThemes; diff --git a/app/scss/panel.scss b/app/scss/panel.scss index eaef66ef7..2e9ea3432 100644 --- a/app/scss/panel.scss +++ b/app/scss/panel.scss @@ -8,7 +8,6 @@ * https://www.ghostery.com/ * * Copyright 2019 Ghostery, Inc. All rights reserved. - * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0 @@ -65,6 +64,7 @@ html body { @import './partials/_account'; @import './partials/_drawer'; @import './partials/_toggle_slider'; +@import './partials/_radio_button'; @import './partials/_pause_button'; @import './partials/_donut_graph'; @import './partials/_ghostery_feature'; diff --git a/app/scss/partials/_radio_button.scss b/app/scss/partials/_radio_button.scss new file mode 100644 index 000000000..c34420731 --- /dev/null +++ b/app/scss/partials/_radio_button.scss @@ -0,0 +1,43 @@ +/** + * Radio Button Sass + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +.RadioButtonGroup__label { + margin-right: 10px !important; + font-weight: bolder; +} +.RadioButtonGroup__container { + margin: 16px 0; + width: 138px; +} +.RadioButton__innerCircle { + &.checked { + height: 8px; + width: 8px; + background-color: #2092bf; + border-radius: 50%; + border: 1px solid #2092bf; + } +} +.RadioButton__outerCircle { + position: relative; + border: 1px solid #4a4a4a; + height: 14px; + width: 14px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + &.checked { + border: 2px solid #2092bf; + } +} diff --git a/app/scss/partials/_settings.scss b/app/scss/partials/_settings.scss index dccf04282..96981e5aa 100644 --- a/app/scss/partials/_settings.scss +++ b/app/scss/partials/_settings.scss @@ -5,7 +5,7 @@ * https://www.ghostery.com/ * * Copyright 2019 Ghostery, Inc. All rights reserved. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0 @@ -58,7 +58,6 @@ text-align: left; } } - .s-tab-title { font-weight: 400; font-size: 18px; @@ -195,12 +194,11 @@ line-height: 1.4; } img.s-question { - top: -5px; - width: 14px; + width: 16px; height: auto; cursor: default; - vertical-align: top; opacity: 1.0; + margin: -6px 0 0 10px; } .s-option-group { margin-bottom: 12px; @@ -279,7 +277,7 @@ border-color: #CCCCCC; background-size: 8px 5px; -moz-appearance: none; - -webkit-appearance:none; + -webkit-appearance: none; } } #select-file { @@ -458,4 +456,4 @@ .blocking-trackers { margin-left: 25px; } -} \ No newline at end of file +} diff --git a/app/scss/partials/_subscribe.scss b/app/scss/partials/_subscribe.scss index 11e7375e4..c771b102d 100644 --- a/app/scss/partials/_subscribe.scss +++ b/app/scss/partials/_subscribe.scss @@ -83,17 +83,16 @@ -webkit-font-smoothing: antialiased; text-align: left; } - .themes-slider-label { - @extend .field-label; - padding-right: 20px !important; - } - .themes-slider-container { - padding-top: 10px !important; - } .column-subscription { padding-left: 30px !important; padding-right: 30px !important; } + .subscription-title { + display: inline; + } + .tooltip-icon { + margin-left: 5px !important; + } .status-label { @extend .field-label; padding-right: 7px !important; From f050b8391b14338fff2f3f79c636ad4dff5d31c1 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 11 Mar 2020 17:59:14 -0400 Subject: [PATCH 2/4] Center inner circle in radio buttons --- .../BuildingBlocks/RadioButtonGroup.jsx | 2 +- app/scss/partials/_radio_button.scss | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx index b49d84497..ba0638e34 100644 --- a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx +++ b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx @@ -50,7 +50,7 @@ class RadioButtonGroup extends React.Component { const { buttons } = this.state; return ( this.props.items.map((item, index) => ( -
+
{t(item.text)} diff --git a/app/scss/partials/_radio_button.scss b/app/scss/partials/_radio_button.scss index c34420731..21b8dfc54 100644 --- a/app/scss/partials/_radio_button.scss +++ b/app/scss/partials/_radio_button.scss @@ -19,25 +19,25 @@ margin: 16px 0; width: 138px; } -.RadioButton__innerCircle { - &.checked { - height: 8px; - width: 8px; - background-color: #2092bf; - border-radius: 50%; - border: 1px solid #2092bf; - } -} .RadioButton__outerCircle { - position: relative; border: 1px solid #4a4a4a; - height: 14px; - width: 14px; + height: 16px; + width: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; + box-sizing: border-box; &.checked { border: 2px solid #2092bf; } } +.RadioButton__innerCircle { + &.checked { + height: 8px; + width: 8px; + background-color: #2092bf; + border-radius: 50%; + border: 1px solid #2092bf; + } +} From f9b154628d18d30ee46099f397b0340b30a0cf74 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 12 Mar 2020 12:42:58 -0400 Subject: [PATCH 3/4] Remove console error --- app/panel/components/BuildingBlocks/RadioButtonGroup.jsx | 4 ++-- app/scss/panel.scss | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx index ba0638e34..866488009 100644 --- a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx +++ b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx @@ -50,12 +50,12 @@ class RadioButtonGroup extends React.Component { const { buttons } = this.state; return ( this.props.items.map((item, index) => ( -
+
{t(item.text)}
- this.handleClick(index)} /> + this.handleClick(index)} />
)) diff --git a/app/scss/panel.scss b/app/scss/panel.scss index 2e9ea3432..cb9ea1ca0 100644 --- a/app/scss/panel.scss +++ b/app/scss/panel.scss @@ -8,6 +8,7 @@ * https://www.ghostery.com/ * * Copyright 2019 Ghostery, Inc. All rights reserved. + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0 From 2a8bce2501619dcc5fb5f19041815f4e9b6c0a37 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 31 Mar 2020 05:08:22 -0400 Subject: [PATCH 4/4] Clean up radio button code --- .../BuildingBlocks/RadioButtonGroup.jsx | 59 ++++++------------- .../Subscription/SubscriptionThemes.jsx | 4 +- app/scss/partials/_radio_button.scss | 3 +- 3 files changed, 21 insertions(+), 45 deletions(-) diff --git a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx index 866488009..883fa60a1 100644 --- a/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx +++ b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx @@ -21,53 +21,30 @@ import RadioButton from './RadioButton'; * @class Implements a radio button group * @memberof PanelBuildingBlocks */ -class RadioButtonGroup extends React.Component { - constructor(props) { - super(props); - this.state = { - buttons: [] - }; - } - - componentDidMount() { - const buttons = new Array(this.props.items.length).fill(false); - buttons[this.props.selectedIndex] = true; - this.setState({ buttons }); - } - - handleClick(indexClicked) { - const { buttons } = this.state; - const updatedButtons = buttons.map((button, index) => (index === indexClicked)); - this.setState({ buttons: updatedButtons }); - this.props.handleItemClick(indexClicked); - } - - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Toggle Slider used throughout the extension - */ - render() { - const { buttons } = this.state; - return ( - this.props.items.map((item, index) => ( -
- - {t(item.text)} - -
- this.handleClick(index)} /> -
+const RadioButtonGroup = (props) => { + const { indexClicked, handleItemClick } = props; + return ( + props.items.map((item, index) => ( +
+ + {t(item.text)} + +
+ handleItemClick(index)} + />
- )) - ); - } -} +
+ )) + ); +}; // PropTypes ensure we pass required props of the correct type RadioButtonGroup.propTypes = { items: PropTypes.arrayOf(PropTypes.object).isRequired, // Number of objects in array is the number of radio buttons handleItemClick: PropTypes.func.isRequired, - selectedIndex: PropTypes.number.isRequired + indexClicked: PropTypes.number.isRequired }; diff --git a/app/panel/components/Subscription/SubscriptionThemes.jsx b/app/panel/components/Subscription/SubscriptionThemes.jsx index 4703300a0..dc1574375 100644 --- a/app/panel/components/Subscription/SubscriptionThemes.jsx +++ b/app/panel/components/Subscription/SubscriptionThemes.jsx @@ -41,7 +41,7 @@ const SubscriptionThemes = (props) => { } ]; - const getSelectedIndex = () => { + const getIndexClicked = () => { const index = themes.findIndex(theme => theme.name === props.theme); return index; }; @@ -62,7 +62,7 @@ const SubscriptionThemes = (props) => {
diff --git a/app/scss/partials/_radio_button.scss b/app/scss/partials/_radio_button.scss index 21b8dfc54..51ddeb4bb 100644 --- a/app/scss/partials/_radio_button.scss +++ b/app/scss/partials/_radio_button.scss @@ -12,7 +12,7 @@ */ .RadioButtonGroup__label { - margin-right: 10px !important; + margin-right: 10px; font-weight: bolder; } .RadioButtonGroup__container { @@ -38,6 +38,5 @@ width: 8px; background-color: #2092bf; border-radius: 50%; - border: 1px solid #2092bf; } }