Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-2247: Add dropdown list of search options #667

Merged
merged 2 commits into from Jan 27, 2021
Merged
Changes from all commits
Commits
File filter
Filter file types
Jump to
Jump to file
Failed to load files.

Always

Just for now

@@ -16,3 +16,4 @@ export const SEARCH_GHOSTERY = 'Ghostery';
export const SEARCH_BING = 'Bing';
export const SEARCH_YAHOO = 'Yahoo';
export const SEARCH_STARTPAGE = 'Startpage';
export const SEARCH_OTHER = t('ghostery_dawn_onboarding_other');
@@ -20,7 +20,8 @@ import {
SEARCH_GHOSTERY,
SEARCH_YAHOO,
SEARCH_STARTPAGE,
SEARCH_BING
SEARCH_BING,
SEARCH_OTHER
} from './ChooseDefaultSearchConstants';
import { Modal } from '../../../../shared-components';

@@ -31,10 +32,20 @@ class ChooseDefaultSearchView extends Component {
this.state = {
chosenSearch: SEARCH_GHOSTERY,
searchBeingConsidered: null,
otherSearchSelected: null,
otherListOpen: false,
modalActive: false,
};
}

componentDidMount() {
document.addEventListener('click', this.handleClickAway);
}

componentWillUnmount() {
document.removeEventListener('click', this.handleClickAway);
}

updateSelection = () => this.setState(prevState => (
{
chosenSearch: prevState.searchBeingConsidered,
@@ -45,13 +56,33 @@ class ChooseDefaultSearchView extends Component {

cancelSelection = () => this.setState({ modalActive: false, searchBeingConsidered: null });

updateOtherSearchSelection = otherSelected => this.setState({ otherSearchSelected: otherSelected });

updateOtherListOpen = open => this.setState({ otherListOpen: open });

triggerConfirmationModal = selection => this.setState({ modalActive: true, searchBeingConsidered: selection });

handleClickAway = (e) => {
const { otherListOpen } = this.state;

const closeDropdownOnClickAway = (open, key, ref) => {
if (open && !ref.contains(e.target)) {
this.setState({ [`${key}`]: false });
}
};
closeDropdownOnClickAway(otherListOpen, 'otherListOpen', this.otherListRef);
}

handleSubmit = () => {
const { chosenSearch } = this.state;
let { chosenSearch } = this.state;
const { otherSearchSelected } = this.state;
const { actions, history } = this.props;
const { setSetupStep, setDefaultSearch } = actions;

if (chosenSearch === SEARCH_OTHER && otherSearchSelected) {
chosenSearch = otherSearchSelected;
}

const payload = {
type: 'setDefaultSearch',
search: chosenSearch,
@@ -87,46 +118,134 @@ class ChooseDefaultSearchView extends Component {

renderStartpageOptionDescription = () => (
<div className="ChooseSearchView__optionDescriptionTitle">{t('ghostery_dawn_onboarding_ad_supported_private_search')}</div>
)
);

renderOtherOptionDescription = () => {
const { otherSearchSelected, otherListOpen } = this.state;
const dropdownOpen = otherListOpen ? 'expanded' : '';
return (
<Fragment>
<div className="ChooseSearchView__optionDescriptionHeader">
<div className="ChooseSearchView__optionTitle">{SEARCH_OTHER}</div>
<div className="ChooseSearchView__optionDescriptionSubtitle">{t('ghostery_dawn_onboarding_choose_alternate_search')}</div>
</div>
<div
ref={(node) => { this.otherListRef = node; }}
className={`ChooseSearchView__optionDropdownContainer ${dropdownOpen}`}
onClick={() => this.updateOtherListOpen(!otherListOpen)}
>
<div className="ChooseSearchView__optionDropdown">
<div className="ChooseSearchView__optionDropdownItem">
{otherSearchSelected || t('ghostery_dawn_onboarding_select_option')}
<img
className={`ChooseSearchView__optionDropdownCaret ${dropdownOpen}`}
src="/app/images/hub/ChooseDefaultSearchView/dropdown-caret.svg"
/>
</div>
{otherListOpen && this.renderOtherOptionsList()}
</div>
</div>
</Fragment>
);
}

renderOtherOptionsList = () => {
const otherSearchOptions = [
'DuckDuck Go',
'Ecosia',
'Ekoru',
'Gibiru',
'Google',
'OneSearch',
'Privado',
'Qwant',
'Search Encrypt',
'Tailcat',
];

return (
<Fragment>
{otherSearchOptions.map(otherSearchOption => (
<div
key={`option-${otherSearchOption}`}
className="ChooseSearchView__optionDropdownItem"
onClick={() => {
this.updateOtherSearchSelection(otherSearchOption);
this.triggerConfirmationModal(SEARCH_OTHER);
}}
>
{otherSearchOption}
</div>
))}
</Fragment>
);
}

renderOptionContainer = (chosenSearch, optionName) => {
const selected = (chosenSearch === optionName);
const containerClasses = ClassNames('ChooseSearchView__optionContainer', { selected });
const logoFilename = `/app/images/hub/ChooseDefaultSearchView/search-engine-logo-${optionName.toLocaleLowerCase()}.svg`;

return (
<div onClick={() => this.triggerConfirmationModal(optionName)} className={containerClasses}>
<div
onClick={() => {
if (optionName !== SEARCH_OTHER) {
this.triggerConfirmationModal(optionName);
}
}}
className={containerClasses}
>
<div className="ChooseSearchView__optionRadioButtonContainer">
<RadioButton
checked={selected}
handleClick={() => {}}
altDesign
/>
{(optionName !== SEARCH_OTHER || (optionName === SEARCH_OTHER && selected)) && (
<RadioButton
checked={selected}
handleClick={() => {}}
altDesign
/>
)
}
</div>
<div className="ChooseSearchView__optionDescriptionContainer">
<img src={logoFilename} />
{(optionName !== SEARCH_OTHER) && (
<img src={logoFilename} />
)}
{(optionName === SEARCH_GHOSTERY) && this.renderGhosteryOptionDescription()}
{(optionName === SEARCH_STARTPAGE) && this.renderStartpageOptionDescription()}
{(optionName === SEARCH_OTHER) && this.renderOtherOptionDescription()}
</div>
</div>
);
}

renderConfirmationModal = () => {
const { searchBeingConsidered } = this.state;
const { searchBeingConsidered, otherSearchSelected } = this.state;
const logoFilename = `/app/images/hub/ChooseDefaultSearchView/search-engine-logo-${searchBeingConsidered.toLocaleLowerCase()}.svg`;

return (
<Modal show>
<div className="ChooseSearchView__modalContent">
<img src="/app/images/hub/ChooseDefaultSearchView/ghostery-browser-logo.svg" />
<div className="ChooseSearchView__modalMain">
<img className="ChooseSearchView__modalOptionLogo" src={logoFilename} />
{searchBeingConsidered === SEARCH_OTHER ? (
<div className="ChooseSearchView__modalHeader">
{SEARCH_OTHER}
</div>
) :
<img className="ChooseSearchView__modalOptionLogo" src={logoFilename} />
}
<div className="ChooseSearchView__modalDescription">
{searchBeingConsidered === SEARCH_STARTPAGE && t('ghostery_dawn_onboarding_startpage_warning')}
{searchBeingConsidered === SEARCH_BING && t('ghostery_dawn_onboarding_bing_warning')}
{searchBeingConsidered === SEARCH_YAHOO && t('ghostery_dawn_onboarding_yahoo_warning')}
{searchBeingConsidered === SEARCH_GHOSTERY && t('ghostery_dawn_onboarding_glow_benefit')}
{(searchBeingConsidered === SEARCH_OTHER) && (
<Fragment>
{
`${t('ghostery_dawn_onboarding_you_have_selected_an_alternate_serach_engine')} \n
${otherSearchSelected}`
}
</Fragment>
)}
</div>
<div className="ChooseSearchView__modalButtonsContainer">
<button
@@ -172,6 +291,7 @@ class ChooseDefaultSearchView extends Component {
{this.renderOptionContainer(chosenSearch, SEARCH_GHOSTERY)}
{this.renderOptionContainer(chosenSearch, SEARCH_STARTPAGE)}
{this.renderOptionContainer(chosenSearch, SEARCH_BING)}
{this.renderOptionContainer(chosenSearch, SEARCH_OTHER)}
{this.renderOptionContainer(chosenSearch, SEARCH_YAHOO)}
</div>
<button
@@ -75,11 +75,24 @@

&.selected {
border: solid 4px $ghosty-blue;

& .ChooseSearchView__optionDropdownContainer {
top: 7px;
}
}

&:hover {
border: solid 4px $ghosty-blue;
}

& .ChooseSearchView__optionDropdownContainer {
top: 7px;
}
}

& .ChooseSearchView__optionDropdownContainer {
top: 10px;
}

}

.ChooseSearchView__optionRadioButtonContainer {
@@ -90,6 +103,7 @@
}

.ChooseSearchView__optionDescriptionContainer {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
@@ -98,8 +112,26 @@
padding-right: 15%;
}

.ChooseSearchView__optionDescriptionHeader {
position: absolute;
margin-top: -45px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

@mixin option-font {
line-height: 0.8;
font-size: 16px;
font-weight: 500;
}
.ChooseSearchView__optionTitle {
@include option-font;
}

.ChooseSearchView__optionDescriptionTitle {
padding-top: 10px;
padding-top: 10px;
line-height: 0.8;
font-size: 14px;
font-weight: 500;
@@ -109,6 +141,65 @@
font-size: 14px;
}

.ChooseSearchView__optionDropdownContainer {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 50px;
width: 184px;
height: 50px;
border-radius: 4px;
border: solid 2px #4a4a4a;
background-color: rgba(255, 255, 255, 0.95);

&.expanded {
height: 330px;
}

&:hover {
border-color: #00aef0
}
}

.ChooseSearchView__optionDropdown {
position: absolute;
top: 18px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}

.ChooseSearchView__optionDropdownCaret {
position: relative;
left: 18px;
&.expanded {
transform: rotate(180deg);
}
}

.ChooseSearchView__optionDropdownItem {
@include option-font;
margin-bottom: 16px;
&:hover {
color: #00aef0;
text-decoration: underline;
}
}

.ChooseSearchView__caret {
height: 35px;
width: 25px;
padding-top: 21px;
padding-right: 0;
background-repeat: no-repeat;
background-position: center center;
background-size: 9px 5px;
background-image: buildIconCaretDown(#4a4a4a);
}

.ChooseSearchView__nextButton {
display: flex;
justify-content: center;
@@ -211,7 +302,8 @@
text-align: center;
color: $tundora;
width: 70%;
margin-bottom: 113px;
margin-bottom: 113px;
white-space: pre-line;
}

.ChooseSearchView__modalContent {
@@ -229,3 +321,9 @@
flex-direction: column;
align-items: center;
}

.ChooseSearchView__modalHeader {
font-size: 48px;
font-weight: 500;
margin-bottom: 110px;
}
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="11" viewBox="0 0 16 11">
<g fill="none" fill-rule="evenodd">
<g fill="#4A4A4A" stroke="#4A4A4A" stroke-width=".614">
<path d="M772 484L779 494 765 494z" transform="translate(-764 -483) matrix(1 0 0 -1 0 978)"/>
</g>
</g>
</svg>
ProTip! Use n and p to navigate between commits in a pull request.