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

SVG Icons! #2028

Merged
merged 50 commits into from
Feb 1, 2018
Merged

SVG Icons! #2028

merged 50 commits into from
Feb 1, 2018

Conversation

giladgray
Copy link
Contributor

@giladgray giladgray commented Jan 24, 2018

Fixes #365

Checklist

  • 🔥 Update documentation
  • how to handle the CSS API now?
    • Buttons, all the way down
    • Input Group CSS API examples
  • improve example
  • Callout still uses icon classes because it relies on ::before and position trickery
  • MenuItem icon color

Changes proposed in this pull request:

  • generate-icons-source script now generates iconSvgPaths.tsx file with export const IconSvgPaths{16,20}: Record<IconName, string[]> where value is array of <path d="..." /> strings
  • 🔥 IconName only includes short names. LegacyIconName extends it with long names.
    • I made this change for the Record type above to support indexing
    • open to a less-breaking approach, but i'd also love to drop support for the long names.
  • 🔥 core Icon component now renders SVG content instead of span.pt-icon...
    • removed iconSize prop, replaced with width/height. this isn't totally ideal, I could use some help figuring out the ideal API here.

Reviewers should focus on:

  • what to do with the CSS icon API?
  • how to support 16px / 20px switch?

Follow-up work

@blueprint-bot
Copy link

revert documentalist usage

Preview: documentation | landing | table

@giladgray
Copy link
Contributor Author

I think I'm going to refactor to const iconSvgs: Record<IconName, string[]> where the value is an array of the <path d="{}"> strings, then i'll render <svg><paths></svg> in the component. this'll avoid the cloneElement and reduce the size of iconSvgs.ts as much as possible (cuz we'll likely need two data files -- 16px and 20px). gotta wait till my svgo@1.0 types publish later today to make this change, though.

@blueprint-bot
Copy link

fix tree test

Preview: documentation | landing | table

@blueprint-bot
Copy link

remove style tag from endorsed icon (the only one that had it)

Preview: documentation | landing | table

@@ -69,7 +69,7 @@ export class Alert extends AbstractPureComponent<IAlertProps, {}> {
return (
<Dialog className={classNames(Classes.ALERT, className)} isOpen={isOpen} style={style}>
<div className={Classes.ALERT_BODY}>
<Icon iconName={iconName} iconSize="inherit" intent={Intent.DANGER} />
<Icon iconName={iconName} width={40} intent={Intent.DANGER} />
Copy link
Contributor

@llorca llorca Jan 24, 2018

Choose a reason for hiding this comment

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

You never want to set width and height on an icon, only the size of its bounding box, so what about a size prop? I don't follow why iconSize has to be removed in the first place

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i think size is likely the way to go cuz we want them to be squared.
borrowed this pattern from researching other SVG icons, but it doesn't work super well.

const filepath = path.resolve(__dirname, `../../resources/icons/16px/${icon.className}.svg`);
const svg = fs.readFileSync(filepath, "utf-8");
const pathStrings = await svgo
.optimize(svg, { path: filepath })
Copy link
Contributor

Choose a reason for hiding this comment

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

👌 💯

@llorca
Copy link
Contributor

llorca commented Jan 24, 2018

What are the downsides of dropping support for the CSS API?

return (
<svg className={classes} width={width} height={height} viewBox="0 0 16 16">
<title>{iconName}</title>
{IconSvgPaths[shortName].map((d, i) => <path key={i} d={d} clip-rule="evenodd" fill-rule="evenodd" />)}
Copy link
Contributor

Choose a reason for hiding this comment

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

I was imagining that the consumer would feed this IconSvgPaths as a prop (iconSet?), so they can use this component with custom icon sets. Would also solve the 16px / 20px switch, since these two sizes are actually two different sets

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 heck no, we want this to be as easy as possible for blueprint users. i think this Icon component should be the Blueprint icon component. if you want to use your own icons, you should do icon={<MyOwnIcon />} instead of icon="time".

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh I see, that wasn't clear to me, name feels very generic. Then maybe what you want is something like <Icon16 /> <Icon20 /> to reflect the icon set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What if the default behavior would choose 16 or 20 based on the given iconSize? like, the one that requires less scaling? just gotta figure out the math.

Dialog is also a generic name 😄

const shortName = iconName.replace("pt-icon-", "") as IconName;
const classes = classNames(Classes.ICON, Classes.iconClass(iconName), Classes.intentClass(intent), className);
return (
<svg className={classes} width={width} height={height} viewBox="0 0 16 16">
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't viewBox use the size/width props?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, viewBox is based on the size of the image itself, so it's either 16 or 20. width/height scale the viewBox.

@giladgray
Copy link
Contributor Author

giladgray commented Jan 24, 2018

@llorca main downside of dropping CSS support is that our CSS API sections won't be able to easily show icons (short of copy-pasting the SVG code every time).
edit also the massive breaks it would cause to every legacy corner of the blueprint ecosystem.

Gilad Gray added 2 commits January 23, 2018 21:05
really awkward experience. makes me think we want to use 20 whenever iconSize > 20.
@giladgray
Copy link
Contributor Author

just pushed a commit 5e042e5 that's worth checking out:

  1. IconExample on #core/components/icon!
  2. this example chooses 16 or 20 based on iconSize, so play with that slider!
  3. after fiddling with the auto-chooser for a sec, i think it's pretty clearly undesirable behavior.
    most desirable may be the simplest: iconSize > 16 ? use 20 : use 16.
  4. i'm not sure the cost of two icon sizes (bundle size, maintenance, code complexity) is worth it with SVGs 😐

iconsize-slider

@blueprint-bot
Copy link

fix tests

Preview: documentation | landing | table

Gilad Gray added 2 commits January 31, 2018 18:08
@blueprint-bot
Copy link

examples/core-examples/common/IconSelect

Preview: documentation | landing | table

@@ -87,6 +87,14 @@ Styleguide pt-button
color: $pt-icon-color;
}

// icon-only button
.pt-icon:first-child:last-child {
Copy link
Contributor

Choose a reason for hiding this comment

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

🎩

<Icon className={Classes.ALIGN_RIGHT} iconName={rightIconName} key="icon" />,
];
return (
<>
Copy link
Contributor

Choose a reason for hiding this comment

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

so. nice.

@llorca
Copy link
Contributor

llorca commented Feb 1, 2018

Monster work, killing it dude 🔥

Didn't QE everything yet, I'll need some more time. So far I have these comments:

  • I'd remove (none), doesn't seem useful for the example. Would be nice to have a default icon selected

screen shot 2018-01-31 at 8 10 25 pm

  • in menu items with intent, the icon on the left should become white

screen shot 2018-01-31 at 8 13 47 pm

  • the icon button on the right side of the input group has both width and height inaccurate

screen shot 2018-01-31 at 8 13 38 pm

refactor iconName?: IconName prop ⇒ icon?: IconName | JSX.Element?

that is 🔑, it is a reason why this PR exists in the first place 👍

@@ -4,15 +4,62 @@
See [**Icons**](#icons) for a searchable list of all available UI icons.
</div>

<div class="pt-callout pt-intent-primary pt-icon-info-sign">
<h5>SVG icons in 2.0</h5>
Blueprint 2.0 introduced SVG icon support throughout the ecosystem, and moved icon resources to a separate
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: no need for the comma here (I noticed your prose style does this a lot and I generally don't mind for code comments, but I'm more picky about it in actual documentation).

Copy link
Contributor

Choose a reason for hiding this comment

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

also, "throughout the ecosystem" is a bit of a stretch. I would remove that phrase entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sometimes, i use commas to indicate where to pause, for maximal dramatic effect. 🎭

<h5>SVG icons in 2.0</h5>
Blueprint 2.0 introduced SVG icon support throughout the ecosystem, and moved icon resources to a separate
__@blueprintjs/icons__ package. The `Icon` component now renders SVG paths and the icon classes are no longer
used by any Blueprint React component. Icon font support has been preserved but should be considered legacy,
Copy link
Contributor

Choose a reason for hiding this comment

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

"... but should be considered a legacy feature that will be removed in a future major version."

The component also accepts all valid HTML props for an `<svg>` element.

Data files in the __@blueprintjs/icons__ package provide SVG path information for Blueprint's 300+ icons
for 16px and 20px grids. The `iconName` prop dictates which SVG is rendered, and `iconSize` determines
Copy link
Contributor

Choose a reason for hiding this comment

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

another unnecessary comma here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i actually thought long and hard about this one

```tsx
// string literals are supported through IconName union type
<Icon iconName="cross" />
<Icon iconName="pt-icon-globe" iconSize={20} />
Copy link
Contributor

Choose a reason for hiding this comment

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

probably shouldn't encourage the long names with pt- if you want to eventually remove them

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow-up PR. still technically supported.

@## CSS API

<div class="pt-callout pt-intent-warning pt-icon-warning-sign">
<h5>Icon fonts are legacy in 2.0</h5>
Blueprint's icon fonts should be considered legacy, and we plan to remove them in a future major version.
Copy link
Contributor

Choose a reason for hiding this comment

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

use the same phrasing I suggested above


public render() {
if (this.props.iconName == null) {
return null;
}
const { className, iconName, intent, iconSize = Icon.SIZE_STANDARD, ...restProps } = this.props;
const { className, iconName: iconNameProp, iconSize = Icon.SIZE_STANDARD, intent, ...svgProps } = this.props;
const iconName = iconNameProp.replace("pt-icon-", "") as IconName;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: leave the prop as iconName (also do this destructuring on the first line of the function, so you can use it on L46) and make this variable named normalizedIconName

@pkwi
Copy link
Contributor

pkwi commented Feb 1, 2018

Amazing stuff @giladgray!

Couple of smaller issues:

  • Both download links use 16px icons. It should be respectively - 16px icon preview for the 16px SVG download and 20px preview for the 20px download, as we had before.

screen shot 2018-02-01 at 3 42 34 pm

  • Adding to @llorca point - for the dark theme - both hovered and selected menu item, the icon on the left should become white - as in this example

screen shot 2018-02-01 at 4 05 39 pm

@adidahiya
Copy link
Contributor

refactor iconName?: IconName prop ⇒ icon?: IconName | JSX.Element?

separate PR please

can we drop support for the long names pt-icon-time?

can you elaborate? we'd still support it in the CSS API, right? does this mean all usages of iconName={IconClasses...} would break? what else is likely to break? I'm looking for a comprehensive list.

@giladgray
Copy link
Contributor Author

giladgray commented Feb 1, 2018

@adidahiya yes, dropping long names precisely means that iconName={IconClasses.*} would break. I'm down to add IconNames.*, though the IconName union type effectively provides this in your editor. thoughts?

👍 separate PR for icon prop (#2066)

@adidahiya
Copy link
Contributor

ok, in that case I think we can make the break. iconName should always be unprefixed.

An IconNames object would be nice for consistency with other string enums in our API, but isn't strictly necessary for 2.0.

@giladgray
Copy link
Contributor Author

i checked off completed issues in comments above.
@pkwi - 20px download link can't be implemented currently. will follow up in #2066.
@llorca - IconSelect is generally lackluster at the moment. let's improve it in follow-ups.

@blueprint-bot
Copy link

fix input-group button icon size

Preview: documentation | landing | table

* Callout renders SVG Icon component

- added .pt-callout-icon class to adjust padding & layout when icon is present
- existing CSS API is unchanged
- add Callout Example in docs

* fix tests

* 👏 totally non-breaking API! only one use of .pt-callout-icon

* iconName=null overrides intent
@blueprint-bot
Copy link

Callout renders SVG Icon component (#2060)

Preview: documentation | landing | table

@blueprint-bot
Copy link

clarify callout icon logic

Preview: documentation | landing | table

@giladgray giladgray merged commit db083fa into develop Feb 1, 2018
@giladgray giladgray deleted the gg/svg-icons branch February 1, 2018 22:52
@adidahiya
Copy link
Contributor

adidahiya commented Feb 1, 2018

things that still use the icon font:

  • .pt-tag-remove
  • navbar??
    image
  • table column reorder handle
    image

other bugs:

  • toasts
    image
  • this dialog icon looks too dark?
    image
  • Select package is somehow in Core components (probably unrelated to this PR, but it's not present on the live docs
    image
  • numeric input regression
    image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for SVG icons
5 participants