mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-25 03:58:39 +02:00
Merge branch 'main' into za/3526-fix-link-accessibility-issues
This commit is contained in:
commit
1aaf0de5b7
1 changed files with 75 additions and 65 deletions
|
@ -69,13 +69,14 @@ export class MembersTable extends BaseTable {
|
||||||
const kebabHTML = customTableOptions.hasAdditionalActions ? generateKebabHTML('remove-member', unique_id, cancelInvitationButton, `Expand for more options for ${member.name}`): '';
|
const kebabHTML = customTableOptions.hasAdditionalActions ? generateKebabHTML('remove-member', unique_id, cancelInvitationButton, `Expand for more options for ${member.name}`): '';
|
||||||
|
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
|
row.classList.add('hide-td-borders');
|
||||||
let admin_tagHTML = ``;
|
let admin_tagHTML = ``;
|
||||||
if (member.is_admin)
|
if (member.is_admin)
|
||||||
admin_tagHTML = `<span class="usa-tag margin-left-1 bg-primary-dark text-semibold">Admin</span>`
|
admin_tagHTML = `<span class="usa-tag margin-left-1 bg-primary-dark text-semibold">Admin</span>`
|
||||||
|
|
||||||
// generate html blocks for domains and permissions for the member
|
// generate html blocks for domains and permissions for the member
|
||||||
let domainsHTML = this.generateDomainsHTML(num_domains, member.domain_names, member.domain_urls, member.action_url);
|
let domainsHTML = this.generateDomainsHTML(num_domains, member.domain_names, member.domain_urls, member.action_url, unique_id);
|
||||||
let permissionsHTML = this.generatePermissionsHTML(member.is_admin, member.permissions, customTableOptions.UserPortfolioPermissionChoices);
|
let permissionsHTML = this.generatePermissionsHTML(member.is_admin, member.permissions, customTableOptions.UserPortfolioPermissionChoices, unique_id);
|
||||||
|
|
||||||
// domainsHTML block and permissionsHTML block need to be wrapped with hide/show toggle, Expand
|
// domainsHTML block and permissionsHTML block need to be wrapped with hide/show toggle, Expand
|
||||||
let showMoreButton = '';
|
let showMoreButton = '';
|
||||||
|
@ -96,20 +97,26 @@ export class MembersTable extends BaseTable {
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
showMoreRow.innerHTML = `<td colspan='4' headers="header-member row-header-${unique_id}" class="padding-top-0"><div class='grid-row grid-gap-2'>${domainsHTML} ${permissionsHTML}</div></td>`;
|
showMoreRow.innerHTML = `
|
||||||
showMoreRow.classList.add('show-more-content');
|
<td colspan='4' headers="header-member row-header-${unique_id}" class="padding-top-0">
|
||||||
showMoreRow.classList.add('display-none');
|
${showMoreButton}
|
||||||
|
<div class='grid-row grid-gap-2 show-more-content display-none'>
|
||||||
|
${domainsHTML}
|
||||||
|
${permissionsHTML}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
showMoreRow.id = unique_id;
|
showMoreRow.id = unique_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<th role="rowheader" headers="header-member" data-label="member email" id='row-header-${unique_id}'>
|
<th class="padding-bottom-0" role="rowheader" headers="header-member" data-label="Member" id='row-header-${unique_id}'>
|
||||||
${member.member_display} ${admin_tagHTML} ${showMoreButton}
|
${member.member_display} ${admin_tagHTML}
|
||||||
</th>
|
</th>
|
||||||
<td headers="header-last-active row-header-${unique_id}" data-sort-value="${last_active.sort_value}" data-label="last_active">
|
<td class="padding-bottom-0" headers="header-last-active row-header-${unique_id}" data-sort-value="${last_active.sort_value}" data-label="Last active">
|
||||||
${last_active.display_value}
|
${last_active.display_value}
|
||||||
</td>
|
</td>
|
||||||
<td headers="header-action row-header-${unique_id}" class="width--action-column">
|
<td class="padding-bottom-0" headers="header-action row-header-${unique_id}" class="width--action-column">
|
||||||
<div class="tablet:display-flex tablet:flex-row flex-align-center">
|
<div class="tablet:display-flex tablet:flex-row flex-align-center">
|
||||||
<a href="${member.action_url}" ${customTableOptions.hasAdditionalActions ? "class='margin-right-2'" : ''}>
|
<a href="${member.action_url}" ${customTableOptions.hasAdditionalActions ? "class='margin-right-2'" : ''}>
|
||||||
<svg class="usa-icon top-1px" aria-hidden="true" focusable="false" role="img" width="24">
|
<svg class="usa-icon top-1px" aria-hidden="true" focusable="false" role="img" width="24">
|
||||||
|
@ -146,16 +153,15 @@ export class MembersTable extends BaseTable {
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} toggleButton - The button that toggles the content visibility.
|
* @param {HTMLElement} toggleButton - The button that toggles the content visibility.
|
||||||
* @param {HTMLElement} contentDiv - The content div whose visibility is toggled.
|
* @param {HTMLElement} contentDiv - The content div whose visibility is toggled.
|
||||||
* @param {HTMLElement} buttonParentRow - The parent row element containing the button.
|
|
||||||
*/
|
*/
|
||||||
function toggleShowMoreButton(toggleButton, contentDiv, buttonParentRow) {
|
function toggleShowMoreButton(toggleButton, contentDiv) {
|
||||||
const spanElement = toggleButton.querySelector('span');
|
const spanElement = toggleButton.querySelector('span');
|
||||||
const useElement = toggleButton.querySelector('use');
|
const useElement = toggleButton.querySelector('use');
|
||||||
if (contentDiv.classList.contains('display-none')) {
|
if (contentDiv.classList.contains('display-none')) {
|
||||||
showElement(contentDiv);
|
showElement(contentDiv);
|
||||||
spanElement.textContent = 'Close';
|
spanElement.textContent = 'Close';
|
||||||
useElement.setAttribute('xlink:href', '/public/img/sprite.svg#expand_less');
|
useElement.setAttribute('xlink:href', '/public/img/sprite.svg#expand_less');
|
||||||
buttonParentRow.classList.add('hide-td-borders');
|
toggleButton.classList.add('margin-bottom-2');
|
||||||
|
|
||||||
let ariaLabelText = "Close additional information";
|
let ariaLabelText = "Close additional information";
|
||||||
let ariaLabelPlaceholder = toggleButton.getAttribute("aria-label-placeholder");
|
let ariaLabelPlaceholder = toggleButton.getAttribute("aria-label-placeholder");
|
||||||
|
@ -169,7 +175,7 @@ export class MembersTable extends BaseTable {
|
||||||
hideElement(contentDiv);
|
hideElement(contentDiv);
|
||||||
spanElement.textContent = 'Expand';
|
spanElement.textContent = 'Expand';
|
||||||
useElement.setAttribute('xlink:href', '/public/img/sprite.svg#expand_more');
|
useElement.setAttribute('xlink:href', '/public/img/sprite.svg#expand_more');
|
||||||
buttonParentRow.classList.remove('hide-td-borders');
|
toggleButton.classList.remove('margin-bottom-2');
|
||||||
|
|
||||||
let ariaLabelText = "Expand for additional information";
|
let ariaLabelText = "Expand for additional information";
|
||||||
let ariaLabelPlaceholder = toggleButton.getAttribute("aria-label-placeholder");
|
let ariaLabelPlaceholder = toggleButton.getAttribute("aria-label-placeholder");
|
||||||
|
@ -182,14 +188,11 @@ export class MembersTable extends BaseTable {
|
||||||
|
|
||||||
let toggleButtons = document.querySelectorAll('.usa-button--show-more-button');
|
let toggleButtons = document.querySelectorAll('.usa-button--show-more-button');
|
||||||
toggleButtons.forEach((toggleButton) => {
|
toggleButtons.forEach((toggleButton) => {
|
||||||
|
|
||||||
// get contentDiv for element specified in data-for attribute of toggleButton
|
|
||||||
let dataFor = toggleButton.dataset.for;
|
|
||||||
let contentDiv = document.getElementById(dataFor);
|
|
||||||
let buttonParentRow = toggleButton.parentElement.parentElement;
|
let buttonParentRow = toggleButton.parentElement.parentElement;
|
||||||
if (contentDiv && contentDiv.tagName.toLowerCase() === 'tr' && contentDiv.classList.contains('show-more-content') && buttonParentRow && buttonParentRow.tagName.toLowerCase() === 'tr') {
|
let contentDiv = buttonParentRow.querySelector(".show-more-content");
|
||||||
|
if (contentDiv && buttonParentRow && buttonParentRow.tagName.toLowerCase() === 'tr') {
|
||||||
toggleButton.addEventListener('click', function() {
|
toggleButton.addEventListener('click', function() {
|
||||||
toggleShowMoreButton(toggleButton, contentDiv, buttonParentRow);
|
toggleShowMoreButton(toggleButton, contentDiv);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.warn('Found a toggle button with no associated toggleable content or parent row');
|
console.warn('Found a toggle button with no associated toggleable content or parent row');
|
||||||
|
@ -240,33 +243,40 @@ export class MembersTable extends BaseTable {
|
||||||
* @param {number} num_domains - The number of domains the member is assigned to.
|
* @param {number} num_domains - The number of domains the member is assigned to.
|
||||||
* @param {Array} domain_names - An array of domain names.
|
* @param {Array} domain_names - An array of domain names.
|
||||||
* @param {Array} domain_urls - An array of corresponding domain URLs.
|
* @param {Array} domain_urls - An array of corresponding domain URLs.
|
||||||
|
* @param {Array} unique_id - A unique row id.
|
||||||
* @returns {string} - A string of HTML displaying the domains assigned to the member.
|
* @returns {string} - A string of HTML displaying the domains assigned to the member.
|
||||||
*/
|
*/
|
||||||
generateDomainsHTML(num_domains, domain_names, domain_urls, action_url) {
|
generateDomainsHTML(num_domains, domain_names, domain_urls, action_url, unique_id) {
|
||||||
// Initialize an empty string for the HTML
|
// Initialize an empty string for the HTML
|
||||||
let domainsHTML = '';
|
let domainsHTML = '';
|
||||||
|
|
||||||
// Only generate HTML if the member has one or more assigned domains
|
// Only generate HTML if the member has one or more assigned domains
|
||||||
|
|
||||||
domainsHTML += "<div class='desktop:grid-col-4 margin-bottom-2 desktop:margin-bottom-0'>";
|
domainsHTML += "<div class='desktop:grid-col-4 margin-bottom-2 desktop:margin-bottom-0'>";
|
||||||
domainsHTML += "<h4 class='font-body-xs margin-y-0'>Domains assigned</h4>";
|
domainsHTML += `<h4 id='domains-assigned--heading-${unique_id}' class='font-body-xs margin-y-0'>Domains assigned</h4>`;
|
||||||
|
domainsHTML += `<section aria-labelledby='domains-assigned--heading-${unique_id}' tabindex='0'>`
|
||||||
if (num_domains > 0) {
|
if (num_domains > 0) {
|
||||||
domainsHTML += `<p class='font-body-xs text-base-darker margin-y-0'>This member is assigned to ${num_domains} domain${num_domains > 1 ? 's' : ''}:</p>`;
|
domainsHTML += `<p class='font-body-xs text-base-darker margin-y-0'>This member is assigned to ${num_domains} domain${num_domains > 1 ? 's' : ''}:</p>`;
|
||||||
domainsHTML += "<ul class='usa-list usa-list--unstyled margin-y-0'>";
|
if (num_domains > 1) {
|
||||||
|
domainsHTML += "<ul class='usa-list usa-list--unstyled margin-y-0'>";
|
||||||
|
|
||||||
// Display up to 6 domains with their URLs
|
// Display up to 6 domains with their URLs
|
||||||
for (let i = 0; i < num_domains && i < 6; i++) {
|
for (let i = 0; i < num_domains && i < 6; i++) {
|
||||||
domainsHTML += `<li><a class="font-body-xs" href="${domain_urls[i]}">${domain_names[i]}</a></li>`;
|
domainsHTML += `<li><a class="font-body-xs" href="${domain_urls[i]}">${domain_names[i]}</a></li>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
domainsHTML += "</ul>";
|
||||||
|
} else {
|
||||||
|
// We don't display this in a list for better screenreader support, when only one item exists.
|
||||||
|
domainsHTML += `<a class="font-body-xs" href="${domain_urls[0]}">${domain_names[0]}</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
domainsHTML += "</ul>";
|
|
||||||
} else {
|
} else {
|
||||||
domainsHTML += `<p class='font-body-xs text-base-darker margin-y-0'>This member is assigned to 0 domains.</p>`;
|
domainsHTML += `<p class='font-body-xs text-base-darker margin-y-0'>This member is assigned to 0 domains.</p>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are more than 6 domains, display a "View assigned domains" link
|
// If there are more than 6 domains, display a "View assigned domains" link
|
||||||
domainsHTML += `<p class="font-body-xs"><a href="${action_url}/domains">View domain assignments</a></p>`;
|
domainsHTML += `<p class="font-body-xs"><a href="${action_url}/domains">View domain assignments</a></p>`;
|
||||||
|
domainsHTML += "</section>"
|
||||||
domainsHTML += "</div>";
|
domainsHTML += "</div>";
|
||||||
|
|
||||||
return domainsHTML;
|
return domainsHTML;
|
||||||
|
@ -365,7 +375,7 @@ export class MembersTable extends BaseTable {
|
||||||
* - VIEW_ALL_REQUESTS
|
* - VIEW_ALL_REQUESTS
|
||||||
* - EDIT_MEMBERS
|
* - EDIT_MEMBERS
|
||||||
* - VIEW_MEMBERS
|
* - VIEW_MEMBERS
|
||||||
*
|
* @param {String} unique_id
|
||||||
* @returns {string} - A string of HTML representing the user's additional permissions.
|
* @returns {string} - A string of HTML representing the user's additional permissions.
|
||||||
* If the user has no specific permissions, it returns a default message
|
* If the user has no specific permissions, it returns a default message
|
||||||
* indicating no additional permissions.
|
* indicating no additional permissions.
|
||||||
|
@ -380,51 +390,51 @@ export class MembersTable extends BaseTable {
|
||||||
* - If no relevant permissions are found, the function returns a message stating that the user has no additional permissions.
|
* - If no relevant permissions are found, the function returns a message stating that the user has no additional permissions.
|
||||||
* - The resulting HTML always includes a header "Additional permissions for this member" and appends the relevant permission descriptions.
|
* - The resulting HTML always includes a header "Additional permissions for this member" and appends the relevant permission descriptions.
|
||||||
*/
|
*/
|
||||||
generatePermissionsHTML(is_admin, member_permissions, UserPortfolioPermissionChoices) {
|
generatePermissionsHTML(is_admin, member_permissions, UserPortfolioPermissionChoices, unique_id) {
|
||||||
let permissionsHTML = '';
|
// 1. Role
|
||||||
|
const memberAccessValue = is_admin ? "Admin" : "Basic";
|
||||||
// Define shared classes across elements for easier refactoring
|
|
||||||
let sharedParagraphClasses = "font-body-xs text-base-darker margin-top-1 p--blockquote";
|
// 2. Domain access
|
||||||
|
let domainValue = "No access";
|
||||||
// Member access
|
|
||||||
if (is_admin) {
|
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Member access: <strong>Admin</strong></p>`;
|
|
||||||
} else {
|
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Member access: <strong>Basic</strong></p>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check domain-related permissions
|
|
||||||
if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)) {
|
if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)) {
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Domains: <strong>Viewer</strong></p>`;
|
domainValue = "Viewer";
|
||||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)) {
|
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)) {
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Domains: <strong>Viewer, limited</strong></p>`;
|
domainValue = "Viewer, limited";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check request-related permissions
|
// 3. Request access
|
||||||
|
let requestValue = "No access";
|
||||||
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_REQUESTS)) {
|
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_REQUESTS)) {
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Domain requests: <strong>Creator</strong></p>`;
|
requestValue = "Creator";
|
||||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS)) {
|
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS)) {
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Domain requests: <strong>Viewer</strong></p>`;
|
requestValue = "Viewer";
|
||||||
} else {
|
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Domain requests: <strong>No access</strong></p>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check member-related permissions
|
// 4. Member access
|
||||||
|
let memberValue = "No access";
|
||||||
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_MEMBERS)) {
|
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_MEMBERS)) {
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Members: <strong>Manager</strong></p>`;
|
memberValue = "Manager";
|
||||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MEMBERS)) {
|
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MEMBERS)) {
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Members: <strong>Viewer</strong></p>`;
|
memberValue = "Viewer";
|
||||||
} else {
|
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>Members: <strong>No access</strong></p>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no specific permissions are assigned, display a message indicating no additional permissions
|
// Helper function for faster element refactoring
|
||||||
if (!permissionsHTML) {
|
const createPermissionItem = (label, value) => {
|
||||||
permissionsHTML += `<p class='${sharedParagraphClasses}'>No additional permissions: <strong>There are no additional permissions for this member</strong>.</p>`;
|
return `<p class="font-body-xs text-base-darker margin-top-1 p--blockquote">${label}: <strong>${value}</strong></p>`;
|
||||||
}
|
};
|
||||||
|
const permissionsHTML = `
|
||||||
// Add a permissions header and wrap the entire output in a container
|
<div class="desktop:grid-col-8">
|
||||||
permissionsHTML = `<div class='desktop:grid-col-8'><h4 class='font-body-xs margin-y-0'>Member access and permissions</h4>${permissionsHTML}</div>`;
|
<h4 id="member-access--heading-${unique_id}" class="font-body-xs margin-y-0">
|
||||||
|
Member access and permissions
|
||||||
|
</h4>
|
||||||
|
<section aria-labelledby="member-access--heading-${unique_id}" tabindex="0">
|
||||||
|
${createPermissionItem("Member access", memberAccessValue)}
|
||||||
|
${createPermissionItem("Domains", domainValue)}
|
||||||
|
${createPermissionItem("Domain requests", requestValue)}
|
||||||
|
${createPermissionItem("Members", memberValue)}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
return permissionsHTML;
|
return permissionsHTML;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue