Merge pull request #811 from 0xProject/feature/website/onboarding-polish-2

Add Hotjar, and other small improvements
This commit is contained in:
Francesco Agosti
2018-07-03 14:43:02 -07:00
committed by GitHub
10 changed files with 111 additions and 48 deletions

View File

@@ -70,7 +70,18 @@
})(document, 'script', 'twitter-wjs');
</script>
<!-- End Twitter SDK -->
<!-- Hotjar Tracking Code for https://0xproject.com/ -->
<script>
(function (h, o, t, j, a, r) {
h.hj = h.hj || function () { (h.hj.q = h.hj.q || []).push(arguments) };
h._hjSettings = { hjid: 935597, hjsv: 6 };
a = o.getElementsByTagName('head')[0];
r = o.createElement('script'); r.async = 1;
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
a.appendChild(r);
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
</script>
<!-- End Hotjar Tracking Code -->
<!-- Main -->
<script type="text/javascript" crossorigin="anonymous" src="/bundle.js" charset="utf-8"></script>
</body>

View File

@@ -39,7 +39,7 @@ export const OnboardingCard: React.StatelessComponent<OnboardingCardProps> = ({
borderRadius,
}) => (
<Island borderRadius={borderRadius}>
<Container paddingRight="30px" paddingLeft="30px" maxWidth={350} paddingTop="15px" paddingBottom="15px">
<Container paddingRight="30px" paddingLeft="30px" paddingTop="15px" paddingBottom="15px">
<div className="flex flex-column">
<div className="flex justify-between">
<Title>{title}</Title>

View File

@@ -6,13 +6,29 @@ import { ContinueButtonDisplay, OnboardingTooltip } from 'ts/components/onboardi
import { Animation } from 'ts/components/ui/animation';
import { Container } from 'ts/components/ui/container';
import { Overlay } from 'ts/components/ui/overlay';
import { PointerDirection } from 'ts/components/ui/pointer';
import { zIndex } from 'ts/style/z_index';
export interface Step {
export interface FixedPositionSettings {
type: 'fixed';
top?: string;
bottom?: string;
left?: string;
right?: string;
pointerDirection?: PointerDirection;
}
export interface TargetPositionSettings {
type: 'target';
target: string;
placement: Placement;
}
export interface Step {
// Provide either a CSS selector, or fixed position settings. Only applies to desktop.
position: TargetPositionSettings | FixedPositionSettings;
title?: string;
content: React.ReactNode;
placement?: Placement;
shouldHideBackButton?: boolean;
shouldHideNextButton?: boolean;
continueButtonDisplay?: ContinueButtonDisplay;
@@ -40,18 +56,30 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
return null;
}
let onboardingElement = null;
const currentStep = this._getCurrentStep();
if (this.props.isMobile) {
onboardingElement = <Animation type="easeUpFromBottom">{this._renderOnboardignCard()}</Animation>;
} else {
onboardingElement = <Animation type="easeUpFromBottom">{this._renderOnboardingCard()}</Animation>;
} else if (currentStep.position.type === 'target') {
const { placement, target } = currentStep.position;
onboardingElement = (
<Popper
referenceElement={this._getElementForStep()}
placement={this._getCurrentStep().placement}
positionFixed={true}
>
<Popper referenceElement={document.querySelector(target)} placement={placement} positionFixed={true}>
{this._renderPopperChildren.bind(this)}
</Popper>
);
} else if (currentStep.position.type === 'fixed') {
const { top, right, bottom, left, pointerDirection } = currentStep.position;
onboardingElement = (
<Container
position="fixed"
zIndex={zIndex.aboveOverlay}
top={top}
right={right}
bottom={bottom}
left={left}
>
{this._renderToolTip(pointerDirection)}
</Container>
);
}
if (this.props.disableOverlay) {
return onboardingElement;
@@ -63,9 +91,6 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
</div>
);
}
private _getElementForStep(): Element {
return document.querySelector(this._getCurrentStep().target);
}
private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode {
const customStyles = { zIndex: zIndex.aboveOverlay };
// On re-render, we want to re-center the popper.
@@ -76,7 +101,7 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
</div>
);
}
private _renderToolTip(): React.ReactNode {
private _renderToolTip(pointerDirection?: PointerDirection): React.ReactNode {
const { steps, stepIndex } = this.props;
const step = steps[stepIndex];
const isLastStep = steps.length - 1 === stepIndex;
@@ -94,12 +119,13 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
continueButtonDisplay={step.continueButtonDisplay}
continueButtonText={step.continueButtonText}
onContinueButtonClick={step.onContinueButtonClick}
pointerDirection={pointerDirection}
/>
</Container>
);
}
private _renderOnboardignCard(): React.ReactNode {
private _renderOnboardingCard(): React.ReactNode {
const { steps, stepIndex } = this.props;
const step = steps[stepIndex];
const isLastStep = steps.length - 1 === stepIndex;

View File

@@ -9,7 +9,12 @@ import { AddEthOnboardingStep } from 'ts/components/onboarding/add_eth_onboardin
import { CongratsOnboardingStep } from 'ts/components/onboarding/congrats_onboarding_step';
import { InstallWalletOnboardingStep } from 'ts/components/onboarding/install_wallet_onboarding_step';
import { IntroOnboardingStep } from 'ts/components/onboarding/intro_onboarding_step';
import { OnboardingFlow, Step } from 'ts/components/onboarding/onboarding_flow';
import {
FixedPositionSettings,
OnboardingFlow,
Step,
TargetPositionSettings,
} from 'ts/components/onboarding/onboarding_flow';
import { SetAllowancesOnboardingStep } from 'ts/components/onboarding/set_allowances_onboarding_step';
import { UnlockWalletOnboardingStep } from 'ts/components/onboarding/unlock_wallet_onboarding_step';
import {
@@ -45,8 +50,6 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
private _unlisten: () => void;
public componentDidMount(): void {
this._adjustStepIfShould();
// Wait until the step is adjusted to decide whether we should show onboarding.
setTimeout(this._autoStartOnboardingIfShould.bind(this), 1000);
// If there is a route change, just close onboarding.
this._unlisten = this.props.history.listen(() => this.props.updateIsRunning(false));
}
@@ -61,6 +64,9 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
document.querySelector('.wallet').scrollIntoView();
}
}
if (!prevProps.blockchainIsLoaded && this.props.blockchainIsLoaded) {
this._autoStartOnboardingIfShould();
}
}
public render(): React.ReactNode {
return (
@@ -76,56 +82,61 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
);
}
private _getSteps(): Step[] {
const nextToWalletPosition: TargetPositionSettings = {
type: 'target',
target: '.wallet',
placement: 'right',
};
const underMetamaskExtension: FixedPositionSettings = {
type: 'fixed',
top: '30px',
right: '10px',
pointerDirection: 'top',
};
const steps: Step[] = [
{
target: '.wallet',
position: nextToWalletPosition,
title: '0x Ecosystem Setup',
content: <InstallWalletOnboardingStep />,
placement: 'right',
shouldHideBackButton: true,
shouldHideNextButton: true,
},
{
target: '.wallet',
position: underMetamaskExtension,
title: '0x Ecosystem Setup',
content: <UnlockWalletOnboardingStep />,
placement: 'right',
shouldHideBackButton: true,
shouldHideNextButton: true,
},
{
target: '.wallet',
position: nextToWalletPosition,
title: '0x Ecosystem Account Setup',
content: <IntroOnboardingStep />,
placement: 'right',
shouldHideBackButton: true,
continueButtonDisplay: 'enabled',
},
{
target: '.wallet',
position: nextToWalletPosition,
title: 'Step 1: Add ETH',
content: (
<AddEthOnboardingStep userEthBalanceInWei={this.props.userEtherBalanceInWei || new BigNumber(0)} />
),
placement: 'right',
continueButtonDisplay: this._userHasVisibleEth() ? 'enabled' : 'disabled',
},
{
target: '.wallet',
position: nextToWalletPosition,
title: 'Step 2: Wrap ETH',
content: <WrapEthOnboardingStep1 />,
placement: 'right',
continueButtonDisplay: 'enabled',
},
{
target: '.wallet',
position: nextToWalletPosition,
title: 'Step 2: Wrap ETH',
content: <WrapEthOnboardingStep2 />,
placement: 'right',
continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled',
},
{
target: '.wallet',
position: nextToWalletPosition,
title: 'Step 2: Wrap ETH',
content: (
<WrapEthOnboardingStep3
@@ -134,11 +145,10 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
}
/>
),
placement: 'right',
continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled',
},
{
target: '.wallet',
position: nextToWalletPosition,
title: 'Step 3: Unlock Tokens',
content: (
<SetAllowancesOnboardingStep
@@ -147,14 +157,12 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
doesUserHaveAllowancesForWethAndZrx={this._doesUserHaveAllowancesForWethAndZrx()}
/>
),
placement: 'right',
continueButtonDisplay: this._doesUserHaveAllowancesForWethAndZrx() ? 'enabled' : 'disabled',
},
{
target: '.wallet',
position: nextToWalletPosition,
title: '🎉 The Ecosystem Awaits',
content: <CongratsOnboardingStep />,
placement: 'right',
continueButtonDisplay: 'enabled',
shouldHideNextButton: true,
continueButtonText: 'Enter the 0x Ecosystem',
@@ -221,7 +229,7 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
}
private _autoStartOnboardingIfShould(): void {
if (
(this.props.stepIndex === 0 && !this.props.isRunning) ||
(this.props.stepIndex === 0 && !this.props.isRunning && this.props.blockchainIsLoaded) ||
(!this.props.isRunning && !this.props.hasBeenClosed && this.props.blockchainIsLoaded)
) {
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];

View File

@@ -10,7 +10,7 @@ export const UnlockWalletOnboardingStep: React.StatelessComponent<UnlockWalletOn
<Container marginTop="15px" marginBottom="15px">
<img src="/images/metamask_icon.png" height="50px" width="50px" />
</Container>
<Text center={true}>Unlock your metamask extension to get started.</Text>
<Text center={true}>Unlock your MetaMask extension to get started.</Text>
</div>
</div>
);

View File

@@ -9,6 +9,7 @@ import { Container } from 'ts/components/ui/container';
import { Image } from 'ts/components/ui/image';
import { Island } from 'ts/components/ui/island';
import { colors } from 'ts/style/colors';
import { media } from 'ts/style/media';
import { styled } from 'ts/style/theme';
import { WebsiteBackendRelayerInfo } from 'ts/types';
import { utils } from 'ts/utils/utils';
@@ -111,6 +112,9 @@ const GridTile = styled(PlainGridTile)`
&:hover {
transform: translate(0px, -3px);
}
${media.small`
transform: none;
`};
`;
interface SectionProps {

View File

@@ -13,9 +13,11 @@ export const store: ReduxStore<State> = createStore(
);
store.subscribe(
_.throttle(() => {
const state = store.getState();
// Persisted state
stateStorage.saveState({
hasPortalOnboardingBeenClosed: store.getState().hasPortalOnboardingBeenClosed,
hasPortalOnboardingBeenClosed: state.hasPortalOnboardingBeenClosed,
isPortalOnboardingShowing: state.isPortalOnboardingShowing,
});
}, ONE_SECOND),
);

View File

@@ -0,0 +1,14 @@
import { css } from 'ts/style/theme';
import { ScreenWidths } from 'ts/types';
const generateMediaWrapper = (screenWidth: ScreenWidths) => (...args: any[]) => css`
@media (max-width: ${screenWidth}) {
${css.apply(css, args)};
}
`;
export const media = {
small: generateMediaWrapper(ScreenWidths.Sm),
medium: generateMediaWrapper(ScreenWidths.Md),
large: generateMediaWrapper(ScreenWidths.Lg),
};

View File

@@ -215,10 +215,11 @@ export interface ContractEvent {
}
export type ValidatedBigNumberCallback = (isValid: boolean, amount?: BigNumber) => void;
// Associated values are in `em` units
export enum ScreenWidths {
Sm = 'SM',
Md = 'MD',
Lg = 'LG',
Sm = 40,
Md = 52,
Lg = 64,
}
export enum AlertTypes {

View File

@@ -29,9 +29,6 @@ import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import * as u2f from 'ts/vendor/u2f_api';
const LG_MIN_EM = 64;
const MD_MIN_EM = 52;
const isDogfood = (): boolean => _.includes(window.location.href, configs.DOMAIN_DOGFOOD);
export const utils = {
@@ -136,9 +133,9 @@ export const utils = {
// This logic mirrors the CSS media queries in BassCSS for the `lg-`, `md-` and `sm-` CSS
// class prefixes. Do not edit these.
if (widthInEm > LG_MIN_EM) {
if (widthInEm > ScreenWidths.Lg) {
return ScreenWidths.Lg;
} else if (widthInEm > MD_MIN_EM) {
} else if (widthInEm > ScreenWidths.Md) {
return ScreenWidths.Md;
} else {
return ScreenWidths.Sm;