Skip to content

Commit

Permalink
[core] fix(Callout): load intent icons statically (#6254)
Browse files Browse the repository at this point in the history
  • Loading branch information
adidahiya authored Jul 6, 2023
1 parent 9669182 commit 8623da5
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 29 deletions.
30 changes: 17 additions & 13 deletions packages/core/src/components/callout/callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import classNames from "classnames";
import * as React from "react";

import { IconName } from "@blueprintjs/icons";
import { Error, IconName, InfoSign, Tick, WarningSign } from "@blueprintjs/icons";

import {
AbstractPureComponent,
Expand Down Expand Up @@ -73,42 +73,46 @@ export class Callout extends AbstractPureComponent<CalloutProps> {

public render() {
const { className, children, icon, intent, title, ...htmlProps } = this.props;
const iconName = this.getIconName(icon, intent);
const iconElement = this.renderIcon(icon, intent);
const classes = classNames(
Classes.CALLOUT,
Classes.intentClass(intent),
{ [Classes.CALLOUT_ICON]: iconName != null },
{ [Classes.CALLOUT_ICON]: iconElement != null },
className,
);

return (
<div className={classes} {...htmlProps}>
{iconName && <Icon icon={iconName} aria-hidden={true} tabIndex={-1} />}
{iconElement}
{title && <H5>{title}</H5>}
{children}
</div>
);
}

private getIconName(icon?: CalloutProps["icon"], intent?: Intent): IconName | MaybeElement {
private renderIcon(icon?: CalloutProps["icon"], intent?: Intent): IconName | MaybeElement {
// 1. no icon
if (icon === null) {
if (icon === null || icon === false) {
return undefined;
}
// 2. defined iconName prop

const iconProps = { "aria-hidden": true, tabIndex: -1 };

// 2. icon specified by name or as a custom SVG element
if (icon !== undefined) {
return icon;
return <Icon icon={icon} {...iconProps} />;
}
// 3. default intent icon

// 3. icon specified by intent prop
switch (intent) {
case Intent.DANGER:
return "error";
return <Error {...iconProps} />;
case Intent.PRIMARY:
return "info-sign";
return <InfoSign {...iconProps} />;
case Intent.WARNING:
return "warning-sign";
return <WarningSign {...iconProps} />;
case Intent.SUCCESS:
return "tick";
return <Tick {...iconProps} />;
default:
return undefined;
}
Expand Down
45 changes: 29 additions & 16 deletions packages/core/test/callout/calloutTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,56 @@
*/

import { assert } from "chai";
import { shallow } from "enzyme";
import { mount } from "enzyme";
import * as React from "react";

import { Callout, Classes, H5, Icon, Intent } from "../../src";
import { IconNames } from "@blueprintjs/icons";

import { Callout, Classes, H5, Intent } from "../../src";

describe("<Callout>", () => {
let containerElement: HTMLElement | undefined;

beforeEach(() => {
containerElement = document.createElement("div");
document.body.appendChild(containerElement);
});
afterEach(() => {
containerElement?.remove();
});

it("supports className", () => {
const wrapper = shallow(<Callout className="foo" />);
const wrapper = mount(<Callout className="foo" />, { attachTo: containerElement });
assert.isFalse(wrapper.find(H5).exists(), "expected no H5");
assert.isTrue(wrapper.hasClass(Classes.CALLOUT));
assert.isTrue(wrapper.hasClass("foo"));
assert.isTrue(wrapper.find(`.${Classes.CALLOUT}`).hostNodes().exists());
assert.isTrue(wrapper.find(`.foo`).hostNodes().exists());
});

it("supports icon", () => {
const wrapper = shallow(<Callout icon="graph" />);
assert.isTrue(wrapper.find(Icon).exists());
const wrapper = mount(<Callout icon={IconNames.GRAPH} />, { attachTo: containerElement });
assert.isTrue(wrapper.find(`[data-icon="${IconNames.GRAPH}"]`).exists());
});

it("supports intent", () => {
const wrapper = shallow(<Callout intent={Intent.DANGER} />);
assert.isTrue(wrapper.hasClass(Classes.INTENT_DANGER));
const wrapper = mount(<Callout intent={Intent.DANGER} />, { attachTo: containerElement });
assert.isTrue(wrapper.find(`.${Classes.INTENT_DANGER}`).hostNodes().exists());
});

it("intent renders default icon", () => {
const wrapper = shallow(<Callout intent={Intent.PRIMARY} />);
assert.isTrue(wrapper.find(Icon).exists());
it("intent='primary' renders the associated default icon", () => {
const wrapper = mount(<Callout intent={Intent.PRIMARY} />, { attachTo: containerElement });
assert.isTrue(wrapper.find(`[data-icon="${IconNames.INFO_SIGN}"]`).exists());
});

it("icon=null removes intent icon", () => {
const wrapper = shallow(<Callout icon={null} intent={Intent.PRIMARY} />);
assert.isFalse(wrapper.find(Icon).exists());
const wrapper = mount(<Callout icon={null} intent={Intent.PRIMARY} />, { attachTo: containerElement });
assert.isFalse(wrapper.find(`[data-icon]`).exists());
});

it("renders optional title element", () => {
const wrapper = shallow(<Callout title="title" />);
const wrapper = mount(<Callout title="title" />, { attachTo: containerElement });
assert.isTrue(wrapper.find(H5).exists());
// NOTE: JSX cannot be passed through `title` prop due to conflict with HTML props
// shallow(<Callout title={<em>typings fail</em>} />);
// @ts-expect-error
mount(<Callout title={<em>typings fail</em>} />);
});
});

1 comment on commit 8623da5

@adidahiya
Copy link
Contributor Author

Choose a reason for hiding this comment

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

[core] fix(Callout): load intent icons statically (#6254)

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Please sign in to comment.