Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@kevinSuttle
Last active September 21, 2016 17:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinSuttle/fb3d4b33c83e091e7f980dda9ff8df8e to your computer and use it in GitHub Desktop.
Save kevinSuttle/fb3d4b33c83e091e7f980dda9ff8df8e to your computer and use it in GitHub Desktop.
Flow, defaultProps, and wrapper classes
// @flow
// "flow-bin": "^0.29.0",
import React from 'react';
require('./Button.css');
export type ButtonProps = {
onClick: () => void,
type: 'button' | 'reset' | 'submit',
design: 'primary' | 'secondary' | 'page' | 'tooltipInfo' | 'tooltipDocs',
onFocus: () => void,
onmouseover: () => void,
onmouseout: () => void,
className:string,
children: Array<HTMLElement>,
}
class Button extends React.Component {
static defaultProps = {
type: 'button',
design: 'primary',
className: 'btn',
}
constructor(props:ButtonProps){
super(props);
}
render(){
return (
<button
className={[`${this.props.design} ${this.props.className}`]}
onClick={this.props.onClick}
>
{this.props.children}
</button>
)
}
}
export default Button;
// @flow
import React from 'react';
require('./Icon.css');
import * as Glyphs from './Glyphs.jsx';
export type IconProps = {
glyph:string,
className?: string,
title?: string,
width?: string,
height?: string,
};
export default class Icon extends React.Component {
static defaultProps = {
'className': 'icon',
'width': '16',
'height': '16',
}
constructor(props:IconProps){
super(props);
}
render(){
return(
<svg
id="icon"
className={this.props.className}
viewBox={`0 0 ${this.props.width} ${this.props.height}`}
aria-labelledby="title"
preserveAspectRatio="xMidYMid meet"
>
<title id={this.props.title}>{this.props.title}</title>
{Glyphs[this.props.glyph] || null}
</svg>
)
}
}
// @flow
import React from 'react';
import {Button} from './Button.jsx';
import type ButtonProps from './Button.jsx';
import {Icon} from '../Icons/Icon.jsx';
import type IconProps from '../Icons/Icon.jsx';
const IconButtonProps = Object.assign(...ButtonProps, ...IconProps)
export const IconButton = (props:IconButtonProps) =>
<Button
design={props.design}
onClick={props.onClick}
className={`iconBtn ${props.className}`}
>
<Icon glyph={props.glyph} />
</Button>
// @flow
import React from 'react';
import type ButtonProps from './Button.jsx';
import Button from './Button.jsx';
type LabelProps = {
label:string,
}
type LabelButtonProps = ButtonProps & LabelProps;
export const LabelButton = (props:LabelButtonProps) =>
<Button design={props.design} onClick={props.onClick}>
<label>{props.label}</label>
</Button>
// @flow
/* eslint-disable no-duplicate-imports */
import React from 'react';
import { Button } from './Button.jsx';
import type { ButtonProps } from './Button.jsx';
import { Icon } from '../Icons/Icon.jsx';
import type { IconProps } from '../Icons/Icon.jsx';
type LabelProps = {
label:string
}
type LabeledIconButtonProps = ButtonProps & IconProps & LabelProps;
export const LabeledIconButton = (props:LabeledIconButtonProps) =>
<Button design={props.design} className="labeledIconBtn">
<Icon glyph={props.glyph} />
<label>{props.label}</label>
</Button>;
@joshblack
Copy link

So I think what you did is totally fine, just don't need to define prop types since you have flow annotations now. Common way is just to write out the input prop types explicitly:

// Disjoint union for the glyphs
type GlyphType = 'foo' | 'bar' | 'baz';

type IconButtonPropsType = {
  glyph: GlyphType;
  className: string;
  title: string;
  width: string;
  height: string;
};

const Icon = ({
  glyph,
  className = "icon",
  width = "16",
  height = "16"
} : IconButtonPropsType) => (
  <svg
    id="icon"
    className={className}
    viewBox={`0 0 ${width} ${height}`}
    aria-labelledby="title"
    preserveAspectRatio="xMidYMid meet"
  >
    <title id={Icon.title}>{Icon.title}</title>
    {Glyphs[glyph] || null}
  </svg>
);

@kevinSuttle
Copy link
Author

kevinSuttle commented Jul 14, 2016

Ah, I had that almost exactly earlier. The one thing that I feel is a little too magic is not explicitly showing what the propTypes are in an HOC. If I'm someone consuming this, it's like an undocumented API, right?

@jedwards1211
Copy link

Does const IconButtonProps = Object.assign(...ButtonProps, ...IconProps) work? I've never known for sure whether import/export type statements actually create normal JS variables. ButtonProps & IconProps should work though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment