From 317e19ea2ae9b256638e1d21d398d9b9681d380b Mon Sep 17 00:00:00 2001 From: Danial Date: Thu, 2 Aug 2018 09:07:23 +1200 Subject: [PATCH] Add support for simple-icons, colored icons with ?logoColor (#1810) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add simple-icons * handle undefined * support logoColor param * our icon > simple-icon * dont crash :white_check_mark: * return false → undefined * update test * add test * support logoColor on our logos * cache as base64, pre-load-simple-icons, logo-helper * add ?logoColor information * and simple-icons reference, link to github master branch for our logos * update simple-icons update to 1.7.1 * Revert "and simple-icons reference, link to github master branch for our logos" This reverts commit 5e99d5f8db360ed49ce08851d3ba84e35682b7c5. * add link to simple-icons * Add snapshot test * support dash in place of space for logo name --- __snapshots__/make-badge.spec.js | 32 +++++++++++++++++++++++++++ frontend/components/usage.js | 8 ++++++- lib/badge-data.js | 38 +++++++++++++++++++++++++------- lib/badge-data.spec.js | 21 +++++++++--------- lib/load-logos.js | 10 ++++++--- lib/load-simple-icons.js | 18 +++++++++++++++ lib/logo-helper.js | 18 +++++++++++++++ lib/logo-helper.spec.js | 27 +++++++++++++++++++++++ lib/make-badge.spec.js | 21 ++++++++++++++++++ lib/request-handler.js | 1 + package-lock.json | 16 +++++++++++--- package.json | 1 + 12 files changed, 186 insertions(+), 25 deletions(-) create mode 100644 lib/load-simple-icons.js create mode 100644 lib/logo-helper.js create mode 100644 lib/logo-helper.spec.js diff --git a/__snapshots__/make-badge.spec.js b/__snapshots__/make-badge.spec.js index c1c2ac2404103..ebe7fecfcc33c 100644 --- a/__snapshots__/make-badge.spec.js +++ b/__snapshots__/make-badge.spec.js @@ -8,3 +8,35 @@ exports['The badge generator JSON should always produce the same JSON (unless we "value": "grown" } ` + +exports['shields GitHub logo default color (#333333) 1'] = ` + labellabelmessagemessage +` + +exports['shields GitHub logo custom color (whitesmoke) 1'] = ` + labellabelmessagemessage +` + +exports['simple-icons javascript logo default color (#F7DF1E) 1'] = ` + labellabelmessagemessage +` + +exports['simple-icons javascript logo custom color (rgba(46,204,113,0.8)) 1'] = ` + labellabelmessagemessage +` + +exports['The badge generator badges with logos should always produce the same badge shields GitHub logo default color (#333333) 1'] = ` + labellabelmessagemessage +` + +exports['The badge generator badges with logos should always produce the same badge shields GitHub logo custom color (whitesmoke) 1'] = ` + labellabelmessagemessage +` + +exports['The badge generator badges with logos should always produce the same badge simple-icons javascript logo default color (#F7DF1E) 1'] = ` + labellabelmessagemessage +` + +exports['The badge generator badges with logos should always produce the same badge simple-icons javascript logo custom color (rgba(46,204,113,0.8)) 1'] = ` + labellabelmessagemessage +` diff --git a/frontend/components/usage.js b/frontend/components/usage.js index 448fad5755691..2f615b16c4ddc 100644 --- a/frontend/components/usage.js +++ b/frontend/components/usage.js @@ -169,7 +169,7 @@ export default class Usage extends React.PureComponent { ?logo=appveyor - Insert one of the named logos ({this.constructor.renderNamedLogos()}) + Insert one of the named logos from ({this.constructor.renderNamedLogos()}) or simple-icons @@ -178,6 +178,12 @@ export default class Usage extends React.PureComponent { Insert custom logo image (≥ 14px high) + + + ?logoColor=violet + + Set the color of the logo (hex, rgb, rgba, hsl, hsla and css named colors supported) + ?logoWidth=40 diff --git a/lib/badge-data.js b/lib/badge-data.js index 8c72bbb393244..8ac9f7db97b8d 100644 --- a/lib/badge-data.js +++ b/lib/badge-data.js @@ -2,6 +2,11 @@ const isCSSColor = require('is-css-color'); const logos = require('./load-logos')(); +const simpleIcons = require('./load-simple-icons')(); +const { + svg2base64, + isDataUri, +} = require('./logo-helper'); const colorschemes = require('./colorscheme.json'); function toArray(val) { @@ -14,10 +19,6 @@ function toArray(val) { } } -function isDataUri(s) { - return s !== undefined && /^(data:)([^;]+);([^,]+),(.+)$/.test(s); -} - function prependPrefix(s, prefix) { if (s === undefined) { return undefined; @@ -39,8 +40,12 @@ function isHexColor (s = ''){ function makeColor(color) { if (isHexColor(color)) { return '#' + color; - } else { + } else if (colorschemes[color] !== undefined){ + return colorschemes[color].colorB; + } else if (isCSSColor(color)){ return color; + } else { + return undefined; } } @@ -69,9 +74,27 @@ function makeLabel(defaultLabel, overrides) { return '' + (overrides.label === undefined ? defaultLabel || '' : overrides.label); } +function getShieldsIcon(icon = '', color = ''){ + icon = typeof icon === 'string' ? icon.toLowerCase() : ''; + if (!logos[icon]){ + return undefined; + } + color = makeColor(color); + return color ? logos[icon].svg.replace(/fill="(.+?)"/g, `fill="${color}"`) : logos[icon].base64; +} + +function getSimpleIcon(icon = '', color = null){ + icon = typeof icon === 'string' ? icon.toLowerCase().replace(/ /g, '-') : ''; + if (!simpleIcons[icon]){ + return undefined; + } + color = makeColor(color); + return color ? simpleIcons[icon].svg.replace(' { - given('data:image/svg+xml;base64,PHN2ZyB4bWxu').expect(true); - forCases([ - given('data:foobar'), - given('foobar'), - ]).expect(false); - }); - test(isHexColor, () => { forCases([ given('f00bae'), @@ -80,11 +72,20 @@ describe('Badge data helpers', function() { logoPosition: 10, logoWidth: 25, links: ['https://example.com/'], - colorA: 'blue', + colorA: '#007ec6', colorB: '#f00bae', }); }); + test(makeColor, () => { + given('red').expect('#e05d44'); + given('blue').expect('#007ec6'); + given('4c1').expect('#4c1'); + given('f00f00').expect('#f00f00'); + given('papayawhip').expect('papayawhip'); + given('purple').expect('purple'); + }); + test(setBadgeColor, () => { given({}, 'red').expect({ colorscheme: 'red' }); given({}, 'f00f00').expect({ colorB: '#f00f00' }); diff --git a/lib/load-logos.js b/lib/load-logos.js index b41f7fd3a5061..e7503f5c0112e 100644 --- a/lib/load-logos.js +++ b/lib/load-logos.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); +const { svg2base64 } = require('./logo-helper'); function loadLogos () { // Cache svg logos from disk in base64 string @@ -12,11 +13,14 @@ function loadLogos () { if (filename[0] === '.') { return; } // filename is eg, github.svg const svg = fs.readFileSync(logoDir + '/' + filename).toString(); + const base64 = svg2base64(svg); // eg, github - const name = filename.slice(0, -('.svg'.length)); - logos[name] = 'data:image/svg+xml;base64,' + - Buffer.from(svg).toString('base64'); + const name = filename.slice(0, -('.svg'.length)).toLowerCase(); + logos[name] = { + svg, + base64 + }; }); return logos; } diff --git a/lib/load-simple-icons.js b/lib/load-simple-icons.js new file mode 100644 index 0000000000000..278f381c19e7d --- /dev/null +++ b/lib/load-simple-icons.js @@ -0,0 +1,18 @@ +'use strict'; + +const simpleIcons = require('simple-icons'); +const { svg2base64 } = require('./logo-helper'); + +function loadSimpleIcons(){ + Object.keys(simpleIcons).forEach(function (key) { + const k = key.toLowerCase().replace(/ /g, '-'); + if (k !== key) { + simpleIcons[k] = simpleIcons[key]; + delete simpleIcons[key]; + } + simpleIcons[k].base64 = svg2base64(simpleIcons[k].svg.replace(' { + given('data:image/svg+xml;base64,PHN2ZyB4bWxu').expect('data:image/svg+xml;base64,PHN2ZyB4bWxu'); + given('').expect('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciLz4='); + given(undefined).expect(undefined); + }); + + test(isDataUri, () => { + given('data:image/svg+xml;base64,PHN2ZyB4bWxu').expect(true); + forCases([ + given('data:foobar'), + given('foobar'), + ]).expect(false); + }); +}); diff --git a/lib/make-badge.spec.js b/lib/make-badge.spec.js index cd733776b6d25..898e2eebff4dd 100644 --- a/lib/make-badge.spec.js +++ b/lib/make-badge.spec.js @@ -127,4 +127,25 @@ describe('The badge generator', () => { expect(svg).to.include('""').and.to.include('some-value'); }); }); + describe('badges with logos should always produce the same badge', () => { + it('shields GitHub logo default color (#333333)', () => { + const svg = makeBadge({ text: ['label', 'message'], format: 'svg', logo: 'github' }); + snapshot(svg); + }); + + it('shields GitHub logo custom color (whitesmoke)', () => { + const svg = makeBadge({ text: ['label', 'message'], format: 'svg', logo: 'github', logoColor: 'whitesmoke' }); + snapshot(svg); + }); + + it('simple-icons javascript logo default color (#F7DF1E)', () => { + const svg = makeBadge({ text: ['label', 'message'], format: 'svg', logo: 'javascript' }); + snapshot(svg); + }); + + it('simple-icons javascript logo custom color (rgba(46,204,113,0.8))', () => { + const svg = makeBadge({ text: ['label', 'message'], format: 'svg', logo: 'javascript', logoColor: 'rgba(46,204,113,0.8)' }); + snapshot(svg); + }); + }); }); diff --git a/lib/request-handler.js b/lib/request-handler.js index 998d80e5694b2..8918666f73258 100644 --- a/lib/request-handler.js +++ b/lib/request-handler.js @@ -40,6 +40,7 @@ const globalQueryParams = new Set([ 'style', 'link', 'logo', + 'logoColor', 'logoPosition', 'logoWidth', 'link', diff --git a/package-lock.json b/package-lock.json index 8ae2da771c864..478f1159afa16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5369,6 +5369,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, + "optional": true, "requires": { "delayed-stream": "~1.0.0" } @@ -5440,7 +5441,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "dev": true, + "optional": true }, "delegates": { "version": "1.0.0", @@ -5765,13 +5767,15 @@ "version": "1.27.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", - "dev": true + "dev": true, + "optional": true }, "mime-types": { "version": "2.1.15", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", "dev": true, + "optional": true, "requires": { "mime-db": "~1.27.0" } @@ -5855,7 +5859,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "optional": true }, "oauth-sign": { "version": "0.8.2", @@ -13130,6 +13135,11 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-icons": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-1.7.1.tgz", + "integrity": "sha1-xoVlvjKsRsq4N7IZrnGuu6T9JVM=" + }, "sinon": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-6.0.0.tgz", diff --git a/package.json b/package.json index 27d8a09d5f16e..7c2054a079639 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "redis": "~2.6.2", "request": "~2.87.0", "semver": "~5.5.0", + "simple-icons": "^1.7.1", "svgo": "~1.0.5", "xml2js": "~0.4.16", "xmldom": "~0.1.27",