Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@kaw2k
Last active March 22, 2017 02:53
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 kaw2k/2ad5bf8e601efc6f94815a0ff97633ca to your computer and use it in GitHub Desktop.
Save kaw2k/2ad5bf8e601efc6f94815a0ff97633ca to your computer and use it in GitHub Desktop.
import * as React from 'react';
import * as cx from 'classnames';
import * as Router from 'react-router';
import Icon, { IconOptions } from './icon';
/*
We have a few things that are `clickable`:
- buttons
- internal links
- external links
Any `clickable` can apear like a button or a link, and all have to have optional
icons. Before I had button as the root component and links would extend the props of button
and re-create the functionality in its own component. This turned out to be a bad idea.
Instead, I want to unify them with a heavier base clasee, clickable, and have lighter
wrappers for `Button` and `Link`.
*/
// =============================
// CLICKABLE
// =============================
interface IClickableProps {
// Functional props
// I tried making this prop required, but since TS doesn't have subtraction types
// there was no simple way to signify that this prop was fulfilled by the parent
component?: React.ReactChild; // IE: 'a' or 'button' or ReactRouter.Link
label?: React.ReactNode; // The text of the clickable element
iconName?: IconOptions;
iconPosition?: 'before' | 'after';
// Style props
theme?: 'primary' | 'secondary' | 'green' | 'yellow' | 'orange' | 'white';
flat?: boolean;
inline?: boolean;
flow?: 'horizontal' | 'vertical';
// HTML props
onClick?: (e: React.MouseEvent<HTMLElement>) => void;
style?: React.CSSProperties;
tabIndex?: number;
// ...:sob:...
// I tried several routes to remove this. We need some way to pass additional
// props to this component. Since we accept `component` as a prop, we can conceivably
// have any number of additional props this component needs. I tried generics, it
// wouldn't work. I also tried union types, same deal.
[key: string]: any;
}
const Clickable: React.SFC<IClickableProps> = ({
component = 'button',
theme,
iconName,
iconPosition = 'after',
flat = false,
inline = false,
className = '',
flow = 'horizontal',
label,
children,
...props
}) => {
const icon = iconName && <Icon iconName={iconName} className={iconPosition} />;
return React.createElement(component as any, {
...props,
className: cx(`clickable clickable-theme--${theme} clickable-flow--${flow}`, className, {
flat,
inline,
'icon-only': !label && !children,
}),
}, [
<div className="clickable-body">
{iconPosition === 'before' && icon}
{label && <span className="clickable-label">{label}</span>}
{children}
{iconPosition === 'after' && icon}
</div>
]);
};
// =============================
// BUTTON
// =============================
interface IButtonProps extends IClickableProps {
type?: 'submit' | 'button';
disabled?: boolean;
}
const Button: React.SFC<IButtonProps> = ({ className='', ...props }) =>
<Clickable {...props} component="button" className={cx('button', className)} />;
// =============================
// LINK
// =============================
interface ILinkProps extends IClickableProps {
external?: boolean;
to: string;
target?: string;
activeClassName?: string;
title?: string;
}
const Link: React.SFC<ILinkProps> = ({
external,
to,
className = '',
activeClassName,
...props
}) =>
external
? <Clickable target="_blank"
{...props}
component="a"
className={cx('link', className)}
href={to} />
: <Clickable target="_blank"
{...props}
component={Router.Link as any}
activeClassName={activeClassName}
className={cx('link', className)}
to={to} />;
@samuelmaddock
Copy link

I think this is a good approach if you want to keep your button/links consistent throughout the app. Of course, this comes at the cost of adding potential nastyness when something breaks conventions in the design.

I feel like IClickableProps's label property should be dropped in favor children. Seems more natural to just pass that along as the text. It also allows for any special styling as needed.

Some things confuse me like the inline and flow props. Doesn't seem to follow my line of thinking and I feel like they should be in combined?

@kaw2k
Copy link
Author

kaw2k commented Mar 22, 2017

Yea... consistent design is another fight all together 😂. Each of the theme props were one-offs until we standardized them. I can dig dropping label and favoring children. I had it that way before and sort of liked how label worked, but really don't care. children seem like the way to go.

inline and flow are both props I just thought of when I was making up the interface. We will see if they are useful in practice.

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