Merge pull request #2655 from cisagov/hotgov/2544-design-review

Issue #2544: Design review on view and manage domains, org feature - [HOTGOV]
This commit is contained in:
Rachid Mrad 2024-09-05 12:21:54 -04:00 committed by GitHub
commit cebbd42ce6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 146 additions and 117 deletions

View file

@ -1220,7 +1220,7 @@ document.addEventListener('DOMContentLoaded', function() {
const expirationDateFormatted = expirationDate ? expirationDate.toLocaleDateString('en-US', options) : '';
const expirationDateSortValue = expirationDate ? expirationDate.getTime() : '';
const actionUrl = domain.action_url;
const suborganization = domain.suborganization ? domain.suborganization : '';
const suborganization = domain.suborganization ? domain.suborganization : '';
const row = document.createElement('tr');
@ -1229,7 +1229,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (!noPortfolioFlag) {
markupForSuborganizationRow = `
<td>
<span class="${suborganization ? 'ellipsis ellipsis--30 vertical-align-middle' : ''}" aria-label="${suborganization}" title="${suborganization}">${suborganization}</span>
<span class="text-wrap" aria-label="${domain.suborganization ? suborganization : 'No suborganization'}">${suborganization}</span>
</td>
`
}
@ -1910,7 +1910,7 @@ document.addEventListener('DOMContentLoaded', function() {
let editableFormGroup = button.parentElement.parentElement.parentElement;
if (editableFormGroup){
let readonlyField = editableFormGroup.querySelector(".input-with-edit-button__readonly-field")
let readonlyField = editableFormGroup.querySelector(".toggleable_input__readonly-field")
let inputField = document.getElementById(`id_${fieldName}`);
if (!inputField || !readonlyField) {
return;
@ -1936,8 +1936,8 @@ document.addEventListener('DOMContentLoaded', function() {
// Keep the path before '#' and replace the part after '#' with 'invalid'
const newHref = parts[0] + '#error';
svg.setAttribute('xlink:href', newHref);
fullNameField.classList.add("input-with-edit-button__error")
label = fullNameField.querySelector(".input-with-edit-button__readonly-field")
fullNameField.classList.add("toggleable_input__error")
label = fullNameField.querySelector(".toggleable_input__readonly-field")
label.innerHTML = "Unknown";
}
}
@ -2043,11 +2043,11 @@ document.addEventListener('DOMContentLoaded', function() {
// Due to the nature of how uswds works, this is slightly hacky.
// Use a MutationObserver to watch for changes in the dropdown list
const dropdownList = document.querySelector(`#${input.id}--list`);
const dropdownList = comboBox.querySelector(`#${input.id}--list`);
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === "childList") {
addBlankOption(clearInputButton, dropdownList, initialValue);
addBlankOption(clearInputButton, dropdownList, initialValue);
}
});
});
@ -2111,7 +2111,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (!initialValue){
blankOption.classList.add("usa-combo-box__list-option--selected")
}
blankOption.textContent = "---------";
blankOption.textContent = "";
dropdownList.insertBefore(blankOption, dropdownList.firstChild);
blankOption.addEventListener("click", (e) => {

View file

@ -33,16 +33,19 @@ body {
}
#wrapper.dashboard--portfolio {
background-color: color('gray-1');
padding-top: units(4)!important;
}
#wrapper.dashboard--grey-1 {
background-color: color('gray-1');
}
.section--outlined {
.section-outlined {
background-color: color('white');
border: 1px solid color('base-lighter');
border-radius: 4px;
padding: 0 units(2) units(3);
padding: 0 units(4) units(3) units(2);
margin-top: units(3);
&.margin-top-0 {
@ -72,9 +75,13 @@ body {
}
}
.section--outlined__header--no-portfolio {
.section--outlined__search,
.section--outlined__utility-button {
.section-outlined--border-base-light {
border: 1px solid color('base-light');
}
.section-outlined__header--no-portfolio {
.section-outlined__search,
.section-outlined__utility-button {
margin-top: units(2);
}
@ -82,11 +89,11 @@ body {
display: flex;
column-gap: units(3);
.section--outlined__search,
.section--outlined__utility-button {
.section-outlined__search,
.section-outlined__utility-button {
margin-top: 0;
}
.section--outlined__search {
.section-outlined__search {
flex-grow: 4;
// Align right
max-width: 383px;
@ -192,3 +199,7 @@ abbr[title] {
max-width: 50ch;
}
}
.margin-right-neg-4px {
margin-right: -4px;
}

View file

@ -124,10 +124,6 @@ a.withdraw:active {
background-color: color('error-darker');
}
.usa-button--unstyled .usa-icon {
vertical-align: bottom;
}
a.usa-button--unstyled:visited {
color: color('primary');
}
@ -162,14 +158,14 @@ a.usa-button--unstyled:visited {
}
}
.input-with-edit-button {
.toggleable_input {
svg.usa-icon {
width: 1.5em !important;
height: 1.5em !important;
color: #{$dhs-green};
position: absolute;
}
&.input-with-edit-button__error {
&.toggleable_input__error {
svg.usa-icon {
color: #{$dhs-red};
}
@ -205,12 +201,32 @@ a.usa-button--unstyled:visited {
}
}
.dotgov-table a,
.usa-link--icon,
.usa-button--with-icon {
display: flex;
align-items: flex-start;
color: color('primary');
column-gap: units(.5);
align-items: center;
}
.dotgov-table a,
.usa-link--icon {
&:visited {
color: color('primary');
}
}
a .usa-icon,
.usa-button--with-icon .usa-icon {
height: 1.3em;
width: 1.3em;
}
.usa-icon.usa-icon--big {
margin: 0;
height: 1.5em;
width: 1.5em;
}
.margin-right-neg-4px {
margin-right: -4px;
}

View file

@ -1,18 +0,0 @@
@use "uswds-core" as *;
.dotgov-table a,
.usa-link--icon {
display: flex;
align-items: flex-start;
color: color('primary');
&:visited {
color: color('primary');
}
.usa-icon {
// align icon with x height
margin-top: units(0.5);
margin-right: units(0.5);
}
}

View file

@ -1,5 +1,10 @@
@use "uswds-core" as *;
td,
th {
vertical-align: top;
}
.dotgov-table--stacked {
td, th {
padding: units(1) units(2) units(2px) 0;
@ -12,7 +17,7 @@
tr {
border-bottom: none;
border-top: 2px solid color('base-light');
border-top: 2px solid color('base-lighter');
margin-top: units(2);
&:first-child {
@ -39,10 +44,6 @@
.dotgov-table {
width: 100%;
th[data-sortable]:not([aria-sort]) .usa-table__header__button {
right: auto;
}
tbody th {
word-break: break-word;
}
@ -56,7 +57,7 @@
}
td, th {
border-bottom: 1px solid color('base-light');
border-bottom: 1px solid color('base-lighter');
}
thead th {
@ -72,11 +73,17 @@
td, th,
.usa-tabel th{
padding: units(2) units(2) units(2) 0;
padding: units(2) units(4) units(2) 0;
}
thead tr:first-child th:first-child {
border-top: none;
}
}
@include at-media(tablet-lg) {
th[data-sortable]:not([aria-sort]) .usa-table__header__button {
right: auto;
}
}
}

View file

@ -10,7 +10,6 @@
--- Custom Styles ---------------------------------*/
@forward "base";
@forward "typography";
@forward "links";
@forward "lists";
@forward "accordions";
@forward "buttons";

View file

@ -417,7 +417,7 @@ class SeniorOfficialContactForm(ContactForm):
# This action should be blocked by the UI, as the text fields are readonly.
# If they get past this point, we forbid it this way.
# This could be malicious, so lets reserve information for the backend only.
raise ValueError("Senior Official cannot be modified for federal or tribal domains.")
raise ValueError("Senior official cannot be modified for federal or tribal domains.")
elif db_so.has_more_than_one_join("information_senior_official"):
# Handle the case where the domain information object is available and the SO Contact
# has more than one joined object.

View file

@ -7,7 +7,9 @@ for now we just carry the attribute to both the parent element and the select.
<div class="usa-combo-box"
{% for name, value in widget.attrs.items %}
{{ name }}="{{ value }}"
{% if name != 'id' %}
{{ name }}="{{ value }}"
{% endif %}
{% endfor %}
>
{% include "django/forms/widgets/select.html" %}

View file

@ -63,10 +63,10 @@
<div class="grid-row margin-top-1">
<div class="grid-col">
<button type="button" class="usa-button usa-button--unstyled display-block float-right-tablet delete-record">
<button type="button" class="usa-button usa-button--unstyled usa-button--with-icon float-right-tablet delete-record">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#delete"></use>
</svg><span class="margin-left-05">Delete</span>
</svg>Delete
</button>
</div>
</div>
@ -74,10 +74,10 @@
</fieldset>
{% endfor %}
<button type="button" class="usa-button usa-button--unstyled display-block margin-bottom-2" id="add-form">
<button type="button" class="usa-button usa-button--unstyled usa-button--with-icon margin-bottom-2" id="add-form">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#add_circle"></use>
</svg><span class="margin-left-05">Add new record</span>
</svg>Add new record
</button>
<button

View file

@ -52,20 +52,20 @@
{% endwith %}
</div>
<div class="tablet:grid-col-2">
<button type="button" class="usa-button usa-button--unstyled display-block delete-record margin-bottom-075">
<button type="button" class="usa-button usa-button--unstyled usa-button--with-icon delete-record margin-bottom-075">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#delete"></use>
</svg><span class="margin-left-05">Delete</span>
</svg>Delete
</button>
</div>
</div>
</div>
{% endfor %}
<button type="button" class="usa-button usa-button--unstyled display-block" id="add-form">
<button type="button" class="usa-button usa-button--unstyled usa-button--with-icon" id="add-form">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#add_circle"></use>
</svg><span class="margin-left-05">Add another name server</span>
</svg>Add another name server
</button>
{% comment %} Work around USWDS' button margins to add some spacing between the submit and the 'add more'

View file

@ -42,7 +42,7 @@
{% input_with_errors form.state_territory %}
{% with add_class="usa-input--small" %}
{% with add_class="usa-input--small" sublabel_text="Enter a zip code in the required format, like 12345 or 12345-6789." %}
{% input_with_errors form.zipcode %}
{% endwith %}

View file

@ -33,7 +33,7 @@
{% input_with_errors forms.0.state_territory %}
{% with add_class="usa-input--small" %}
{% with add_class="usa-input--small" sublabel_text="Enter a zip code in the required format, like 12345 or 12345-6789." %}
{% input_with_errors forms.0.zipcode %}
{% endwith %}

View file

@ -1,7 +1,7 @@
{% extends "domain_base.html" %}
{% load static field_helpers%}
{% block title %}Suborganization{% endblock %}
{% block title %}Suborganization{% if suborganization_name %} | suborganization_name{% endif %} | {% endblock %}
{% block domain_content %}
{# this is right after the messages block in the parent template #}

View file

@ -21,7 +21,7 @@
</ul>
{% if domain.permissions %}
<section class="section--outlined">
<section class="section-outlined">
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table--stacked dotgov-table">
<h2 class> Domain managers </h2>
<caption class="sr-only">Domain managers</caption>
@ -112,7 +112,7 @@
</section>
{% if domain.invitations.exists %}
<section class="section--outlined">
<section class="section-outlined">
<h2>Invitations</h2>
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table--stacked dotgov-table">
<caption class="sr-only">Domain invitations</caption>

View file

@ -3,7 +3,7 @@
{% comment %} Stores the json endpoint in a url for easier access {% endcomment %}
{% url 'get_domain_requests_json' as url %}
<span id="get_domain_requests_json_url" class="display-none">{{url}}</span>
<section class="section--outlined domain-requests" id="domain-requests">
<section class="section-outlined domain-requests{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domain-requests">
<div class="grid-row">
{% if not has_domain_requests_portfolio_permission %}
<div class="mobile:grid-col-12 desktop:grid-col-6">

View file

@ -5,8 +5,8 @@
{% comment %} Stores the json endpoint in a url for easier access {% endcomment %}
{% url 'get_domains_json' as url %}
<span id="get_domains_json_url" class="display-none">{{url}}</span>
<section class="section--outlined domains{% if not portfolio %} margin-top-0{% endif %}" id="domains">
<div class="section--outlined__header margin-bottom-3 {% if not portfolio %} section--outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}">
<section class="section-outlined domains margin-top-0{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domains">
<div class="section-outlined__header margin-bottom-3 {% if not portfolio %} section-outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}">
{% if not portfolio %}
<h2 id="domains-header" class="display-inline-block">Domains</h2>
<span class="display-none" id="no-portfolio-js-flag"></span>
@ -14,7 +14,7 @@
<!-- Embedding the portfolio value in a data attribute -->
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
{% endif %}
<div class="section--outlined__search {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6{% endif %}">
<div class="section-outlined__search {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6{% endif %}">
<section aria-label="Domains search component" class="margin-top-2">
<form class="usa-search usa-search--small" method="POST" role="search">
{% csrf_token %}
@ -43,10 +43,10 @@
</section>
</div>
{% if user_domain_count and user_domain_count > 0 %}
<div class="section--outlined__utility-button mobile-lg:padding-right-105 {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6 desktop:padding-left-3{% endif %}">
<section aria-label="Domains report component" class="mobile-lg:margin-top-205">
<a href="{% url 'export_data_type_user' %}" class="usa-button usa-button--unstyled" role="button">
<svg class="usa-icon usa-icon--big margin-right-neg-4px" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<div class="section-outlined__utility-button mobile-lg:padding-right-105 {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6 desktop:padding-left-3{% endif %}">
<section aria-label="Domains report component" class="margin-top-205">
<a href="{% url 'export_data_type_user' %}" class="usa-button usa-button--unstyled usa-button--with-icon" role="button">
<svg class="usa-icon usa-icon--big" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#file_download"></use>
</svg>Export as CSV
</a>

View file

@ -12,6 +12,29 @@
<button type="button" class="usa-nav__close">
<img src="{%static 'img/usa-icons/close.svg'%}" role="img" alt="Close" />
</button>
<div class="usa-nav__secondary">
<ul class="usa-nav__secondary-links">
<li class="usa-nav__secondary-item">
{% if user.is_authenticated %}
<span class="ellipsis usa-nav__username">{{ user.email }}</span>
</li>
{% if has_profile_feature_flag %}
<li class="usa-nav__secondary-item">
{% url 'user-profile' as user_profile_url %}
{% url 'finish-user-profile-setup' as finish_setup_url %}
<a class="usa-nav-link {% if path == user_profile_url or path == finish_setup_url %}usa-current{% endif %}" href="{{ user_profile_url }}">
Your profile
</a>
</li>
{% endif %}
<li class="usa-nav__secondary-item">
<a class="usa-nav-link" href="{% url 'logout' %}">Sign out</a>
{% else %}
<a class="usa-nav-link" href="{% url 'login' %}">Sign in</a>
{% endif %}
</li>
</ul>
</div>
<ul class="usa-nav__primary usa-accordion">
<li class="usa-nav__primary-item">
{% if has_domains_portfolio_permission %}
@ -45,36 +68,13 @@
<li class="usa-nav__primary-item">
{% url 'organization' as url %}
<!-- Move the padding from the a to the span so that the descenders do not get cut off -->
<a href="{{ url }}" class="usa-nav-link padding-y-0">
<a href="{{ url }}" class="usa-nav-link padding-y-0 {% if request.path == '/organization/' %} usa-current{% endif %}">
<span class="ellipsis ellipsis--23 ellipsis--desktop-50 padding-y-1 desktop:padding-y-2">
{{ portfolio.organization_name }}
</span>
</a>
</li>
</ul>
<div class="usa-nav__secondary">
<ul class="usa-nav__secondary-links">
<li class="usa-nav__secondary-item">
{% if user.is_authenticated %}
<span class="ellipsis usa-nav__username">{{ user.email }}</span>
</li>
{% if has_profile_feature_flag %}
<li class="usa-nav__secondary-item">
{% url 'user-profile' as user_profile_url %}
{% url 'finish-user-profile-setup' as finish_setup_url %}
<a class="usa-nav-link {% if path == user_profile_url or path == finish_setup_url %}usa-current{% endif %}" href="{{ user_profile_url }}">
Your profile
</a>
</li>
{% endif %}
<li class="usa-nav__secondary-item">
<a class="usa-nav-link" href="{% url 'logout' %}">Sign out</a>
{% else %}
<a class="usa-nav-link" href="{% url 'login' %}">Sign in</a>
{% endif %}
</li>
</ul>
</div>
</div>
</nav>
{% endblock %}

View file

@ -4,7 +4,7 @@
{% include "includes/form_errors.html" with form=form %}
{% endif %}
<h1>Senior Official</h1>
<h1>Senior official</h1>
<p>
Your senior official is a person within your organization who can authorize domain requests.

View file

@ -1,6 +1,6 @@
{% load static field_helpers url_helpers custom_filters %}
<div id="{{field.name}}__edit-button-readonly" class="margin-top-2 margin-bottom-1 input-with-edit-button {% if not field.value and field.field.required %}input-with-edit-button__error{% endif %}">
<div id="{{field.name}}__edit-button-readonly" class="margin-top-2 margin-bottom-1 toggleable_input {% if not field.value and field.field.required %}toggleable_input__error{% endif %}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
{% if field.value or not field.field.required %}
<use xlink:href="{%static 'img/sprite.svg'%}#check_circle"></use>
@ -8,7 +8,7 @@
<use xlink:href="{%static 'img/sprite.svg'%}#error"></use>
{%endif %}
</svg>
<div class="display-inline padding-left-05 margin-left-3 input-with-edit-button__readonly-field {% if not field.field.required %}text-base{% endif %}">
<div class="display-inline padding-left-05 margin-left-3 toggleable_input__readonly-field {% if not field.field.required %}text-base{% endif %}">
{% if field.name != "phone" %}
{{ field.value }}
{% else %}

View file

@ -5,9 +5,10 @@
{% block title %} Domains | {% endblock %}
{% block portfolio_content %}
<div id="main-content">
<h1 id="domains-header">Domains</h1>
<section class="section--outlined">
<div class="section--outlined__header margin-bottom-3">
<section class="section-outlined">
<div class="section-outlined__header margin-bottom-3">
<h2 id="domains-header" class="display-inline-block">You arent managing any domains.</h2>
{% if portfolio_administrators %}
<p>If you believe you should have access to a domain, reach out to your organizations administrators.</p>
@ -27,4 +28,5 @@
{% endif %}
</div>
</section>
</div>
{% endblock %}

View file

@ -1,10 +1,10 @@
{% extends "base.html" %}
{% block wrapper %}
<div id="wrapper" class="dashboard--portfolio">
{% block content %}
<div id="wrapper" class="{% block wrapper_class %}dashboard--portfolio{% endblock %}">
{% block content %}
<main id="main-content" class="grid-container">
<main class="grid-container">
{% if user.is_authenticated %}
{# the entire logged in page goes here #}
@ -26,10 +26,8 @@
{% endif %}
</main>
{% endblock %}
{% endblock content%}
<div role="complementary">{% block complementary %}{% endblock %}</div>
{% block content_bottom %}{% endblock %}
</div>
{% endblock wrapper %}

View file

@ -4,7 +4,13 @@
{% block title %} Domains | {% endblock %}
{% block wrapper_class %}
{{ block.super }} dashboard--grey-1
{% endblock %}
{% block portfolio_content %}
<div id="main-content">
<h1 id="domains-header">Domains</h1>
{% include "includes/domains_table.html" with portfolio=portfolio user_domain_count=user_domain_count %}
</div>
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends 'portfolio_base.html' %}
{% load static field_helpers%}
{% block title %}Organization mailing address | {{ portfolio.name }} | {% endblock %}
{% block title %}Organization mailing address | {{ portfolio.name }}{% endblock %}
{% load static %}
@ -17,7 +17,7 @@
{% include 'portfolio_organization_sidebar.html' %}
</div>
<div class="tablet:grid-col-9">
<div class="tablet:grid-col-9" id="main-content">
<h1>Organization</h1>
@ -41,7 +41,7 @@
{% input_with_errors form.address_line2 %}
{% input_with_errors form.city %}
{% input_with_errors form.state_territory %}
{% with add_class="usa-input--small" %}
{% with add_class="usa-input--small" sublabel_text="Enter a zip code in the required format, like 12345 or 12345-6789." %}
{% input_with_errors form.zipcode %}
{% endwith %}
<button type="submit" class="usa-button">

View file

@ -4,7 +4,12 @@
{% block title %} Domain requests | {% endblock %}
{% block wrapper_class %}
{{ block.super }} dashboard--grey-1
{% endblock %}
{% block portfolio_content %}
<div id="main-content">
<h1 id="domain-requests-header">Domain requests</h1>
{% comment %}
@ -20,4 +25,5 @@
</p>
{% include "includes/domain_requests_table.html" with portfolio=portfolio %}
</div>
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends 'portfolio_base.html' %}
{% load static field_helpers%}
{% block title %}Senior Official | {{ portfolio.name }} | {% endblock %}
{% block title %}Senior official | {{ portfolio.name }}{% endblock %}
{% load static %}
@ -17,7 +17,7 @@
{% include 'portfolio_organization_sidebar.html' %}
</div>
<div class="tablet:grid-col-9">
<div class="tablet:grid-col-9" id="main-content">
{% include "includes/senior_official.html" with can_edit=False %}
</div>
</div>

View file

@ -1153,7 +1153,7 @@ class TestDomainSeniorOfficial(TestDomainOverview):
def test_domain_senior_official(self):
"""Can load domain's senior official page."""
page = self.client.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
self.assertContains(page, "Senior official", count=3)
self.assertContains(page, "Senior official", count=4)
@less_console_noise_decorator
def test_domain_senior_official_content(self):