Merge pull request #2879 from cisagov/rjm/1846-copy-email-btn

#2846: Fix bugs with the copy email button UI plus code cleanup - [RJM]
This commit is contained in:
Rachid Mrad 2024-10-08 16:45:17 -04:00 committed by GitHub
commit f299dc822c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 90 additions and 112 deletions

View file

@ -515,10 +515,14 @@ document.addEventListener('DOMContentLoaded', function() {
const formLabel = document.querySelector('label[for="id_action_needed_reason_email"]'); const formLabel = document.querySelector('label[for="id_action_needed_reason_email"]');
let lastSentEmailContent = document.getElementById("last-sent-email-content"); let lastSentEmailContent = document.getElementById("last-sent-email-content");
const initialDropdownValue = dropdown ? dropdown.value : null; const initialDropdownValue = dropdown ? dropdown.value : null;
const initialEmailValue = textarea.value; let initialEmailValue;
if (textarea)
initialEmailValue = textarea.value
// We will use the const to control the modal // We will use the const to control the modal
let isEmailAlreadySentConst = lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, ''); let isEmailAlreadySentConst;
if (lastSentEmailContent)
isEmailAlreadySentConst = lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
// We will use the function to control the label and help // We will use the function to control the label and help
function isEmailAlreadySent() { function isEmailAlreadySent() {
return lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, ''); return lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
@ -706,18 +710,6 @@ document.addEventListener('DOMContentLoaded', function() {
} }
return ''; return '';
} }
// Extract the submitter name, title, email, and phone number
const submitterDiv = document.querySelector('.form-row.field-submitter');
const submitterNameElement = document.getElementById('id_submitter');
// We have to account for different superuser and analyst markups
const submitterName = submitterNameElement
? submitterNameElement.options[submitterNameElement.selectedIndex].text
: submitterDiv.querySelector('a').text;
const submitterTitle = extractTextById('contact_info_title', submitterDiv);
const submitterEmail = extractTextById('contact_info_email', submitterDiv);
const submitterPhone = extractTextById('contact_info_phone', submitterDiv);
let submitterInfo = `${submitterName}${submitterTitle}${submitterEmail}${submitterPhone}`;
//------ Senior Official //------ Senior Official
const seniorOfficialDiv = document.querySelector('.form-row.field-senior_official'); const seniorOfficialDiv = document.querySelector('.form-row.field-senior_official');
@ -734,7 +726,6 @@ document.addEventListener('DOMContentLoaded', function() {
`<strong>Current Websites:</strong> ${existingWebsites.join(', ')}</br>` + `<strong>Current Websites:</strong> ${existingWebsites.join(', ')}</br>` +
`<strong>Rationale:</strong></br>` + `<strong>Rationale:</strong></br>` +
`<strong>Alternative Domains:</strong> ${alternativeDomains.join(', ')}</br>` + `<strong>Alternative Domains:</strong> ${alternativeDomains.join(', ')}</br>` +
`<strong>Submitter:</strong> ${submitterInfo}</br>` +
`<strong>Senior Official:</strong> ${seniorOfficialInfo}</br>` + `<strong>Senior Official:</strong> ${seniorOfficialInfo}</br>` +
`<strong>Other Employees:</strong> ${otherContactsSummary}</br>`; `<strong>Other Employees:</strong> ${otherContactsSummary}</br>`;

View file

@ -385,6 +385,7 @@ a.button,
font-kerning: auto; font-kerning: auto;
font-family: inherit; font-family: inherit;
font-weight: normal; font-weight: normal;
text-decoration: none !important;
} }
.button svg, .button svg,
.button span, .button span,
@ -392,6 +393,9 @@ a.button,
.usa-button--dja span { .usa-button--dja span {
vertical-align: middle; vertical-align: middle;
} }
.usa-button--dja.usa-button--unstyled {
color: var(--link-fg);
}
.usa-button--dja:not(.usa-button--unstyled, .usa-button--outline, .usa-modal__close, .usa-button--secondary) { .usa-button--dja:not(.usa-button--unstyled, .usa-button--outline, .usa-modal__close, .usa-button--secondary) {
background: var(--button-bg); background: var(--button-bg);
} }
@ -421,11 +425,34 @@ input[type=submit].button--dja-toolbar {
input[type=submit].button--dja-toolbar:focus, input[type=submit].button--dja-toolbar:hover { input[type=submit].button--dja-toolbar:focus, input[type=submit].button--dja-toolbar:hover {
border-color: var(--body-quiet-color); border-color: var(--body-quiet-color);
} }
// Targets the DJA buttom with a nested icon .admin-icon-group {
button .usa-icon, position: relative;
.button .usa-icon, display: inline;
.button--clipboard .usa-icon { align-items: center;
vertical-align: middle;
input {
// Allow for padding around the copy button
padding-right: 35px !important;
}
button {
width: max-content;
}
@media (max-width: 1000px) {
button {
display: block;
}
}
span {
padding-left: 0.05rem;
}
}
.usa-button__small-text,
.usa-button__small-text span {
font-size: 13px;
} }
.module--custom { .module--custom {
@ -673,71 +700,10 @@ address.dja-address-contact-list {
} }
} }
// Make the clipboard button "float" inside of the input box
.admin-icon-group {
position: relative;
display: inline;
align-items: center;
input {
// Allow for padding around the copy button
padding-right: 35px !important;
// Match the height of other inputs
min-height: 2.25rem !important;
}
button {
line-height: 14px;
width: max-content;
font-size: unset;
text-decoration: none !important;
}
@media (max-width: 1000px) {
button {
display: block;
padding-top: 8px;
}
}
span {
padding-left: 0.1rem;
}
}
.admin-icon-group.admin-icon-group__clipboard-link {
position: relative;
display: inline;
align-items: center;
.usa-button--icon {
position: absolute;
right: auto;
left: 4px;
height: 100%;
top: -1px;
}
button {
font-size: unset !important;
display: inline-flex;
padding-top: 4px;
line-height: 14px;
width: max-content;
font-size: unset;
text-decoration: none !important;
}
}
.no-outline-on-click:focus { .no-outline-on-click:focus {
outline: none !important; outline: none !important;
} }
.usa-button__small-text {
font-size: small;
}
// Get rid of padding on all help texts // Get rid of padding on all help texts
form .aligned p.help, form .aligned div.help { form .aligned p.help, form .aligned div.help {
padding-left: 0px !important; padding-left: 0px !important;
@ -887,6 +853,9 @@ div.dja__model-description{
padding-top: 0 !important; padding-top: 0 !important;
} }
.padding-bottom-0 {
padding-bottom: 0 !important;
}
.flex-container { .flex-container {
@media screen and (min-width: 700px) and (max-width: 1150px) { @media screen and (min-width: 700px) and (max-width: 1150px) {

View file

@ -20,10 +20,11 @@
</li> </li>
{% if opts.model_name == 'domainrequest' %} {% if opts.model_name == 'domainrequest' %}
<li> <li>
<a id="id-copy-to-clipboard-summary" class="button--clipboard" type="button" href="#"> <a id="id-copy-to-clipboard-summary" class="usa-button--dja" type="button" href="#">
<svg class="usa-icon" > <svg class="usa-icon" >
<use xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use> <use xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
</svg> </svg>
<!-- the span is targeted in JS, do not remove -->
<span>{% translate "Copy request summary" %}</span> <span>{% translate "Copy request summary" %}</span>
</a> </a>
</li> </li>

View file

@ -8,7 +8,7 @@ Template for an input field with a clipboard
<div class="admin-icon-group"> <div class="admin-icon-group">
{{ field }} {{ field }}
<button <button
class="usa-button usa-button--unstyled padding-left-1 usa-button--icon button--clipboard copy-to-clipboard" class="usa-button--dja usa-button usa-button__small-text usa-button--unstyled padding-left-1 usa-button--icon copy-to-clipboard"
type="button" type="button"
> >
<div class="no-outline-on-click"> <div class="no-outline-on-click">
@ -17,15 +17,17 @@ Template for an input field with a clipboard
> >
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use> <use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
</svg> </svg>
Copy <!-- the span is targeted in JS, do not remove -->
<span>Copy</span>
</div> </div>
</button> </button>
</div> </div>
{% else %} {% else %}
<div class="admin-icon-group admin-icon-group__clipboard-link"> <div class="admin-icon-group">
<input aria-hidden="true" class="display-none" value="{{ field.email }}" /> <input aria-hidden="true" class="display-none" value="{{ field.email }}" />
{% if field.email is not None %}
<button <button
class="usa-button usa-button--unstyled padding-right-1 usa-button--icon button--clipboard copy-to-clipboard text-no-underline padding-left-05" class="usa-button--dja usa-button usa-button__small-text usa-button--unstyled padding-right-1 usa-button--icon copy-to-clipboard text-no-underline padding-left-05"
type="button" type="button"
> >
<svg <svg
@ -33,7 +35,9 @@ Template for an input field with a clipboard
> >
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use> <use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
</svg> </svg>
Copy <!-- the span is targeted in JS, do not remove -->
<span>Copy</span>
</button> </button>
{% endif %}
</div> </div>
{% endif %} {% endif %}

View file

@ -26,7 +26,7 @@
{% if user.email %} {% if user.email %}
<span id="contact_info_email">{{ user.email }}</span> <span id="contact_info_email">{{ user.email }}</span>
{% include "admin/input_with_clipboard.html" with field=user invisible_input_field=True %} {% include "admin/input_with_clipboard.html" with field=user invisible_input_field=True %}
<br class="admin-icon-group__br"> <br>
{% else %} {% else %}
None<br> None<br>
{% endif %} {% endif %}

View file

@ -254,7 +254,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
<table> <table>
<thead> <thead>
<tr> <tr>
<th colspan="4">Other contact information</th> <th colspan="5">Other contact information</th>
<tr> <tr>
</thead> </thead>
<tbody> <tbody>
@ -267,9 +267,20 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
</td> </td>
<td class="padding-left-1">{{ contact.phone }}</td> <td class="padding-left-1">{{ contact.phone }}</td>
<td class="padding-left-1 text-size-small"> <td class="padding-left-1 text-size-small">
{% if contact.email %}
<input aria-hidden="true" class="display-none" value="{{ contact.email }}" /> <input aria-hidden="true" class="display-none" value="{{ contact.email }}" />
<button <button
class="usa-button usa-button--unstyled padding-right-1 usa-button--icon button--clipboard copy-to-clipboard usa-button__small-text text-no-underline" class="
usa-button--dja
usa-button
usa-button__small-text
usa-button--unstyled
padding-right-1
padding-top-0
padding-bottom-0
usa-button--icon
copy-to-clipboard
text-no-underline"
type="button" type="button"
> >
<svg <svg
@ -277,8 +288,10 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
> >
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use> <use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
</svg> </svg>
<!-- the span is targeted in JS, do not remove -->
<span>Copy email</span> <span>Copy email</span>
</button> </button>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -654,7 +654,7 @@ class TestDomainInformationAdmin(TestCase):
self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields)
# Test for the copy link # Test for the copy link
self.assertContains(response, "button--clipboard", count=3) self.assertContains(response, "copy-to-clipboard", count=3)
# cleanup this test # cleanup this test
domain_info.delete() domain_info.delete()

View file

@ -535,7 +535,7 @@ class TestDomainAdminWithClient(TestCase):
self.assertContains(response, "Testy Tester") self.assertContains(response, "Testy Tester")
# Test for the copy link # Test for the copy link
self.assertContains(response, "button--clipboard") self.assertContains(response, "copy-to-clipboard")
# cleanup from this test # cleanup from this test
domain.delete() domain.delete()

View file

@ -1511,7 +1511,7 @@ class TestDomainRequestAdmin(MockEppLib):
self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields)
# Test for the copy link # Test for the copy link
self.assertContains(response, "button--clipboard", count=4) self.assertContains(response, "copy-to-clipboard", count=4)
# Test that Creator counts display properly # Test that Creator counts display properly
self.assertNotContains(response, "Approved domains") self.assertNotContains(response, "Approved domains")