Merge branch 'main' into sspj/domain-validation

This commit is contained in:
Seamus Johnston 2023-02-03 15:46:07 -06:00
commit 070f85c58e
No known key found for this signature in database
GPG key ID: 2F21225985069105
16 changed files with 881 additions and 554 deletions

View file

@ -128,15 +128,36 @@ type
docker-compose run owasp docker-compose run owasp
``` ```
## USWDS and styling # Images, stylesheets, and JavaScript
We use the U.S. Web Design System (USWDS) for building and styling our applications. Additionally, we utilize the [uswds-compile tool](https://designsystem.digital.gov/documentation/getting-started/developers/phase-two-compile/) from USWDS to compile and package the static assets.
We use the U.S. Web Design System (USWDS) for styling our applications.
Static files (images, CSS stylesheets, JavaScripts, etc) are known as "assets".
Assets are stored in `registrar/assets` during development and served from `registrar/public`. During deployment, assets are copied from `registrar/assets` into `registrar/public`. Any assets which need processing, such as USWDS Sass files, are processed before copying.
We utilize the [uswds-compile tool](https://designsystem.digital.gov/documentation/getting-started/developers/phase-two-compile/) from USWDS to compile and package USWDS assets.
## Making and view style changes
When you run `docker-compose up` the `node` service in the container will begin to watch for changes in the `registrar/assets` folder, and will recompile once any changes are made. When you run `docker-compose up` the `node` service in the container will begin to watch for changes in the `registrar/assets` folder, and will recompile once any changes are made.
Within the `registrar/assets` folder, the `_theme` folder contains three files initially generated by `uswds-compile`: Within the `registrar/assets` folder, the `_theme` folder contains three files initially generated by `uswds-compile`:
1. `_uswds-theme-custom-styles` contains all the custom styles created for this application 1. `_uswds-theme-custom-styles` contains all the custom styles created for this application
2. `_uswds-theme` contains all the custom theme settings (e.g. primary colors, fonts, banner color, etc..) 2. `_uswds-theme` contains all the custom theme settings (e.g. primary colors, fonts, banner color, etc..)
3. `styles.css` a entry point or index for the styles, forwards all of the other style files used in the project (i.e. the USWDS source code, the settings, and all custom stylesheets). 3. `styles.css` a entry point or index for the styles, forwards all of the other style files used in the project (i.e. the USWDS source code, the settings, and all custom stylesheets).
You can also compile the sass at any time using `npx gulp compile`. Similarly, you can copy over other static assets (images and javascript files), using `npx gulp copyAssets`. You can also compile the **Sass** at any time using `npx gulp compile`. Similarly, you can copy over **other static assets** (images and javascript files), using `npx gulp copyAssets`.
## Upgrading USWDS and other JavaScript packages
Version numbers can be manually controlled in `package.json`. Edit that, if desired.
Now run `docker-compose run node npm update`.
Then run `docker-compose up` to recompile and recopy the assets.
Examine the results in the running application (remember to empty your cache!) and commit `package.json` and `package-lock.json` if all is well.
## Finite State Machines ## Finite State Machines

932
src/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@uswds/uswds": "^3.1.0", "@uswds/uswds": "^3.3.0",
"pa11y-ci": "^3.0.1", "pa11y-ci": "^3.0.1",
"sass": "^1.54.8" "sass": "^1.54.8"
}, },

View file

@ -19,8 +19,9 @@ i.e.
@include u-padding-right('05'); @include u-padding-right('05');
---------------------------------------- ----------------------------------------
*/ */
// USWDS tokens are a bit too coarse in their letter-spacing steps
$letter-space--xs: .0125em; // Finer grained letterspacing adjustments
$letter-space--xs: .0125em;
@use "uswds-core" as *; @use "uswds-core" as *;
@ -29,35 +30,72 @@ $letter-space--xs: .0125em;
@include sr-only; @include sr-only;
} }
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.usa-logo {
margin-top: units(2);
}
.usa-logo__text {
@include typeset('sans', 'xl', 2);
color: color('primary-darker');
}
.usa-nav__primary {
margin-top: units(1);
}
h1 { h1 {
@include typeset('sans', '2xl', 2); @include typeset('sans', '2xl', 2);
margin: 0 0 units(1); margin: 0 0 units(2);
color: color('primary-darker');
} }
h2 { h2 {
font-weight: font-weight('semibold'); font-weight: font-weight('semibold');
line-height: line-height('heading', 3); line-height: line-height('heading', 3);
margin: units(4) 0 units(1); margin: units(4) 0 units(1);
&:first-of-type {
margin-top: units(2);
}
} }
.register-form-step > h1 {
//align to top of sidebar on first page of the form
margin-top: units(-1);
}
//Tighter spacing when H2 is immediatly after H1
.register-form-step .usa-fieldset:first-of-type h2:first-of-type,
.register-form-step h1 + h2 {
margin-top: units(1);
}
.register-form-step h3 { .register-form-step h3 {
color: color('primary-dark'); color: color('primary-dark');
letter-spacing: $letter-space--xs; letter-spacing: $letter-space--xs;
margin-top: units(3); margin-top: units(3);
margin-bottom: 0; margin-bottom: 0;
+ p {
margin-top: units(0.5);
}
} }
.register-form-step p { .register-form-step h4 {
margin-bottom: 0;
+ p {
margin-top: units(0.5);
}
}
.register-form-step p,
.register-form-step .usa-list li,
.dashboard p
{
@include typeset('sans', 'sm', 5); @include typeset('sans', 'sm', 5);
max-width: measure(5); max-width: measure(5);
&:last-of-type {
margin-bottom: 0;
}
} }
.register-form-step a { .register-form-step a {
@ -68,6 +106,10 @@ $letter-space--xs: .0125em;
} }
} }
.register-form-step .usa-label:first-of-type {
margin-top: units(1);
}
/* Make "placeholder" links visually obvious */ /* Make "placeholder" links visually obvious */
a[href$="todo"]::after { a[href$="todo"]::after {
background-color: yellow; background-color: yellow;
@ -99,9 +141,9 @@ a.breadcrumb__back {
.usa-icon { .usa-icon {
flex-shrink: 0; flex-shrink: 0;
//align lock body to x-height //align lock body to x-height
margin: units('2px') units(1) 0 0; margin: units('2px') units(1) 0 0;
} }
} }
} }
.stepnav { .stepnav {
@ -112,15 +154,169 @@ a.breadcrumb__back {
margin-top: units(1); margin-top: units(1);
} }
.review__step__name { .domain_example {
font-weight: font-weight('semibold'); p {
margin-bottom: 0;
}
.usa-list {
margin-top: units(0.5);
}
} }
footer { .review__step {
//Workaround because USWDS units jump from 10 to 15 margin-top: units(3);
margin-top: units(10) + units(2);
&:first-of-type {
margin-top: units(4);
}
} }
.review__step hr {
border: none; //reset
border-top: 1px solid color('primary-dark');
margin-top: 0;
margin-bottom: units(0.5);
}
.review__step__title a:visited {
color: color('primary');
}
.review__step__name {
color: color('primary-dark');
font-weight: font-weight('semibold');
margin-bottom: units(0.5);
}
/** ---- DASHBOARD ---- */
#wrapper.dashboard {
background-color: color('primary-lightest');
padding-top: units(5);
}
section.dashboard {
background-color: color('white');
border: 1px solid color('base-lighter');
border-radius: 4px;
padding: 0 units(2) units(3);
margin-top: units(3);
h2 {
margin-bottom: units(2);
}
p {
margin-bottom: 0;
}
.usa-table {
width: 100%;
a {
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);
}
}
}
// Table on small mobile
.usa-table--stacked {
td, th {
padding: units(1) units(2) units(2px) 0;
border: none;
}
tr:first-child th:first-child {
border-top: none;
}
tr {
border-bottom: none;
border-top: 2px solid color('base-light');
margin-top: units(2);
&:first-child {
margin-top: 0;
}
}
td[data-label]:before,
th[data-label]:before {
color: color('primary-darker');
padding-bottom: units(2px);
}
}
@include at-media(mobile-lg) {
margin-top: units(5);
h2 {
margin-bottom: units(3);
}
.usa-table tr {
border: none;
}
.usa-table {
td, th {
border-bottom: 1px solid color('base-light');
}
thead th {
color: color('primary-darker');
border-bottom: 2px solid color('base-light');
}
tbody tr:last-of-type {
td, th {
border-bottom: 0;
}
}
td, th {
padding: units(2);
}
th:first-of-type {
padding-left: 0;
}
thead tr:first-child th:first-child {
border-top: none;
}
}
}
}
#wrapper {
padding-top: units(3);
padding-bottom: units(6) * 2 ; //Workaround because USWDS units jump from 10 to 15
}
footer {
border-top: 1px solid color('primary-darker');
}
.usa-footer__secondary-section {
background-color: color('primary-lightest');
}
abbr[title] { abbr[title] {
// workaround for underlining abbr element // workaround for underlining abbr element
border-bottom: none; border-bottom: none;

View file

@ -26,8 +26,15 @@ in the form $setting: value,
/*---------------------------- /*----------------------------
# Banner Settings # Banner Settings
-----------------------------*/ -----------------------------*/
$theme-banner-background-color: "primary-darker", $theme-banner-background-color: "primary-dark",
$theme-banner-link-color: "primary-lighter", $theme-banner-link-color: default,
/*
# Footer Settings
*/
$theme-identifier-background-color: 'primary-darker',
$theme-identifier-identity-domain-color: 'base-lighter',
$theme-identifier-secondary-link-color: 'base-lighter',
/*---------------------------- /*----------------------------
@ -87,6 +94,8 @@ in the form $setting: value,
$theme-color-accent-cool: $dhs-light-blue-60, $theme-color-accent-cool: $dhs-light-blue-60,
$theme-color-accent-cool-dark: $dhs-light-blue-70, $theme-color-accent-cool-dark: $dhs-light-blue-70,
$theme-color-accent-cool-light: $dhs-light-blue-40, $theme-color-accent-cool-light: $dhs-light-blue-40,
$theme-color-accent-cool-lighter: $dhs-light-blue-30,
$theme-color-accent-cool-lightest: $dhs-light-blue-15,
/*--------------------------- /*---------------------------
## Error state ## Error state
@ -112,4 +121,14 @@ in the form $setting: value,
----------------------------*/ ----------------------------*/
$theme-input-line-height: 5, $theme-input-line-height: 5,
/*---------------------------
# Component settings
-----------------------------
## Sorted Table
----------------------------*/
$theme-table-sorted-header-background-color: "accent-cool-lighter",
$theme-table-sorted-background-color: "accent-cool-lightest",
$theme-table-sorted-icon-color: "primary-darker",
$theme-table-unsorted-icon-color: "primary",
); );

View file

@ -3,7 +3,7 @@
{% block title %}Thank you for your domain request{% endblock %} {% block title %}Thank you for your domain request{% endblock %}
{% block content %} {% block content %}
<h1>Thank you!</h1> <h1>Thank you</h1>
<p> <p>
Thank you for your domain request. We'll email a copy of your request to you, Thank you for your domain request. We'll email a copy of your request to you,
@ -14,9 +14,11 @@ your authorizing official, and any contacts you added.</p>
<ul> <ul>
<li> We'll review your request. This could take up to two weeks. During <li> We'll review your request. This could take up to two weeks. During
this review we'll verify that your organization is eligible for a .gov domain, that this review we'll verify that your <ul>
your authorizing official approves your request, and that your domain <li>Organization is eligible for a .gov domain</li>
meets our naming requirements. <li>Authorizing official approves your request</li>
<li>Domain meets our naming requirements</li>
</ul>
</li> </li>
<li> You can <a href="{% url 'todo' %}"><del>check the <li> You can <a href="{% url 'todo' %}"><del>check the
@ -27,14 +29,4 @@ your authorizing official, and any contacts you added.</p>
review.</li> review.</li>
</ul> </ul>
<h2>Option to enter domain name server information</h2>
<p>Before your domain can be used we'll need information about your
domain name servers. If you have this information you can enter it now.
If you don't have it, that's okay. You can enter it later on the
<a href="{% url 'todo' %}"><del>manage your domains page</del></a>.
</p>
<p><a href="{% url 'todo' %}" class="usa-button">Enter DNS name servers</a></p>
{% endblock %} {% endblock %}

View file

@ -21,7 +21,7 @@
organization.</p> organization.</p>
<p>Here are a few domain examples for your type of organization.</p> <p>Here are a few domain examples for your type of organization.</p>
<div class="domain-example"> <div class="domain_example">
{% include "includes/domain_example.html" %} {% include "includes/domain_example.html" %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -3,7 +3,7 @@
{% block form_fields %} {% block form_fields %}
{% for step in steps.all|slice:":-1" %} {% for step in steps.all|slice:":-1" %}
<section class="review__step margin-top-205"> <section class="review__step">
<hr /> <hr />
<div class="review__step__title display-flex flex-justify"> <div class="review__step__title display-flex flex-justify">
<div class="review__step__value"> <div class="review__step__value">

View file

@ -128,11 +128,9 @@
<div class="usa-navbar"> <div class="usa-navbar">
{% block logo %} {% block logo %}
<div class="usa-logo" id="extended-logo"> <div class="usa-logo" id="extended-logo">
<em class="usa-logo__text"> <a href="{% url 'home' %}">
<a href="{% url 'home' %}" title="Home" aria-label="Home"> <strong class="usa-logo__text" >.gov </strong>
{% block site_name %}Home{% endblock %} </a>
</a>
</em>
</div> </div>
{% endblock %} {% endblock %}
<button type="button" class="usa-menu-btn">Menu</button> <button type="button" class="usa-menu-btn">Menu</button>
@ -162,6 +160,8 @@
</div> </div>
</header> </header>
{% endblock banner %} {% endblock banner %}
{% block wrapper %}
<div id="wrapper"> <div id="wrapper">
{% block messages %} {% block messages %}
{% if messages %} {% if messages %}
@ -184,6 +184,7 @@
{% block content_bottom %}{% endblock %} {% block content_bottom %}{% endblock %}
</div> </div>
{% endblock wrapper%}
{% include "includes/footer.html" %} {% include "includes/footer.html" %}
</div> <!-- /#wrapper --> </div> <!-- /#wrapper -->

View file

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block wrapper %}
<div id="wrapper" class="dashboard">
{% block messages %}
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}" {% endif %}>
{{ message }}
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}
{% block section_nav %}{% endblock %}
{% block hero %}{% endblock %}
{% block content %}{% endblock %}
<div role="complementary">{% block complementary %}{% endblock %}</div>
{% block content_bottom %}{% endblock %}
</div>
{% endblock wrapper %}

View file

@ -1,59 +1,98 @@
{% extends 'base.html' %} {% extends 'dashboard_base.html' %}
{% load static %}
{% block title %} Hello {% endblock %} {% block title %} Hello {% endblock %}
{% block hero %}
<section class="usa-hero">
<div class="usa-grid">
<div class="usa-hero-callout usa-section-dark">
<h1>
<span class="usa-hero-callout-alt">Welcome to the .gov registrar</span>
</h1>
</div>
</section>
{% endblock %}
{% block content %} {% block content %}
<main id="main-content" class="grid-container"> <main id="main-content" class="grid-container">
<p>This is the .gov registrar.</p>
{% if user.is_authenticated %} {% if user.is_authenticated %}
{# the entire logged in page goes here #}
{% if domain_applications %} <div class="tablet:grid-offset-1 desktop:grid-offset-2">
<h2>Your domain applications</h2> <h1>Manage your domains</h2>
<table class="usa-table usa-table--borderless">
<caption class="sr-only">Your domain applications</caption> <p><a href="{% url 'application:' %}" class="usa-button">Start a new domain request</a></p>
<thead>
<tr> <section class="dashboard tablet:grid-col-11 desktop:grid-col-10">
<th>Name</th> <h2>Registered domains</h2>
<th>Status</th> <p>You don't have any registered domains yet</p>
</tr> </section>
</thead>
<tbody> <section class="dashboard tablet:grid-col-11 desktop:grid-col-10">
{% for application in domain_applications %} <h2>Active domain requests</h2>
<tr> {% if domain_applications %}
<th> <table class="usa-table usa-table--borderless usa-table--stacked">
<a href="{% url 'edit-application' application.pk %}"> <caption class="sr-only">Your domain applications</caption>
<thead>
<tr>
<th data-sortable scope="col" role="columnheader">Domain name</th>
<th data-sortable scope="col" role="columnheader">Date created</th>
<th data-sortable scope="col" role="columnheader">Status</th>
<th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th>
</tr>
</thead>
<tbody>
{% for application in domain_applications %}
<tr>
<th th scope="row" role="rowheader" data-label="Domain name">
{{ application.requested_domain.name|default:"New domain request" }} {{ application.requested_domain.name|default:"New domain request" }}
</a> </th>
</th> <td data-sort-value="{{ application.created_at|date:"U" }}" data-label="Date created">{{ application.created_at|date }}</td>
<td>{{ application.status }}</td> <td data-label="Status">{{ application.status|title }}</td>
</tr> <td>
{% endfor %} <a href="{% url 'edit-application' application.pk %}">
</tbody> {% if application.status == "started" %}
</table> <svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="{%static 'img/sprite.svg'%}#edit"></use>
</svg>
Edit <span class="usa-sr-only">{{ application.requested_domain.name|default:"New domain request" }} </span>
{% else %}
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="{%static 'img/sprite.svg'%}#settings"></use>
</svg>
Manage <span class="usa-sr-only">{{application.requested_domain.name}} </span>
{% endif %}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div
class="usa-sr-only usa-table__announcement-region"
aria-live="polite"
></div>
{% else %}
<p>You don't have any active domain requests right now</p>
{% endif %}
<p><a href="{% url 'application:' %}" class="usa-button">Start a new domain request</a></p>
</section>
<section class="dashboard tablet:grid-col-11 desktop:grid-col-10">
<h2>Archived domains</h2>
<p>You don't have any archived domains</p>
</section>
<section class="tablet:grid-col-11 desktop:grid-col-10">
<h2 class="padding-top-1 mobile-lg:padding-top-3"> Export domains</h2>
<p>If you would like to analyze your list of domains further, you can download the list of domains and their statuses as csv file</p>
<a href="{% url 'todo' %}" class="usa-button usa-button--outline">
Export domains as csv
</a>
</section>
</div>
{% else %} {# not user.is_authenticated #}
{# the entire logged out page goes here #}
<p><a class="usa-button" href="{% url 'login' %}">
Sign in
</a></p>
{% endif %} {% endif %}
<p><a href="{% url 'application:' %}" class="usa-button">Apply</a></p>
<p><a href="{% url 'edit-profile' %}">Edit profile</a></p>
{% if user.is_staff %}
<p><a href="{% url 'admin:index' %}">CISA admin panel</a></p>
{% endif %}
<p><a href="{% url 'logout' %}">Click here to log out.</a></p>
{% else %}
<p><a href="{% url 'login' %}">Click here to log in.</a></p>
{% endif %}
</main> </main>
{% endblock %} {% endblock %}

View file

@ -44,6 +44,7 @@
<p>Domain requests from state legislatures and courts must be authorized by an agencys <strong>Chief Information Officer</strong> or <strong>highest-ranking executive</strong>.</p> <p>Domain requests from state legislatures and courts must be authorized by an agencys <strong>Chief Information Officer</strong> or <strong>highest-ranking executive</strong>.</p>
{% elif organization_type == 'tribal' %} {% elif organization_type == 'tribal' %}
<p>Domain requests from federally-recognized tribal governments must be authorized by <strong>the leader of the tribe</strong>, as recognized by the as noted by the <a href="https://www.bia.gov/service/tribal-leaders-directory" class="usa-link"> Bureau of Indian Affairs.</a></p> <p>Domain requests from federally-recognized tribal governments must be authorized by the leader of the tribe, as recognized by the <a href="https://www.bia.gov/service/tribal-leaders-directory" class="usa-link">Bureau of Indian Affairs.</a></p>
<p>Domain requests from state-recognized tribal governments must be authorized by the leader of the tribe, as determined by the states tribal recognition initiative.</p>
{% endif %} {% endif %}

View file

@ -8,12 +8,10 @@
{% elif federal_type == 'judicial' %} {% elif federal_type == 'judicial' %}
<p><strong>Examples:</strong></p> <p><strong>Examples:</strong></p>
<p>
<ul class="usa-list"> <ul class="usa-list">
<li> usprobation.gov </li> <li> usprobation.gov </li>
<li> ustaxcourt.gov </li> <li> ustaxcourt.gov </li>
</ul> </ul>
</p>
{% elif federal_type == 'legislative' %} {% elif federal_type == 'legislative' %}
<p><strong>Examples:</strong></p> <p><strong>Examples:</strong></p>

View file

@ -16,7 +16,7 @@
class="usa-footer__logo-img" class="usa-footer__logo-img"
src="{% static 'img/dottedgov-round.svg' %}" src="{% static 'img/dottedgov-round.svg' %}"
alt="dot gov registrar logo" alt="dot gov registrar logo"
width="50px" width="56px"
/> />
</div> </div>
</div> </div>

View file

@ -25,7 +25,7 @@ class TestViews(TestCase):
"""Home page should be available without a login.""" """Home page should be available without a login."""
response = self.client.get("/") response = self.client.get("/")
self.assertContains(response, "registrar", status_code=200) self.assertContains(response, "registrar", status_code=200)
self.assertContains(response, "log in") self.assertContains(response, "Sign in")
def test_whoami_page_no_user(self): def test_whoami_page_no_user(self):
"""Whoami page not accessible without a logged-in user.""" """Whoami page not accessible without a logged-in user."""
@ -69,7 +69,8 @@ class LoggedInTests(TestWithUser):
creator=self.user, requested_domain=site creator=self.user, requested_domain=site
) )
response = self.client.get("/") response = self.client.get("/")
self.assertContains(response, "igorville.gov", count=1) # count = 2 because it is also in screenreader content
self.assertContains(response, "igorville.gov", count=2)
# clean up # clean up
application.delete() application.delete()

View file

@ -70,10 +70,10 @@ class ApplicationWizard(LoginRequiredMixin, TemplateView):
TITLES = { TITLES = {
Step.ORGANIZATION_TYPE: _("Type of organization"), Step.ORGANIZATION_TYPE: _("Type of organization"),
Step.TRIBAL_GOVERNMENT: _("Tribal government"), Step.TRIBAL_GOVERNMENT: _("Tribal government"),
Step.ORGANIZATION_FEDERAL: _("Type of organization Federal"), Step.ORGANIZATION_FEDERAL: _("Type of organization: Federal"),
Step.ORGANIZATION_ELECTION: _("Type of organization — Election board"), Step.ORGANIZATION_ELECTION: _("Type of organization: Election office"),
Step.ORGANIZATION_CONTACT: _("Organization name and mailing address"), Step.ORGANIZATION_CONTACT: _("Organization name and mailing address"),
Step.TYPE_OF_WORK: _("Type of Work"), Step.TYPE_OF_WORK: _("Type of work"),
Step.AUTHORIZING_OFFICIAL: _("Authorizing official"), Step.AUTHORIZING_OFFICIAL: _("Authorizing official"),
Step.CURRENT_SITES: _("Organization website"), Step.CURRENT_SITES: _("Organization website"),
Step.DOTGOV_DOMAIN: _(".gov domain"), Step.DOTGOV_DOMAIN: _(".gov domain"),