• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / blurts-server / #12110

pending completion
#12110

push

circleci

web-flow
MNTOR-983: Settings page frontend (#2765)

* migrate email to new platform MNTOR-1057

* frontend work for v2 settings MNTOR-983

* chore: Format settings page layout

* feat: Add basic settings page styles

* fix: Close add email dialog

* chore: Enhance layout for smaller viewports

* chore: Add settings strings

* fix: Add empty line

* fix: Linter

* fix: Fluent linter

* chore: Reorganize and adjust string IDs

* chore: Add localized title string

* chore: Replace settings pngs with svgs

* chore: Update conditions

* chore: Update settings classes and IDs

* fix: Email label string

* chore: Remove unused PNG

* chore: Address string changes

* chore: Use if instead of ternary operator for sorting emails

* chore: Clean up email-2022 template

* feat: Migrate monthly unresolved email partial

* chore: Migrate email breach alert layout

* chore: Add license headers

* chore: Remove templates for breach alerts and monthly unresolved emails

* chore: Add license headers

* chore: Remove email templates

* chore: Update settings.ftl

* chore: Remove CSS variable --main-dark

* chore: Set heading to black

* chore: Move settings script into partials

* chore: Use class instead of id

* chore: Fix settings layout for smaller non-mobile viewports

* fix: Input checked attribute values

* fix: Set users communication preferences in settings

* chore: Remove console log

* chore: Replace unused CSS var

---------

Co-authored-by: Robert Helmer <rhelmer@mozilla.com>

282 of 1281 branches covered (22.01%)

Branch coverage included in aggregate %.

86 of 86 new or added lines in 3 files covered. (100.0%)

959 of 3457 relevant lines covered (27.74%)

2.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/views/partials/settings.js
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4

5
import AppConstants from '../../app-constants.js'
6
import { getMessage } from '../../utils/fluent.js'
7

8
const emailNeedsVerificationSub = email => `
×
9
  <span class='verification-required'>
10
    ${getMessage('settings-email-verification-callout')}
11
  </span>
12

13
  <a class='js-settings-resend-email' data-email-id='${email.id}' href='#'>
14
    ${getMessage('settings-resend-email-verification-link')}
15
  </a>
16
`
17

18
const deleteButton = email => `
×
19
  <button
20
    data-subscriber-id='${email.subscriber_id}'
21
    data-email-id='${email.id}'
22
    class='settings-email-remove-button js-remove-email-button'
23
  >
24
    <img src='/images/icon-delete.svg'>
25
  </button>
26
`
27

28
const createEmailItem = (email, breachCounts) => `
×
29
  <li class='settings-email-item'>
30
    <strong>
31
      ${email.primary
×
32
          ? `${getMessage('settings-email-label-primary', { email: email.email })}`
33
          : email.email}
34
    </strong>
35
    ${email.verified
×
36
        ? getMessage('settings-email-number-of-breaches-info', {
37
            breachCount: breachCounts.get(email.email)
38
          })
39
        : emailNeedsVerificationSub(email)}
40

41
    ${email.primary ? '' : deleteButton(email)}
×
42
  </li>
43
`
44

45
// Moves the primary email to the front and sorts the rest alphabeticaly.
46
const getSortedEmails = emails => [...emails].sort((a, b) => {
×
47
  if (a.primary) {
×
48
    return -1
×
49
  }
50

51
  if (b.primary) {
×
52
    return 1
×
53
  }
54

55
  return a.email.localeCompare(b.email)
×
56
})
57

58
const createEmailList = (emails, breachCounts) => `
×
59
  <ul class='settings-email-list'>
60
    ${getSortedEmails(emails)
61
      .map(email => createEmailItem(email, breachCounts))
×
62
      .join('')}
63
  </ul>
64
`
65

66
const optionInput = (csrfToken, { isChecked, option }) => `
×
67
  <input
68
    ${isChecked ? 'checked' : ''}
×
69
    class='js-settings-alert-options-input'
70
    data-alert-option='${option}'
71
    data-csrf-token='${csrfToken}'
72
    name='settings-alert-options'
73
    type='radio'
74
  >
75
`
76

77
const alertOptions = ({ csrfToken, allEmailsToPrimary }) => `
×
78
  <div class='settings-alert-options'>
79
    <label class='settings-radio-input'>
80
    ${optionInput(csrfToken, {
81
      isChecked: !allEmailsToPrimary,
82
      option: 0
83
    })}
84
    <span class='settings-radio-label'>
85
      ${getMessage('settings-alert-preferences-option-one')}
86
    </span>
87
  </label>
88

89
  <label class='settings-radio-input'>
90
    ${optionInput(csrfToken, {
91
      isChecked: allEmailsToPrimary,
92
      option: 1
93
    })}
94
    <span class='settings-radio-label'>
95
      ${getMessage('settings-alert-preferences-option-two')}
96
    </span>
97
  </label>
98
  </div>
99
`
100

101
const createAddEmailModal = limit => `
×
102
  <dialog id='js-settings-modal' class='settings-email-modal'>
103
    <button id='js-settings-close'>
104
      <img src='/images/icon-close.svg'>
105
    </button>
106
    <img src='/images/settings-dialog-email.svg'>
107

108
    <h3 class='settings-section-title'>
109
      ${getMessage('settings-email-dialog-title')}
110
    </h3>
111
    <div id='js-settings-modal-content'>
112
      ${getMessage('settings-email-limit-info', { limit })}
113
      ${getMessage('settings-add-email-text')}
114
    </div>
115

116
    <div id='js-settings-modal-controls'>
117
      <label>
118
        ${getMessage('settings-email-input-label')}
119
        <input id='js-settings-email-modal-input' type='text'>
120
      </label>
121
      <button id='js-settings-modal-send-verification' class='primary'>
122
        ${getMessage('settings-send-email-verification-button')}
123
      </button>
124
    </div>
125
  </dialog>
126
`
127

128
export const settings = data => {
×
129
  const { allEmailsToPrimary, breachCounts, csrfToken, emails, limit } = data
×
130

131
  return `
×
132
    <div class='settings js-settings' data-csrf-token='${csrfToken}'>
133
      <h2 class='settings-title'>${getMessage('settings-page-title')}</h2>
134

135
      <div class='settings-content'>
136
        <!-- Breach alert preferences -->
137
        <section>
138
          <h3 class='settings-section-title'>
139
            ${getMessage('settings-alert-preferences-title')}
140
          </h3>
141
          ${alertOptions({ csrfToken, allEmailsToPrimary })}
142
        </section>
143

144
        <hr>
145

146
        <!-- Monitored email addresses -->
147
        <section>
148
          <h3 class='settings-section-title'>
149
            ${getMessage('settings-email-list-title')}
150
          </h3>
151
          <p>${getMessage('settings-email-limit-info', { limit })}</p>
152

153
          ${createEmailList(emails, breachCounts)}
154
          <button class='settings-add-email-button primary js-settings-add-email-opener'>
155
            ${getMessage('settings-add-email-button')}
156
          </button>
157

158
          ${createAddEmailModal(limit)}
159
        </section>
160

161
        <hr>
162

163
        <!-- Deactivate account -->
164
        <section>
165
          <h3 class='settings-section-title'>
166
            ${getMessage('settings-deactivate-account-title')}
167
          </h3>
168
          <p>${getMessage('settings-deactivate-account-info')}</p>
169
          <a
170
            class='settings-link-fxa'
171
            href='${AppConstants.FXA_SETTINGS_URL}'
172
            target='_blank'
173
          >
174
            ${getMessage('settings-fxa-link-label')}
175
          </a>
176
        </section>
177
      </div>
178
    </div>
179
  `
180
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc