diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 05499c4d1..8a02576ff 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1817,8 +1817,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..883fa60a1 --- /dev/null +++ b/app/panel/components/BuildingBlocks/RadioButtonGroup.jsx @@ -0,0 +1,51 @@ +/** + * 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 + */ +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, + indexClicked: 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..dc1574375 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 getIndexClicked = () => { + 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..cb9ea1ca0 100644 --- a/app/scss/panel.scss +++ b/app/scss/panel.scss @@ -65,6 +65,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..51ddeb4bb --- /dev/null +++ b/app/scss/partials/_radio_button.scss @@ -0,0 +1,42 @@ +/** + * 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; + font-weight: bolder; +} +.RadioButtonGroup__container { + margin: 16px 0; + width: 138px; +} +.RadioButton__outerCircle { + border: 1px solid #4a4a4a; + 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%; + } +} 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;