Merge branch 'main' into nl/1300-domain-request-button-tag-correction

This commit is contained in:
CuriousX 2025-03-03 15:44:11 -07:00 committed by GitHub
commit c52eda5e7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 90 additions and 147 deletions

View file

@ -163,6 +163,18 @@ class MyUserAdminForm(UserChangeForm):
"user_permissions": NoAutocompleteFilteredSelectMultiple("user_permissions", False), "user_permissions": NoAutocompleteFilteredSelectMultiple("user_permissions", False),
} }
# Loads "tabtitle" for this admin page so that on render the <title>
# element will only have the model name instead of
# the default string loaded by native Django admin code.
# (Eg. instead of "Select contact to change", display "Contacts")
# see "base_site.html" for the <title> code.
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = str(self.opts.verbose_name_plural).title()
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Custom init to modify the user form""" """Custom init to modify the user form"""
super(MyUserAdminForm, self).__init__(*args, **kwargs) super(MyUserAdminForm, self).__init__(*args, **kwargs)
@ -523,6 +535,18 @@ class CustomLogEntryAdmin(LogEntryAdmin):
"user_url", "user_url",
] ]
# Loads "tabtitle" for this admin page so that on render the <title>
# element will only have the model name instead of
# the default string loaded by native Django admin code.
# (Eg. instead of "Select contact to change", display "Contacts")
# see "base_site.html" for the <title> code.
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = str(self.opts.verbose_name_plural).title()
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
# We name the custom prop 'resource' because linter # We name the custom prop 'resource' because linter
# is not allowing a short_description attr on it # is not allowing a short_description attr on it
# This gets around the linter limitation, for now. # This gets around the linter limitation, for now.
@ -542,13 +566,6 @@ class CustomLogEntryAdmin(LogEntryAdmin):
change_form_template = "admin/change_form_no_submit.html" change_form_template = "admin/change_form_no_submit.html"
add_form_template = "admin/change_form_no_submit.html" add_form_template = "admin/change_form_no_submit.html"
# Select log entry to change -> Log entries
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Log entries"
return super().changelist_view(request, extra_context=extra_context)
# #786: Skipping on updating audit log tab titles for now # #786: Skipping on updating audit log tab titles for now
# def change_view(self, request, object_id, form_url="", extra_context=None): # def change_view(self, request, object_id, form_url="", extra_context=None):
# if extra_context is None: # if extra_context is None:
@ -629,6 +646,18 @@ class AdminSortFields:
class AuditedAdmin(admin.ModelAdmin): class AuditedAdmin(admin.ModelAdmin):
"""Custom admin to make auditing easier.""" """Custom admin to make auditing easier."""
# Loads "tabtitle" for this admin page so that on render the <title>
# element will only have the model name instead of
# the default string loaded by native Django admin code.
# (Eg. instead of "Select contact to change", display "Contacts")
# see "base_site.html" for the <title> code.
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = str(self.opts.verbose_name_plural).title()
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
def history_view(self, request, object_id, extra_context=None): def history_view(self, request, object_id, extra_context=None):
"""On clicking 'History', take admin to the auditlog view for an object.""" """On clicking 'History', take admin to the auditlog view for an object."""
return HttpResponseRedirect( return HttpResponseRedirect(
@ -1029,6 +1058,18 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
extra_context = {"domain_requests": domain_requests, "domains": domains, "portfolios": portfolios} extra_context = {"domain_requests": domain_requests, "domains": domains, "portfolios": portfolios}
return super().change_view(request, object_id, form_url, extra_context) return super().change_view(request, object_id, form_url, extra_context)
# Loads "tabtitle" for this admin page so that on render the <title>
# element will only have the model name instead of
# the default string loaded by native Django admin code.
# (Eg. instead of "Select contact to change", display "Contacts")
# see "base_site.html" for the <title> code.
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = str(self.opts.verbose_name_plural).title()
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
class HostIPInline(admin.StackedInline): class HostIPInline(admin.StackedInline):
"""Edit an ip address on the host page.""" """Edit an ip address on the host page."""
@ -1053,14 +1094,6 @@ class MyHostAdmin(AuditedAdmin, ImportExportModelAdmin):
search_help_text = "Search by domain or host name." search_help_text = "Search by domain or host name."
inlines = [HostIPInline] inlines = [HostIPInline]
# Select host to change -> Host
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Host"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
class HostIpResource(resources.ModelResource): class HostIpResource(resources.ModelResource):
"""defines how each field in the referenced model should be mapped to the corresponding fields in the """defines how each field in the referenced model should be mapped to the corresponding fields in the
@ -1076,14 +1109,6 @@ class HostIpAdmin(AuditedAdmin, ImportExportModelAdmin):
resource_classes = [HostIpResource] resource_classes = [HostIpResource]
model = models.HostIP model = models.HostIP
# Select host ip to change -> Host ip
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Host IP"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
class ContactResource(resources.ModelResource): class ContactResource(resources.ModelResource):
"""defines how each field in the referenced model should be mapped to the corresponding fields in the """defines how each field in the referenced model should be mapped to the corresponding fields in the
@ -1205,14 +1230,6 @@ class ContactAdmin(ListHeaderAdmin, ImportExportModelAdmin):
return super().change_view(request, object_id, form_url, extra_context=extra_context) return super().change_view(request, object_id, form_url, extra_context=extra_context)
# Select contact to change -> Contacts
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Contacts"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
# Clear warning messages before saving # Clear warning messages before saving
storage = messages.get_messages(request) storage = messages.get_messages(request)
@ -1527,14 +1544,6 @@ class DomainInvitationAdmin(BaseInvitationAdmin):
# Override for the delete confirmation page on the domain table (bulk delete action) # Override for the delete confirmation page on the domain table (bulk delete action)
delete_selected_confirmation_template = "django/admin/domain_invitation_delete_selected_confirmation.html" delete_selected_confirmation_template = "django/admin/domain_invitation_delete_selected_confirmation.html"
# Select domain invitations to change -> Domain invitations
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Domain invitations"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
def change_view(self, request, object_id, form_url="", extra_context=None): def change_view(self, request, object_id, form_url="", extra_context=None):
"""Override the change_view to add the invitation obj for the change_form_object_tools template""" """Override the change_view to add the invitation obj for the change_form_object_tools template"""
@ -1673,14 +1682,6 @@ class PortfolioInvitationAdmin(BaseInvitationAdmin):
change_form_template = "django/admin/portfolio_invitation_change_form.html" change_form_template = "django/admin/portfolio_invitation_change_form.html"
delete_confirmation_template = "django/admin/portfolio_invitation_delete_confirmation.html" delete_confirmation_template = "django/admin/portfolio_invitation_delete_confirmation.html"
# Select portfolio invitations to change -> Portfolio invitations
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Portfolio invitations"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
""" """
Override the save_model method. Override the save_model method.
@ -2070,14 +2071,6 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
readonly_fields.extend([field for field in self.analyst_readonly_fields]) readonly_fields.extend([field for field in self.analyst_readonly_fields])
return readonly_fields # Read-only fields for analysts return readonly_fields # Read-only fields for analysts
# Select domain information to change -> Domain information
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Domain information"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
def formfield_for_foreignkey(self, db_field, request, **kwargs): def formfield_for_foreignkey(self, db_field, request, **kwargs):
"""Customize the behavior of formfields with foreign key relationships. This will customize """Customize the behavior of formfields with foreign key relationships. This will customize
the behavior of selects. Customized behavior includes sorting of objects in list.""" the behavior of selects. Customized behavior includes sorting of objects in list."""
@ -2898,11 +2891,6 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
if next_char.isdigit(): if next_char.isdigit():
should_apply_default_filter = True should_apply_default_filter = True
# Select domain request to change -> Domain requests
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Domain requests"
if should_apply_default_filter: if should_apply_default_filter:
# modify the GET of the request to set the selected filter # modify the GET of the request to set the selected filter
modified_get = copy.deepcopy(request.GET) modified_get = copy.deepcopy(request.GET)
@ -3959,14 +3947,6 @@ class DraftDomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
# If no redirection is needed, return the original response # If no redirection is needed, return the original response
return response return response
# Select draft domain to change -> Draft domains
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Draft domains"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
class PublicContactResource(resources.ModelResource): class PublicContactResource(resources.ModelResource):
"""defines how each field in the referenced model should be mapped to the corresponding fields in the """defines how each field in the referenced model should be mapped to the corresponding fields in the
@ -4388,14 +4368,6 @@ class UserGroupAdmin(AuditedAdmin):
def user_group(self, obj): def user_group(self, obj):
return obj.name return obj.name
# Select user groups to change -> User groups
def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "User groups"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)
class WaffleFlagAdmin(FlagAdmin): class WaffleFlagAdmin(FlagAdmin):
"""Custom admin implementation of django-waffle's Flag class""" """Custom admin implementation of django-waffle's Flag class"""
@ -4412,6 +4384,13 @@ class WaffleFlagAdmin(FlagAdmin):
if extra_context is None: if extra_context is None:
extra_context = {} extra_context = {}
extra_context["dns_prototype_flag"] = flag_is_active_for_user(request.user, "dns_prototype_flag") extra_context["dns_prototype_flag"] = flag_is_active_for_user(request.user, "dns_prototype_flag")
# Loads "tabtitle" for this admin page so that on render the <title>
# element will only have the model name instead of
# the default string loaded by native Django admin code.
# (Eg. instead of "Select waffle flags to change", display "Waffle Flags")
# see "base_site.html" for the <title> code.
extra_context["tabtitle"] = str(self.opts.verbose_name_plural).title()
return super().changelist_view(request, extra_context=extra_context) return super().changelist_view(request, extra_context=extra_context)

View file

@ -25,7 +25,6 @@
// Note, width is determined by a custom width class on one of the children // Note, width is determined by a custom width class on one of the children
position: absolute; position: absolute;
z-index: 1; z-index: 1;
left: 0;
border-radius: 4px; border-radius: 4px;
border: solid 1px color('base-lighter'); border: solid 1px color('base-lighter');
padding: units(2) units(2) units(3) units(2); padding: units(2) units(2) units(3) units(2);
@ -42,6 +41,14 @@
} }
} }
// This will work in responsive tables if we overwrite the overflow value on the table container
// Works with styles in _tables
@include at-media(desktop) {
.usa-accordion--more-actions .usa-accordion__content {
left: 0;
}
}
.usa-accordion--select .usa-accordion__content { .usa-accordion--select .usa-accordion__content {
top: 33.88px; top: 33.88px;
} }
@ -59,10 +66,12 @@
// This won't work on the Members table rows because that table has show-more rows // This won't work on the Members table rows because that table has show-more rows
// Currently, that's not an issue since that Members table is not wrapped in the // Currently, that's not an issue since that Members table is not wrapped in the
// reponsive wrapper. // reponsive wrapper.
tr:last-of-type .usa-accordion--more-actions .usa-accordion__content { @include at-media-max("desktop") {
top: auto; tr:last-of-type .usa-accordion--more-actions .usa-accordion__content {
bottom: -10px; top: auto;
right: 30px; bottom: -10px;
right: 30px;
}
} }
// A CSS only show-more/show-less based on usa-accordion // A CSS only show-more/show-less based on usa-accordion

View file

@ -226,11 +226,6 @@ abbr[title] {
} }
} }
// Boost this USWDS utility class for the accordions in the portfolio requests table
.left-auto {
left: auto!important;
}
.usa-banner__inner--widescreen { .usa-banner__inner--widescreen {
max-width: $widescreen-max-width; max-width: $widescreen-max-width;
} }

View file

@ -152,3 +152,12 @@ th {
.usa-table--full-borderless th { .usa-table--full-borderless th {
border: none !important; border: none !important;
} }
// This is an override to overflow on certain tables (note the custom class)
// so that a popup menu can appear and starddle the edge of the table on large
// screen sizes. Works with styles in _accordions
@include at-media(desktop) {
.usa-table-container--scrollable.usa-table-container--override-overflow {
overflow-y: visible;
}
}

View file

@ -2,6 +2,10 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block title %}
Registrar Analytics | Django admin
{% endblock %}
{% block content_title %}<h1>Registrar Analytics</h1>{% endblock %} {% block content_title %}<h1>Registrar Analytics</h1>{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}

View file

@ -33,8 +33,8 @@
{{ tabtitle }} | {{ tabtitle }} |
{% else %} {% else %}
{{ title }} | {{ title }} |
{% endif %} {% endif %}
{{ site_title|default:_('Django site admin') }} Django admin
{% endblock %} {% endblock %}
{% block extrastyle %}{{ block.super }} {% block extrastyle %}{{ block.super }}

View file

@ -29,7 +29,10 @@
{% csrf_token %} {% csrf_token %}
{% if domain.domain_info.generic_org_type == 'federal' %} {% if domain.domain_info.generic_org_type == 'federal' %}
{% input_with_errors form.federal_agency %} <h4 class="margin-bottom-05">Federal Agency</h4>
<p class="margin-top-0">
{{ domain.domain_info.federal_agency }}
</p>
{% endif %} {% endif %}
{% input_with_errors form.organization_name %} {% input_with_errors form.organization_name %}

View file

@ -163,7 +163,7 @@
</div> </div>
{% endif %} {% endif %}
<div class="display-none usa-table-container--scrollable margin-top-0" tabindex="0" id="domain-requests__table-wrapper"> <div class="display-none usa-table-container--scrollable usa-table-container--override-overflow margin-top-0" tabindex="0" id="domain-requests__table-wrapper">
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked"> <table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked">
<caption class="sr-only">Your domain requests</caption> <caption class="sr-only">Your domain requests</caption>
<thead> <thead>

View file

@ -198,7 +198,7 @@
</svg> </svg>
</button> </button>
</div> </div>
<div class="display-none usa-table-container--scrollable margin-top-0" tabindex="0" id="domains__table-wrapper"> <div class="display-none usa-table-container--scrollable usa-table-container--override-overflow margin-top-0" tabindex="0" id="domains__table-wrapper">
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked"> <table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked">
<caption class="sr-only">Your registered domains</caption> <caption class="sr-only">Your registered domains</caption>
<thead> <thead>

View file

@ -2088,62 +2088,6 @@ class TestDomainOrganization(TestDomainOverview):
# Check for the value we want to update # Check for the value we want to update
self.assertContains(success_result_page, "Faketown") self.assertContains(success_result_page, "Faketown")
@less_console_noise_decorator
def test_domain_org_name_address_form_federal(self):
"""
Submitting a change to federal_agency is blocked for federal domains
"""
fed_org_type = DomainInformation.OrganizationChoices.FEDERAL
self.domain_information.generic_org_type = fed_org_type
self.domain_information.save()
try:
federal_agency, _ = FederalAgency.objects.get_or_create(agency="AMTRAK")
self.domain_information.federal_agency = federal_agency
self.domain_information.save()
except ValueError as err:
self.fail(f"A ValueError was caught during the test: {err}")
self.assertEqual(self.domain_information.generic_org_type, fed_org_type)
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
form = org_name_page.forms[0]
# Check the value of the input field
agency_input = form.fields["federal_agency"][0]
self.assertEqual(agency_input.value, str(federal_agency.id))
# Check if the input field is disabled
self.assertTrue("disabled" in agency_input.attrs)
self.assertEqual(agency_input.attrs.get("disabled"), "")
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
org_name_page.form["federal_agency"] = FederalAgency.objects.filter(agency="Department of State").get().id
org_name_page.form["city"] = "Faketown"
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# Make the change. The agency should be unchanged, but city should be modifiable.
success_result_page = org_name_page.form.submit()
self.assertEqual(success_result_page.status_code, 200)
# Check that the agency has not changed
self.assertEqual(self.domain_information.federal_agency.agency, "AMTRAK")
# Do another check on the form itself
form = success_result_page.forms[0]
# Check the value of the input field
organization_name_input = form.fields["federal_agency"][0]
self.assertEqual(organization_name_input.value, str(federal_agency.id))
# Check if the input field is disabled
self.assertTrue("disabled" in organization_name_input.attrs)
self.assertEqual(organization_name_input.attrs.get("disabled"), "")
# Check for the value we want to update
self.assertContains(success_result_page, "Faketown")
@less_console_noise_decorator @less_console_noise_decorator
def test_federal_agency_submit_blocked(self): def test_federal_agency_submit_blocked(self):
""" """