Skip to content

Commit

Permalink
feat(ripple): typescript (#527)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: withRipple is no longer the default export.
  • Loading branch information
Matt Goo committed Dec 28, 2018
1 parent 862c110 commit 4dcf389
Show file tree
Hide file tree
Showing 30 changed files with 964 additions and 777 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -5,7 +5,7 @@
"scripts": {
"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 && webpack --config packages/webpack.config.js --progress --colors",
"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",
"clean": "rm -rf build/** build packages/**/dist/",
"commitmsg": "validate-commit-msg",
Expand Down Expand Up @@ -144,6 +144,7 @@
"ts-node": "^7.0.1",
"typescript": "^3.2.1",
"typescript-eslint-parser": "^21.0.1",
"utility-types": "^2.1.0",
"uuid": "^3.3.2",
"validate-commit-msg": "^2.14.0",
"webpack": "^3.11.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/button/index.js
Expand Up @@ -23,7 +23,7 @@
import React from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';

export const Button = (props) => {
const {
Expand Down
2 changes: 1 addition & 1 deletion packages/card/PrimaryContent.js
Expand Up @@ -24,7 +24,7 @@ import React from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';

import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';

export const PrimaryContentBase = (props) => {
const {
Expand Down
67 changes: 33 additions & 34 deletions packages/checkbox/index.tsx
Expand Up @@ -21,34 +21,34 @@
// THE SOFTWARE.
import * as React from 'react';
import * as classnames from 'classnames';
// @ts-ignore
// @ts-ignore no mdc .d.ts file
import {MDCCheckboxFoundation, MDCCheckboxAdapter} from '@material/checkbox/dist/mdc.checkbox';
// TODO: fix with #528
// @ts-ignore
import withRipple from '@material/react-ripple';
import * as Ripple from '@material/react-ripple';

import NativeControl from './NativeControl';

export interface CheckboxProps {
checked: boolean;
className: string;
disabled: boolean;
indeterminate: boolean;
export interface CheckboxProps extends Ripple.InjectedProps<HTMLDivElement, HTMLInputElement> {
checked?: boolean;
className?: string;
disabled?: boolean;
indeterminate?: boolean;
nativeControlId?: string;
onChange: (evt: React.ChangeEvent<HTMLInputElement>) => void;
initRipple: (surface: HTMLDivElement, activator: HTMLInputElement) => void;
onChange?: (evt: React.ChangeEvent<HTMLInputElement>) => void;
initRipple: (surface: HTMLDivElement, activator?: HTMLInputElement) => void;
children?: React.ReactNode;
unbounded: boolean;
};

interface CheckboxState {
checked: boolean;
indeterminate: boolean;
checked?: boolean;
indeterminate?: boolean;
classList: Set<string>;
'aria-checked': boolean;
};

export class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
inputElement_: React.RefObject<HTMLInputElement> = React.createRef();
foundation_ = MDCCheckboxFoundation;
inputElement: React.RefObject<HTMLInputElement> = React.createRef();
foundation = MDCCheckboxFoundation;

constructor(props: CheckboxProps) {
super(props);
Expand All @@ -66,18 +66,17 @@ export class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
disabled: false,
indeterminate: false,
onChange: () => {},
initRipple: () => {},
unbounded: true,
};

componentDidMount() {
this.foundation_ = new MDCCheckboxFoundation(this.adapter);
this.foundation_.init();
this.foundation_.setDisabled(this.props.disabled);
this.foundation = new MDCCheckboxFoundation(this.adapter);
this.foundation.init();
this.foundation.setDisabled(this.props.disabled);
// indeterminate property on checkboxes is not supported:
// https://github.com/facebook/react/issues/1798#issuecomment-333414857
if (this.inputElement_.current) {
this.inputElement_.current.indeterminate = this.state.indeterminate;
if (this.inputElement.current) {
this.inputElement.current.indeterminate = this.state.indeterminate!;
}
}

Expand All @@ -87,29 +86,29 @@ export class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
checked !== prevProps.checked ||
indeterminate !== prevProps.indeterminate
) {
this.handleChange(checked, indeterminate);
this.handleChange(checked!, indeterminate!);
}
if (disabled !== prevProps.disabled) {
this.foundation_.setDisabled(disabled);
this.foundation.setDisabled(disabled);
}
}

componentWillUnmount() {
if (this.foundation_) {
this.foundation_.destroy();
if (this.foundation) {
this.foundation.destroy();
}
}

init = (el: HTMLDivElement) => {
if (!this.inputElement_.current) return;
this.props.initRipple(el, this.inputElement_.current);
if (!this.inputElement.current) return;
this.props.initRipple(el, this.inputElement.current);
};

handleChange = (checked: boolean, indeterminate: boolean) => {
this.setState({checked, indeterminate}, () => {
this.foundation_.handleChange();
if (this.inputElement_.current) {
this.inputElement_.current.indeterminate = indeterminate;
this.foundation.handleChange();
if (this.inputElement.current) {
this.inputElement.current.indeterminate = indeterminate;
}
});
};
Expand Down Expand Up @@ -156,7 +155,7 @@ export class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
const {onChange} = this.props;
const {checked, indeterminate} = evt.target;
this.handleChange(checked, indeterminate);
onChange(evt);
onChange!(evt);
}

render() {
Expand All @@ -177,7 +176,7 @@ export class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
return (
<div
className={this.classes}
onAnimationEnd={() => this.foundation_.handleAnimationEnd()}
onAnimationEnd={() => this.foundation.handleAnimationEnd()}
ref={this.init}
{...otherProps}
>
Expand All @@ -187,7 +186,7 @@ export class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
disabled={disabled}
aria-checked={this.state['aria-checked']}
onChange={this.onChange}
rippleActivatorRef={this.inputElement_}
rippleActivatorRef={this.inputElement}
/>
<div className='mdc-checkbox__background'>
<svg
Expand All @@ -208,4 +207,4 @@ export class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
}
}

export default withRipple(Checkbox);
export default Ripple.withRipple<CheckboxProps, HTMLDivElement, HTMLInputElement>(Checkbox);
2 changes: 1 addition & 1 deletion packages/chips/Chip.js
Expand Up @@ -23,7 +23,7 @@
import React, {Component} from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';
import {MDCChipFoundation} from '@material/chips/dist/mdc.chips';

export class Chip extends Component {
Expand Down
2 changes: 1 addition & 1 deletion packages/fab/index.js
Expand Up @@ -24,7 +24,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';

export class Fab extends React.Component {
get classes() {
Expand Down
2 changes: 1 addition & 1 deletion packages/icon-button/index.js
Expand Up @@ -23,7 +23,7 @@
import React, {Component} from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';
import {MDCIconButtonToggleFoundation} from '@material/icon-button/dist/mdc.iconButton';
import IconToggle from './IconToggle';

Expand Down
2 changes: 1 addition & 1 deletion packages/material-icon/index.js
Expand Up @@ -24,7 +24,7 @@ import React from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';

import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';

export default class MaterialIcon extends React.Component {
render() {
Expand Down
2 changes: 1 addition & 1 deletion packages/radio/index.js
Expand Up @@ -24,7 +24,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import {MDCRadioFoundation} from '@material/radio/dist/mdc.radio';
import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';
import NativeControl from './NativeControl';

class Radio extends React.Component {
Expand Down
49 changes: 47 additions & 2 deletions packages/ripple/README.md
Expand Up @@ -40,7 +40,7 @@ import '@material/react-ripple/dist/ripple.css';
To wrap a component with the ripple HOC, please follow this example:

```js
import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';

const Icon = (props) => {
const {
Expand Down Expand Up @@ -75,6 +75,51 @@ const RippleIcon = withRipple(Icon);
Wrap your Icon component with the HOC `withRipple`, which returns a component
with a ripple capable surface.

### Typescript

If you're using TS, you will need to extend from the provided InjectedProps.

```js

import {withRipple, InjectedProps} from '@material/react-ripple';

interface IconProps extends InjectedProps<HTMLDivElement> {
children?: React.ReactNode;
className: string;
initRipple: React.Ref<HTMLDivElement>;
unbounded: boolean;
}

const Icon = (props) => {
const {
children,
className = '',
// You must call `initRipple` from the root element's ref. This attaches the ripple
// to the element.
initRipple,
// include `unbounded` to remove warnings when passing `otherProps` to the
// root element.
unbounded,
...otherProps
} = props;

// any classes needed on your component needs to be merged with
// `className` passed from `props`.
const classes = `ripple-icon-component ${className}`;

return (
<div
className={classes}
ref={initRipple}
{...otherProps}>
{children}
</div>
);
};

const RippleIcon = withRipple<IconProps, HTMLDivElement>(Icon);
```

## Advanced Usage

### Ripple surface and ripple activator
Expand All @@ -84,7 +129,7 @@ You may want to apply the visual treatment (CSS classes and styles) for a ripple
The `initRipple` callback prop can take in an extra `activator` argument for the case where the ripple activator differs from the ripple surface. If the `activator` argument is not provided, the ripple surface will also serve as the ripple activator.

```js
import withRipple from '@material/react-ripple';
import {withRipple} from '@material/react-ripple';

const MyInput = (props) => {
const {
Expand Down

0 comments on commit 4dcf389

Please sign in to comment.