SCSS refactor, theme cleanup

This commit is contained in:
Alex Janousek
2025-11-05 09:24:31 -05:00
parent 1566c70027
commit 937417480e
33 changed files with 269 additions and 223 deletions

View File

@@ -228,9 +228,9 @@
var userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
var url = type === 'service'
? `/services/${currentServiceId}/daily-stats.json?timezone=${encodeURIComponent(userTimezone)}`
: `/services/${currentServiceId}/daily-stats-by-user.json?timezone=${encodeURIComponent(userTimezone)}`;
var url = (type === 'service' ?
`/services/${currentServiceId}/daily-stats.json?timezone=${encodeURIComponent(userTimezone)}` :
`/services/${currentServiceId}/daily-stats-by-user.json?timezone=${encodeURIComponent(userTimezone)}`);
try {
const response = await fetch(url);
@@ -292,20 +292,20 @@
}
};
function startPolling() {
var startPolling = function() {
fetchData(currentType);
pollInterval = setInterval(() => {
fetchData(currentType);
}, POLL_INTERVAL_MS);
}
};
function stopPolling() {
var stopPolling = function() {
if (pollInterval) {
clearInterval(pollInterval);
pollInterval = null;
}
}
};
document.addEventListener('visibilitychange', () => {
if (document.hidden) {

View File

@@ -28,9 +28,9 @@
// Determine button label
const isMultiple = name !== thing;
const buttonLabel = isMultiple
? `Copy ${thing}`
: `Copy ${thing} to clipboard`;
const buttonLabel = (isMultiple ?
`Copy ${thing}` :
`Copy ${thing} to clipboard`);
const srSuffix = isMultiple ? ` for ${name}` : '';
// Create simple HTML structure

View File

@@ -27,10 +27,11 @@
var modules = this.find(container);
for (var i = 0, l = modules.length; i < l; i++) {
var type;
try {
var module;
var element = modules[i];
var type = this.camelCaseAndCapitalise(element.dataset.module);
type = this.camelCaseAndCapitalise(element.dataset.module);
var started = element.dataset.moduleStarted;
if (typeof window.NotifyModules[type] === 'function' && !started) {

View File

@@ -0,0 +1,25 @@
@use 'uswds-core' as *;
.usa-banner__header-action {
font-size: size('body', 2);
}
.usa-nav__primary > .usa-nav__primary-item > a.usa-nav__link {
font-weight: 400;
}
h1 {
font-weight: bold;
}
.usa-section--dark h1 {
color: color('blue-30v');
@include at-media-max('desktop') {
font-size: size('body', 12);
}
}
p,
.list li {
font-family: family('sans');
font-size: size('body', 6);
}

View File

@@ -1,148 +0,0 @@
@use "uswds-core" as *;
$delivered: color('blue-50v');
$pending: color('green-cool-40v');
$failed: color('gray-cool-20');
.chart-container {
display: flex;
&.usage {
height: units(4);
}
svg {
overflow: visible;
}
}
#totalMessageChartContainer {
max-width: 600px;
}
.bar {
border-radius: units(0.5);
&.delivered, &.usage {
background-color: $delivered;
margin-right: 1px;
}
&.pending{
background-color: $pending;
margin-right: 1px;
}
&.failed, &.remaining {
background-color: $failed;
}
}
.legend {
display: flex;
margin: units(1) 0;
.legend-item {
display: flex;
align-items: flex-start;
margin-right: units(2);
}
.legend-color {
width: units(3);
height: units(3);
margin-right: 0;
padding: 0;
border-radius: 2px;
background-color: $delivered;
&.pending {
background-color: $pending;
}
&.failed, &.remaining {
background-color: $failed;
}
}
.legend-value {
margin: 0 units(1);
}
}
.progress-bar {
width: 300px;
height: 20px;
background-color: #eee;
border-radius: 5px;
margin-bottom: 10px;
}
.progress-bar-inner {
height: 100%;
background-color: #007bff;
border-radius: inherit;
}
.chart {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: units(1) units(1) 0;
}
.chart-subtitle {
font-size: size("body", 6);
font-weight: bold;
text-align: left;
width: 100%;
padding: 0;
margin: 0;
}
.axis text {
font-size: size("body", 1);
}
.axis line,
.axis path {
shape-rendering: crispEdges;
stroke: #000;
fill: none;
}
.bar {
fill-opacity: 0.8;
}
.chart-container {
width: 100%;
position: relative;
}
.chart-legend {
display: flex;
align-items: center;
}
.legend-item {
display: flex;
align-items: center;
margin-right: units(2);
.legend-rect {
width: units(2);
height: units(2);
margin-right: units(1);
}
}
#tooltip {
position: absolute;
display: none;
background: color('ink');
color: #FFF;
border: 1px solid #ccc;
padding: units(1);
border-radius: units(1);
pointer-events: none;
z-index: 100;
font-size: size("body", 3);
line-height: 1.3;
}

View File

@@ -4,7 +4,7 @@
display: flex;
&.usage {
height: units(4);
height: units(5);
}
svg {
@@ -17,7 +17,7 @@
}
.bar {
border-radius: units(0.5);
border-radius: units(1);
&.delivered,
&.usage {
@@ -42,12 +42,6 @@
display: flex;
margin: units(1) 0;
.legend-item {
display: flex;
align-items: flex-start;
margin-right: units(2);
}
.legend-color {
width: units(3);
height: units(3);
@@ -88,10 +82,14 @@
}
}
.legend .legend-item {
align-items: flex-start;
}
.progress-bar {
width: 300px;
height: 20px; // Specific design requirement
background-color: color('gray-5');
width: units('card');
height: units(3); // Specific design requirement
background-color: color('gray-cool-5');
border-radius: units(1);
margin-bottom: units(2);
}

View File

@@ -26,7 +26,7 @@
&-alternate-link {
display: inline-block;
line-height: 35px; // Specific design requirement
line-height: units(4); // Specific design requirement
a {
font-weight: bold;
@@ -36,10 +36,10 @@
.page-footer {
position: relative;
margin-bottom: 30px; // Specific design requirement
margin-bottom: units(4); // Specific design requirement
&-link {
line-height: 40px; // Specific design requirement
line-height: units(5); // Specific design requirement
padding: 1px 0 0 units(2);
font-weight: normal;

View File

@@ -9,7 +9,7 @@
padding: units(2);
background: color('gray-cool-10');
border: 1px solid color('gray-cool-10');
border-radius: units(0.5);
border-radius: units(1);
white-space: normal;
margin: 0 0 units(1) 0;
clear: both;
@@ -49,17 +49,17 @@
.sms-message-file-name,
.sms-message-scheduler,
.sms-message-template {
margin: units(0.5) 0 0;
margin: units(1) 0 0;
}
.sms-message-recipient {
color: color('gray-cool-90');
margin: units(0.5) 0 units(2);
margin: units(1) 0 units(2);
}
.sms-message-status {
color: color('gray-cool-90');
margin: -20px units(1) 20px units(1); // Specific spacing requirement
margin: units('neg-205') units(1) units('205') units(1); // Specific spacing requirement
}
.sms-message-status-outbound {
@@ -67,7 +67,7 @@
}
h2.sms-message-header {
margin-bottom: units(0.5);
margin-bottom: units(1);
}
.usa-prose > * + h2.message-header {
@@ -75,7 +75,7 @@ h2.sms-message-header {
}
h2.recipient-list {
margin-bottom: units(0.5);
margin-bottom: units(1);
}
.sms-message-row {

View File

@@ -28,7 +28,7 @@
td {
border-top: 0;
min-width: $spreadsheet-column-width - $spreadsheet-column-padding;
min-width: calc(#{$spreadsheet-column-width} - #{$spreadsheet-column-padding});
&:first-child {
min-width: auto;
@@ -76,7 +76,7 @@
position: absolute;
top: 0;
right: 0;
width: units(0.5);
width: units(1);
height: 100%;
z-index: $z-index-fullscreen-shadow;
@@ -153,7 +153,7 @@
}
&-scrolled-table {
padding-bottom: 20px; // Specific design requirement
padding-bottom: units(3); // Specific design requirement
.table-field-heading-first,
.table-field-index {
@@ -168,11 +168,11 @@
width: 100%;
position: relative;
z-index: 9;
margin-bottom: 30px; // Specific design requirement
margin-bottom: units(4); // Specific design requirement
pointer-events: none;
& + .table-show-more-link {
margin-top: -20px; // Specific design requirement
margin-top: units('neg-3'); // Specific design requirement
}
}
}

View File

@@ -0,0 +1,52 @@
@use 'uswds-core' as *;
.usa-banner__header-action {
font-size: size('body', 2);
}
.usa-nav__primary > .usa-nav__primary-item > a.usa-nav__link {
font-size: size('body', 5);
cursor: pointer;
font-weight: 400;
}
h1 {
font-weight: bold;
}
.usa-section--dark h1 {
color: color('blue-30v');
@include at-media-max('desktop') {
font-size: size('body', 12);
}
}
p,
.list li {
font-family: family('sans');
font-size: size('body', 6);
}
.large-number {
font-size: size('body', 16);
}
.template-footer .usa-link,
.template-content-count,
.table-empty-message,
td.table-empty-message {
font-size: size('body', 'sm');
}
.textbox-highlight-background,
.textbox-highlight-foreground,
.textbox-highlight-mask,
.textbox-highlight-textbox {
font-size: units(2);
line-height: 1.2;
padding: units(1);
font-family: family('sans');
}
.hint {
font-size: size('body', 'sm');
font-weight: normal;
}

View File

@@ -1,5 +1,33 @@
@use 'uswds-core' as *;
// Fix nav container to maintain content width but extend border
.usa-header--extended {
.usa-nav {
@include at-media('desktop') {
border-top: 1px solid color('gray-cool-10');
position: relative;
// Extend border beyond container
&::before {
content: "";
position: absolute;
top: -1px;
left: 50%;
transform: translateX(-50%);
width: 100vw;
height: 1px;
background-color: color('gray-cool-10');
z-index: -1;
}
}
}
.usa-nav-container {
max-width: 100%;
padding: 0;
}
}
.usa-header--extended .usa-logo {
font-family: family('sans');
margin: units(3) 0;

View File

@@ -0,0 +1,23 @@
@use 'uswds-core' as *;
.usa-nav__primary {
& > .usa-nav__primary-item > a {
font-size: size('body', 5);
cursor: pointer;
@include at-media('desktop') {
line-height: 1;
padding: units(2);
align-items: center;
color: color('gray-cool-60');
display: flex;
}
}
& > .usa-nav__primary-item {
@include at-media('desktop') {
font-size: size('body', 3);
line-height: 1;
}
}
}

View File

@@ -14,15 +14,15 @@ $z-index-fullscreen-header: 200;
$z-index-spreadsheet-fixed: 1000;
// SMS message bubbles
$sms-bubble-max-width: 464px;
$sms-bubble-arrow-size: 10px;
$sms-bubble-arrow-width: 13px;
$sms-bubble-arrow-offset: 20px;
$sms-bubble-max-width: units('mobile'); // ~464px
$sms-bubble-arrow-size: units('105'); // ~10px
$sms-bubble-arrow-width: units(2); // ~13px
$sms-bubble-arrow-offset: units('205'); // ~20px
$sms-bubble-arrow-rotation: 17deg;
// Spreadsheet viewer
$spreadsheet-column-width: 194px;
$spreadsheet-column-padding: 11px;
$spreadsheet-scrollbar-height: 11px;
$spreadsheet-column-width: 194px; // Keep as pixels for specific layout requirement
$spreadsheet-column-padding: 16px; // Keep as pixels for calc compatibility
$spreadsheet-scrollbar-height: units(2); // ~11px
$spreadsheet-scrollbar-thumb-radius: units(1);
$spreadsheet-scrollbar-border: 2px;
$spreadsheet-scrollbar-border: 2px; // Keep as pixels for webkit scrollbar

View File

@@ -1,15 +1,27 @@
// Load USWDS theme configuration first
@forward "uswds-theme";
// Then load USWDS which will use the theme settings
@forward "uswds";
// Custom tokens
@forward "settings/tokens";
// Components
@forward "components/sms-message";
@forward "components/file-upload";
@forward "components/spreadsheet";
@forward "components/charts";
// Utilities
@forward "utilities/utilities";
// USWDS overrides
@forward "overrides/typography";
@forward "overrides/usa-header";
@forward "overrides/usa-nav";
@forward "overrides/usa-search";
@forward "overrides/modal";
// Custom overrides that MUST come last to override USWDS defaults
@use "custom-overrides";

View File

@@ -0,0 +1,38 @@
@use 'uswds-core' as *;
// Utility classes to replace inline styles
.text-error {
color: color('red-60v');
}
.width-half {
width: 50%;
}
.width-quarter {
width: 25%;
}
.no-js-toggle {
display: none;
.no-js & {
display: block;
}
}
.js-enabled-toggle {
display: none;
.js-enabled & {
display: block;
}
}
.visibility-hidden {
visibility: hidden;
}
.text-spaced {
letter-spacing: 1px;
}

View File

@@ -78,7 +78,9 @@ def get_organization_message_allowance(org_id):
def get_services_dashboard_data(organization, year):
try:
dashboard_data = organizations_client.get_organization_dashboard(organization.id, year)
dashboard_data = organizations_client.get_organization_dashboard(
organization.id, year
)
services = dashboard_data.get("services", [])
except Exception as e:
current_app.logger.error(f"Error fetching dashboard data: {e}")
@@ -100,7 +102,9 @@ def get_services_dashboard_data(organization, year):
usage_parts.append(f"{emails_sent:,} emails")
if sms_sent > 0 or sms_remainder > 0:
if sms_cost > 0:
usage_parts.append(f"{sms_sent:,} sms ({sms_remainder:,} remaining, ${sms_cost:,.2f})")
usage_parts.append(
f"{sms_sent:,} sms ({sms_remainder:,} remaining, ${sms_cost:,.2f})"
)
else:
usage_parts.append(f"{sms_sent:,} sms ({sms_remainder:,} remaining)")

View File

@@ -21,7 +21,7 @@
{% block bodyStart %}
{% block extra_javascripts_before_body %}
<noscript><iframe sandbox src="https://www.googletagmanager.com/ns.html?id=GTM-WX5NGWF"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
height="0" width="0" class="display-none visibility-hidden"></iframe></noscript>
{% endblock %}
{% endblock %}

View File

@@ -62,7 +62,7 @@
{% endif %}
{% endfor %}
</ul>
<div class="usa-nav__secondary margin-bottom-6">
<div class="usa-nav__secondary">
<ul class="usa-nav__secondary-links">
{% if secondaryNavigation %}
{% for item in secondaryNavigation %}

View File

@@ -29,7 +29,7 @@
</p>
{{ usaInsetText({
"html": '<p class="usa-body">First paragraph</p>
<p class="usa-body" style="letter-spacing: 1px;">---</p>
<p class="usa-body text-spaced">---</p>
<p class="usa-body">Second paragraph</p>',
"classes": ""})
}}

View File

@@ -110,7 +110,7 @@
{% endif %}
{{ usaButton({
"element": "a",
"text": "Add a new service",
"text": "Add service",
"href": url_for('.add_service'),
"classes": ""
}) }}

View File

@@ -66,7 +66,7 @@
</p>
{% endif %}
{% if user.failed_login_count > 0 %}
<p style="color:#b10e1e;">
<p class="text-error">
{{ user.failed_login_count }} failed login attempts
</p>
{% endif %}

View File

@@ -33,7 +33,7 @@
<div>
{{ live_search(target_selector='.user-list-item', show=True, form=form) }}
</div>
<div class="js-live-search-no-results" style="display: none;">
<div class="js-live-search-no-results display-none">
<p class="usa-body margin-0">No results found</p>
</div>
{% endif %}

View File

@@ -31,7 +31,7 @@ import usaButton %} {% block meta %}
</div>
</section>
<section class="usa-section usa-prose" aria-label="Introduction">
<div class="grid-container padding-y-4">
<div class="grid-container">
<div class="grid-row grid-gap display-flex flex-align-center">
<div class="desktop:grid-col-8 tablet:grid-col-12">
<h2 class="font-body-xl">

View File

@@ -32,7 +32,7 @@
form=search_form,
autofocus=True
) }}
<div class="js-live-search-no-results" style="display: none;">
<div class="js-live-search-no-results display-none">
<p class="usa-body margin-top-2">No templates found</p>
</div>
<nav id="template-list" aria-label="Copy template list">

View File

@@ -1,6 +1,6 @@
const { urls, baseUrl } = require('./urls');
const MISMATCH_THRESHOLD = .2;
const MISMATCH_THRESHOLD = .3;
const SCREENSHOT_DELAY = 2000;
const createScenariosFromUrls = (urls, delay = SCREENSHOT_DELAY) => {

View File

@@ -171,7 +171,7 @@ def test_choose_account_should_show_choose_accounts_page_if_no_services(
assert len(links) == 1
add_service_link = links[0]
assert normalize_spaces(page.h1.text) == "Choose service"
assert normalize_spaces(add_service_link.text) == "Add a new service"
assert normalize_spaces(add_service_link.text) == "Add service"
assert not page.select("main h2")
assert add_service_link["href"] == url_for("main.add_service")

View File

@@ -1585,9 +1585,24 @@ def test_organization_dashboard_shows_service_counts(
"app.organizations_client.get_organization_dashboard",
return_value={
"services": [
{"service_id": "1", "service_name": "Live Service", "active": True, "restricted": False},
{"service_id": "2", "service_name": "Trial Service", "active": True, "restricted": True},
{"service_id": "3", "service_name": "Suspended", "active": False, "restricted": False},
{
"service_id": "1",
"service_name": "Live Service",
"active": True,
"restricted": False,
},
{
"service_id": "2",
"service_name": "Trial Service",
"active": True,
"restricted": True,
},
{
"service_id": "3",
"service_name": "Suspended",
"active": False,
"restricted": False,
},
]
},
)
@@ -1615,11 +1630,17 @@ def test_organization_dashboard_services_table(
mocker,
active_user_with_permissions,
):
mocker.patch.dict("flask.current_app.config", {"ORGANIZATION_DASHBOARD_ENABLED": True})
mocker.patch.dict(
"flask.current_app.config", {"ORGANIZATION_DASHBOARD_ENABLED": True}
)
mocker.patch(
"app.organizations_client.get_organization_message_usage",
return_value={"messages_sent": 0, "messages_remaining": 0, "total_message_limit": 0},
return_value={
"messages_sent": 0,
"messages_remaining": 0,
"total_message_limit": 0,
},
)
mocker.patch(
"app.organizations_client.get_organization_services",

View File

@@ -17,7 +17,7 @@ def test_non_gov_user_cannot_see_add_service_button(
):
client_request.login(api_nongov_user_active)
page = client_request.get("main.choose_account")
assert "Add a new service" not in page.text
assert "Add service" not in page.text
@pytest.mark.parametrize(

View File

@@ -108,7 +108,7 @@ def e2e_created_service(e2e_test_service): # noqa: PT022
page.goto(f"{E2E_TEST_URI}/accounts")
page.wait_for_load_state("domcontentloaded")
add_service_button = page.get_by_role("button", name="Add a new service")
add_service_button = page.get_by_role("button", name="Add service")
add_service_button.click()
page.wait_for_load_state("domcontentloaded")

View File

@@ -29,9 +29,7 @@ def test_add_new_service_workflow(e2e_test_service):
expect(sign_in_heading).to_be_visible()
# Retrieve some prominent elements on the page for testing.
add_service_button = page.get_by_role(
"button", name=re.compile("Add a new service")
)
add_service_button = page.get_by_role("button", name=re.compile("Add service"))
expect(add_service_button).to_be_visible()

View File

@@ -119,9 +119,7 @@ def test_create_new_template(end_to_end_context):
expect(sign_in_heading).to_be_visible()
# Retrieve some prominent elements on the page for testing.
add_service_button = page.get_by_role(
"button", name=re.compile("Add a new service")
)
add_service_button = page.get_by_role("button", name=re.compile("Add service"))
expect(add_service_button).to_be_visible()

View File

@@ -32,9 +32,7 @@ def _setup(page):
expect(sign_in_heading).to_be_visible()
# Retrieve some prominent elements on the page for testing.
add_service_button = page.get_by_role(
"button", name=re.compile("Add a new service")
)
add_service_button = page.get_by_role("button", name=re.compile("Add service"))
expect(add_service_button).to_be_visible()

View File

@@ -34,9 +34,7 @@ def _setup(page):
expect(sign_in_heading).to_be_visible()
# Retrieve some prominent elements on the page for testing.
add_service_button = page.get_by_role(
"button", name=re.compile("Add a new service")
)
add_service_button = page.get_by_role("button", name=re.compile("Add service"))
expect(add_service_button).to_be_visible()