From 7af77d3eb0a73a0bd27898f5d3842c7dc7afef77 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 22 May 2018 10:15:58 -0700 Subject: Basic onboarding flow infrastructure set up --- .../types/react-highlight/index.d.ts | 1 + .../types/react-joyride/index.d.ts | 54 ++++++++++++++++++++++ packages/website/package.json | 1 + .../ts/components/onboarding/onboarding_flow.tsx | 31 +++++++++++++ .../onboarding/portal_onboarding_flow.tsx | 29 ++++++++++++ packages/website/ts/components/portal/portal.tsx | 50 +++++++++++++------- .../components/relayer_index/relayer_grid_tile.tsx | 12 ++--- packages/website/ts/components/top_bar/top_bar.tsx | 3 +- packages/website/ts/components/ui/container.tsx | 16 +++++++ packages/website/ts/components/ui/island.tsx | 33 +++++++++++++ packages/website/ts/components/wallet/wallet.tsx | 12 ++--- .../ts/containers/portal_onboarding_flow.ts | 23 +++++++++ packages/website/ts/redux/dispatcher.ts | 7 +++ packages/website/ts/redux/reducer.ts | 21 ++++++++- packages/website/ts/types.ts | 8 ++++ packages/website/ts/utils/style.ts | 4 ++ 16 files changed, 268 insertions(+), 37 deletions(-) create mode 100644 packages/typescript-typings/types/react-joyride/index.d.ts create mode 100644 packages/website/ts/components/onboarding/onboarding_flow.tsx create mode 100644 packages/website/ts/components/onboarding/portal_onboarding_flow.tsx create mode 100644 packages/website/ts/components/ui/container.tsx create mode 100644 packages/website/ts/components/ui/island.tsx create mode 100644 packages/website/ts/containers/portal_onboarding_flow.ts create mode 100644 packages/website/ts/utils/style.ts (limited to 'packages') diff --git a/packages/typescript-typings/types/react-highlight/index.d.ts b/packages/typescript-typings/types/react-highlight/index.d.ts index 875721533..e0bb910d8 100644 --- a/packages/typescript-typings/types/react-highlight/index.d.ts +++ b/packages/typescript-typings/types/react-highlight/index.d.ts @@ -1 +1,2 @@ declare module 'react-highlight'; + diff --git a/packages/typescript-typings/types/react-joyride/index.d.ts b/packages/typescript-typings/types/react-joyride/index.d.ts new file mode 100644 index 000000000..21e46074f --- /dev/null +++ b/packages/typescript-typings/types/react-joyride/index.d.ts @@ -0,0 +1,54 @@ +// Type definitions for react-joyride 2.0.0-11 +// Project: https://github.com/gilbarbara/react-joyride + +declare module 'react-joyride' { + import * as React from 'react'; + export interface StyleOptions { + arrowColor?: string, + backgroundColor?: string, + primaryColor?: string, + textColor?: string, + overlayColor?: string, + spotlightShadow?: string, + beaconSize?: number, + zIndex?: number, + } + + export interface Step { + title?: string; + content: React.ReactNode; + target: string; + placement?: "top" | "top-left" | "top-right" | "bottom" | "bottom-left" | "bottom-right" | "right" | "left"; + type?: "click" | "hover"; + isFixed?: boolean; + allowClicksThruHole?: boolean; + disableBeacon?: boolean; + style?: StyleOptions; + [prop: string]: any; + } + + export interface Props { + steps?: Step[]; + beaconComponent?: React.ComponentClass; + run?: boolean; + stepIndex?: number; + callback?: (options: any) => void; + debug?: boolean; + styles?: { options: StyleOptions }; + } + + export interface State { + action: string, + controlled: boolean, + index: number, + lifecycle: string, + size: 0, + status: string, + } + + export default class Joyride extends React.Component { + constructor(props: Props); + + static defaultProps: Props; + } +} \ No newline at end of file diff --git a/packages/website/package.json b/packages/website/package.json index cd0bfaf5e..484639746 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -42,6 +42,7 @@ "react-document-title": "^2.0.3", "react-dom": "15.6.1", "react-ga": "^2.4.1", + "react-joyride": "^2.0.0-11", "react-redux": "^5.0.3", "react-router-dom": "^4.1.1", "react-scroll": "^1.5.2", diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx new file mode 100644 index 000000000..5bbf84e1b --- /dev/null +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -0,0 +1,31 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import Joyride, { Step, StyleOptions } from 'react-joyride'; + +import { zIndex } from 'ts/utils/style'; + +interface OnboardingFlowProps { + steps: Step[]; + stepIndex?: number; + isRunning: boolean; +} + +const style: StyleOptions = { + zIndex: zIndex.overlay, +}; + +// Wrapper around Joyride with defaults and styles set +export class OnboardingFlow extends React.Component { + public render(): React.ReactNode { + const { steps, stepIndex, isRunning } = this.props; + return ( + + ); + } +}; diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx new file mode 100644 index 000000000..57b209fac --- /dev/null +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; + +import { OnboardingFlow } from 'ts/components/onboarding/onboarding_flow'; + +export interface PortalOnboardingFlowProps { + stepIndex: number; + isRunning: boolean; +} + +const steps = [ + { + target: '.wallet', + content: 'Hey!', + placement: 'right', + disableBeacon: true, + }, +]; + +export class PortalOnboardingFlow extends React.Component { + public render(): React.ReactNode { + return ( + + ) + } +}; diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 1bd318c28..a4e3a006e 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -19,8 +19,11 @@ import { TextHeader } from 'ts/components/portal/text_header'; import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; import { TokenBalances } from 'ts/components/token_balances'; import { TopBar, TopBarDisplayType } from 'ts/components/top_bar/top_bar'; +import { PortalOnboardingFlow } from 'ts/containers/portal_onboarding_flow'; +import { Island } from 'ts/components/ui/island'; import { TradeHistory } from 'ts/components/trade_history/trade_history'; import { FlashMessage } from 'ts/components/ui/flash_message'; +import { Container } from 'ts/components/ui/container'; import { Wallet } from 'ts/components/wallet/wallet'; import { GenerateOrderForm } from 'ts/containers/generate_order_form'; import { localStorage } from 'ts/local_storage/local_storage'; @@ -183,6 +186,7 @@ export class Portal extends React.Component { : TokenVisibility.TRACKED; return (
+ { const allTokens = _.values(this.props.tokenByAddress); const trackedTokens = _.filter(allTokens, t => t.isTracked); return ( - +
+ + + + {/** TODO: Implement real styles. */} +

Start onboarding flow.

+
+
+
); } + + private _startOnboarding(): void { + this.props.dispatcher.updatePortalOnboardingShowing(true); + } private _renderWalletSection(): React.ReactNode { return
} body={this._renderWallet()} />; } diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index dc9eeb29d..23c2f0b56 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -4,6 +4,7 @@ import { GridTile } from 'material-ui/GridList'; import * as React from 'react'; import { TopTokens } from 'ts/components/relayer_index/relayer_top_tokens'; +import { Island } from 'ts/components/ui/island'; import { TokenIcon } from 'ts/components/ui/token_icon'; import { Token, WebsiteBackendRelayerInfo } from 'ts/types'; import { colors } from 'ts/utils/colors'; @@ -15,13 +16,6 @@ export interface RelayerGridTileProps { const styles: Styles = { root: { - backgroundColor: colors.white, - borderBottomRightRadius: 10, - borderBottomLeftRadius: 10, - borderTopRightRadius: 10, - borderTopLeftRadius: 10, - boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, - overflow: 'hidden', boxSizing: 'border-box', }, innerDiv: { @@ -68,7 +62,7 @@ const FALLBACK_IMG_SRC = '/images/landing/hero_chip_image.png'; export const RelayerGridTile: React.StatelessComponent = (props: RelayerGridTileProps) => { const link = props.relayerInfo.appUrl || props.relayerInfo.url; return ( - +
- + ); }; diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index f2d0c6177..3f11f9779 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -20,6 +20,7 @@ import { Deco, Key, ProviderType, WebsiteLegacyPaths, WebsitePaths } from 'ts/ty import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; +import { zIndex } from 'ts/utils/style'; export enum TopBarDisplayType { Default, @@ -59,7 +60,7 @@ const styles: Styles = { width: '100%', position: 'relative', top: 0, - zIndex: 1100, + zIndex: zIndex.topBar, paddingBottom: 1, }, bottomBar: { diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx new file mode 100644 index 000000000..7565dd1ae --- /dev/null +++ b/packages/website/ts/components/ui/container.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; + +interface ContainerProps { + marginTop?: string | number; + marginBottom?: string | number; + marginRight?: string | number; + marginLeft?: string | number; + children?: React.ReactNode; +} + +export const Container: React.StatelessComponent = (props: ContainerProps) => { + const { children, ...style } = props; + return
{children}
; +}; + +Container.displayName = 'Container'; diff --git a/packages/website/ts/components/ui/island.tsx b/packages/website/ts/components/ui/island.tsx new file mode 100644 index 000000000..da3602c3e --- /dev/null +++ b/packages/website/ts/components/ui/island.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { Styleable } from 'ts/types'; +import { colors } from 'ts/utils/colors'; + +export interface IslandProps { + style?: React.CSSProperties; + children?: React.ReactNode; + className?: string; + Component?: string | React.ComponentClass | React.StatelessComponent; +} + +const defaultStyle: React.CSSProperties = { + backgroundColor: colors.white, + borderBottomRightRadius: 10, + borderBottomLeftRadius: 10, + borderTopRightRadius: 10, + borderTopLeftRadius: 10, + boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, + overflow: 'hidden', +}; + +export const Island: React.StatelessComponent = (props: IslandProps) => ( + + {props.children} + +); + +Island.defaultProps = { + Component: 'div', + style: {}, +}; + +Island.displayName = 'Island'; diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index d0354580d..1790f7258 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -25,6 +25,7 @@ import { Blockchain } from 'ts/blockchain'; import { AllowanceToggle } from 'ts/components/inputs/allowance_toggle'; import { IconButton } from 'ts/components/ui/icon_button'; import { Identicon } from 'ts/components/ui/identicon'; +import { Island } from 'ts/components/ui/island'; import { TokenIcon } from 'ts/components/ui/token_icon'; import { WalletDisconnectedItem } from 'ts/components/wallet/wallet_disconnected_item'; import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item'; @@ -84,13 +85,6 @@ interface AccessoryItemConfig { const styles: Styles = { root: { width: '100%', - backgroundColor: colors.white, - borderBottomRightRadius: 10, - borderBottomLeftRadius: 10, - borderTopRightRadius: 10, - borderTopLeftRadius: 10, - boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, - overflow: 'hidden', }, headerItemInnerDiv: { paddingLeft: 65, @@ -198,11 +192,11 @@ export class Wallet extends React.Component { const isReadyToRender = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError; const isAddressAvailable = !_.isEmpty(this.props.userAddress); return ( -
+ {isReadyToRender && isAddressAvailable ? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows()) : _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows())} -
+ ); } private _renderDisconnectedHeaderRows(): React.ReactElement<{}> { diff --git a/packages/website/ts/containers/portal_onboarding_flow.ts b/packages/website/ts/containers/portal_onboarding_flow.ts new file mode 100644 index 000000000..46c23f62f --- /dev/null +++ b/packages/website/ts/containers/portal_onboarding_flow.ts @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { + PortalOnboardingFlow as PortalOnboardingFlowComponent, + PortalOnboardingFlowProps as PortalOnboardingFlowComponentProps, +} from 'ts/components/onboarding/portal_onboarding_flow'; +import { State } from 'ts/redux/reducer'; + +interface ConnectedState { + stepIndex: number; + isRunning: boolean; +} + +const mapStateToProps = (state: State, ownProps: PortalOnboardingFlowComponentProps): ConnectedState => { + return { + stepIndex: state.portalOnboardingStep, + isRunning: state.isPortalOnboardingShowing, + }; +}; + +export const PortalOnboardingFlow: React.ComponentClass = connect(mapStateToProps)(PortalOnboardingFlowComponent); diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index 340b80d49..0b4cc3938 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -174,6 +174,13 @@ export class Dispatcher { }); } + public updatePortalOnboardingShowing(isShowing: boolean): void { + this._dispatch({ + data: isShowing, + type: ActionTypes.UpdatePortalOnboardingShowing, + }); + } + // Docs public updateCurrentDocsVersion(version: string): void { this._dispatch({ diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index fba6afa5d..e61345c87 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -40,6 +40,8 @@ export interface State { lastForceTokenStateRefetch: number; userAddress: string; userEtherBalanceInWei: BigNumber; + portalOnboardingStep: number; + isPortalOnboardingShowing: boolean; // Note: cache of supplied orderJSON in fill order step. Do not use for anything else. userSuppliedOrderCache: Order; @@ -80,7 +82,8 @@ const INITIAL_STATE: State = { userAddress: '', userEtherBalanceInWei: new BigNumber(0), userSuppliedOrderCache: undefined, - + portalOnboardingStep: 0, + isPortalOnboardingShowing: false, // Docs docsVersion: DEFAULT_DOCS_VERSION, availableDocVersions: [DEFAULT_DOCS_VERSION], @@ -293,6 +296,22 @@ export function reducer(state: State = INITIAL_STATE, action: Action): State { }; } + case ActionTypes.UpdatePortalOnboardingStep: { + const portalOnboardingStep = action.data; + return { + ...state, + portalOnboardingStep, + }; + } + + case ActionTypes.UpdatePortalOnboardingShowing: { + const isPortalOnboardingShowing = action.data; + return { + ...state, + isPortalOnboardingShowing, + }; + } + // Docs case ActionTypes.UpdateLibraryVersion: { return { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 294a58f64..aca3edae8 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,6 +1,7 @@ import { ECSignature } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; +import * as React from 'react'; export enum Side { Receive = 'RECEIVE', @@ -21,6 +22,11 @@ export interface TokenByAddress { [address: string]: Token; } +export interface Styleable { + style: React.CSSProperties; + children: React.ReactNode; +} + export interface AssetToken { address?: string; amount?: BigNumber; @@ -125,6 +131,8 @@ export enum ActionTypes { UpdateUserSuppliedOrderCache = 'UPDATE_USER_SUPPLIED_ORDER_CACHE', UpdateOrderFillAmount = 'UPDATE_ORDER_FILL_AMOUNT', UpdateShouldBlockchainErrDialogBeOpen = 'UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN', + UpdatePortalOnboardingStep = 'UPDATE_ONBOARDING_STEP', + UpdatePortalOnboardingShowing = 'UPDATE_PORTAL_ONBOARDING_SHOWING', // Docs UpdateLibraryVersion = 'UPDATE_LIBRARY_VERSION', diff --git a/packages/website/ts/utils/style.ts b/packages/website/ts/utils/style.ts new file mode 100644 index 000000000..f3593b198 --- /dev/null +++ b/packages/website/ts/utils/style.ts @@ -0,0 +1,4 @@ +export const zIndex = { + topBar: 1100, + overlay: 1101, +}; \ No newline at end of file -- cgit v1.2.3