Permalink
Browse files

Examples basic form (#59)

* initial commit

* add type checking for session

* add dummy session data

* add register component

* create design system

* add nwb as dependency for web rendering

* add web entry point

* add button component

* add strength meter component

* add border-box to button styles

* add prop-types package

* add a few more states to sessions

* add default font family to design system and components

* unifying styling between sketch and web

* add spacing component

* layout set of components in sketch and web

* add password state to web component

* all textbox primitives to render child components

* remove render targets from the Register component into their corresponding entry points

* remove unnecessary dependencies

* remove destructing of styles

* remove magic numbers from text box styling

* remove whitespace

* add some basic instructions

* add border to textbox

* remove magic numbers from strength meter

* fix margin bug on web version

* update heading

* add screenshot of example

* update skpm dependency

* ensure button has props

* ensure strength meter has props

* remove explicit importing of TextBox web and sketch components

* update password checker lengths

* avoid duplicate defining of register component

* add proptypes

* add default state to web

* add props

* detach strength meter from TextBox and render it directly from form

* ensure props where required

* improve contrast of yellow

* use flexbox when rendering to sketch only

* add default props to Register

* fix linting issues

* update readme

* rename to form-validation

* nwb seems to need explicit extract-text-webpack-plugin
  • Loading branch information...
1 parent 3e2b848 commit c46abc0ba42a4940cf2c7746c9b7c820321ed73e @lloydwheeler lloydwheeler committed with jongold May 10, 2017
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,31 @@
+# Form Validation
+
+## How to use
+Download the example or [clone the repo](http://github.com/airbnb/react-sketchapp):
+```
+curl https://codeload.github.com/airbnb/react-sketchapp/tar.gz/master | tar -xz --strip=2 react-sketchapp-master/examples/form-validation
+cd form-validation
+```
+
+Install the dependencies
+```
+npm install
+```
+
+Run with live reloading in Sketch
+```
+npm run render
+```
+
+Or, to install as a Sketch plugin:
+```
+npm run build
+npm run link-plugin
+```
+Then, open Sketch and navigate to `Plugins → react-sketchapp: Basic Form`
+
+## The idea behind the example
+
+`react-sketchapp` makes it simple to render all potential states of a web component to sketch.
+
+![examples-form-validation](https://cloud.githubusercontent.com/assets/1606253/25585002/5cff9264-2e90-11e7-80dc-101f10ecad6d.png)
@@ -0,0 +1,30 @@
+{
+ "name": "form-validation",
+ "private": true,
+ "main": "form-validation.sketchplugin",
+ "manifest": "src/manifest.json",
+ "scripts": {
+ "build": "skpm build",
+ "watch": "skpm build --watch",
+ "render": "skpm build --watch --run",
+ "render:once": "skpm build --run",
+ "link-plugin": "skpm link",
+ "web": "react run src/web.js"
+ },
+ "author": "Lloyd Wheeler <lloyd@lloydwheeler.co.uk>",
+ "license": "MIT",
+ "dependencies": {
+ "prop-types": "^15.5.8",
+ "react": "^15.4.2",
+ "react-dom": "^15.4.2",
+ "react-native": "^0.42.3",
+ "react-primitives": "^0.3.4",
+ "react-sketchapp": "^0.10.0",
+ "react-test-renderer": "^15.4.1"
+ },
+ "devDependencies": {
+ "extract-text-webpack-plugin": "^2.1.0",
+ "nwb": "^0.15.6",
+ "skpm": "^0.9.0"
+ }
+}
@@ -0,0 +1,29 @@
+/* @flow */
+import React from 'react';
+import { Text } from 'react-primitives';
+import { spacing, colors, fontFamily } from '../designSystem';
+
+type Props = {
+ label: string,
+ backgroundColor: string,
+};
+
+const buttonStyle = {
+ borderRadius: 3,
+ boxSizing: 'border-box',
+ color: colors.White,
+ cursor: 'pointer',
+ fontFamily,
+ fontWeight: 'bold',
+ padding: spacing.Medium,
+ textAlign: 'center',
+ width: 300,
+};
+
+const Button = ({ label, backgroundColor }: Props) => (
+ <Text style={{ ...buttonStyle, backgroundColor }}>
+ {label}
+ </Text>
+);
+
+export default Button;
@@ -0,0 +1,63 @@
+/* @flow */
+import React from 'react';
+import { View, Text, StyleSheet } from 'react-primitives';
+import { spacing, colors, typeRamp, fontFamily } from '../designSystem';
+import type { Session } from '../types';
+import TextBox from './TextBox';
+import StrengthMeter from './StrengthMeter';
+import Button from './Button';
+
+type Props = {
+ session: Session,
+};
+
+const styles = StyleSheet.create({
+ register: {
+ backgroundColor: colors.LightGrey,
+ padding: spacing.Large,
+ boxSizing: 'border-box',
+ },
+ heading: {
+ color: colors.Purple,
+ fontSize: typeRamp.Medium,
+ fontFamily,
+ fontWeight: 'bold',
+ textAlign: 'center',
+ marginBottom: spacing.Medium,
+ width: 300,
+ },
+});
+
+const Register = ({ session }: Props) => (
+ <View style={styles.register}>
+ <Text style={styles.heading}>Register an Account</Text>
+ <TextBox
+ label={'Email'}
+ value={session.email}
+ type={'email'}
+ />
+ <TextBox
+ label={'Password'}
+ value={session.password}
+ type={'password'}
+ >
+ <StrengthMeter
+ password={session.password}
+ />
+ </TextBox>
+ <Button
+ label={'Register'}
+ backgroundColor={colors.Purple}
+ />
+ </View>
+);
+
+Register.defaultProps = {
+ session: {
+ email: '',
+ password: '',
+ },
+};
+
+export default Register;
+
@@ -0,0 +1,22 @@
+/* @flow */
+import React from 'react';
+import { View } from 'react-primitives';
+
+type Props = {
+ h?: number,
+ v?: number,
+ children?: React$Element<any>,
+};
+
+const Space = ({ h, v, children }: Props): React$Element<any> => (
+ <View
+ style={{
+ paddingHorizontal: h,
+ paddingVertical: v,
+ }}
+ >
+ {children}
+ </View>
+);
+
+export default Space;
@@ -0,0 +1,94 @@
+/* @flow */
+import React from 'react';
+import { View, Text } from 'react-primitives';
+import { colors, fontFamily, spacing, typeRamp } from '../designSystem';
+
+type Props = {
+ password: string,
+};
+
+const strengths = {
+ short: {
+ width: 75,
+ label: 'Too short',
+ backgroundColor: colors.Rose,
+ },
+ fair: {
+ width: 150,
+ label: 'Fair',
+ backgroundColor: colors.Yellow,
+ },
+ good: {
+ width: 225,
+ label: 'Good',
+ backgroundColor: colors.Yellow,
+ },
+ strong: {
+ width: 300,
+ label: 'Strong',
+ backgroundColor: colors.Green,
+ },
+};
+
+const styles = {
+ meter: {
+ boxSizing: 'border-box',
+ height: 5,
+ width: 300,
+ backgroundColor: '#ddd',
+ marginTop: spacing.Medium,
+ marginBottom: spacing.Large,
+ borderRadius: 5,
+ },
+ innerMeter: {
+ boxSizing: 'border-box',
+ height: 5,
+ borderRadius: 5,
+ },
+ meterLabel: {
+ fontFamily,
+ textAlign: 'right',
+ width: 300,
+ fontSize: typeRamp.Small,
+ marginTop: 5,
+ },
+};
+
+const passwordStrength = (password) => {
+ // Faux password checking
+ if (password.length <= 6) {
+ return 'short';
+ } else if (password.length <= 9) {
+ return 'fair';
+ } else if (password.length <= 12) {
+ return 'good';
+ }
+
+ return 'strong';
+};
+
+const StrengthMeter = ({ password }: Props) => (
+ <View>
+ {password.length > 0 &&
+ <View style={styles.meter}>
+ <View
+ style={{
+ ...styles.innerMeter,
+ width: strengths[passwordStrength(password)].width,
+ backgroundColor: strengths[passwordStrength(password)].backgroundColor,
+ }}
+ />
+ <Text
+ style={{
+ ...styles.meterLabel,
+ color: strengths[passwordStrength(password)].backgroundColor,
+ }}
+ >
+ {strengths[passwordStrength(password)].label}
+ </Text>
+ </View>
+ }
+ </View>
+);
+
+export default StrengthMeter;
@@ -0,0 +1,52 @@
+/* @flow */
+import React, { Component } from 'react';
+import styles from './style';
+
+class TextBox extends Component {
+
+ constructor(props) {
+ super(props);
+
+ this.handleChange = this.handleChange.bind(this);
+
+ this.state = {
+ value: this.props.value,
+ };
+ }
+
+ props: {
+ label: string,
+ type: string,
+ value: string,
+ children?: React$Element<any>,
+ };
+
+ handleChange(event) {
+ this.setState({ value: event.target.value });
+ }
+
+ render() {
+ return (
+ <div style={styles.formElement}>
+ <label
+ style={styles.label}
+ htmlFor={this.props.type}
+ >
+ {this.props.label}
+ </label>
+ <input
+ id={this.props.type}
+ style={{ ...styles.textbox, lineHeight: '100%' }}
+ type={this.props.type}
+ value={this.state.value}
+ onChange={this.handleChange}
+ />
+ {this.props.children &&
+ React.cloneElement(this.props.children, { password: this.state.value })
+ }
+ </div>
+ );
+ }
+}
+
+export default TextBox;
@@ -0,0 +1,20 @@
+/* @flow */
+import React from 'react';
+import { View, Text } from 'react-primitives';
+import styles from './style';
+
+type Props = {
+ label: string,
+ value: string,
+ children?: React$Element<any>,
+};
+
+const TextBox = ({ label, value, children }: Props) => (
+ <View style={styles.formElement}>
+ <Text style={styles.label}>{label}</Text>
+ <View style={styles.textbox}>{value}</View>
+ {children}
+ </View>
+);
+
+export default TextBox;
@@ -0,0 +1,25 @@
+import { colors, spacing, fontFamily, typeRamp } from '../../designSystem';
+
+export default {
+ formElement: {
+ marginBottom: spacing.Medium,
+ },
+ label: {
+ display: 'block',
+ fontFamily,
+ marginBottom: spacing.Small,
+ fontSize: typeRamp.Medium - 2,
+ },
+ textbox: {
+ boxSizing: 'border-box',
+ borderWidth: 1,
+ borderStyle: 'solid',
+ borderColor: colors.Grey,
+ backgroundColor: colors.White,
+ fontFamily,
+ fontSize: typeRamp.Medium,
+ lineHeight: typeRamp.Medium,
+ padding: spacing.Medium,
+ width: 300,
+ },
+};
@@ -0,0 +1,22 @@
+export default [
+ {
+ email: 'john.hornsby@example.com',
+ password: '',
+ },
+ {
+ email: 'john.hornsby@example.com',
+ password: 'hello',
+ },
+ {
+ email: 'john.hornsby@example.com',
+ password: '!H3ll0!',
+ },
+ {
+ email: 'john.hornsby@example.com',
+ password: 'IL0v3ToasT!',
+ },
+ {
+ email: 'john.hornsby@example.com',
+ password: 'IRea11yL0v3ToasT!',
+ },
+];
Oops, something went wrong.

0 comments on commit c46abc0

Please sign in to comment.