aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website
diff options
context:
space:
mode:
authorfragosti <francesco.agosti93@gmail.com>2018-06-02 08:25:50 +0800
committerfragosti <francesco.agosti93@gmail.com>2018-06-02 08:25:50 +0800
commit073a96cf63a8b2e5639d15133d09545f7bde1388 (patch)
tree8c2250740101b6b26e3d48745e06bca46e59260d /packages/website
parent817d9b0d3e1becdb2e8dbf51caa09edab8d14ab0 (diff)
downloaddexon-sol-tools-073a96cf63a8b2e5639d15133d09545f7bde1388.tar
dexon-sol-tools-073a96cf63a8b2e5639d15133d09545f7bde1388.tar.gz
dexon-sol-tools-073a96cf63a8b2e5639d15133d09545f7bde1388.tar.bz2
dexon-sol-tools-073a96cf63a8b2e5639d15133d09545f7bde1388.tar.lz
dexon-sol-tools-073a96cf63a8b2e5639d15133d09545f7bde1388.tar.xz
dexon-sol-tools-073a96cf63a8b2e5639d15133d09545f7bde1388.tar.zst
dexon-sol-tools-073a96cf63a8b2e5639d15133d09545f7bde1388.zip
Implement subscription form
Diffstat (limited to 'packages/website')
-rw-r--r--packages/website/ts/components/forms/subscribe_form.tsx121
-rw-r--r--packages/website/ts/components/ui/button.tsx79
-rw-r--r--packages/website/ts/components/ui/container.tsx15
-rw-r--r--packages/website/ts/components/ui/input.tsx43
-rw-r--r--packages/website/ts/components/ui/text.tsx56
-rw-r--r--packages/website/ts/pages/landing/landing.tsx38
-rw-r--r--packages/website/ts/utils/backend_client.ts4
7 files changed, 288 insertions, 68 deletions
diff --git a/packages/website/ts/components/forms/subscribe_form.tsx b/packages/website/ts/components/forms/subscribe_form.tsx
index 3a6d0901f..99686efce 100644
--- a/packages/website/ts/components/forms/subscribe_form.tsx
+++ b/packages/website/ts/components/forms/subscribe_form.tsx
@@ -1,7 +1,12 @@
import { colors } from '@0xproject/react-shared';
import * as React from 'react';
-import RaisedButton from 'material-ui/RaisedButton';
+import { Button } from 'ts/components/ui/button';
+import { Input } from 'ts/components/ui/input';
+import { Text } from 'ts/components/ui/text';
+import { logUtils } from '@0xproject/utils';
+import { Container } from 'ts/components/ui/container';
+import { styled } from 'ts/style/theme';
import { backendClient } from 'ts/utils/backend_client';
export interface SubscribeFormProps {}
@@ -15,54 +20,102 @@ export enum SubscribeFormStatus {
export interface SubscribeFormState {
emailText: string;
+ lastSubmittedEmail: string;
status: SubscribeFormStatus;
}
export class SubscribeForm extends React.Component<SubscribeFormProps, SubscribeFormState> {
public state = {
emailText: '',
+ lastSubmittedEmail: '',
status: SubscribeFormStatus.None,
};
public render(): React.ReactNode {
+ const formFontSize = '15px';
return (
- <div>
- Subscribe to our newsletter for 0x relayer and dApp updates
- <div>
- <input value={this.state.emailText} onChange={this._handleEmailInputChange.bind(this)} />
- <RaisedButton
- labelStyle={{
- textTransform: 'none',
- fontSize: 15,
- fontWeight: 400,
- }}
- buttonStyle={{
- borderRadius: 6,
- }}
- style={{
- boxShadow: '0px 0px 4px rgba(0, 0, 0, 0.25)',
- borderRadius: 6,
- height: 48,
- width: 120,
- }}
- labelColor="white"
- backgroundColor="#252525"
- onClick={this._handleSubscribeClickAsync.bind(this)}
- label="Subscribe"
- />
- </div>
- </div>
+ <Container className="flex flex-column items-center justify-between md-mx2 sm-mx2">
+ <Container marginBottom="15px">
+ <Text fontFamily="Roboto Mono" fontColor={colors.grey} center={true}>
+ Subscribe to our newsletter for 0x relayer and dApp updates
+ </Text>
+ </Container>
+ <form onSubmit={this._handleFormSubmitAsync.bind(this)}>
+ <Container className="flex flex-wrap justify-center items-center">
+ <Container marginTop="15px">
+ <Input
+ placeholder="you@email.com"
+ value={this.state.emailText}
+ fontColor={colors.white}
+ fontSize={formFontSize}
+ backgroundColor="#343333"
+ width="275px"
+ onChange={this._handleEmailInputChange.bind(this)}
+ />
+ </Container>
+ <Container marginLeft="15px" marginTop="15px">
+ <Button
+ type="submit"
+ backgroundColor="#252525"
+ fontColor={colors.white}
+ fontSize={formFontSize}
+ >
+ Subscribe
+ </Button>
+ </Container>
+ </Container>
+ </form>
+ {this._renderMessage()}
+ </Container>
);
}
+ private _renderMessage(): React.ReactNode {
+ let message = null;
+ switch (this.state.status) {
+ case SubscribeFormStatus.Error:
+ message = 'Sorry, something went wrong. Try again later.';
+ break;
+ case SubscribeFormStatus.Loading:
+ message = 'One second...';
+ break;
+ case SubscribeFormStatus.Success:
+ message = `Thanks! ${this.state.lastSubmittedEmail} is now on the mailing list.`;
+ break;
+ case SubscribeFormStatus.None:
+ break;
+ }
+ return (
+ <Container isHidden={!message} marginTop="30px">
+ <Text center={true} fontFamily="Roboto Mono" fontColor={colors.grey}>
+ {message || 'spacer text'}
+ </Text>
+ </Container>
+ );
+ }
+
private _handleEmailInputChange(event: React.ChangeEvent<HTMLInputElement>): void {
this.setState({ emailText: event.target.value });
}
- private async _handleSubscribeClickAsync(): Promise<void> {
- this._setStatus(SubscribeFormStatus.Loading);
- const isSuccess = await backendClient.subscribeToNewsletterAsync(this.state.emailText);
- const status = isSuccess ? SubscribeFormStatus.Success : SubscribeFormStatus.Error;
- this._setStatus(status);
+ private async _handleFormSubmitAsync(event: React.FormEvent<HTMLInputElement>): Promise<void> {
+ event.preventDefault();
+ if (!this.state.emailText) {
+ return;
+ }
+ this.setState({
+ status: SubscribeFormStatus.Loading,
+ lastSubmittedEmail: this.state.emailText,
+ });
+ try {
+ const response = await backendClient.subscribeToNewsletterAsync(this.state.emailText);
+ const status = response.status === 200 ? SubscribeFormStatus.Success : SubscribeFormStatus.Error;
+ this._setStatus(status);
+ } catch (error) {
+ logUtils.log(error);
+ this._setStatus(SubscribeFormStatus.Error);
+ } finally {
+ this.setState({ emailText: '' });
+ }
}
- private _setStatus(status: SubscribeFormStatus): void {
- this.setState({ status });
+ private _setStatus(status: SubscribeFormStatus, then?: () => void): void {
+ this.setState({ status }, then);
}
}
diff --git a/packages/website/ts/components/ui/button.tsx b/packages/website/ts/components/ui/button.tsx
new file mode 100644
index 000000000..e6e31374f
--- /dev/null
+++ b/packages/website/ts/components/ui/button.tsx
@@ -0,0 +1,79 @@
+import { colors } from '@0xproject/react-shared';
+import { darken } from 'polished';
+import * as React from 'react';
+import { styled } from 'ts/style/theme';
+
+export interface ButtonProps {
+ className?: string;
+ fontSize?: string;
+ fontColor?: string;
+ backgroundColor?: string;
+ borderColor?: string;
+ width?: string;
+ type?: string;
+ onClick?: (event: React.MouseEvent<HTMLElement>) => void;
+}
+
+const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, onClick, type, className }) => (
+ <button type={type} className={className} onClick={onClick}>
+ {children}
+ </button>
+);
+
+export const Button = styled(PlainButton)`
+ cursor: pointer;
+ font-size: ${props => props.fontSize};
+ color: ${props => props.fontColor};
+ padding: 0.8em 2.2em;
+ border-radius: 6px;
+ box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
+ font-weight: 500;
+ font-family: 'Roboto';
+ width: ${props => props.width};
+ background-color: ${props => props.backgroundColor};
+ border: ${props => (props.borderColor ? `1px solid ${props.borderColor}` : 'none')};
+ &:hover {
+ background-color: ${props => darken(0.1, props.backgroundColor)};
+ }
+ &:active {
+ background-color: ${props => darken(0.2, props.backgroundColor)};
+ }
+`;
+
+Button.defaultProps = {
+ fontSize: '12px',
+ backgroundColor: colors.white,
+ width: 'auto',
+};
+
+Button.displayName = 'Button';
+
+type CTAType = 'light' | 'dark';
+
+export interface CTAProps {
+ type?: CTAType;
+ fontSize?: string;
+ width?: string;
+}
+
+export const CTA: React.StatelessComponent<CTAProps> = ({ children, type, fontSize, width }) => {
+ const isLight = type === 'light';
+ const backgroundColor = isLight ? colors.white : colors.heroGrey;
+ const fontColor = isLight ? colors.heroGrey : colors.white;
+ const borderColor = isLight ? undefined : colors.white;
+ return (
+ <Button
+ fontSize={fontSize}
+ backgroundColor={backgroundColor}
+ fontColor={fontColor}
+ width={width}
+ borderColor={borderColor}
+ >
+ {children}
+ </Button>
+ );
+};
+
+CTA.defaultProps = {
+ type: 'dark',
+};
diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx
index d577447b0..c6a78e181 100644
--- a/packages/website/ts/components/ui/container.tsx
+++ b/packages/website/ts/components/ui/container.tsx
@@ -11,13 +11,20 @@ export interface ContainerProps {
paddingBottom?: StringOrNum;
paddingRight?: StringOrNum;
paddingLeft?: StringOrNum;
+ backgroundColor?: string;
+ borderRadius?: StringOrNum;
maxWidth?: StringOrNum;
- children?: React.ReactNode;
+ isHidden?: boolean;
+ className?: string;
}
-export const Container: React.StatelessComponent<ContainerProps> = (props: ContainerProps) => {
- const { children, ...style } = props;
- return <div style={style}>{children}</div>;
+export const Container: React.StatelessComponent<ContainerProps> = ({ children, className, isHidden, ...style }) => {
+ const visibility = isHidden ? 'hidden' : undefined;
+ return (
+ <div style={{ ...style, visibility }} className={className}>
+ {children}
+ </div>
+ );
};
Container.displayName = 'Container';
diff --git a/packages/website/ts/components/ui/input.tsx b/packages/website/ts/components/ui/input.tsx
new file mode 100644
index 000000000..75a453eae
--- /dev/null
+++ b/packages/website/ts/components/ui/input.tsx
@@ -0,0 +1,43 @@
+import { colors } from '@0xproject/react-shared';
+import * as React from 'react';
+import { styled } from 'ts/style/theme';
+
+export interface InputProps {
+ className?: string;
+ value?: string;
+ width?: string;
+ fontSize?: string;
+ fontColor?: string;
+ placeholderColor?: string;
+ placeholder?: string;
+ backgroundColor?: string;
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
+}
+
+const PlainInput: React.StatelessComponent<InputProps> = ({ value, className, placeholder, onChange }) => (
+ <input className={className} value={value} onChange={onChange} placeholder={placeholder} />
+);
+
+export const Input = styled(PlainInput)`
+ font-size: ${props => props.fontSize};
+ width: ${props => props.width};
+ padding: 0.8em 1.2em;
+ border-radius: 3px;
+ font-family: 'Roboto Mono';
+ color: ${props => props.fontColor};
+ border: none;
+ background-color: ${props => props.backgroundColor};
+ &::placeholder {
+ color: ${props => props.placeholder};
+ }
+`;
+
+Input.defaultProps = {
+ width: 'auto',
+ backgroundColor: colors.white,
+ fontColor: colors.darkestGrey,
+ placeholderColor: colors.grey500,
+ fontSize: '12px',
+};
+
+Input.displayName = 'Input';
diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx
new file mode 100644
index 000000000..259365618
--- /dev/null
+++ b/packages/website/ts/components/ui/text.tsx
@@ -0,0 +1,56 @@
+import { colors } from '@0xproject/react-shared';
+import * as React from 'react';
+import { styled } from 'ts/style/theme';
+import { Deco, Key } from 'ts/types';
+import { Translate } from 'ts/utils/translate';
+
+export type TextTag = 'p' | 'div' | 'span' | 'label';
+
+export interface TextProps {
+ className?: string;
+ Tag?: TextTag;
+ fontSize?: string;
+ fontFamily?: string;
+ fontColor?: string;
+ lineHeight?: string;
+ center?: boolean;
+ fontWeight?: number;
+}
+
+const PlainText: React.StatelessComponent<TextProps> = ({ children, className, Tag }) => (
+ <Tag className={className}>{children}</Tag>
+);
+
+export const Text = styled(PlainText)`
+ font-family: ${props => props.fontFamily};
+ font-weight: ${props => props.fontWeight};
+ font-size: ${props => props.fontSize};
+ ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')};
+ ${props => (props.center ? 'text-align: center' : '')};
+ color: ${props => props.fontColor};
+`;
+
+Text.defaultProps = {
+ fontFamily: 'Roboto',
+ fontWeight: 400,
+ fontColor: colors.white,
+ fontSize: '14px',
+ Tag: 'div',
+};
+
+Text.displayName = 'Text';
+
+interface TranslatedProps {
+ children: Key;
+ translate: Translate;
+ deco?: Deco;
+}
+
+export type TranslatedTextProps = TextProps & TranslatedProps;
+
+export const TranslatedText: React.StatelessComponent<TranslatedTextProps> = ({
+ translate,
+ children,
+ deco,
+ ...textProps
+}) => <Text {...textProps}>{translate.get(children, deco)}</Text>;
diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx
index 016c62a30..0911bb2cf 100644
--- a/packages/website/ts/pages/landing/landing.tsx
+++ b/packages/website/ts/pages/landing/landing.tsx
@@ -1,7 +1,7 @@
import { colors } from '@0xproject/react-shared';
import * as _ from 'lodash';
-import RaisedButton from 'material-ui/RaisedButton';
import * as React from 'react';
+import { CTA } from 'ts/components/ui/button';
import DocumentTitle = require('react-document-title');
import { Link } from 'react-router-dom';
import { Footer } from 'ts/components/footer';
@@ -237,7 +237,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
<div className="clearfix py4" style={{ backgroundColor: colors.heroGrey }}>
<div className="mx-auto max-width-4 clearfix">
{this._renderWhatsNew()}
- <div className="lg-pt4 md-pt4 sm-pt2 lg-pb4 md-pb4 lg-my4 md-my4 sm-mt2 sm-mb4 clearfix">
+ <div className="lg-pt4 md-pt4 sm-pt2 lg-pb4 md-pb4 lg-mt4 md-mt4 sm-mt2 sm-mb4 clearfix">
<div className="col lg-col-5 md-col-5 col-12 sm-center">
<img src="/images/landing/hero_chip_image.png" height={isSmallScreen ? 300 : 395} />
</div>
@@ -272,13 +272,9 @@ export class Landing extends React.Component<LandingProps, LandingState> {
<div className="pt3 clearfix sm-mx-auto" style={{ maxWidth: 389 }}>
<div className="lg-pr2 md-pr2 col col-6 sm-center">
<Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 157.36 }}
- buttonStyle={{ borderRadius: 6 }}
- labelStyle={buttonLabelStyle}
- label={this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
- onClick={_.noop}
- />
+ <CTA width="175px" type="light">
+ {this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
+ </CTA>
</Link>
</div>
<div className="col col-6 sm-center">
@@ -287,23 +283,17 @@ export class Landing extends React.Component<LandingProps, LandingState> {
target="_blank"
className="text-decoration-none"
>
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 150 }}
- buttonStyle={lightButtonStyle}
- labelColor="white"
- backgroundColor={colors.heroGrey}
- labelStyle={buttonLabelStyle}
- label={this.props.translate.get(Key.CommunityCallToAction, Deco.Cap)}
- onClick={_.noop}
- />
+ <CTA width="175px">
+ {this.props.translate.get(Key.CommunityCallToAction, Deco.Cap)}
+ </CTA>
</a>
</div>
</div>
- <SubscribeForm />
</div>
</div>
</div>
</div>
+ <SubscribeForm />
</div>
);
}
@@ -784,15 +774,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
<div className="sm-center sm-pt2 lg-table-cell md-table-cell">
<Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 150 }}
- buttonStyle={lightButtonStyle}
- labelColor={colors.white}
- backgroundColor={colors.heroGrey}
- labelStyle={buttonLabelStyle}
- label={this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
- onClick={_.noop}
- />
+ <CTA fontSize="15px">{this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}</CTA>
</Link>
</div>
</div>
diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts
index fb7c21c59..6b16aea6b 100644
--- a/packages/website/ts/utils/backend_client.ts
+++ b/packages/website/ts/utils/backend_client.ts
@@ -34,11 +34,11 @@ export const backendClient = {
const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), WIKI_ENDPOINT);
return result;
},
- async subscribeToNewsletterAsync(email: string): Promise<boolean> {
+ async subscribeToNewsletterAsync(email: string): Promise<Response> {
const result = await fetchUtils.postAsync(utils.getBackendBaseUrl(), SUBSCRIBE_SUBSTACK_NEWSLETTER_ENDPOINT, {
email,
referrer: window.location.href,
});
- return result.status === 200;
+ return result;
},
};