Skip to content

Commit

Permalink
Merge pull request #9576 from alphagov/content-modelling/631-add-copy…
Browse files Browse the repository at this point in the history
…-code-link

(631) Add copy code link
  • Loading branch information
pezholio authored Nov 6, 2024
2 parents e43c7fb + 50a32f9 commit d34a9ad
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 8 deletions.
2 changes: 2 additions & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
//= require admin/views/organisation-form
//= require admin/views/unpublish-display-conditions

//= require content_block_manager/application

'use strict'
window.GOVUK.approveAllCookieTypes()
window.GOVUK.cookie('cookies_preferences_set', 'true', { days: 365 })
2 changes: 1 addition & 1 deletion app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ $govuk-page-width: 1140px;
@import "./admin/views/whats-new";
@import "./admin/views/worldwide-organisations-choose-main-office";

@import "../../../lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/application";
@import "./content_block_manager/application";

.app-js-only {
display: none;
Expand Down
5 changes: 5 additions & 0 deletions config/initializers/assets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@

# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join("node_modules")

# Add engines to the assets load path
Dir.glob(Rails.root.join("lib/engines/**/assets/*")).each do |path|
Rails.application.config.assets.paths << path
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//= require ./modules/copy-embed-code
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
'use strict'
window.GOVUK = window.GOVUK || {}
window.GOVUK.Modules = window.GOVUK.Modules || {}
;(function (Modules) {
function CopyEmbedCode(module) {
this.module = module
this.copyLink = this.createLink.bind(this)()
}

CopyEmbedCode.prototype.init = function () {
const dd = document.createElement('dd')
dd.classList.add('govuk-summary-list__actions')
dd.append(this.copyLink)

this.module.append(dd)
}

CopyEmbedCode.prototype.createLink = function () {
const copyLink = document.createElement('a')
copyLink.classList.add('govuk-link')
copyLink.classList.add('govuk-link__copy-link')
copyLink.setAttribute('href', '#')
copyLink.setAttribute('role', 'button')
copyLink.textContent = 'Copy code'
copyLink.addEventListener('click', this.copyCode.bind(this))
// Handle when a keyboard user highlights the link and clicks return
copyLink.addEventListener(
'keydown',
function (e) {
if (e.keyCode === 13) {
this.copyCode.bind(this)
}
}.bind(this)
)

return copyLink
}

CopyEmbedCode.prototype.copyCode = function (e) {
e.preventDefault()

const embedCode = this.module.dataset.embedCode
this.writeToClipboard(embedCode).then(this.copySuccess.bind(this))
}

CopyEmbedCode.prototype.copySuccess = function () {
const originalText = this.copyLink.textContent
this.copyLink.textContent = 'Code copied'
this.copyLink.focus()

setTimeout(this.restoreText.bind(this, originalText), 2000)
}

CopyEmbedCode.prototype.restoreText = function (originalText) {
this.copyLink.textContent = originalText
}

// This is a fallback for browsers that do not support the async clipboard API
CopyEmbedCode.prototype.writeToClipboard = function (embedCode) {
return new Promise(function (resolve) {
// Create a textarea element with the embed code
const textArea = document.createElement('textarea')
textArea.value = embedCode

document.body.appendChild(textArea)

// Select the text in the textarea
textArea.select()

// Copy the selected text
document.execCommand('copy')

// Remove our textarea
document.body.removeChild(textArea)

resolve()
})
}

Modules.CopyEmbedCode = CopyEmbedCode
})(window.GOVUK.Modules)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%= render "components/summary_card", {
<%= render "govuk_publishing_components/components/summary_card", {
title:,
rows:,
summary_card_actions:,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def rows
{
key: item[:field],
value: item[:value],
data: item[:data],
}
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ def embed_code_item
{
field: "Embed code",
value: content_block_document.embed_code,
data: {
module: "copy-embed-code",
"embed-code": content_block_document.embed_code,
},
}
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Feature: Search for a content object
| title | ministry address |
| email_address | ministry@example.com |
| organisation | Ministry of Example |

Scenario: GDS Editor can filter by organisation
When I visit the Content Block Manager home page
Then my organisation is already selected as a filter
And I should see the details for all documents from my organisation
Expand All @@ -28,23 +30,36 @@ Feature: Search for a content object
Then "3" content blocks are returned

Scenario: GDS Editor searches for a content object by keyword in title
When I enter the keyword "example search"
When I visit the Content Block Manager home page
And I enter the keyword "example search"
And I click to view results
Then I should see the content block with title "example search title" returned
And "1" content blocks are returned

Scenario: GDS Editor searches for a content object by keyword in details
When I enter the keyword "ABC123"
When I visit the Content Block Manager home page
And I enter the keyword "ABC123"
And I click to view results
Then I should see the content block with title "an address" returned
And "1" content blocks are returned

Scenario: GDS Editor searches for a content object by block type
When I check the block type "Email address"
When I visit the Content Block Manager home page
And I select the lead organisation "All organisations"
And I check the block type "Email address"
And I click to view results
And "2" content blocks are returned

Scenario: GDS Editor searches for a content object by lead organisation
When I select the lead organisation "Ministry of Example"
When I visit the Content Block Manager home page
And I select the lead organisation "Ministry of Example"
And I click to view results
And "1" content blocks are returned

@javascript
Scenario: GDS Editor can copy embed code
When I visit the Content Block Manager home page
And I select the lead organisation "Ministry of Example"
And I click to view results
And I click to copy the embed code for the content block "ministry address"
Then the embed code should be copied to my clipboard
Original file line number Diff line number Diff line change
Expand Up @@ -595,3 +595,24 @@ def click_save_and_continue
Then("I should see the content block manager home page") do
expect(page).to have_content("All content blocks")
end

When("I click to copy the embed code") do
find("a", text: "Copy code").click
has_text?("Code copied")
@embed_code = @content_block.document.embed_code
end

When("I click to copy the embed code for the content block {string}") do |content_block_name|
within(".govuk-summary-card", text: content_block_name) do
find("a", text: "Copy code").click
has_text?("Code copied")
document = ContentBlockManager::ContentBlock::Document.find_by(title: content_block_name)
@embed_code = document.embed_code
end
end

Then("the embed code should be copied to my clipboard") do
page.driver.browser.execute_cdp("Browser.grantPermissions", origin: page.server_url, permissions: %w[clipboardReadWrite])
clip_text = page.evaluate_async_script("navigator.clipboard.readText().then(arguments[0])")
expect(clip_text).to eq(@embed_code)
end
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,11 @@ Feature: View a content object
And I click to view the document
Then I should see the dependent content listed

@javascript
Scenario: GDS Editor can copy embed code
When I visit the Content Block Manager home page
Then I should see the details for all documents
When I click to view the document
And I click to copy the embed code
Then the embed code should be copied to my clipboard

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
describe('GOVUK.Modules.CopyEmbedCode', function () {
let fixture, embedCode, copyEmbedCode, copyLink, fakeTextarea

beforeEach(function () {
embedCode = 'something'
fixture = document.createElement('div')
fixture.setAttribute('data-embed-code', embedCode)
fixture.innerHTML = `
<dt class="govuk-summary-list__key">Embed code</dt>
<dd class="govuk-summary-list__value">${embedCode}</dd>
`
document.body.append(fixture)

copyEmbedCode = new GOVUK.Modules.CopyEmbedCode(fixture)
copyEmbedCode.init()

copyLink = document.querySelector('.govuk-link__copy-link')

fakeTextarea = document.createElement('textarea')
spyOn(document, 'createElement').and.returnValue(fakeTextarea)
})

afterEach(function () {
fixture.innerHTML = ''
})

it('should add a link to copy the embed code', function () {
expect(copyLink).toBeTruthy()
expect(copyLink.textContent).toBe('Copy code')
})

it('should create and populate a textarea', function () {
window.GOVUK.triggerEvent(copyLink, 'click')

expect(fakeTextarea.value).toEqual(embedCode)
})

it('should select the text in the textarea and run the copy command', function () {
const copySpy = spyOn(document, 'execCommand')
const selectSpy = spyOn(fakeTextarea, 'select')

window.GOVUK.triggerEvent(copyLink, 'click')

expect(selectSpy).toHaveBeenCalled()
expect(copySpy).toHaveBeenCalled()
})

it('should add and remove the textarea', function () {
const appendSpy = spyOn(document.body, 'appendChild')
const removeSpy = spyOn(document.body, 'removeChild')

window.GOVUK.triggerEvent(copyLink, 'click')

expect(appendSpy).toHaveBeenCalled()
expect(removeSpy).toHaveBeenCalled()
})

it('changes and restores the link text', async function () {
jasmine.clock().install()

await window.GOVUK.triggerEvent(copyLink, 'click')

copyLink = document.querySelector('.govuk-link__copy-link')

expect(copyLink.textContent).toEqual('Code copied')
jasmine.clock().tick(2000)

expect(copyLink.textContent).toEqual('Copy code')

jasmine.clock().uninstall()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class ContentBlockManager::ContentBlock::Document::Index::SummaryCardComponentTe
assert_selector ".govuk-summary-list__key", text: "Creator"
assert_selector ".govuk-summary-list__value", text: content_block_edition.creator.name

assert_selector ".govuk-summary-list__row[data-module='copy-embed-code']", text: "Embed code"
assert_selector ".govuk-summary-list__row[data-embed-code='#{content_block_document.embed_code}']", text: "Embed code"
assert_selector ".govuk-summary-list__key", text: "Embed code"
assert_selector ".govuk-summary-list__value", text: content_block_document.embed_code

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class ContentBlockManager::ContentBlock::Document::Show::SummaryListComponentTes
assert_selector ".govuk-summary-list__key", text: "Creator"
assert_selector ".govuk-summary-list__value", text: content_block_edition.creator.name

assert_selector ".govuk-summary-list__row[data-module='copy-embed-code']", text: "Embed code"
assert_selector ".govuk-summary-list__row[data-embed-code='#{content_block_document.embed_code}']", text: "Embed code"
assert_selector ".govuk-summary-list__key", text: "Embed code"
assert_selector ".govuk-summary-list__value", text: content_block_document.embed_code

Expand Down
5 changes: 3 additions & 2 deletions spec/support/jasmine-browser.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"application-*.js",
"all-*.js"
],
"specDir": "spec/javascripts",
"specDir": ".",
"specFiles": [
"**/*[sS]pec.js"
"spec/javascripts/**/*[sS]pec.js",
"lib/engines/**/spec/**/*[sS]pec.js"
],
"helpers": [
"https://unpkg.com/[email protected]/lib/mock-ajax.js",
Expand Down

0 comments on commit d34a9ad

Please sign in to comment.