Skip to content

Commit

Permalink
Add option to generate govspeak gem compatible classes
Browse files Browse the repository at this point in the history
  • Loading branch information
chrispymm authored and paulrobertlloyd committed Aug 3, 2023
1 parent 476a821 commit 13b30dc
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 13 deletions.
8 changes: 8 additions & 0 deletions lib/class-generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const defaultClassNames = require('./class-names').default
const gemCompatibleClassNames = require('./class-names').compatible

module.exports = function (component) {
const classNames = this.parser.options.govspeakGemCompatibility ? gemCompatibleClassNames : defaultClassNames

return classNames[component] ?? ''
}
31 changes: 31 additions & 0 deletions lib/class-names.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module.exports.default = {
address: 'govspeak-address h-adr',
button: 'govuk-button',
'button--start': 'govuk-button govuk-button--start',
'call-to-action': 'govspeak-call-to-action',
contact: 'govspeak-contact',
example: 'govspeak-example',
'form-download': 'govspeak-form-download',
'information-callout': 'govspeak-information-callout',
information: 'govspeak-information',
place: 'govspeak-place',
'stat-headline': 'govspeak-stat-headline',
steps: 'govspeak-steps',
'warning-callout': 'govspeak-warning-callout'
}

module.exports.compatible = {
address: 'address',
button: 'gem-c-button govuk-button',
'button--start': 'gem-c-button govuk-button govuk-button--start',
'call-to-action': 'call-to-action',
contact: 'contact',
example: 'example',
'form-download': 'form-download',
'information-callout': 'application-notice info-notice',
information: 'information',
place: 'place',
'stat-headline': 'stat-headline',
steps: 'steps',
'warning-callout': 'application-notice help-notice'
}
3 changes: 2 additions & 1 deletion lib/extensions/address.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const blockTokenizer = require('../block-tokenizer')
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-address',
Expand All @@ -10,7 +11,7 @@ module.exports = {
return blockTokenizer.bind(this)(src, 'govspeak-address', '$A')
},
renderer (token) {
return `<address class="govspeak-address h-adr">
return `<address class="${classGenerator.bind(this)('address')}">
${this.parser.parse(token.tokens)}
</address>`
}
Expand Down
6 changes: 4 additions & 2 deletions lib/extensions/button.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-button',
level: 'inline',
Expand Down Expand Up @@ -33,9 +35,9 @@ module.exports = {
},
renderer (token) {
if (token.isStartButton) {
return `<a class="govuk-button govuk-button--start" href="${token.tokens[0].href}" role="button">${token.tokens[0].text}<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" aria-hidden="true" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"/></svg></a>`
return `<a class="${classGenerator.bind(this)('button--start')}" href="${token.tokens[0].href}" role="button">${token.tokens[0].text}<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" aria-hidden="true" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"/></svg></a>`
}

return `<a class="govuk-button" href="${token.tokens[0].href}" role="button">${token.tokens[0].text}</a>`
return `<a class="${classGenerator.bind(this)('button')}" href="${token.tokens[0].href}" role="button">${token.tokens[0].text}</a>`
}
}
3 changes: 2 additions & 1 deletion lib/extensions/call-to-action.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const blockTokenizer = require('../block-tokenizer')
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-call-to-action',
Expand All @@ -10,7 +11,7 @@ module.exports = {
return blockTokenizer.bind(this)(src, 'govspeak-call-to-action', '$CTA')
},
renderer (token) {
return `<div class="govspeak-call-to-action">
return `<div class="${classGenerator.bind(this)('call-to-action')}">
${this.parser.parse(token.tokens)}
</div>`
}
Expand Down
3 changes: 2 additions & 1 deletion lib/extensions/contact.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const blockTokenizer = require('../block-tokenizer')
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-contact',
Expand All @@ -10,7 +11,7 @@ module.exports = {
return blockTokenizer.bind(this)(src, 'govspeak-contact', '$C')
},
renderer (token) {
return `<div class="govspeak-contact">
return `<div class="${classGenerator.bind(this)('contact')}">
${this.parser.parse(token.tokens)}
</div>`
}
Expand Down
3 changes: 2 additions & 1 deletion lib/extensions/example.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const blockTokenizer = require('../block-tokenizer')
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-example',
Expand All @@ -10,7 +11,7 @@ module.exports = {
return blockTokenizer.bind(this)(src, 'govspeak-example', '$E')
},
renderer (token) {
return `<div class="govspeak-example">
return `<div class="${classGenerator.bind(this)('example')}">
${this.parser.parse(token.tokens)}
</div>`
}
Expand Down
3 changes: 2 additions & 1 deletion lib/extensions/form-download.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const blockTokenizer = require('../block-tokenizer')
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-form-download',
Expand All @@ -10,7 +11,7 @@ module.exports = {
return blockTokenizer.bind(this)(src, 'govspeak-form-download', '$D')
},
renderer (token) {
return `<div class="govspeak-form-download">
return `<div class="${classGenerator.bind(this)('form-download')}">
${this.parser.parse(token.tokens)}
</div>`
}
Expand Down
4 changes: 3 additions & 1 deletion lib/extensions/information-callout.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-information-callout',
level: 'block',
Expand All @@ -19,7 +21,7 @@ module.exports = {
}
},
renderer (token) {
return `<div class="govspeak-information-callout" role="note" aria-label="Information">
return `<div class="${classGenerator.bind(this)('information-callout')}" role="note" aria-label="Information">
<p>${this.parser.parseInline(token.text)}</p>
</div>`
}
Expand Down
3 changes: 2 additions & 1 deletion lib/extensions/information.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const blockTokenizer = require('../block-tokenizer')
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-information',
Expand All @@ -10,7 +11,7 @@ module.exports = {
return blockTokenizer.bind(this)(src, 'govspeak-information', '$I')
},
renderer (token) {
return `<div class="govspeak-information">
return `<div class="${classGenerator.bind(this)('information')}">
${this.parser.parse(token.tokens)}
</div>`
}
Expand Down
3 changes: 2 additions & 1 deletion lib/extensions/place.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const blockTokenizer = require('../block-tokenizer')
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-place',
Expand All @@ -10,7 +11,7 @@ module.exports = {
return blockTokenizer.bind(this)(src, 'govspeak-place', '$P')
},
renderer (token) {
return `<div class="govspeak-place">
return `<div class="${classGenerator.bind(this)('place')}">
${this.parser.parse(token.tokens)}
</div>`
}
Expand Down
3 changes: 2 additions & 1 deletion lib/extensions/stat-headline.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const blockTokenizer = require('../block-tokenizer')
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-stat-headline',
Expand All @@ -10,7 +11,7 @@ module.exports = {
return blockTokenizer.bind(this)(src, 'govspeak-stat-headline', '{stat-headline}', '{/stat-headline}')
},
renderer (token) {
return `<div class="govspeak-stat-headline">
return `<div class="${classGenerator.bind(this)('stat-headline')}">
${this.parser.parse(token.tokens)}
</div>`
}
Expand Down
4 changes: 3 additions & 1 deletion lib/extensions/steps.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const classGenerator = require('../class-generator.js')

module.exports.steps = {
name: 'govspeak-steps',
level: 'block',
Expand All @@ -20,7 +22,7 @@ module.exports.steps = {
}
},
renderer (token) {
return `<ol class="govspeak-steps">${this.parser.parseInline(token.tokens)}\n</ol>`
return `<ol class="${classGenerator.bind(this)('steps')}">${this.parser.parseInline(token.tokens)}\n</ol>`
}
}

Expand Down
4 changes: 3 additions & 1 deletion lib/extensions/warning-callout.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const classGenerator = require('../class-generator.js')

module.exports = {
name: 'govspeak-warning-callout',
level: 'block',
Expand All @@ -19,7 +21,7 @@ module.exports = {
}
},
renderer (token) {
return `<div class="govspeak-warning-callout" role="note" aria-label="Warning">
return `<div class="${classGenerator.bind(this)('warning-callout')}" role="note" aria-label="Warning">
<p>${this.parser.parseInline(token.text)}</p>
</div>`
}
Expand Down
103 changes: 103 additions & 0 deletions tests/compatibility.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import test from 'ava'
import { marked } from 'marked'
import govspeak from '../index.js'

marked.setOptions({ govspeakGemCompatibility: true })
marked.use({ extensions: govspeak })

test('Renders address', t => {
const result = marked('$A\nLine 1 \nLine 2\n$A')
t.is(result, `<address class="address">
<p>Line 1<br>Line 2</p>
</address>`)
})

test('Renders button', t => {
const result = marked('{button}[Click me](https://gov.uk){/button}')
t.is(result, '<p><a class="gem-c-button govuk-button" href="https://gov.uk" role="button">Click me</a></p>\n')
})

test('Renders start button', t => {
const result = marked('{button start}[Click me](https://gov.uk){/button}')
t.is(result, '<p><a class="gem-c-button govuk-button govuk-button--start" href="https://gov.uk" role="button">Click me<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" aria-hidden="true" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"/></svg></a></p>\n')
})

test('Renders call to action', t => {
const result = marked('$CTA\nCall to action\n$CTA')
t.is(result, `<div class="call-to-action">
<p>Call to action</p>
</div>`)
})

test('Renders contact', t => {
const result = marked('$C\nPhone \nEmail\n$C')
t.is(result, `<div class="contact">
<p>Phone<br>Email</p>
</div>`)
})

test('Renders form download', t => {
const result = marked('$D\n[Download (PDF, 14KB)](https://example.com/file.pdf)\n$D')
t.is(result, `<div class="form-download">
<p><a href="https://example.com/file.pdf">Download (PDF, 14KB)</a></p>
</div>`)
})

test('Renders example', t => {
const result = marked('$E\nExample\n$E')
t.is(result, `<div class="example">
<p>Example</p>
</div>`)
})

test('Renders information callout', t => {
const result = marked('^information^')
t.is(result, `<div class="application-notice info-notice" role="note" aria-label="Information">
<p>information</p>
</div>`)
})

test('Renders information', t => {
const result = marked('$I\ninformation\n$I')
t.is(result, `<div class="information">
<p>information</p>
</div>`)
})

test('Renders place', t => {
const result = marked('$P\nThis is a place\n$P')
t.is(result, `<div class="place">
<p>This is a place</p>
</div>`)
})

test('Renders stat headline', t => {
const result = marked('{stat-headline}\n*13.8bn* years since the big bang{/stat-headline}')
t.is(result, `<div class="stat-headline">
<p><em>13.8bn</em> years since the big bang</p>
</div>`)
})

test('Renders steps', t => {
const result = marked('s1. Add numbers.\ns2. Check numbers.\ns3. Love numbers.')
t.is(result, `<ol class="steps">
<li>Add numbers.</li>
<li>Check numbers.</li>
<li>Love numbers.</li>
</ol>`)
})

test('Renders warning callout', t => {
const result = marked('%warning%')
t.is(result, `<div class="application-notice help-notice" role="note" aria-label="Warning">
<p>warning</p>
</div>`)
})

0 comments on commit 13b30dc

Please sign in to comment.