Skip to content

Commit

Permalink
fix: improve svg icon support (#933)
Browse files Browse the repository at this point in the history
Co-authored-by: Lee Chase <[email protected]>
  • Loading branch information
lee-chase and lee-chase authored Jun 24, 2020
1 parent 64ab976 commit 2fc40e4
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 17 deletions.
10 changes: 10 additions & 0 deletions packages/core/src/assets/images/example-icon-svg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default {
exampleSvgString: `
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" id="root">
<g fill-rule="evenodd" id="g">
<path id="path" d="M7 7H4v2h3v3h2V9h3V7H9V4H7v3zm1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/>
</g>
</svg>
`,
};
6 changes: 6 additions & 0 deletions packages/core/src/assets/images/example-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion packages/core/src/components/cv-button/cv-button-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ http://www.carbondesignsystem.com/components/button/code
- kind: 'primary' (default), 'secondary', ghost, or 'danger'. Optional.
- small: (deprecated prefer size) If true smaller version of button.
- size: optional value 'field' or 'small'
- icon: is optional. It takes an Vue Component expected to be an icon or a string path to an SVG icon.
- icon: is optional. It takes an Vue Component expected to be a an icon that follows the pattern used for fill/color/stroke in Carbon Icons. It can be in the form of: a component (e.g. @carbon/icons-vue), an SVG symbol path, an SVG path, raw SVG.
- icon-href: deprecated in favour of icon attribute. Expects tring path to SVG icon..

NOTE: Recommend using icons as components or SVG symbols.
NOTE 2: Using an SVG path without an element ID will not style correctly. This is due to SVG1.1 not supporting 'use' tag without ID being specified, as a result an img tag is used.

# cv-icon-button

Rather than add extra properties that do not work with the standard button, an icon only button has been created. If in future the standard button supports the tooltip then these properties will be merged back into CvButton.
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/components/cv-button/cv-button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@
<button class="cv-button" :class="buttonClasses" v-on="inputListeners" role="button">
<slot></slot>

<component v-if="typeof icon === 'object'" :is="icon" class="bx--temp-fix" :class="`${carbonPrefix}--btn__icon`" />
<svg v-if="typeof icon === 'string' || iconHref" :class="`${carbonPrefix}--btn__icon`">
<use :href="icon || iconHref" />
</svg>
<CvSvg v-if="icon || iconHref" :svg="icon || iconHref" :class="`${carbonPrefix}--btn__icon`" />
</button>
</template>

<script>
import buttonMixin from './button-mixin';
import carbonPrefixMixin from '../../mixins/carbon-prefix-mixin';
import CvSvg from '../cv-svg/_cv-svg';
export default {
name: 'CvButton',
mixins: [buttonMixin, carbonPrefixMixin],
components: { CvSvg },
computed: {
buttonClasses() {
return this.buttonClassOpts();
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/components/cv-button/cv-icon-button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@
<button class="cv-button" :class="buttonClasses" v-on="inputListeners" type="button">
<span :class="`${carbonPrefix}--assistive-text`">{{ label }}</span>

<component v-if="typeof icon === 'object'" :is="icon" class="bx--temp-fix" :class="`${carbonPrefix}--btn__icon`" />
<svg v-if="typeof icon === 'string' || iconHref" :class="`${carbonPrefix}--btn__icon`">
<use :href="icon || iconHref" />
</svg>
<CvSvg v-if="icon || iconHref" :svg="icon || iconHref" :class="`${carbonPrefix}--btn__icon`" />
</button>
</template>

<script>
import buttonMixin from './button-mixin';
import carbonPrefixMixin from '../../mixins/carbon-prefix-mixin';
import CvSvg from '../cv-svg/_cv-svg';
export default {
name: 'CvIconButton',
mixins: [buttonMixin, carbonPrefixMixin],
components: { CvSvg },
props: {
label: { type: String, default: undefined },
tipPosition: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@
:aria-selected="`${dataSelected}`"
@click="open"
>
<component v-if="typeof icon === 'object'" :is="icon" class="bx--content-switcher__icon" />
<svg v-if="typeof icon === 'string'" class="bx--content-switcher__icon" height="16" width="16">
<use :href="icon" />
</svg>
<CvSvg v-if="icon" :svg="icon" class="bx--content-switcher__icon" height="16" width="16" />
<span class="bx--content-switcher__label">
<slot></slot>
</span>
Expand All @@ -37,10 +34,12 @@

<script>
import uidMixin from '../../mixins/uid-mixin';
import CvSvg from '../cv-svg/_cv-svg';

export default {
name: 'CvContentSwitcherButton',
mixins: [uidMixin],
components: { CvSvg },
props: {
contentSelector: { type: String, default: undefined },
icon: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ http://www.carbondesignsystem.com/components/content-switcher/code

- ownerId : Used with CvContentSwitcherPanel
- content-selector : DOM CSS selector used to manipulate the DOM directly.
- icon: is optional. It takes an Vue Component expected to be an icon or a string path to an SVG icon.
- icon: is optional. It takes an Vue Component expected to be a an icon that follows the pattern used for fill/color/stroke in Carbon Icons. It can be in the form of: a component (e.g. @carbon/icons-vue), an SVG symbol path, an SVG path, raw SVG.

NOTE: Recommend using icons as components or SVG symbols.
NOTE 2: Using an SVG path without an element ID will not style correctly. This is due to SVG1.1 not supporting 'use' tag without ID being specified, as a result an img tag is used.

NOTE: Prefer ownerId

Expand Down
41 changes: 41 additions & 0 deletions packages/core/src/components/cv-svg/_cv-svg.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script>
import CvWrapper from '../cv-wrapper/_cv-wrapper';
export default {
name: 'CvSvg',
components: { CvWrapper },
props: {
svg: {
type: [String, Object],
default: undefined,
validator(val) {
if (!val || typeof val === 'string') {
return true;
}
return val.render !== null;
},
},
},
computed: {
isSvg() {
return this.svg !== undefined && this.svg.indexOf('<svg') >= 0;
},
isSymbol() {
return this.svg !== undefined && !this.isSvg && this.svg.indexOf('#') >= 0;
},
},
render(createElement) {
if (typeof this.svg === 'object') {
return createElement('component', { is: this.svg });
} else if (this.isSvg) {
return createElement('svg', { domProps: { innerHTML: this.svg } });
} else {
if (this.isSymbol) {
return createElement('svg', {}, [createElement('use', { attrs: { href: this.svg } })]);
} else {
return createElement('img', { attrs: { src: this.svg } });
}
}
},
};
</script>
41 changes: 38 additions & 3 deletions storybook/stories/cv-button-story.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import { CvButton, CvIconButton, CvButtonSkeleton, CvButtonSet } from '../../pac

const storiesDefault = storiesOf('Components/CvButton', module);
// const storiesExperimental = storiesOf('Experimental/CvButton', module);
const exampleIconPath = require('../../packages/core/src/assets/images/example-icons.svg');
import exampleIconSvg from '../../packages/core/src/assets/images/example-icon-svg.js';
const exampleIconSvgString = exampleIconSvg.exampleSvgString;
import exampleIconPathSymbol from '../../packages/core/src/assets/images/example-icons.svg';
import exampleIconPathSvg from '../../packages/core/src/assets/images/example-icon.svg';
import AddFilled16 from '@carbon/icons-vue/es/add--filled/16';

let preKnobs = {
Expand Down Expand Up @@ -120,12 +123,32 @@ let variants = [
excludes: ['iconAlways'],
},
{
name: 'icon as path',
name: 'icon as SVG path',
excludes: ['size', 'disabled', 'icon', 'iconHref', 'iconAlways'],
extra: {
icon: {
group: 'attr',
value: `icon="${exampleIconPath}#icon--add--solid"`,
value: `icon="${exampleIconPathSvg}"`,
},
},
},
{
name: 'icon as SVG symbol path',
excludes: ['size', 'disabled', 'icon', 'iconHref', 'iconAlways'],
extra: {
icon: {
group: 'attr',
value: `icon="${exampleIconPathSymbol}#icon--add--solid"`,
},
},
},
{
name: 'icon as SVG',
excludes: ['size', 'disabled', 'icon', 'iconHref', 'iconAlways'],
extra: {
icon: {
group: 'attr',
value: `:icon="exampleIconSvgString"`,
},
},
},
Expand Down Expand Up @@ -157,6 +180,12 @@ for (const story of storySet) {
sv-margin
sv-source='${templateString.trim()}'>
<template slot="component">${templateString}</template>
<template slot="other">
<p>NOTE: Until SVG2 using a non-symbol SVG path with use does not work. Using img tags has styling issues.</p>
<br />
<p>Svg String</p>
<p v-if="exampleIconSvgString" v-text="exampleIconSvgString" />
</template>
</sv-template-view>
`;

Expand All @@ -168,6 +197,9 @@ for (const story of storySet) {
},
template: templateViewString,
props: settings.props,
data() {
return { exampleIconSvgString: story.name === 'icon as SVG' ? exampleIconSvgString : '' };
},
};
},
{
Expand Down Expand Up @@ -203,6 +235,9 @@ for (const story of storySet) {
sv-margin
sv-source='${templateString.trim()}'>
<template slot="component">${templateString}</template>
<template slot="other">
<p>NOTE: Until SVG2 using a non-symbol SVG path with use does not work. Using img tags has styling issues.</p>
</template>
</sv-template-view>
`;

Expand Down

0 comments on commit 2fc40e4

Please sign in to comment.