From 9e4a8cec09b1dd33d91d8ae19710fc44c787814d Mon Sep 17 00:00:00 2001
From: Paul Melnikow <github@paulmelnikow.com>
Date: Thu, 21 Feb 2019 21:07:27 -0500
Subject: [PATCH] Consolidate badge URL generation functions (#3032)

This consolidates all the badge URL generation functions into a single place, and updates the rest of the calling code to use it. It renames some things and updates labels to bring the names into alignment with the current API.

Resolve #2027
---
 core/badge-urls/make-badge-url.js            | 34 ++++++++++
 core/badge-urls/make-badge-url.spec.js       | 47 ++++++++++++++
 frontend/components/customizer/customizer.js | 11 ++--
 frontend/components/development/logo-page.js | 18 ++++--
 frontend/components/dynamic-badge-maker.js   | 31 ++++++---
 frontend/components/footer.js                | 55 ++++++++++------
 frontend/components/static-badge-maker.js    | 32 +++++-----
 frontend/components/suggestion-and-search.js |  3 +-
 frontend/components/usage.js                 | 12 ++--
 frontend/lib/badge-url.js                    | 53 ----------------
 frontend/lib/badge-url.spec.js               | 67 --------------------
 frontend/lib/resolve-url.js                  | 17 -----
 frontend/lib/resolve-url.spec.js             | 29 ---------
 frontend/pages/endpoint.js                   |  9 ++-
 14 files changed, 187 insertions(+), 231 deletions(-)
 delete mode 100644 frontend/lib/badge-url.js
 delete mode 100644 frontend/lib/badge-url.spec.js
 delete mode 100644 frontend/lib/resolve-url.js
 delete mode 100644 frontend/lib/resolve-url.spec.js

diff --git a/core/badge-urls/make-badge-url.js b/core/badge-urls/make-badge-url.js
index 6646e95ab9..3fd23042e6 100644
--- a/core/badge-urls/make-badge-url.js
+++ b/core/badge-urls/make-badge-url.js
@@ -71,9 +71,43 @@ function staticBadgeUrl({
   return `${baseUrl}/badge/${path}.${format}${suffix}`
 }
 
+function dynamicBadgeUrl({
+  baseUrl,
+  datatype,
+  label,
+  dataUrl,
+  query,
+  prefix,
+  suffix,
+  color,
+  style,
+  format = 'svg',
+}) {
+  const queryParams = {
+    label,
+    url: dataUrl,
+    query,
+    style,
+  }
+
+  if (color) {
+    queryParams.color = color
+  }
+  if (prefix) {
+    queryParams.prefix = prefix
+  }
+  if (suffix) {
+    queryParams.suffix = suffix
+  }
+
+  const outQueryString = queryString.stringify(queryParams)
+  return `${baseUrl}/badge/dynamic/${datatype}.${format}?${outQueryString}`
+}
+
 module.exports = {
   badgeUrlFromPath,
   badgeUrlFromPattern,
   encodeField,
   staticBadgeUrl,
+  dynamicBadgeUrl,
 }
diff --git a/core/badge-urls/make-badge-url.spec.js b/core/badge-urls/make-badge-url.spec.js
index 491035a59a..3ca61b9fcc 100644
--- a/core/badge-urls/make-badge-url.spec.js
+++ b/core/badge-urls/make-badge-url.spec.js
@@ -6,6 +6,7 @@ const {
   badgeUrlFromPattern,
   encodeField,
   staticBadgeUrl,
+  dynamicBadgeUrl,
 } = require('./make-badge-url')
 
 describe('Badge URL generation functions', function() {
@@ -79,4 +80,50 @@ describe('Badge URL generation functions', function() {
       color: 'blue',
     }).expect('/badge/-blue-blue.svg')
   })
+
+  test(dynamicBadgeUrl, () => {
+    const dataUrl = 'http://example.com/foo.json'
+    const query = '$.bar'
+    const prefix = 'value: '
+    given({
+      baseUrl: 'http://img.example.com',
+      datatype: 'json',
+      label: 'foo',
+      dataUrl,
+      query,
+      prefix,
+      style: 'plastic',
+    }).expect(
+      [
+        'http://img.example.com/badge/dynamic/json.svg',
+        '?label=foo',
+        `&prefix=${encodeURIComponent(prefix)}`,
+        `&query=${encodeURIComponent(query)}`,
+        '&style=plastic',
+        `&url=${encodeURIComponent(dataUrl)}`,
+      ].join('')
+    )
+    const suffix = '<- value'
+    const color = 'blue'
+    given({
+      baseUrl: 'http://img.example.com',
+      datatype: 'json',
+      label: 'foo',
+      dataUrl,
+      query,
+      suffix,
+      color,
+      style: 'plastic',
+    }).expect(
+      [
+        'http://img.example.com/badge/dynamic/json.svg',
+        '?color=blue',
+        '&label=foo',
+        `&query=${encodeURIComponent(query)}`,
+        '&style=plastic',
+        `&suffix=${encodeURIComponent(suffix)}`,
+        `&url=${encodeURIComponent(dataUrl)}`,
+      ].join('')
+    )
+  })
 })
diff --git a/frontend/components/customizer/customizer.js b/frontend/components/customizer/customizer.js
index 0e1d12debf..b3e35540f6 100644
--- a/frontend/components/customizer/customizer.js
+++ b/frontend/components/customizer/customizer.js
@@ -1,7 +1,7 @@
 import React from 'react'
 import PropTypes from 'prop-types'
 import clipboardCopy from 'clipboard-copy'
-import { staticBadgeUrl } from '../../lib/badge-url'
+import { staticBadgeUrl } from '../../../core/badge-urls/make-badge-url'
 import { generateMarkup } from '../../lib/generate-image-markup'
 import { Badge } from '../common'
 import PathBuilder from './path-builder'
@@ -61,12 +61,11 @@ export default class Customizer extends React.Component {
     if (pathIsComplete) {
       src = this.generateBuiltBadgeUrl()
     } else {
-      src = staticBadgeUrl(
+      src = staticBadgeUrl({
         baseUrl,
-        'preview',
-        'some parameters missing',
-        'lightgray'
-      )
+        label: 'preview',
+        message: 'some parameters missing',
+      })
     }
     return (
       <p>
diff --git a/frontend/components/development/logo-page.js b/frontend/components/development/logo-page.js
index cf9577a89c..0fecbe4bd6 100644
--- a/frontend/components/development/logo-page.js
+++ b/frontend/components/development/logo-page.js
@@ -1,7 +1,7 @@
 import React from 'react'
 import PropTypes from 'prop-types'
 import styled from 'styled-components'
-import { staticBadgeUrl } from '../../lib/badge-url'
+import { staticBadgeUrl } from '../../../core/badge-urls/make-badge-url'
 import { baseUrl } from '../../constants'
 import Meta from '../meta'
 import Header from '../header'
@@ -33,16 +33,24 @@ const NamedLogoTable = ({ logoNames }) => (
           <td>
             <Badge
               alt={`logo: ${name}`}
-              src={staticBadgeUrl(baseUrl, 'named logo', name, 'blue', {
-                logo: name,
+              src={staticBadgeUrl({
+                baseUrl,
+                label: 'named logo',
+                message: name,
+                color: 'blue',
+                namedLogo: name,
               })}
             />
           </td>
           <td>
             <Badge
               alt={`logo: ${name}`}
-              src={staticBadgeUrl(baseUrl, 'Named Logo', name, 'blue', {
-                logo: name,
+              src={staticBadgeUrl({
+                baseUrl,
+                label: 'Named Logo',
+                message: name,
+                color: 'blue',
+                namedLogo: name,
                 style: 'social',
               })}
             />
diff --git a/frontend/components/dynamic-badge-maker.js b/frontend/components/dynamic-badge-maker.js
index 437dd9a5b1..0eefa166c1 100644
--- a/frontend/components/dynamic-badge-maker.js
+++ b/frontend/components/dynamic-badge-maker.js
@@ -1,6 +1,6 @@
 import React from 'react'
 import PropTypes from 'prop-types'
-import { dynamicBadgeUrl } from '../lib/badge-url'
+import { dynamicBadgeUrl } from '../../core/badge-urls/make-badge-url'
 import { InlineInput } from './common'
 
 export default class DynamicBadgeMaker extends React.Component {
@@ -11,7 +11,7 @@ export default class DynamicBadgeMaker extends React.Component {
   state = {
     datatype: '',
     label: '',
-    url: '',
+    dataUrl: '',
     query: '',
     color: '',
     prefix: '',
@@ -19,9 +19,22 @@ export default class DynamicBadgeMaker extends React.Component {
   }
 
   makeBadgeUrl() {
-    const { datatype, label, url, query, color, prefix, suffix } = this.state
+    const {
+      datatype,
+      label,
+      dataUrl,
+      query,
+      color,
+      prefix,
+      suffix,
+    } = this.state
     const { baseUrl: baseUrl = document.location.href } = this.props
-    return dynamicBadgeUrl(baseUrl, datatype, label, url, query, {
+    return dynamicBadgeUrl({
+      baseUrl: baseUrl || window.location.href,
+      datatype,
+      label,
+      dataUrl,
+      query,
       color,
       prefix,
       suffix,
@@ -34,8 +47,8 @@ export default class DynamicBadgeMaker extends React.Component {
   }
 
   get isValid() {
-    const { datatype, label, url, query } = this.state
-    return datatype && label && url && query
+    const { datatype, label, dataUrl, query } = this.state
+    return datatype && label && dataUrl && query
   }
 
   render() {
@@ -61,9 +74,9 @@ export default class DynamicBadgeMaker extends React.Component {
         />
         <InlineInput
           className="short"
-          onChange={event => this.setState({ url: event.target.value })}
-          placeholder="url"
-          value={this.state.url}
+          onChange={event => this.setState({ dataUrl: event.target.value })}
+          placeholder="data url"
+          value={this.state.dataUrl}
         />
         <InlineInput
           className="short"
diff --git a/frontend/components/footer.js b/frontend/components/footer.js
index 5a69f5b1f8..a95d870ae5 100644
--- a/frontend/components/footer.js
+++ b/frontend/components/footer.js
@@ -1,7 +1,7 @@
 import React from 'react'
 import PropTypes from 'prop-types'
 import styled from 'styled-components'
-import resolveUrl from '../lib/resolve-url'
+import { badgeUrlFromPath } from '../../core/badge-urls/make-badge-url'
 import { H2 } from './common'
 
 const SpacedA = styled.a`
@@ -16,42 +16,55 @@ const Footer = ({ baseUrl }) => (
     <p>
       <object
         alt="Follow @shields_io"
-        data={resolveUrl(
-          '/twitter/follow/shields_io.svg?style=social&label=Follow',
-          baseUrl
-        )}
+        data={badgeUrlFromPath({
+          baseUrl,
+          path: '/twitter/follow/shields_io',
+          queryParams: { label: 'Follow' },
+          style: 'social',
+        })}
       />{' '}
       {}
       <object
         alt="Donate to us!"
-        data={resolveUrl(
-          '/opencollective/backers/shields.svg?style=social&link=https://opencollective.com/shields',
-          baseUrl
-        )}
+        data={badgeUrlFromPath({
+          baseUrl,
+          path: '/opencollective/backers/shields',
+          queryParams: { link: 'https://opencollective.com/shields' },
+          style: 'social',
+        })}
       />{' '}
       {}
       <object
         alt="Donate to us!"
-        data={resolveUrl(
-          '/opencollective/sponsors/shields.svg?style=social&link=https://opencollective.com/shields',
-          baseUrl
-        )}
+        data={badgeUrlFromPath({
+          baseUrl,
+          path: '/opencollective/sponsors/shields',
+          queryParams: { link: 'https://opencollective.com/shields' },
+          style: 'social',
+        })}
       />{' '}
       {}
       <object
         alt="Fork on GitHub"
-        data={resolveUrl(
-          '/github/forks/badges/shields.svg?style=social&label=Fork',
-          baseUrl
-        )}
+        data={badgeUrlFromPath({
+          baseUrl,
+          path: '/github/forks/badges/shields',
+          queryParams: { label: 'Fork' },
+          style: 'social',
+        })}
       />{' '}
       {}
       <object
         alt="chat on Discord"
-        data={resolveUrl(
-          '/discord/308323056592486420.svg?style=social&label=Chat&link=https://discord.gg/HjJCwm5',
-          baseUrl
-        )}
+        data={badgeUrlFromPath({
+          baseUrl,
+          path: '/discord/308323056592486420',
+          queryParams: {
+            label: 'Chat',
+            link: 'link=https://discord.gg/HjJCwm5',
+          },
+          style: 'social',
+        })}
       />
     </p>
 
diff --git a/frontend/components/static-badge-maker.js b/frontend/components/static-badge-maker.js
index 10db22f78d..8a9b8003a0 100644
--- a/frontend/components/static-badge-maker.js
+++ b/frontend/components/static-badge-maker.js
@@ -1,6 +1,6 @@
 import React from 'react'
 import PropTypes from 'prop-types'
-import { staticBadgeUrl } from '../lib/badge-url'
+import { staticBadgeUrl } from '../../core/badge-urls/make-badge-url'
 import { InlineInput } from './common'
 
 export default class StaticBadgeMaker extends React.Component {
@@ -9,8 +9,8 @@ export default class StaticBadgeMaker extends React.Component {
   }
 
   state = {
-    subject: '',
-    status: '',
+    label: '',
+    message: '',
     color: '',
   }
 
@@ -18,13 +18,13 @@ export default class StaticBadgeMaker extends React.Component {
     e.preventDefault()
 
     const { baseUrl } = this.props
-    const { subject, status, color } = this.state
-    const badgeUrl = staticBadgeUrl(
-      baseUrl || window.location.href,
-      subject,
-      status,
-      color
-    )
+    const { label, message, color } = this.state
+    const badgeUrl = staticBadgeUrl({
+      baseUrl: baseUrl || window.location.href,
+      label,
+      message,
+      color,
+    })
 
     document.location = badgeUrl
   }
@@ -33,14 +33,14 @@ export default class StaticBadgeMaker extends React.Component {
     return (
       <form onSubmit={e => this.handleSubmit(e)}>
         <InlineInput
-          onChange={event => this.setState({ subject: event.target.value })}
-          placeholder="subject"
-          value={this.state.subject}
+          onChange={event => this.setState({ label: event.target.value })}
+          placeholder="label"
+          value={this.state.label}
         />
         <InlineInput
-          onChange={event => this.setState({ status: event.target.value })}
-          placeholder="status"
-          value={this.state.status}
+          onChange={event => this.setState({ message: event.target.value })}
+          placeholder="message"
+          value={this.state.message}
         />
         <InlineInput
           list="default-colors"
diff --git a/frontend/components/suggestion-and-search.js b/frontend/components/suggestion-and-search.js
index 82a631f79d..dc98159c38 100644
--- a/frontend/components/suggestion-and-search.js
+++ b/frontend/components/suggestion-and-search.js
@@ -2,7 +2,6 @@ import React from 'react'
 import PropTypes from 'prop-types'
 import fetchPonyfill from 'fetch-ponyfill'
 import debounce from 'lodash.debounce'
-import resolveUrl from '../lib/resolve-url'
 import BadgeExamples from './badge-examples'
 import { BlockInput } from './common'
 
@@ -42,7 +41,7 @@ export default class SuggestionAndSearch extends React.Component {
       const { baseUrl } = this.props
       const { projectUrl } = this.state
 
-      const url = resolveUrl('/$suggest/v1', baseUrl, { url: projectUrl })
+      const url = `${baseUrl}/$suggest/v1?url=${encodeURIComponent(projectUrl)}`
 
       const fetch = window.fetch || fetchPonyfill
       const res = await fetch(url)
diff --git a/frontend/components/usage.js b/frontend/components/usage.js
index 4c54ea1d17..f323bfa237 100644
--- a/frontend/components/usage.js
+++ b/frontend/components/usage.js
@@ -2,7 +2,7 @@ import React from 'react'
 import { Link } from 'gatsby'
 import PropTypes from 'prop-types'
 import styled from 'styled-components'
-import { staticBadgeUrl } from '../lib/badge-url'
+import { staticBadgeUrl } from '../../core/badge-urls/make-badge-url'
 import { advertisedStyles, shieldsLogos } from '../../supported-features.json'
 import StaticBadgeMaker from './static-badge-maker'
 import DynamicBadgeMaker from './dynamic-badge-maker'
@@ -69,7 +69,7 @@ const ColorExamples = ({ baseUrl, colors }) => (
       <Badge
         alt={color}
         key={color}
-        src={staticBadgeUrl(baseUrl, '', color, color)}
+        src={staticBadgeUrl({ baseUrl, label: '', message: color, color })}
       />
     ))}
   </span>
@@ -91,8 +91,12 @@ export default class Usage extends React.PureComponent {
         <tbody>
           {advertisedStyles.map(style => {
             const snippet = `?style=${style}&logo=appveyor`
-            const badgeUrl = staticBadgeUrl(baseUrl, 'style', style, 'green', {
-              logo: 'appveyor',
+            const badgeUrl = staticBadgeUrl({
+              baseUrl,
+              label: 'style',
+              message: style,
+              color: 'green',
+              namedLogo: 'appveyor',
               style,
             })
             return (
diff --git a/frontend/lib/badge-url.js b/frontend/lib/badge-url.js
deleted file mode 100644
index 5ff3aca4c7..0000000000
--- a/frontend/lib/badge-url.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import resolveUrl from './resolve-url'
-import { staticBadgeUrl as makeStaticBadgeUrl } from '../../core/badge-urls/make-badge-url'
-
-export default function resolveBadgeUrl(
-  url,
-  baseUrl,
-  { longCache, style, queryParams: inQueryParams, format = 'svg' } = {}
-) {
-  const outQueryParams = Object.assign({}, inQueryParams)
-  if (longCache) {
-    outQueryParams.maxAge = '2592000'
-  }
-  if (style) {
-    outQueryParams.style = style
-  }
-
-  return resolveUrl(`${url}.${format}`, baseUrl, outQueryParams)
-}
-
-export function staticBadgeUrl(baseUrl, label, message, color, options) {
-  const path = makeStaticBadgeUrl({ label, message, color })
-  return resolveUrl(path, baseUrl, options)
-}
-
-// Options can include: { prefix, suffix, color, longCache, style, queryParams }
-export function dynamicBadgeUrl(
-  baseUrl,
-  datatype,
-  label,
-  dataUrl,
-  query,
-  { prefix, suffix, color, queryParams = {}, ...rest } = {}
-) {
-  Object.assign(queryParams, {
-    label,
-    url: dataUrl,
-    query,
-  })
-
-  if (color) {
-    queryParams.color = color
-  }
-  if (prefix) {
-    queryParams.prefix = prefix
-  }
-  if (suffix) {
-    queryParams.suffix = suffix
-  }
-
-  const outOptions = Object.assign({ queryParams }, rest)
-
-  return resolveBadgeUrl(`/badge/dynamic/${datatype}`, baseUrl, outOptions)
-}
diff --git a/frontend/lib/badge-url.spec.js b/frontend/lib/badge-url.spec.js
deleted file mode 100644
index 4817e4cb13..0000000000
--- a/frontend/lib/badge-url.spec.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import { test, given } from 'sazerac'
-import resolveBadgeUrl, { staticBadgeUrl, dynamicBadgeUrl } from './badge-url'
-
-const resolveBadgeUrlWithLongCache = (url, baseUrl) =>
-  resolveBadgeUrl(url, baseUrl, { longCache: true })
-
-describe('Badge URL functions', function() {
-  test(resolveBadgeUrl, () => {
-    given('/badge/foo-bar-blue', undefined).expect('/badge/foo-bar-blue.svg')
-    given('/badge/foo-bar-blue', 'http://example.com').expect(
-      'http://example.com/badge/foo-bar-blue.svg'
-    )
-  })
-
-  test(resolveBadgeUrlWithLongCache, () => {
-    given('/badge/foo-bar-blue', undefined).expect(
-      '/badge/foo-bar-blue.svg?maxAge=2592000'
-    )
-    given('/badge/foo-bar-blue', 'http://example.com').expect(
-      'http://example.com/badge/foo-bar-blue.svg?maxAge=2592000'
-    )
-  })
-
-  test(staticBadgeUrl, () => {
-    given('http://img.example.com', 'foo', 'bar', 'blue', {
-      style: 'plastic',
-    }).expect('http://img.example.com/badge/foo-bar-blue.svg?style=plastic')
-  })
-
-  test(dynamicBadgeUrl, () => {
-    const dataUrl = 'http://example.com/foo.json'
-    const query = '$.bar'
-    const prefix = 'value: '
-
-    given('http://img.example.com', 'json', 'foo', dataUrl, query, {
-      prefix,
-      style: 'plastic',
-    }).expect(
-      [
-        'http://img.example.com/badge/dynamic/json.svg',
-        '?label=foo',
-        `&url=${encodeURIComponent(dataUrl)}`,
-        `&query=${encodeURIComponent(query)}`,
-        `&prefix=${encodeURIComponent(prefix)}`,
-        '&style=plastic',
-      ].join('')
-    )
-
-    const suffix = '<- value'
-    const color = 'blue'
-    given('http://img.example.com', 'json', 'foo', dataUrl, query, {
-      suffix,
-      color,
-      style: 'plastic',
-    }).expect(
-      [
-        'http://img.example.com/badge/dynamic/json.svg',
-        '?label=foo',
-        `&url=${encodeURIComponent(dataUrl)}`,
-        `&query=${encodeURIComponent(query)}`,
-        '&color=blue',
-        `&suffix=${encodeURIComponent(suffix)}`,
-        '&style=plastic',
-      ].join('')
-    )
-  })
-})
diff --git a/frontend/lib/resolve-url.js b/frontend/lib/resolve-url.js
deleted file mode 100644
index f0f626ccba..0000000000
--- a/frontend/lib/resolve-url.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// I played with build-url and url-resolve-browser and neither of them did the
-// right thing. Previously this was based on url-path, which patched around
-// the URL API. This caused problems in Firefox 57, but only in the production
-// build.
-
-// Let's rewrite these without a deprecated API!
-// eslint-disable-next-line node/no-deprecated-api
-import { resolve, parse, format } from 'url'
-
-// baseUrl and queryParams are optional.
-export default function resolveUrl(url, baseUrl, queryParams) {
-  const resolved = baseUrl ? resolve(baseUrl, url) : url
-  const parsed = parse(resolved, /* parseQueryString */ true)
-  parsed.query = Object.assign({}, parsed.query, queryParams)
-  delete parsed.search
-  return format(parsed)
-}
diff --git a/frontend/lib/resolve-url.spec.js b/frontend/lib/resolve-url.spec.js
deleted file mode 100644
index 25c5ffb9af..0000000000
--- a/frontend/lib/resolve-url.spec.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { test, given, forCases } from 'sazerac'
-import resolveUrl from './resolve-url'
-
-describe('URL resolver', function() {
-  test(resolveUrl, () => {
-    forCases([
-      given('/foo/bar'),
-      given('/foo/bar', '/'),
-      given('/foo/bar', '/baz'),
-      given('/foo/bar', '/baz/'),
-      given('/foo/bar', ''),
-      given('/foo/bar', undefined),
-    ]).expect('/foo/bar')
-
-    given('foo/bar', '/baz/').expect('/baz/foo/bar')
-
-    forCases([
-      given('http://foo/bar'),
-      given('bar', 'http://foo/'),
-      given('/bar', 'http://foo/'),
-    ]).expect('http://foo/bar')
-
-    given('/foo/bar', '/baz', { baz: 'bazinga' }).expect('/foo/bar?baz=bazinga')
-
-    given('/foo/bar?thing=1', undefined, { other: '2' }).expect(
-      '/foo/bar?thing=1&other=2'
-    )
-  })
-})
diff --git a/frontend/pages/endpoint.js b/frontend/pages/endpoint.js
index f7a7f3f5e5..5f35314c74 100644
--- a/frontend/pages/endpoint.js
+++ b/frontend/pages/endpoint.js
@@ -1,7 +1,7 @@
 import React from 'react'
 import PropTypes from 'prop-types'
 import styled from 'styled-components'
-import { staticBadgeUrl } from '../lib/badge-url'
+import { staticBadgeUrl } from '../../core/badge-urls/make-badge-url'
 import { baseUrl } from '../constants'
 import Meta from '../components/meta'
 import Header from '../components/header'
@@ -101,7 +101,12 @@ const EndpointPage = () => (
     <p>Shields response:</p>
     <Badge
       alt="hello | sweet world"
-      src={staticBadgeUrl(baseUrl, 'hello', 'sweet world', 'orange')}
+      src={staticBadgeUrl({
+        baseUrl,
+        label: 'hello',
+        message: 'sweet world',
+        color: 'orange',
+      })}
     />
     <Explanation>
       <p>
-- 
GitLab