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

short icon names only #2072

Merged
merged 4 commits into from
Feb 2, 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
16 changes: 10 additions & 6 deletions packages/core/src/components/icon/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import * as classNames from "classnames";
import * as React from "react";

import { IconName, IconSvgPaths16, IconSvgPaths20, LegacyIconName } from "@blueprintjs/icons";
import { IconName, IconSvgPaths16, IconSvgPaths20 } from "@blueprintjs/icons";
import { Classes, IIntentProps, IProps } from "../../common";

export { IconName };
Expand All @@ -22,7 +22,7 @@ export interface IIconProps extends IIntentProps, IProps {
* Name of the icon (with or without `"pt-icon-"` prefix).
* If omitted or `undefined`, this component will render nothing.
*/
iconName?: LegacyIconName;
iconName?: IconName;

/**
* Size of the icon, in pixels.
Expand All @@ -47,23 +47,27 @@ export class Icon extends React.PureComponent<IIconProps & React.SVGAttributes<S
return null;
}
const { className, iconName, iconSize = Icon.SIZE_STANDARD, intent, ...svgProps } = this.props;
const normalizedIconName = iconName.replace("pt-icon-", "") as IconName;

// choose which pixel grid is most appropriate for given icon size
const pixelGridSize = iconSize >= Icon.SIZE_LARGE ? Icon.SIZE_LARGE : Icon.SIZE_STANDARD;
const paths = this.renderSvgPaths(pixelGridSize, iconName);
if (paths == null) {
return null;
}

const classes = classNames(Classes.ICON, Classes.intentClass(intent), className);
const viewBox = `0 0 ${pixelGridSize} ${pixelGridSize}`;
return (
<svg
{...svgProps}
className={classes}
data-icon={normalizedIconName}
data-icon={iconName}
width={iconSize}
height={iconSize}
viewBox={viewBox}
>
<title>{normalizedIconName}</title>
{this.renderSvgPaths(pixelGridSize, normalizedIconName)}
<title>{iconName}</title>
{paths}
</svg>
);
}
Expand Down
6 changes: 5 additions & 1 deletion packages/core/test/icon/iconTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ describe("<Icon>", () => {

it("renders iconName class", () => assertIcon(<Icon iconName="calendar" />, "calendar"));

it("supports prefixed iconName", () => assertIcon(<Icon iconName={IconClasses.AIRPLANE} />, "airplane"));
it("prefixed iconName renders nothing", () => {
// @ts-ignore invalid iconName
const icon = shallow(<Icon iconName={IconClasses.AIRPLANE} />);
assert.isTrue(icon.isEmptyRender());
});

it("iconName=undefined renders nothing", () => {
const icon = shallow(<Icon iconName={undefined} />);
Expand Down
8 changes: 8 additions & 0 deletions packages/icons/src/iconName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright 2018 Palantir Technologies, Inc. All rights reserved.
*/

import * as IconNames from "./generated/iconNames";

/** String literal union type of all Blueprint UI icon names. */
export type IconName = (typeof IconNames)[keyof typeof IconNames];
11 changes: 7 additions & 4 deletions packages/icons/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
* Licensed under the terms of the LICENSE file distributed with this project.
*/

export { IconClasses } from "./generated/iconClasses";
export { IconName, LegacyIconName } from "./generated/iconName";
export { IconContents } from "./generated/iconStrings";
export { IconSvgPaths16, IconSvgPaths20 } from "./generated/iconSvgPaths";
import * as IconClasses from "./generated/iconClasses";
import * as IconContents from "./generated/iconContents";
import * as IconNames from "./generated/iconNames";
import { IconSvgPaths16, IconSvgPaths20 } from "./generated/iconSvgPaths";

export { IconClasses, IconContents, IconNames, IconSvgPaths16, IconSvgPaths20 };
export { IconName } from "./iconName";
49 changes: 16 additions & 33 deletions packages/node-build-scripts/generate-icons-source
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,27 @@ writeLinesToFile(
"_icon-map.scss",
'@import "icon-variables";',
"$icons: (",
...ICONS_METADATA.map(i => ` "${i.className.replace("pt-icon-", "")}": $${i.className},`),
...ICONS_METADATA.map(icon => ` "${toShortName(icon)}": $${icon.className},`),
");",
);

// simple variable definitions
writeLinesToFile("_icon-variables.scss", ...ICONS_METADATA.map(icon => `$${icon.className}: "${icon.content}";`));

// map ENUM_NAME to className (cast as string constant so it can be used as IconName)
writeLinesToFile("iconClasses.ts", ...buildTSObject("IconClasses", icon => `${icon.className}" as "${icon.className}`));

// union type of all valid string names
writeLinesToFile("iconName.ts", ...buildUnionType());
// map ENUM_NAME to pt-icon-class-name
writeLinesToFile("iconClasses.ts", ...exportIconConsts(icon => icon.className));

// map ENUM_NAME to unicode character
writeLinesToFile("iconStrings.ts", ...buildTSObject("IconContents", icon => icon.content.replace("\\", "\\u")));
writeLinesToFile("iconContents.ts", ...exportIconConsts(icon => icon.content.replace("\\", "\\u")));

// map ENUM_NAME to icon-name
writeLinesToFile("iconNames.ts", ...exportIconConsts(toShortName));

(async () => {
// SVG path strings. IIFE to unwrap async.
writeLinesToFile(
"iconSvgPaths.ts",
'import { IconName } from "./iconName";',
'import { IconName } from "../iconName";',
"",
"export const IconSvgPaths16: Record<IconName, string[]> = {",
...(await buildPathsObject("IconSvgPaths", 16)),
Expand All @@ -79,44 +79,27 @@ async function writeLinesToFile(filename, ...lines) {
fs.writeFileSync(outputPath, contents);
}

/** Removes `pt-icon-` prefix from icon className. */
function toShortName(icon) {
return icon.className.replace("pt-icon-", "");
}

/**
* Converts icon className to uppercase constant name.
* Example: `"pt-icon-time"` &rArr; `"TIME"`
* @param {IconMetadata} icon
*/
function toEnumName(icon) {
return icon.className
.replace("pt-icon-", "")
return toShortName(icon)
.replace(/-/g, "_")
.toUpperCase();
}

/**
* Builds `const ${objectName}`, keyed by icon enum name. Value is result of `valueGetter` function.
* @param {string} objectName
* @param {(icon: IconMetadata) => string} valueGetter
*/
function buildTSObject(objectName, valueGetter) {
return [
`export const ${objectName} = {`,
...ICONS_METADATA.map(icon => ` ${toEnumName(icon)}: "${valueGetter(icon)}",`),
"};",
];
}

/**
* Returns union type of all icon names, including both short (`"time"`) and long (`"pt-icon-time"`) formats.
*/
function buildUnionType() {
const shortNames = ICONS_METADATA.map(({ className }) => `"${className.replace("pt-icon-", "")}"`);
const longNames = ICONS_METADATA.map(({ className }) => `"${className}"`);
// long names extend short names
longNames.unshift("IconName");
return [
`export type IconName =\n | ${shortNames.join("\n | ")};`,
"",
`export type LegacyIconName =\n | ${longNames.join("\n | ")};`,
];
function exportIconConsts(valueGetter) {
return ICONS_METADATA.map(icon => `export const ${toEnumName(icon)} = "${valueGetter(icon)}";`);
}

/**
Expand Down