Skip to content

Commit

Permalink
Get notification button from personalisation API
Browse files Browse the repository at this point in the history
Introduce JS that calls our personalisation API on page load. If the
user is authenticated, and already subscribed to email notifications on
the current page, the account-api serves the "Already subscribed"
version of the button HTML.
  • Loading branch information
danacotoran committed Nov 15, 2021
1 parent e615972 commit f5873c2
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Update the pseudo underline mixin and its usage on the super navigation header ([PR #2439](https://github.com/alphagov/govuk_publishing_components/pull/2439))
* Fix layout glitch seen when scrollbar is permanently visible ([PR #2444](https://github.com/alphagov/govuk_publishing_components/pull/2444 ))
* Add draggable=false to links disguised as buttons ([PR #2448](https://github.com/alphagov/govuk_publishing_components/pull/2448))
* Get single page notification button from personalisation API on load ([PR #2443](https://github.com/alphagov/govuk_publishing_components/pull/2443))

## 27.12.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* global XMLHttpRequest */
window.GOVUK = window.GOVUK || {}
window.GOVUK.Modules = window.GOVUK.Modules || {};

(function (Modules) {
function SinglePageNotificationButton ($module) {
this.$module = $module
this.basePath = this.$module.querySelector('input[name="base_path"]').value
this.personalisationEndpoint = '/api/personalisation/check-email-subscription?base_path=' + this.basePath
}

SinglePageNotificationButton.prototype.init = function () {
var xhr = new XMLHttpRequest()
xhr.open('GET', this.personalisationEndpoint, true)

xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var responseText = xhr.responseText
// if response text exists and is JSON parse-able, parse the response and get the button html
if (responseText && this.responseIsJSON(responseText)) {
var newButton = JSON.parse(responseText).button_html
var html = document.createElement('div')
html.innerHTML = newButton
// test that the html returned contains the button component; if yes, swap the button for the updated version
var responseHasButton = html.querySelector('form.gem-c-single-page-notification-button')
if (responseHasButton) {
this.$module.outerHTML = newButton
}
}
}
}.bind(this)
xhr.send()
}

SinglePageNotificationButton.prototype.responseIsJSON = function (string) {
try {
JSON.parse(string)
} catch (e) {
return false
}
return true
}
Modules.SinglePageNotificationButton = SinglePageNotificationButton
})(window.GOVUK.Modules)
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@
base_path ||= nil
local_assigns[:margin_bottom] ||= 3
already_subscribed ||= false
js_enhancement ||= false
text ||= already_subscribed ? t('components.single_page_notification_button.unsubscribe_text') : t('components.single_page_notification_button.subscribe_text')

shared_helper = GovukPublishingComponents::Presenters::SharedHelper.new(local_assigns)
wrapper_classes = %w(gem-c-single-page-notification-button govuk-!-display-none-print)
wrapper_classes << shared_helper.get_margin_bottom
classes = "govuk-body-s gem-c-single-page-notification-button__submit"
module_names = %w(gem-track-click)
module_names << "single-page-notification-button" if js_enhancement

data_attributes[:module] = module_names.join(" ")
data_attributes[:category] = "Single-page-notification-button"
data_attributes[:action] = already_subscribed ? "Unsubscribe button" : "Subscribe button"
data_attributes[:label] = base_path if base_path
%>
<% button_text = capture do %>
<svg class="gem-c-single-page-notification-button__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 459.334 459.334"><path fill="currentColor" d="M177.216 404.514c-.001.12-.009.239-.009.359 0 30.078 24.383 54.461 54.461 54.461s54.461-24.383 54.461-54.461c0-.12-.008-.239-.009-.359H175.216zM403.549 336.438l-49.015-72.002v-89.83c0-60.581-43.144-111.079-100.381-122.459V24.485C254.152 10.963 243.19 0 229.667 0s-24.485 10.963-24.485 24.485v27.663c-57.237 11.381-100.381 61.879-100.381 122.459v89.83l-49.015 72.002a24.76 24.76 0 0 0 20.468 38.693H383.08a24.761 24.761 0 0 0 20.469-38.694z"/></svg><%= text %>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
name: Single page notification button
description: A button that subscribes the user to email notifications to a page
body: |
By default, the component displays with the "Get emails about this page" state. The component does not render without the `base_path` parameter.
By default, the component displays with the "Get emails about this page" state.
If the `js-enhancement` flag is present, the component uses JavaScript to check if the user has already subscribed to email notifications on the current page. If yes, the state of the component updates accordingly.
The `base_path` is necessary for [checking if an email subscription is active on page load](https://github.com/alphagov/account-api/blob/main/docs/api.md#get-apipersonalisationcheck-email-subscriptiontopic_slug) and the creation/deletion of an email notification subscription.
The component does not render without the `base_path` parameter. The `base_path` is necessary for [checking if an email subscription is active on page load](https://github.com/alphagov/account-api/blob/main/docs/api.md#get-apipersonalisationcheck-email-subscriptiontopic_slug) and the creation/deletion of an email notification subscription.
When the button is clicked, the `base_path` is submitted to an endpoint which proceeds to check the user's authentication status and whether they are already subscribed to the page or not. Depending on these factors, they will be routed accordingly.
accessibility_criteria: |
Expand All @@ -30,3 +31,9 @@ examples:
data:
base_path: '/current-page-path'
margin_bottom: 5
with_js_enhancement:
description: |
If the `js-enhancement` flag is present, the component uses JavaScript to check if the user has already subscribed to email notifications on the current page. If yes, the state of the component updates accordingly.
data:
base_path: '/current-page-path'
js_enhancement: true
23 changes: 21 additions & 2 deletions spec/components/single_page_notification_button_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ def component_name
end

it "has data attributes if data_attributes is specified" do
render_component({ base_path: "/the-current-page", data_attributes: { action: "kaboom!" } })
assert_select ".gem-c-single-page-notification-button[data-action='kaboom!']"
render_component({ base_path: "/the-current-page", data_attributes: { custom_attribute: "kaboom!" } })
assert_select ".gem-c-single-page-notification-button[data-custom-attribute='kaboom!']"
end

it "sets a default bottom margin" do
Expand All @@ -40,4 +40,23 @@ def component_name
render_component({ base_path: "/the-current-page", margin_bottom: 9 })
assert_select '.gem-c-single-page-notification-button.govuk-\!-margin-bottom-9'
end

it "has a data-module attribute for JavaScript, if the js-enhancement flag is present" do
render_component({ base_path: "/the-current-page", js_enhancement: true })
dom = Nokogiri::HTML(rendered)
form_data_module = dom.xpath("//form")[0].attr("data-module")

expect(form_data_module).to include("single-page-notification-button")
end

it "has correct attributes for tracking by default" do
render_component({ base_path: "/the-current-page" })
assert_select ".gem-c-single-page-notification-button[data-category='Single-page-notification-button'][data-action='Subscribe button'][data-label='/the-current-page']"
end

it "has correct attributes for tracking when already_subscribed is true" do
render_component({ base_path: "/the-current-page", already_subscribed: true })

assert_select ".gem-c-single-page-notification-button[data-category='Single-page-notification-button'][data-action='Unsubscribe button'][data-label='/the-current-page']"
end
end
2 changes: 2 additions & 0 deletions spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@
get "error-summary", to: "welcome#errorsummary"
get "tabsexample", to: "welcome#tabsexample"
get "table", to: "welcome#table"
# we fake this URL to prevent the Single Page notification button from causing an error in the component guide
get "/api/personalisation/check-email-subscription", to: proc { [404, {}, [""]] }
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* eslint-env jasmine */
/* global GOVUK */

describe('Single page notification component', function () {
var FIXTURE

beforeEach(function () {
FIXTURE =
'<form class="gem-c-single-page-notification-button old-button-for-test" action="/email/subscriptions/single-page/new" method="POST" data-module="single-page-notification-button">' +
'<input type="hidden" name="base_path" value="/current-page-path">' +
'<button class="gem-c-single-page-notification-button__submit" type="submit">Get emails about this page</button>' +
'</form>'
window.setFixtures(FIXTURE)
jasmine.Ajax.install()
})

afterEach(function () {
jasmine.Ajax.uninstall()
})

it('calls the personalisation API on load', function () {
initButton()
var request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/api/personalisation/check-email-subscription?base_path=/current-page-path')
expect(request.method).toBe('GET')
})

it('replaces the button when the API returns button html', function () {
initButton()

jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: '{\n "base_path": "/current-page-path",\n "active": true,\n "button_html": "<form class=\\"gem-c-single-page-notification-button new-button-for-test\\" action=\\"/email/subscriptions/single-page/new\\" method=\\"POST\\">\\n <input type=\\"hidden\\" name=\\"base_path\\" value=\\"/current-page-path\\">\\n <button class=\\"gem-c-single-page-notification-button__submit\\" type=\\"submit\\">Stop getting emails about this page\\n</button>\\n</form>"\n}'
})

var form = document.querySelector('form.gem-c-single-page-notification-button.new-button-for-test')
expect(form).toHaveText('Stop getting emails about this page')
})

it('should remain unchanged if the response is not JSON', function () {
var responseText = 'I am not JSON, actually'
initButton()

jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: responseText
})

var form = document.querySelector('form.gem-c-single-page-notification-button.old-button-for-test')
expect(form).toHaveText('Get emails about this page')
expect(GOVUK.Modules.SinglePageNotificationButton.prototype.responseIsJSON(responseText)).toBe(false)
})

it('should remain unchanged if response text is empty', function () {
var responseText = ''
initButton()

jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: responseText
})

var form = document.querySelector('form.gem-c-single-page-notification-button.old-button-for-test')
expect(form).toHaveText('Get emails about this page')
expect(GOVUK.Modules.SinglePageNotificationButton.prototype.responseIsJSON(responseText)).toBe(false)
})

it('should remain unchanged if the endpoint fails', function () {
initButton()

jasmine.Ajax.requests.mostRecent().respondWith({
status: 500,
contentType: 'text/plain',
responseText: ''
})

var form = document.querySelector('form.gem-c-single-page-notification-button.old-button-for-test')
expect(form).toHaveText('Get emails about this page')
})

function initButton () {
var element = document.querySelector('[data-module=single-page-notification-button]')
new GOVUK.Modules.SinglePageNotificationButton(element).init()
}
})

0 comments on commit f5873c2

Please sign in to comment.