Skip to content

Commit

Permalink
feat(button): typescript (#508)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Goo committed Dec 27, 2018
1 parent dac62b1 commit a00e596
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 131 deletions.
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -6,7 +6,7 @@
"start": "webpack-dev-server --config test/screenshot/webpack.config.js --content-base test/screenshot",
"stop": "./test/screenshot/stop.sh",
"build": "npm run clean && mkdirp build && node --max_old_space_size=8192 node_modules/.bin/webpack --config packages/webpack.config.js --progress --colors",
"capture": "MDC_COMMIT_HASH=$(git rev-parse --short HEAD) MDC_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) mocha --require ts-node/register --require babel-core/register --ui tdd --timeout 20000 test/screenshot/capture-suite.tsx",
"capture": "MDC_COMMIT_HASH=$(git rev-parse --short HEAD) MDC_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) mocha --require ts-node/register --require babel-core/register --ui tdd --timeout 30000 test/screenshot/capture-suite.tsx",
"clean": "rm -rf build/** build packages/**/dist/",
"commitmsg": "validate-commit-msg",
"fix": "eslint --fix --ext .jsx,.js,.tsx,.ts packages test",
Expand All @@ -18,7 +18,7 @@
"test:watch": "karma start karma.local.js --auto-watch",
"test:unit": "npm run clean && NODE_ENV=test karma start karma.local.js --single-run",
"test:unit-ci": "karma start karma.ci.js --single-run",
"test:image-diff": "MDC_COMMIT_HASH=$(git rev-parse --short HEAD) MDC_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) mocha --require ts-node/register --require babel-core/register --ui tdd --timeout 20000 test/screenshot/diff-suite.tsx",
"test:image-diff": "MDC_COMMIT_HASH=$(git rev-parse --short HEAD) MDC_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) mocha --require ts-node/register --require babel-core/register --ui tdd --timeout 30000 test/screenshot/diff-suite.tsx",
"test:screenshots": "docker run -it --rm --cap-add=SYS_ADMIN -e MDC_GCLOUD_SERVICE_ACCOUNT_KEY=\"${MDC_GCLOUD_SERVICE_ACCOUNT_KEY}\" mdcreact/screenshots /bin/sh -c 'git checkout .; git checkout master; git pull; npm i; /home/pptruser/material-components-web-react/test/screenshot/start.sh; sleep 35s; npm run test:image-diff'",
"upload:screenshots": "node ./test/screenshot/upload-screenshots.js"
},
Expand Down
99 changes: 0 additions & 99 deletions packages/button/index.js

This file was deleted.

103 changes: 103 additions & 0 deletions packages/button/index.tsx
@@ -0,0 +1,103 @@
// The MIT License
//
// Copyright (c) 2018 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import * as React from 'react';
import * as classnames from 'classnames';
import * as Ripple from '@material/react-ripple';

const BUTTON_CLASS_NAME = 'mdc-button__icon';

type ButtonTypes = HTMLAnchorElement | HTMLButtonElement;

export interface ButtonProps<T extends ButtonTypes> extends Ripple.InjectedProps<T>, React.HTMLAttributes<T> {
raised?: boolean;
unelevated?: boolean;
outlined?: boolean;
dense?: boolean;
disabled?: boolean;
className?: string;
icon?: React.ReactElement<React.HTMLProps<HTMLOrSVGElement>>;
href?: string;
}

export const Button = <T extends ButtonTypes>(
{
className = '',
raised = false,
unelevated = false,
outlined = false,
dense = false,
disabled = false,
icon,
href,
children,
initRipple,
// eslint disabled since we do not want to include
// this in ...otherProps.
// if unbounded is passed to the <button> element, it will throw
// a warning.
unbounded = false, // eslint-disable-line no-unused-vars
...otherProps
}: ButtonProps<T>
) => {
const props = {
className: classnames('mdc-button', className, {
'mdc-button--raised': raised,
'mdc-button--unelevated': unelevated,
'mdc-button--outlined': outlined,
'mdc-button--dense': dense,
}),
ref: initRipple,
disabled,
...otherProps,
};

if (href) {
return (
<a {...props as React.HTMLProps<HTMLAnchorElement>} href={href}>
{renderIcon(icon)}
{children}
</a>
);
}

return (
<button {...props as React.HTMLProps<HTMLButtonElement>}>
{renderIcon(icon)}
{children}
</button>
);
};

const renderIcon = (icon?: React.ReactElement<React.HTMLProps<HTMLOrSVGElement>>) => (
icon ?
React.cloneElement(icon, {
className: classnames(BUTTON_CLASS_NAME, icon.props.className),
}) :
null
);

Button.defaultProps = {
initRipple: () => {},
};

export default Ripple.withRipple<ButtonProps<ButtonTypes>, ButtonTypes>(Button);
@@ -1,24 +1,29 @@
import React from 'react';
import * as React from 'react';
import MaterialIcon from '../../../packages/material-icon/index';
import '../../../packages/button/index.scss';
import './index.scss';

import Button from '../../../packages/button/index';
const svgIcon = (<svg
width="24px" height="24px"
xmlns="http://www.w3.org/2000/svg"
className="material-icons"
viewBox="0 0 24 24"
fill="#fff">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M23 12c0-6.07-4.93-11-11-11S1 5.93 1 12s4.93 11 11

const svgIcon = (
<svg
width='24px'
height='24px'
xmlns='http://www.w3.org/2000/svg'
className='material-icons'
viewBox='0 0 24 24'
fill='#fff'
>
<path fill='none' d='M0 0h24v24H0z' />
<path
d='M23 12c0-6.07-4.93-11-11-11S1 5.93 1 12s4.93 11 11
11 11-4.93 11-11zM5 17.64C3.75 16.1 3 14.14 3 12c0-2.13.76-4.08
2-5.63v11.27zM17.64 5H6.36C7.9 3.75 9.86 3 12 3s4.1.75 5.64 2zM12
14.53L8.24 7h7.53L12 14.53zM17 9v8h-4l4-8zm-6 8H7V9l4 8zm6.64
2c-1.55 1.25-3.51 2-5.64 2s-4.1-.75-5.64-2h11.28zM21 12c0 2.14-.75
4.1-2 5.64V6.37c1.24 1.55 2 3.5 2 5.63z"/>
</svg>);

4.1-2 5.64V6.37c1.24 1.55 2 3.5 2 5.63z'
/>
</svg>
);
const ButtonScreenshotTest = () => {
return (
<div>
Expand All @@ -36,32 +41,33 @@ const ButtonScreenshotTest = () => {
</div>

<div className='button-container'>
<Button disabled raised>Disabled Raised</Button>
<Button disabled raised>
Disabled Raised
</Button>
</div>

<div className='button-container'>
<Button dense>Dense</Button>
</div>

<div className='button-container'>
<Button href='https://google.com' raised>Anchor Tag</Button>
<Button href='https://google.com' raised>
Anchor Tag
</Button>
</div>

<div className='button-container'>
<Button raised
icon={<MaterialIcon icon='menu' />}>
<Button raised icon={<MaterialIcon icon='menu' />}>
Raised
</Button>
</div>

<div className='button-container'>
<Button raised
icon={svgIcon}>
<Button raised icon={svgIcon}>
Svg Icon
</Button>
</div>
</div>
);
};

export default ButtonScreenshotTest;
2 changes: 0 additions & 2 deletions test/screenshot/chips/index.tsx
@@ -1,8 +1,6 @@
import * as React from 'react';
import './index.scss';
import '../../../packages/chips/index.scss';
// TODO: fix in #513
// @ts-ignore
import MaterialIcon from '../../../packages/material-icon';
import {ChipProps, Chip, ChipSet} from '../../../packages/chips/index'; // eslint-disable-line no-unused-vars
// no .d.ts file
Expand Down
3 changes: 1 addition & 2 deletions test/screenshot/text-field/refTest.tsx
@@ -1,7 +1,6 @@
import * as React from 'react';
import TextField, {Input} from '../../../packages/text-field';
// @ts-ignore
import Button from '../../../packages/button/index.js';
import Button from '../../../packages/button/index';

class OutlinedTextField extends React.Component<{}, {value: string}> {
inputEl: Input<HTMLInputElement> | null = null;
Expand Down
@@ -1,6 +1,6 @@
import React from 'react';
import * as React from 'react';
import {assert} from 'chai';
import td from 'testdouble';
import * as td from 'testdouble';
import {mount, shallow} from 'enzyme';
import {Button} from '../../../packages/button/index';

Expand Down Expand Up @@ -49,12 +49,14 @@ test('renders a button tag', () => {
});

test('renders a button with an anchor tag', () => {
const wrapper = shallow(<Button href="https://www.google.com" />);
const wrapper = shallow(<Button href='https://www.google.com' />);
assert.equal(wrapper.type(), 'a');
});

test('default initRipple function', () => {
Button.defaultProps.initRipple = td.func();
mount(<Button />);
td.verify(Button.defaultProps.initRipple(td.matchers.isA(Object)), {times: 1});
const initRipple = td.func() as (surface: HTMLButtonElement) => {};
mount(<Button initRipple={initRipple} />);
td.verify(initRipple(td.matchers.isA(Object)), {
times: 1,
});
});

0 comments on commit a00e596

Please sign in to comment.