mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-23 11:16:07 +02:00
Refactor JS addKebob and addModal
This commit is contained in:
parent
24d55f41bf
commit
0f6ab2b6e6
7 changed files with 82 additions and 184 deletions
|
@ -87,49 +87,24 @@ function makeVisible(el) {
|
|||
}
|
||||
|
||||
// TODO: Write caption here
|
||||
function addModal(member_email, member_id, num_domains, submit_delete_url, wrapper_element) {
|
||||
|
||||
let modalHeading = '';
|
||||
let modalDescription = '';
|
||||
|
||||
if (num_domains == 0){
|
||||
modalHeading = `Are you sure you want to delete ${member_email}?`;
|
||||
modalDescription = `They will no longer be able to access this organization. \n
|
||||
This action cannot be undone.`;
|
||||
} else if (num_domains == 1) {
|
||||
modalHeading = `Are you sure you want to delete ${member_email}?`;
|
||||
modalDescription = `<b>${member_email}</b> currently manages ${num_domains} domain in the organization. \n
|
||||
Removing them from the organization will remove all of their domains. They will no longer be able to \n
|
||||
access this organization. This action cannot be undone.`;
|
||||
} else if (num_domains >= 1) {
|
||||
modalHeading = `Are you sure you want to delete ${member_email}?`;
|
||||
modalDescription = `<b>${member_email}</b> currently manages ${num_domains} domains in the organization. \n
|
||||
Removing them from the organization will remove all of their domains. They will no longer be able to \n
|
||||
access this organization. This action cannot be undone.`;
|
||||
}
|
||||
|
||||
const modalSubmit = `
|
||||
<button type="button"
|
||||
class="usa-button usa-button--secondary usa-modal__submit"
|
||||
data-pk = ${submit_delete_url}
|
||||
name="delete-member">Yes, remove from organizaion</button>
|
||||
`
|
||||
function addModal(action, id, ariaLabelledby, ariaDescribedby, modalHeading, modalDescription, modalSubmit, wrapper_element, forceAction) {
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.setAttribute('class', 'usa-modal');
|
||||
modal.setAttribute('id', `toggle-remove-member-${member_id}`);
|
||||
modal.setAttribute('aria-labelledby', 'Are you sure you want to continue?');
|
||||
modal.setAttribute('aria-describedby', 'Member will be removed');
|
||||
modal.setAttribute('data-force-action', '');
|
||||
modal.setAttribute('id', `${action}-${id}`);
|
||||
modal.setAttribute('aria-labelledby', ariaLabelledby);
|
||||
modal.setAttribute('aria-describedby', ariaDescribedby);
|
||||
if (forceAction)
|
||||
modal.setAttribute('data-force-action', '');
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||
<h2 class="usa-modal__heading">
|
||||
${modalHeading}
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
<p id="modal-1-description">
|
||||
<p>
|
||||
${modalDescription}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -169,29 +144,35 @@ function addModal(member_email, member_id, num_domains, submit_delete_url, wrapp
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Write caption here
|
||||
function generateKebabHTML(unique_id, member_name, member_type) {
|
||||
let cancelInvitationButton = member_type === "invitedmember" ? "Cancel invitation" : "Remove member";
|
||||
|
||||
/**
|
||||
* Helper function that creates a dynamic accordion navigation
|
||||
* @param {string} action
|
||||
* @param {string} unique_id - An ID that when combined with action makes a unique identifier
|
||||
* @param {string} modal_button_text - The action button's text
|
||||
* @param {string} screen_reader_text - A screen reader helper
|
||||
*/
|
||||
function generateKebabHTML(action, unique_id, modal_button_text, screen_reader_text) {
|
||||
// The first block displays regiular buttons on mobile. The class visible-mobile-flex controls this.
|
||||
// The second block build a kebob triggered accordion on larger screens, controlled by hidden-mobile-flex.
|
||||
const kebab = `
|
||||
<a
|
||||
role="button"
|
||||
id="button-trigger-remove-member-${unique_id}"
|
||||
href="#toggle-remove-member-${unique_id}"
|
||||
class="usa-button usa-button--unstyled text-no-underline late-loading-modal-trigger margin-top-2 line-height-sans-5 text-secondary visible-mobile-flex"
|
||||
aria-controls="toggle-remove-member-${unique_id}"
|
||||
id="button-trigger-${action}-${unique_id}"
|
||||
href="#toggle-${action}-${unique_id}"
|
||||
class="usa-button usa-button--unstyled text-no-underline late-loading-modal-trigger margin-top-2 line-height-sans-5 text-secondary visible-mobile-flex"
|
||||
aria-controls="toggle-${action}-${unique_id}"
|
||||
data-open-modal
|
||||
>
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#delete"></use>
|
||||
</svg>${cancelInvitationButton} <span class="usa-sr-only">${member_name}</span>
|
||||
</svg>${modal_button_text}
|
||||
<span class="usa-sr-only">${screen_reader_text}</span>
|
||||
</a>
|
||||
|
||||
<div class="usa-accordion usa-accordion--more-actions margin-right-2 hidden-mobile-flex">
|
||||
<div class="usa-accordion__heading">
|
||||
<button
|
||||
type="button"
|
||||
id="button-toggle-more-actions-${unique_id}"
|
||||
class="usa-button usa-button--unstyled usa-button--with-icon usa-accordion__button usa-button--more-actions"
|
||||
aria-expanded="false"
|
||||
aria-controls="more-actions-${unique_id}"
|
||||
|
@ -205,14 +186,14 @@ function generateKebabHTML(unique_id, member_name, member_type) {
|
|||
<h2>More options</h2>
|
||||
<a
|
||||
role="button"
|
||||
id="button-trigger-remove-member-${unique_id}"
|
||||
href="#toggle-remove-member-${unique_id}"
|
||||
id="button-trigger-${action}-${unique_id}"
|
||||
href="#toggle-${action}-${unique_id}"
|
||||
class="usa-button usa-button--unstyled text-no-underline late-loading-modal-trigger margin-top-2 line-height-sans-5 text-secondary"
|
||||
aria-controls="toggle-remove-member-${unique_id}"
|
||||
aria-controls="toggle-${action}-${unique_id}"
|
||||
data-open-modal
|
||||
>
|
||||
${cancelInvitationButton}
|
||||
<span class="usa-sr-only">for ${member_name}</span>
|
||||
${modal_button_text}
|
||||
<span class="usa-sr-only">${screen_reader_text}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1122,9 +1103,7 @@ function initializeTooltips() {
|
|||
*
|
||||
*/
|
||||
function initializeModals() {
|
||||
console.log("We are going to initializeModals")
|
||||
window.modal.on();
|
||||
console.log("Finish initializeModals")
|
||||
|
||||
}
|
||||
|
||||
|
@ -1136,9 +1115,7 @@ function initializeModals() {
|
|||
*
|
||||
*/
|
||||
function unloadModals() {
|
||||
console.log("We are going to unloadModals")
|
||||
window.modal.off();
|
||||
console.log("Finish unloadModals")
|
||||
}
|
||||
|
||||
class LoadTableBase {
|
||||
|
@ -1782,13 +1759,14 @@ class DomainRequestsTable extends LoadTableBase {
|
|||
}
|
||||
}
|
||||
|
||||
// 1st option: Just a modal trigger in any screen size for non-org users
|
||||
modalTrigger = `
|
||||
<a
|
||||
role="button"
|
||||
id="button-toggle-delete-domain-alert-${request.id}"
|
||||
href="#toggle-delete-domain-alert-${request.id}"
|
||||
id="button-toggle-delete-domain-${request.id}"
|
||||
href="#toggle-delete-domain-${request.id}"
|
||||
class="usa-button text-secondary usa-button--unstyled text-no-underline late-loading-modal-trigger line-height-sans-5"
|
||||
aria-controls="toggle-delete-domain-alert-${request.id}"
|
||||
aria-controls="toggle-delete-domain-${request.id}"
|
||||
data-open-modal
|
||||
>
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
|
@ -1803,102 +1781,14 @@ class DomainRequestsTable extends LoadTableBase {
|
|||
name="delete-domain-request">Yes, delete request</button>
|
||||
`
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.setAttribute('class', 'usa-modal');
|
||||
modal.setAttribute('id', `toggle-delete-domain-alert-${request.id}`);
|
||||
modal.setAttribute('aria-labelledby', 'Are you sure you want to continue?');
|
||||
modal.setAttribute('aria-describedby', 'Domain will be removed');
|
||||
modal.setAttribute('data-force-action', '');
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||
${modalHeading}
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
<p id="modal-1-description">
|
||||
${modalDescription}
|
||||
</p>
|
||||
</div>
|
||||
<div class="usa-modal__footer">
|
||||
<ul class="usa-button-group">
|
||||
<li class="usa-button-group__item">
|
||||
${modalSubmit}
|
||||
</li>
|
||||
<li class="usa-button-group__item">
|
||||
<button
|
||||
type="button"
|
||||
class="usa-button usa-button--unstyled padding-105 text-center"
|
||||
data-close-modal
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="usa-button usa-modal__close"
|
||||
aria-label="Close this window"
|
||||
data-close-modal
|
||||
>
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
|
||||
<use xlink:href="/public/img/sprite.svg#close"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
|
||||
this.tableWrapper.appendChild(modal);
|
||||
addModal('toggle-delete-domain', request.id, 'Are you sure you want to continue?', 'Domain will be removed', modalHeading, modalDescription, modalSubmit, tbody, true);
|
||||
|
||||
// Request is deletable, modal and modalTrigger are built. Now check if we are on the portfolio requests page (by seeing if there is a portfolio value) and enhance the modalTrigger accordingly
|
||||
if (this.portfolioValue) {
|
||||
modalTrigger = `
|
||||
<a
|
||||
role="button"
|
||||
id="button-toggle-delete-domain-alert-${request.id}"
|
||||
href="#toggle-delete-domain-alert-${request.id}"
|
||||
class="usa-button text-secondary usa-button--unstyled text-no-underline late-loading-modal-trigger margin-top-2 visible-mobile-flex line-height-sans-5"
|
||||
aria-controls="toggle-delete-domain-alert-${request.id}"
|
||||
data-open-modal
|
||||
>
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#delete"></use>
|
||||
</svg> Delete <span class="usa-sr-only">${domainName}</span>
|
||||
</a>
|
||||
|
||||
<div class="usa-accordion usa-accordion--more-actions margin-right-2 hidden-mobile-flex">
|
||||
<div class="usa-accordion__heading">
|
||||
<button
|
||||
type="button"
|
||||
class="usa-button usa-button--unstyled usa-button--with-icon usa-accordion__button usa-button--more-actions"
|
||||
aria-expanded="false"
|
||||
aria-controls="more-actions-${request.id}"
|
||||
>
|
||||
<svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#more_vert"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="more-actions-${request.id}" class="usa-accordion__content usa-prose shadow-1 left-auto right-0" hidden>
|
||||
<h2>More options</h2>
|
||||
<a
|
||||
role="button"
|
||||
id="button-toggle-delete-domain-alert-${request.id}"
|
||||
href="#toggle-delete-domain-alert-${request.id}"
|
||||
class="usa-button text-secondary usa-button--unstyled text-no-underline late-loading-modal-trigger margin-top-2 line-height-sans-5"
|
||||
aria-controls="toggle-delete-domain-alert-${request.id}"
|
||||
data-open-modal
|
||||
>
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#delete"></use>
|
||||
</svg> Delete <span class="usa-sr-only">${domainName}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
// 2nd option: Just a modal trigger on mobile for org users
|
||||
// 3rd option: kebab + accordion with nested modal trigger on desktop for org users
|
||||
modalTrigger = generateKebabHTML('delete-domain', request.id, 'Delete', domainName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2275,6 +2165,36 @@ class MembersTable extends LoadTableBase {
|
|||
return permissionsHTML;
|
||||
}
|
||||
|
||||
static addMemberModal(num_domains, member_email, submit_delete_url, id, wrapper_element) {
|
||||
let modalHeading = '';
|
||||
let modalDescription = '';
|
||||
|
||||
if (num_domains == 0){
|
||||
modalHeading = `Are you sure you want to delete ${member_email}?`;
|
||||
modalDescription = `They will no longer be able to access this organization.
|
||||
This action cannot be undone.`;
|
||||
} else if (num_domains == 1) {
|
||||
modalHeading = `Are you sure you want to delete ${member_email}?`;
|
||||
modalDescription = `<b>${member_email}</b> currently manages ${num_domains} domain in the organization.
|
||||
Removing them from the organization will remove all of their domains. They will no longer be able to
|
||||
access this organization. This action cannot be undone.`;
|
||||
} else if (num_domains >= 1) {
|
||||
modalHeading = `Are you sure you want to delete ${member_email}?`;
|
||||
modalDescription = `<b>${member_email}</b> currently manages ${num_domains} domains in the organization.
|
||||
Removing them from the organization will remove all of their domains. They will no longer be able to
|
||||
access this organization. This action cannot be undone.`;
|
||||
}
|
||||
|
||||
const modalSubmit = `
|
||||
<button type="button"
|
||||
class="usa-button usa-button--secondary usa-modal__submit"
|
||||
data-pk = ${submit_delete_url}
|
||||
name="delete-member">Yes, remove from organization</button>
|
||||
`
|
||||
|
||||
addModal('toggle-remove-member', id, 'Are you sure you want to continue?', 'Member will be removed', modalHeading, modalDescription, modalSubmit, wrapper_element, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads rows in the members list, as well as updates pagination around the members list
|
||||
* based on the supplied attributes.
|
||||
|
@ -2358,7 +2278,8 @@ class MembersTable extends LoadTableBase {
|
|||
const member_delete_url = member.action_url + "/delete";
|
||||
const num_domains = member.domain_urls.length;
|
||||
const last_active = this.handleLastActive(member.last_active);
|
||||
const kebabHTML = hasEditPermission ? generateKebabHTML(unique_id, member.name, member.type): '';
|
||||
let cancelInvitationButton = member.type === "invitedmember" ? "Cancel invitation" : "Remove member";
|
||||
const kebabHTML = hasEditPermission ? generateKebabHTML('remove-member', unique_id, cancelInvitationButton, `for ${member.name}`): '';
|
||||
|
||||
const row = document.createElement('tr');
|
||||
|
||||
|
@ -2417,7 +2338,7 @@ class MembersTable extends LoadTableBase {
|
|||
}
|
||||
// This easter egg is only for fixtures that dont have names as we are displaying their emails
|
||||
// All prod users will have emails linked to their account
|
||||
if (hasEditPermission) addModal(member.email || "Samwise Gamgee", unique_id, num_domains, member_delete_url, row);
|
||||
if (hasEditPermission) MembersTable.addMemberModal(num_domains, member.email || "Samwise Gamgee", member_delete_url, unique_id, row);
|
||||
});
|
||||
|
||||
this.initShowMoreButtons();
|
||||
|
@ -2998,34 +2919,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
const member_delete_url = `${member_type}-${member_id}/delete`;
|
||||
const unique_id = `${member_type}-${member_id}`;
|
||||
|
||||
wrapperDeleteAction.innerHTML = generateKebabHTML(unique_id, member_name, member_type);
|
||||
|
||||
// Select the button and the menu we just inserted
|
||||
const kebabButton = wrapperDeleteAction.querySelector(`#button-toggle-more-actions-${unique_id}`);
|
||||
const kebabMenu = wrapperDeleteAction.querySelector(`#more-actions-${unique_id}`);
|
||||
|
||||
kebabButton.addEventListener('click', () => {
|
||||
const isExpanded = kebabButton.getAttribute('aria-expanded') === 'true';
|
||||
kebabButton.setAttribute('aria-expanded', !isExpanded);
|
||||
kebabMenu.style.display = isExpanded ? 'none' : 'block';
|
||||
});
|
||||
|
||||
// Handles clicks outside the kebab menu
|
||||
document.addEventListener('click', (event) => {
|
||||
const isClickInsideButton = kebabButton.contains(event.target);
|
||||
const isClickInsideMenu = kebabMenu.contains(event.target);
|
||||
|
||||
if (!isClickInsideButton && !isClickInsideMenu) {
|
||||
kebabButton.setAttribute('aria-expanded', 'false');
|
||||
kebabMenu.style.display = 'none';
|
||||
console.log("Menu is hidden");
|
||||
}
|
||||
});
|
||||
console.log("AFTER LISTENER")
|
||||
let cancelInvitationButton = member_type === "invitedmember" ? "Cancel invitation" : "Remove member";
|
||||
wrapperDeleteAction.innerHTML = generateKebabHTML('remove-member', unique_id, cancelInvitationButton, `for ${member_name}`);
|
||||
|
||||
// This easter egg is only for fixtures that dont have names as we are displaying their emails
|
||||
// All prod users will have emails linked to their account
|
||||
addModal(member_email || "Samwise Gamgee", unique_id, num_domains, member_delete_url, wrapperDeleteAction);
|
||||
MembersTable.addMemberModal(num_domains, member_email || "Samwise Gamgee", member_delete_url, unique_id, wrapperDeleteAction);
|
||||
|
||||
initializeModals();
|
||||
|
||||
|
|
|
@ -478,7 +478,6 @@ class User(AbstractUser):
|
|||
# Get the portfolio from the session using the existing method
|
||||
|
||||
portfolio = request.session.get("portfolio")
|
||||
print(f"Portfolio from session: {portfolio}")
|
||||
|
||||
if not portfolio:
|
||||
return 0 # No portfolio found
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
>
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||
<h2 class="usa-modal__heading">
|
||||
Are you sure you want to extend the expiration date?
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
|
@ -128,7 +128,7 @@
|
|||
>
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||
<h2 class="usa-modal__heading">
|
||||
Are you sure you want to place this domain on hold?
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
|
@ -195,7 +195,7 @@
|
|||
>
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||
<h2 class="usa-modal__heading">
|
||||
Are you sure you want to remove this domain from the registry?
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
>
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||
<h2 class="usa-modal__heading">
|
||||
Are you sure you want to select ineligible status?
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||
<h2 class="usa-modal__heading">
|
||||
{{ modal_heading }}
|
||||
{%if domain_name_modal is not None %}
|
||||
<span class="domain-name-wrap">
|
||||
|
@ -16,7 +16,7 @@
|
|||
{% endif %}
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
<p id="modal-1-description">
|
||||
<p>
|
||||
{{ modal_description }}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<div id="main-content">
|
||||
<div id="toggleable-alert" class="usa-alert usa-alert--slim margin-bottom-2 display-none">
|
||||
<div class="usa-alert__body">
|
||||
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||
<p class="usa-alert__text ">
|
||||
<!-- alert message will be conditionally populated by javascript -->
|
||||
</p>
|
||||
|
|
|
@ -51,11 +51,11 @@ Edit your User Profile |
|
|||
>
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||
<h2 class="usa-modal__heading">
|
||||
Add contact information
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
<p id="modal-1-description">
|
||||
<p>
|
||||
.Gov domain registrants must maintain accurate contact information in the .gov registrar.
|
||||
Before you can manage your domain, we need you to add your contact information.
|
||||
</p>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue