Skip to content

Commit

Permalink
feat(list): typescript (#501)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Goo committed Dec 28, 2018
1 parent 056adb5 commit 3b6c501
Show file tree
Hide file tree
Showing 20 changed files with 610 additions and 560 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"@types/mocha": "^5.2.5",
"@types/prop-types": "^15.5.6",
"@types/puppeteer": "^1.11.1",
"@types/react": "^16.7.7",
"@types/react": "^16.4.4",
"@types/react-dom": "^16.0.11",
"@types/react-router-dom": "^4.3.1",
"@types/uuid": "^3.4.4",
Expand Down
34 changes: 13 additions & 21 deletions packages/list/ListDivider.js → packages/list/ListDivider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,27 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as React from 'react';
import * as classnames from 'classnames';

const ListDivider = (props) => {
const {
tag: Tag,
className,
...otherProps
} = props;
export interface ListDividerProps extends React.HTMLProps<HTMLElement>{
className?: string,
tag?: string,
role?: string
};

const ListDivider: React.FunctionComponent<ListDividerProps> = ({
tag: Tag = 'li', className = '', role = 'separator', ...otherProps // eslint-disable-line react/prop-types
}) => {
return (
// https://github.com/Microsoft/TypeScript/issues/28892
// @ts-ignore
<Tag
className={classnames('mdc-list-divider', className)}
role={role}
{...otherProps}
/>
);
};

ListDivider.propTypes = {
className: PropTypes.string,
tag: PropTypes.string,
role: PropTypes.string,
};

ListDivider.defaultProps = {
className: '',
tag: 'li',
role: 'separator',
};

export default ListDivider;
33 changes: 11 additions & 22 deletions packages/list/ListGroup.js → packages/list/ListGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,24 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as React from 'react';
import * as classnames from 'classnames';

const ListGroup = (props) => {
const {
tag: Tag,
className,
children,
...otherProps
} = props;
export interface ListGroupProps extends React.HTMLProps<HTMLElement>{
className?: string,
tag?: string
};

const ListGroup:React.FunctionComponent<ListGroupProps> = ({
tag: Tag = 'div', className = '', children, ...otherProps // eslint-disable-line react/prop-types
}) => {
return (
// https://github.com/Microsoft/TypeScript/issues/28892
// @ts-ignore
<Tag className={classnames('mdc-list-group', className)} {...otherProps}>
{children}
</Tag>
);
};

ListGroup.propTypes = {
className: PropTypes.string,
children: PropTypes.node,
tag: PropTypes.string,
};

ListGroup.defaultProps = {
className: '',
children: null,
tag: 'div',
};

export default ListGroup;
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,26 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

const ListGroupSubheader = (props) => {
const {
tag: Tag,
className,
children,
...otherProps
} = props;
import * as React from 'react';
import * as classnames from 'classnames';
export interface ListGroupSubheaderProps extends React.HTMLProps<HTMLElement> {
className?: string,
tag?: string
};

const ListGroupSubheader:React.FunctionComponent<ListGroupSubheaderProps> = ({
tag: Tag = 'h3', className = '', children, ...otherProps // eslint-disable-line react/prop-types
}) => {
return (
<Tag className={classnames('mdc-list-group__subheader', className)} {...otherProps}>
// https://github.com/Microsoft/TypeScript/issues/28892
// @ts-ignore
<Tag
className={classnames('mdc-list-group__subheader', className)}
{...otherProps}
>
{children}
</Tag>
);
};

ListGroupSubheader.propTypes = {
className: PropTypes.string,
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element,
]),
tag: PropTypes.string,
};

ListGroupSubheader.defaultProps = {
className: '',
children: '',
tag: 'h3',
};

export default ListGroupSubheader;
121 changes: 65 additions & 56 deletions packages/list/ListItem.js → packages/list/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,65 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as React from 'react';
import * as classnames from 'classnames';

export default class ListItem extends Component {
listItemElement_ = React.createRef();
export interface ListItemProps<T> extends React.HTMLProps<T> {
className: string;
classNamesFromList: string[];
attributesFromList: object;
childrenTabIndex: number;
tabIndex: number;
shouldFocus: boolean;
shouldFollowHref: boolean;
shouldToggleCheckbox: boolean;
onKeyDown: React.KeyboardEventHandler<T>;
onClick: React.MouseEventHandler<T>;
onFocus: React.FocusEventHandler<T>;
onBlur: React.FocusEventHandler<T>;
tag: string;
children: React.ReactNode;
};

componentDidUpdate(prevProps) {
const {
shouldFocus,
shouldFollowHref,
shouldToggleCheckbox,
} = this.props;
if (shouldFocus !== prevProps.shouldFocus && shouldFocus) {
function isAnchorElement(element: any): element is HTMLAnchorElement {
return !!element.href;
}

function isFocusableElement(element: any): element is HTMLElement {
return typeof <HTMLElement>element.focus === 'function';
}

export default class ListItem<T extends {} = HTMLElement> extends React.Component<
ListItemProps<T>,
{}
> {
listItemElement_: React.RefObject<T> = React.createRef();

static defaultProps: Partial<ListItemProps<HTMLElement>> = {
className: '',
classNamesFromList: [],
attributesFromList: {},
childrenTabIndex: -1,
tabIndex: -1,
shouldFocus: false,
shouldFollowHref: false,
shouldToggleCheckbox: false,
onKeyDown: () => {},
onClick: () => {},
onFocus: () => {},
onBlur: () => {},
tag: 'li',
};

componentDidUpdate(prevProps: ListItemProps<T>) {
const {shouldFocus, shouldFollowHref, shouldToggleCheckbox} = this.props;
if (shouldFocus && !prevProps.shouldFocus) {
this.focus();
}
if (shouldFollowHref !== prevProps.shouldFollowHref && shouldFollowHref) {
if (shouldFollowHref && !prevProps.shouldFollowHref) {
this.followHref();
}
if (shouldToggleCheckbox !== prevProps.shouldToggleCheckbox && shouldToggleCheckbox) {
if (shouldToggleCheckbox && !prevProps.shouldToggleCheckbox) {
this.toggleCheckbox();
}
}
Expand All @@ -51,14 +90,14 @@ export default class ListItem extends Component {

focus() {
const element = this.listItemElement_.current;
if (element) {
if (isFocusableElement(element)) {
element.focus();
}
}

followHref() {
const element = this.listItemElement_.current;
if (element && element.href) {
if (isAnchorElement(element)) {
element.click();
}
}
Expand All @@ -83,8 +122,9 @@ export default class ListItem extends Component {
tag: Tag,
...otherProps
} = this.props;

return (
// https://github.com/Microsoft/TypeScript/issues/28892
// @ts-ignore
<Tag
className={this.classes}
{...otherProps}
Expand All @@ -96,44 +136,13 @@ export default class ListItem extends Component {
);
}

renderChild = (child) => {
const props = Object.assign({},
child.props,
{tabIndex: this.props.childrenTabIndex}
);
renderChild = (child: React.ReactChild) => {
if (typeof child === 'string' || typeof child === 'number') {
return child;
}

const tabIndex = this.props.childrenTabIndex;
const props = {...child.props, tabIndex};
return React.cloneElement(child, props);
}
};
}

ListItem.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
classNamesFromList: PropTypes.array,
attributesFromList: PropTypes.object,
childrenTabIndex: PropTypes.number,
tabIndex: PropTypes.number,
shouldFocus: PropTypes.bool,
shouldFollowHref: PropTypes.bool,
shouldToggleCheckbox: PropTypes.bool,
onKeyDown: PropTypes.func,
onClick: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
tag: PropTypes.string,
};

ListItem.defaultProps = {
className: '',
classNamesFromList: [],
attributesFromList: {},
childrenTabIndex: -1,
tabIndex: -1,
shouldFocus: false,
shouldFollowHref: false,
shouldToggleCheckbox: false,
onKeyDown: () => {},
onClick: () => {},
onFocus: () => {},
onBlur: () => {},
tag: 'li',
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,40 +20,30 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as React from 'react';
import * as classnames from 'classnames';

const ListItemGraphic = (props) => {
const {
tabIndex, // eslint-disable-line no-unused-vars
graphic,
tabbableOnListItemFocus,
className,
...otherProps
} = props;
export interface ListItemGraphicProps {
tabbableOnListItemFocus?: boolean;
className?: string;
tabIndex?: number;
graphic: React.ReactElement<any>;
childrenTabIndex?: number;
};

const ListItemGraphic:React.FunctionComponent<ListItemGraphicProps> = ({
tabIndex = -1, // eslint-disable-line no-unused-vars
graphic,
tabbableOnListItemFocus = false,
className = '',
...otherProps
}) => {
const graphicProps = {
className: classnames('mdc-list-item__graphic', className),
tabIndex: tabbableOnListItemFocus ? props.tabIndex : -1,
tabIndex: tabbableOnListItemFocus ? tabIndex : -1,
...otherProps,
};

return React.cloneElement(graphic, graphicProps);
};

ListItemGraphic.propTypes = {
tabbableOnListItemFocus: PropTypes.bool,
className: PropTypes.string,
tabIndex: PropTypes.number,
graphic: PropTypes.element,
};

ListItemGraphic.defaultProps = {
tabbableOnListItemFocus: false,
className: '',
tabIndex: -1,
graphic: {},
};

export default ListItemGraphic;
Loading

0 comments on commit 3b6c501

Please sign in to comment.