diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 0096f59b5..9d00493e9 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -958,6 +958,7 @@ class DomainRequestAdmin(ListHeaderAdmin): """Custom domain requests admin class.""" form = DomainRequestAdminForm + change_form_template = "django/admin/domain_request_change_form.html" class InvestigatorFilter(admin.SimpleListFilter): """Custom investigator filter that only displays users with the manager role""" @@ -1019,8 +1020,6 @@ class DomainRequestAdmin(ListHeaderAdmin): if self.value() == "0": return queryset.filter(Q(is_election_board=False) | Q(is_election_board=None)) - change_form_template = "django/admin/domain_request_change_form.html" - # Columns list_display = [ "requested_domain", diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index 8c60c534f..c23bf4870 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -410,3 +410,60 @@ function enableRelatedWidgetButtons(changeLink, deleteLink, viewLink, elementPk, }); observer.observe({ type: "navigation" }); })(); + +/** An IIFE for toggling the submit bar on domain request forms +*/ +(function (){ + // Get a reference to the button element + const toggleButton = document.getElementById('submitRowToggle'); + const submitRowWrapper = document.querySelector('.submit-row-wrapper'); + + if (toggleButton) { + // Add event listener to toggle the class and update content on click + toggleButton.addEventListener('click', function() { + // Toggle the 'collapsed' class on the bar + submitRowWrapper.classList.toggle('collapsed'); + + // Get a reference to the span element inside the button + const spanElement = this.querySelector('span'); + + // Get a reference to the use element inside the button + const useElement = this.querySelector('use'); + + // Check if the span element text is 'Hide' + if (spanElement.textContent.trim() === 'Hide') { + // Update the span element text to 'Show' + spanElement.textContent = 'Show'; + + // Update the xlink:href attribute to expand_more + useElement.setAttribute('xlink:href', '/public/img/sprite.svg#expand_less'); + } else { + // Update the span element text to 'Hide' + spanElement.textContent = 'Hide'; + + // Update the xlink:href attribute to expand_less + useElement.setAttribute('xlink:href', '/public/img/sprite.svg#expand_more'); + } + }); + + // We have a scroll indicator at the end of the page. + // Observe it. Once it gets on screen, test to see if the row is collapsed. + // If it is, expand it. + const targetElement = document.querySelector(".scroll-indicator"); + const options = { + threshold: 1 // Adjust the threshold as needed (1 indicates when the target element is fully visible) + }; + // Create a new Intersection Observer + const observer = new IntersectionObserver((entries, observer) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + // Refresh reference to submit row wrapper and check if it's collapsed + if (document.querySelector('.submit-row-wrapper').classList.contains('collapsed')) { + toggleButton.click(); + } + } + }); + }, options); + observer.observe(targetElement); + } +})(); \ No newline at end of file diff --git a/src/registrar/assets/js/get-gov-reports.js b/src/registrar/assets/js/get-gov-reports.js index d10cf2dc6..025a94275 100644 --- a/src/registrar/assets/js/get-gov-reports.js +++ b/src/registrar/assets/js/get-gov-reports.js @@ -54,64 +54,75 @@ })(); -document.addEventListener("DOMContentLoaded", function () { - createComparativeColumnChart("myChart1", "Managed domains", "Start Date", "End Date"); - createComparativeColumnChart("myChart2", "Unmanaged domains", "Start Date", "End Date"); - createComparativeColumnChart("myChart3", "Deleted domains", "Start Date", "End Date"); - createComparativeColumnChart("myChart4", "Ready domains", "Start Date", "End Date"); - createComparativeColumnChart("myChart5", "Submitted requests", "Start Date", "End Date"); - createComparativeColumnChart("myChart6", "All requests", "Start Date", "End Date"); -}); +/** An IIFE to initialize the analytics page +*/ +(function () { + function createComparativeColumnChart(canvasId, title, labelOne, labelTwo) { + var canvas = document.getElementById(canvasId); + if (!canvas) { + return + } -function createComparativeColumnChart(canvasId, title, labelOne, labelTwo) { - var canvas = document.getElementById(canvasId); - var ctx = canvas.getContext("2d"); + var ctx = canvas.getContext("2d"); - var listOne = JSON.parse(canvas.getAttribute('data-list-one')); - var listTwo = JSON.parse(canvas.getAttribute('data-list-two')); + var listOne = JSON.parse(canvas.getAttribute('data-list-one')); + var listTwo = JSON.parse(canvas.getAttribute('data-list-two')); - var data = { - labels: ["Total", "Federal", "Interstate", "State/Territory", "Tribal", "County", "City", "Special District", "School District", "Election Board"], - datasets: [ - { - label: labelOne, - backgroundColor: "rgba(255, 99, 132, 0.2)", - borderColor: "rgba(255, 99, 132, 1)", - borderWidth: 1, - data: listOne, + var data = { + labels: ["Total", "Federal", "Interstate", "State/Territory", "Tribal", "County", "City", "Special District", "School District", "Election Board"], + datasets: [ + { + label: labelOne, + backgroundColor: "rgba(255, 99, 132, 0.2)", + borderColor: "rgba(255, 99, 132, 1)", + borderWidth: 1, + data: listOne, + }, + { + label: labelTwo, + backgroundColor: "rgba(75, 192, 192, 0.2)", + borderColor: "rgba(75, 192, 192, 1)", + borderWidth: 1, + data: listTwo, + }, + ], + }; + + var options = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: title + } }, - { - label: labelTwo, - backgroundColor: "rgba(75, 192, 192, 0.2)", - borderColor: "rgba(75, 192, 192, 1)", - borderWidth: 1, - data: listTwo, + scales: { + y: { + beginAtZero: true, + }, }, - ], + }; + + new Chart(ctx, { + type: "bar", + data: data, + options: options, + }); + } + + function initComparativeColumnCharts() { + document.addEventListener("DOMContentLoaded", function () { + createComparativeColumnChart("myChart1", "Managed domains", "Start Date", "End Date"); + createComparativeColumnChart("myChart2", "Unmanaged domains", "Start Date", "End Date"); + createComparativeColumnChart("myChart3", "Deleted domains", "Start Date", "End Date"); + createComparativeColumnChart("myChart4", "Ready domains", "Start Date", "End Date"); + createComparativeColumnChart("myChart5", "Submitted requests", "Start Date", "End Date"); + createComparativeColumnChart("myChart6", "All requests", "Start Date", "End Date"); + }); }; - - var options = { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - position: 'top', - }, - title: { - display: true, - text: title - } - }, - scales: { - y: { - beginAtZero: true, - }, - }, - }; - - new Chart(ctx, { - type: "bar", - data: data, - options: options, - }); -} \ No newline at end of file + initComparativeColumnCharts(); +})(); \ No newline at end of file diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index b7a494aef..bb7be78f9 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -349,4 +349,36 @@ input.admin-confirm-button { .errors span.select2-selection { border: 1px solid var(--error-fg) !important; +} + +// Sticky submit bar for domain requests on desktop +@include at-media(desktop) { + .submit-row-wrapper { + position: fixed; + bottom: 0; + right: 0; + background: var(--darkened-bg); + border-top-left-radius: 6px; + transition: transform .2s ease-out; + .submit-row { + margin-bottom: 0; + } + } + + .submit-row-wrapper.collapsed { + // translate3d is more performant than translateY + // https://stackoverflow.com/questions/22111256/translate3d-vs-translate-performance + transform: translate3d(0, 45px, 0); + } + .submit-row-toggle{ + display: inline-block; + position: absolute; + top: -30px; + right: 0; + background: var(--darkened-bg); + } + + #submitRowToggle { + color: var( --body-fg); + } } \ No newline at end of file diff --git a/src/registrar/assets/sass/_theme/_base.scss b/src/registrar/assets/sass/_theme/_base.scss index 127db5589..dac94255a 100644 --- a/src/registrar/assets/sass/_theme/_base.scss +++ b/src/registrar/assets/sass/_theme/_base.scss @@ -117,6 +117,10 @@ abbr[title] { } } +.visible-desktop { + display: none; +} + @include at-media(desktop) { .float-right-desktop { float: right; @@ -124,33 +128,15 @@ abbr[title] { .float-left-desktop { float: left; } + .visible-desktop { + display: block; + } } .flex-end { align-items: flex-end; } -// Only apply this custom wrapping to desktop -@include at-media(desktop) { - .usa-tooltip__body { - width: 350px; - white-space: normal; - text-align: center; - } -} - -@include at-media(tablet) { - .usa-tooltip__body { - width: 250px !important; - white-space: normal !important; - text-align: center !important; - } -} - -@include at-media(mobile) { - .usa-tooltip__body { - width: 250px !important; - white-space: normal !important; - text-align: center !important; - } -} +.cursor-pointer { + cursor: pointer; +} \ No newline at end of file diff --git a/src/registrar/assets/sass/_theme/_buttons.scss b/src/registrar/assets/sass/_theme/_buttons.scss index ef8635b95..1f5047503 100644 --- a/src/registrar/assets/sass/_theme/_buttons.scss +++ b/src/registrar/assets/sass/_theme/_buttons.scss @@ -138,6 +138,13 @@ a.usa-button--unstyled:visited { } } +.usa-button--unstyled--white, +.usa-button--unstyled--white:hover, +.usa-button--unstyled--white:focus, +.usa-button--unstyled--white:active { + color: color('white'); +} + // Cancel button used on the // DNSSEC main page // We want to center this button on mobile diff --git a/src/registrar/assets/sass/_theme/_tooltips.scss b/src/registrar/assets/sass/_theme/_tooltips.scss new file mode 100644 index 000000000..01348e1b1 --- /dev/null +++ b/src/registrar/assets/sass/_theme/_tooltips.scss @@ -0,0 +1,26 @@ +@use "uswds-core" as *; + +// Only apply this custom wrapping to desktop +@include at-media(desktop) { + .usa-tooltip__body { + width: 350px; + white-space: normal; + text-align: center; + } +} + +@include at-media(tablet) { + .usa-tooltip__body { + width: 250px !important; + white-space: normal !important; + text-align: center !important; + } +} + +@include at-media(mobile) { + .usa-tooltip__body { + width: 250px !important; + white-space: normal !important; + text-align: center !important; + } +} diff --git a/src/registrar/assets/sass/_theme/styles.scss b/src/registrar/assets/sass/_theme/styles.scss index 0239199e7..942501110 100644 --- a/src/registrar/assets/sass/_theme/styles.scss +++ b/src/registrar/assets/sass/_theme/styles.scss @@ -13,6 +13,7 @@ @forward "lists"; @forward "buttons"; @forward "forms"; +@forward "tooltips"; @forward "fieldsets"; @forward "alerts"; @forward "tables"; diff --git a/src/registrar/templates/django/admin/domain_request_change_form.html b/src/registrar/templates/django/admin/domain_request_change_form.html index 95392da1e..32aee7c71 100644 --- a/src/registrar/templates/django/admin/domain_request_change_form.html +++ b/src/registrar/templates/django/admin/domain_request_change_form.html @@ -92,5 +92,24 @@ -{{ block.super }} + +
+ + + + + +

+ Requested domain: {{ original.requested_domain.name }} +

+ {{ block.super }} +
+ + + {% endblock %} \ No newline at end of file diff --git a/src/registrar/templates/domain_users.html b/src/registrar/templates/domain_users.html index e295d2f7e..9d3bbe6e6 100644 --- a/src/registrar/templates/domain_users.html +++ b/src/registrar/templates/domain_users.html @@ -134,7 +134,7 @@ {{ invitation.status|title }}
- {% csrf_token %} + {% csrf_token %}
diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 8ed61d76c..c01106b27 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -2,6 +2,7 @@ from datetime import date from django.test import TestCase, RequestFactory, Client, override_settings from django.contrib.admin.sites import AdminSite from contextlib import ExitStack +from api.tests.common import less_console_noise_decorator from django_webtest import WebTest # type: ignore from django.contrib import messages from django.urls import reverse @@ -1211,6 +1212,28 @@ class TestDomainRequestAdmin(MockEppLib): # Test that approved domain exists and equals requested domain self.assertEqual(domain_request.requested_domain.name, domain_request.approved_domain.name) + @less_console_noise_decorator + def test_sticky_submit_row_shows_requested_domain(self): + """Test that the change_form template contains a string indicative of the customization + of the sticky submit bar.""" + + # make sure there is no user with this email + EMAIL = "mayor@igorville.gov" + User.objects.filter(email=EMAIL).delete() + self.client.force_login(self.superuser) + + # Create a sample domain request + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) + + # Create a mock request + request = self.client.post("/admin/registrar/domainrequest/{}/change/".format(domain_request.pk)) + + # Since we're using client to mock the request, we can only test against + # non-interpolated values + expected_content = "Requested domain:" + + self.assertContains(request, expected_content) + def test_save_model_sets_restricted_status_on_user(self): with less_console_noise(): # make sure there is no user with this email diff --git a/src/registrar/tests/test_views_application.py b/src/registrar/tests/test_views_request.py similarity index 100% rename from src/registrar/tests/test_views_application.py rename to src/registrar/tests/test_views_request.py