Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use HTMLAttributes instead of HTMLProps #2269

Merged
merged 5 commits into from
Mar 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/core/src/common/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import * as React from "react";
import { IconName } from "@blueprintjs/icons";
import { Intent } from "./intent";

/**
* Alias for all valid HTML props for `<div>` element.
* Does not include React's `ref` or `key`.
*/
export type HTMLDivProps = React.HTMLAttributes<HTMLDivElement>;

/**
* Alias for all valid HTML props for `<input>` element.
* Does not include React's `ref` or `key`.
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/components/button/abstractButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ export interface IButtonState {
isActive: boolean;
}

export abstract class AbstractButton<T> extends React.Component<React.HTMLProps<T> & IButtonProps, IButtonState> {
export abstract class AbstractButton<H extends React.HTMLAttributes<any>> extends React.PureComponent<
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤯

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emoji didn't come though, it's just a square. what's the sentiment here?

Copy link
Contributor

@llorca llorca Mar 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

(High Sierra update is worth the new emojis. Maybe.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yes very nice

IButtonProps & H,
IButtonState
> {
public state = {
isActive: false,
};
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/button/buttonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import classNames from "classnames";
import * as React from "react";
import { Alignment } from "../../common/alignment";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";

export interface IButtonGroupProps extends IProps, React.HTMLProps<HTMLDivElement> {
export interface IButtonGroupProps extends IProps, HTMLDivProps {
/**
* Text alignment of button contents.
* This prop only has an effect if buttons are wider than their default widths.
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/button/buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { AbstractButton, IButtonProps } from "./abstractButton";

export { IButtonProps };

export class Button extends AbstractButton<HTMLButtonElement> {
export class Button extends AbstractButton<React.ButtonHTMLAttributes<HTMLButtonElement>> {
public static displayName = "Blueprint2.Button";

public render() {
Expand All @@ -26,7 +26,7 @@ export class Button extends AbstractButton<HTMLButtonElement> {
}
}

export class AnchorButton extends AbstractButton<HTMLAnchorElement> {
export class AnchorButton extends AbstractButton<React.AnchorHTMLAttributes<HTMLAnchorElement>> {
public static displayName = "Blueprint2.AnchorButton";

public render() {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/components/callout/callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
import classNames from "classnames";
import * as React from "react";

import { Classes, IIntentProps, Intent, IProps } from "../../common";
import { Classes, HTMLDivProps, IIntentProps, Intent, IProps } from "../../common";
import { Icon } from "../../index";
import { IconName } from "../icon/icon";

/** This component also supports the full range of HTML `<div>` props. */
export interface ICalloutProps extends IIntentProps, IProps {
export interface ICalloutProps extends IIntentProps, IProps, HTMLDivProps {
/**
* Name of a Blueprint UI icon (or an icon element) to render on the left side.
*
Expand All @@ -30,7 +30,7 @@ export interface ICalloutProps extends IIntentProps, IProps {
title?: string;
}

export class Callout extends React.PureComponent<ICalloutProps & React.HTMLAttributes<HTMLDivElement>, {}> {
export class Callout extends React.PureComponent<ICalloutProps, {}> {
public render() {
const { className, children, icon: _nospread, intent, title, ...htmlProps } = this.props;
const iconName = this.getIconName();
Expand Down
17 changes: 5 additions & 12 deletions packages/core/src/components/card/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import classNames from "classnames";
import * as React from "react";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";

export interface ICardProps extends IProps {
export interface ICardProps extends IProps, HTMLDivProps {
/**
* Controls the intensity of the drop shadow beneath the card: the higher
* the elevation, the higher the drop shadow. At elevation `0`, no drop
Expand Down Expand Up @@ -61,20 +61,13 @@ export class Card extends React.PureComponent<ICardProps, {}> {
};

public render() {
return (
<div className={this.getClassName()} onClick={this.props.onClick}>
{this.props.children}
</div>
);
}

private getClassName() {
const { elevation, interactive, className } = this.props;
return classNames(
const { className, elevation, interactive, ...htmlProps } = this.props;
const classes = classNames(
Classes.CARD,
{ [Classes.INTERACTIVE]: interactive },
ELEVATION_CLASSES[elevation],
className,
);
return <div className={classes} {...htmlProps} />;
}
}
4 changes: 2 additions & 2 deletions packages/core/src/components/forms/controlGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import classNames from "classnames";
import * as React from "react";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";

export interface IControlGroupProps extends React.AllHTMLAttributes<HTMLDivElement>, IProps {
export interface IControlGroupProps extends IProps, HTMLDivProps {
/**
* Whether the control group should take up the full width of its container.
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/forms/fileInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Utils } from "../../common";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";

export interface IFileInputProps extends React.AllHTMLAttributes<HTMLLabelElement>, IProps {
export interface IFileInputProps extends React.LabelHTMLAttributes<HTMLLabelElement>, IProps {
/**
* Whether the file input is non-interactive.
* Setting this to `true` will automatically disable the child input too.
Expand Down Expand Up @@ -56,7 +56,7 @@ export interface IFileInputProps extends React.AllHTMLAttributes<HTMLLabelElemen

// TODO: write tests (ignoring for now to get a build passing quickly)
/* istanbul ignore next */
export class FileInput extends React.Component<IFileInputProps, {}> {
export class FileInput extends React.PureComponent<IFileInputProps, {}> {
public static displayName = "Blueprint2.FileInput";

public static defaultProps: IFileInputProps = {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/components/forms/inputGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import * as Classes from "../../common/classes";
import { HTMLInputProps, IControlledProps, IIntentProps, IProps, removeNonHTMLProps } from "../../common/props";
import { Icon, IconName } from "../icon/icon";

// NOTE: This interface does not extend HTMLInputProps due to incompatiblity with `IControlledProps`.
// Instead, we union the props in the component definition, which does work and properly disallows `string[]` values.
export interface IInputGroupProps extends IControlledProps, IIntentProps, IProps {
/**
* Whether the input is non-interactive.
Expand Down Expand Up @@ -48,7 +50,7 @@ export interface IInputGroupState {
rightElementWidth?: number;
}

export class InputGroup extends React.PureComponent<HTMLInputProps & IInputGroupProps, IInputGroupState> {
export class InputGroup extends React.PureComponent<IInputGroupProps & HTMLInputProps, IInputGroupState> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extend interface instead of & the types (as per convention elsewhere)

public static displayName = "Blueprint2.InputGroup";

public state: IInputGroupState = {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/components/forms/label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as React from "react";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";

export interface ILabelProps extends React.AllHTMLAttributes<HTMLLabelElement>, IProps {
export interface ILabelProps extends React.LabelHTMLAttributes<HTMLLabelElement>, IProps {
/**
* Whether the label is non-interactive.
* Be sure to explicitly disable any child controls as well.
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/components/forms/textArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as React from "react";
import * as Classes from "../../common/classes";
import { IIntentProps, IProps } from "../../common/props";

export interface ITextAreaProps extends React.AllHTMLAttributes<HTMLTextAreaElement>, IIntentProps, IProps {
export interface ITextAreaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>, IIntentProps, IProps {
/**
* Whether the text area should take up the full width of its container.
*/
Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@
import classNames from "classnames";
import * as React from "react";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";
import { NavbarDivider } from "./navbarDivider";
import { NavbarGroup } from "./navbarGroup";
import { NavbarHeading } from "./navbarHeading";

export { INavbarDividerProps } from "./navbarDivider";

// allow the empty interface so we can label it clearly in the docs
// tslint:disable-next-line:no-empty-interface
export interface INavbarProps extends React.HTMLProps<HTMLDivElement>, IProps {
export interface INavbarProps extends IProps, HTMLDivProps {
// Empty
}

Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/components/navbar/navbarDivider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
import classNames from "classnames";
import * as React from "react";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";

// allow the empty interface so we can label it clearly in the docs
// tslint:disable-next-line:no-empty-interface
export interface INavbarDividerProps extends React.HTMLProps<HTMLDivElement>, IProps {
export interface INavbarDividerProps extends IProps, HTMLDivProps {
// Empty
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/navbar/navbarGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import classNames from "classnames";
import * as React from "react";
import { Alignment } from "../../common/alignment";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";

export interface INavbarGroupProps extends React.HTMLProps<HTMLDivElement>, IProps {
export interface INavbarGroupProps extends IProps, HTMLDivProps {
/**
* The side of the navbar on which the group should appear.
* The `Alignment` enum provides constants for these values.
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/components/navbar/navbarHeading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@
import classNames from "classnames";
import * as React from "react";
import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";

// allow the empty interface so we can label it clearly in the docs
// tslint:disable-next-line:no-empty-interface
export interface INavbarHeadingProps extends React.HTMLProps<HTMLDivElement>, IProps {
export interface INavbarHeadingProps extends IProps, HTMLDivProps {
// Empty
}

// this component is simple enough that tests would be purely tautological.
/* istanbul ignore next */
export class NavbarHeading extends React.PureComponent<React.HTMLProps<HTMLDivElement>, {}> {
export class NavbarHeading extends React.PureComponent<INavbarHeadingProps, {}> {
public static displayName = "Blueprint2.NavbarHeading";

public render() {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/popover/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { AbstractPureComponent } from "../../common/abstractPureComponent";
import * as Classes from "../../common/classes";
import * as Errors from "../../common/errors";
import { Position } from "../../common/position";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";
import * as Utils from "../../common/utils";
import { IOverlayableProps, Overlay } from "../overlay/overlay";
import { Tooltip } from "../tooltip/tooltip";
Expand Down Expand Up @@ -406,7 +406,7 @@ export class Popover extends AbstractPureComponent<IPopoverProps, IPopoverState>
private renderPopper(content: JSX.Element) {
const { usePortal, interactionKind, modifiers } = this.props;

const popoverHandlers: React.HTMLAttributes<HTMLDivElement> = {
const popoverHandlers: HTMLDivProps = {
// always check popover clicks for dismiss class
onClick: this.handlePopoverClick,
};
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/portal/portal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import * as ReactDOM from "react-dom";

import * as Classes from "../../common/classes";
import * as Errors from "../../common/errors";
import { IProps } from "../../common/props";
import { HTMLDivProps, IProps } from "../../common/props";
import { safeInvoke } from "../../common/utils";

export interface IPortalProps extends IProps, React.HTMLProps<HTMLDivElement> {
export interface IPortalProps extends IProps, HTMLDivProps {
/**
* Callback invoked when the children of this `Portal` have been added to the DOM.
*/
Expand Down
7 changes: 7 additions & 0 deletions packages/core/test/card/cardTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,11 @@ describe("<Card>", () => {
shallow(<Card onClick={onClick} />).simulate("click");
assert.isTrue(onClick.calledOnce);
});

it("supports HTML props", () => {
const onChange = sinon.spy();
const card = shallow(<Card onChange={onChange} title="foo" tabIndex={4000} />).find("div");
assert.strictEqual(card.prop("onChange"), onChange);
assert.strictEqual(card.prop("title"), "foo");
});
});
5 changes: 3 additions & 2 deletions packages/datetime/test/dateRangeInputTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as sinon from "sinon";

import {
Classes as CoreClasses,
HTMLDivProps,
HTMLInputProps,
IInputGroupProps,
InputGroup,
Expand All @@ -26,8 +27,8 @@ import { DATE_FORMAT } from "./common/dateFormat";
import * as DateTestUtils from "./common/dateTestUtils";

type WrappedComponentRoot = ReactWrapper<any, {}>;
type WrappedComponentInput = ReactWrapper<React.InputHTMLAttributes<HTMLInputElement>, any>;
type WrappedComponentDayElement = ReactWrapper<React.HTMLAttributes<HTMLDivElement>, any>;
type WrappedComponentInput = ReactWrapper<HTMLInputProps, any>;
type WrappedComponentDayElement = ReactWrapper<HTMLDivProps, any>;

type OutOfRangeTestFunction = (
inputGetterFn: (root: WrappedComponentRoot) => WrappedComponentInput,
Expand Down
4 changes: 2 additions & 2 deletions packages/docs-app/src/components/clickToCopy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import classNames from "classnames";
import * as React from "react";

import { IProps, Keys, removeNonHTMLProps, Utils } from "@blueprintjs/core";
import { HTMLDivProps, IProps, Keys, removeNonHTMLProps, Utils } from "@blueprintjs/core";
import { createKeyEventHandler } from "@blueprintjs/docs-theme";

export interface IClickToCopyProps extends IProps, React.HTMLProps<HTMLDivElement> {
export interface IClickToCopyProps extends IProps, HTMLDivProps {
/**
* Additional class names to apply after value has been copied
* @default "docs-clipboard-copied"
Expand Down