Fixed #31349 -- Used :nth-child() CSS pseudo-class to style alternative rows in admin.

This commit is contained in:
Jon Dufresne 2020-03-07 09:35:59 -08:00 committed by Mariusz Felisiak
parent ec292f261d
commit eb77e80de0
11 changed files with 31 additions and 65 deletions

View File

@ -284,14 +284,14 @@ tr.alt {
background: #f6f6f6;
}
.row1, .row-form-errors {
tr:nth-child(odd), .row-form-errors {
background: #fff;
}
.row2,
.row2 .errorlist,
.row1 + .row-form-errors,
.row1 + .row-form-errors .errorlist {
tr:nth-child(even),
tr:nth-child(even) .errorlist,
tr:nth-child(odd) + .row-form-errors,
tr:nth-child(odd) + .row-form-errors .errorlist {
background: #f9f9f9;
}

View File

@ -203,11 +203,6 @@
// Tabular inlines ---------------------------------------------------------
$.fn.tabularFormset = function(selector, options) {
var $rows = $(this);
var alternatingRows = function(row) {
$(selector).not(".add-row").removeClass("row1 row2")
.filter(":even").addClass("row1").end()
.filter(":odd").addClass("row2");
};
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force
@ -254,12 +249,10 @@
deleteCssClass: "inline-deletelink",
deleteText: options.deleteText,
emptyCssClass: "empty-form",
removed: alternatingRows,
added: function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
alternatingRows(row);
},
addButton: options.addButton
});

View File

@ -1,11 +1,11 @@
(function(b){b.fn.formset=function(d){var a=b.extend({},b.fn.formset.defaults,d),c=b(this),k=c.parent(),n=function(a,e,l){var g=new RegExp("("+e+"-(\\d+|__prefix__))");e=e+"-"+l;b(a).prop("for")&&b(a).prop("for",b(a).prop("for").replace(g,e));a.id&&(a.id=a.id.replace(g,e));a.name&&(a.name=a.name.replace(g,e))},h=b("#id_"+a.prefix+"-TOTAL_FORMS").prop("autocomplete","off"),e=parseInt(h.val(),10),l=b("#id_"+a.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off"),q=b("#id_"+a.prefix+"-MIN_NUM_FORMS").prop("autocomplete",
"off"),t=function(g){g.preventDefault();g=b("#"+a.prefix+"-empty");var f=g.clone(!0);f.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+e);r(f);f.find("*").each(function(){n(this,a.prefix,h.val())});f.insertBefore(b(g));b(h).val(parseInt(h.val(),10)+1);e+=1;""!==l.val()&&0>=l.val()-h.val()&&m.parent().hide();p(f.closest(".inline-group"));a.added&&a.added(f);b(document).trigger("formset:added",[f,a.prefix])},r=function(b){b.is("tr")?b.children(":last").append('<div><a class="'+
a.deleteCssClass+'" href="#">'+a.deleteText+"</a></div>"):b.is("ul")||b.is("ol")?b.append('<li><a class="'+a.deleteCssClass+'" href="#">'+a.deleteText+"</a></li>"):b.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="#">'+a.deleteText+"</a></span>");b.find("a."+a.deleteCssClass).on("click",u.bind(this))},u=function(g){g.preventDefault();var f=b(g.target).closest("."+a.formCssClass);g=f.closest(".inline-group");var d=f.prev();d.length&&d.hasClass("row-form-errors")&&d.remove();
f.remove();--e;a.removed&&a.removed(f);b(document).trigger("formset:removed",[f,a.prefix]);f=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(f.length);(""===l.val()||0<l.val()-f.length)&&m.parent().show();p(g);d=function(){n(this,a.prefix,c)};var c=0;for(g=f.length;c<g;c++)n(b(f).get(c),a.prefix,c),b(f.get(c)).find("*").each(d)},p=function(a){""!==q.val()&&0<=q.val()-h.val()?a.find(".inline-deletelink").hide():a.find(".inline-deletelink").show()};c.each(function(e){b(this).not("."+a.emptyCssClass).addClass(a.formCssClass)});
c.filter("."+a.formCssClass+":not(.has_original):not(."+a.emptyCssClass+")").each(function(){r(b(this))});p(c);var m=a.addButton;(function(){if(null===m)if("TR"===c.prop("tagName")){var b=c.eq(-1).children().length;k.append('<tr class="'+a.addCssClass+'"><td colspan="'+b+'"><a href="#">'+a.addText+"</a></tr>");m=k.find("tr:last a")}else c.filter(":last").after('<div class="'+a.addCssClass+'"><a href="#">'+a.addText+"</a></div>"),m=c.filter(":last").next().find("a");m.on("click",t)})();d=""===l.val()||
0<l.val()-h.val();c.length&&d?m.parent().show():m.parent().hide();return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null,addButton:null};b.fn.tabularFormset=function(d,a){var c=b(this),k=function(a){b(d).not(".add-row").removeClass("row1 row2").filter(":even").addClass("row1").end().filter(":odd").addClass("row2")},n=function(){"undefined"!==
typeof SelectFilter&&(b(".selectfilter").each(function(a,b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!1)}),b(".selectfilterstacked").each(function(a,b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!0)}))},h=function(a){a.find(".prepopulated_field").each(function(){var e=b(this).find("input, select, textarea"),d=e.data("dependency_list")||[],c=[];b.each(d,function(b,e){c.push("#"+a.find(".field-"+e).find("input, select, textarea").attr("id"))});c.length&&e.prepopulate(c,
e.attr("maxlength"))})};c.formset({prefix:a.prefix,addText:a.addText,formCssClass:"dynamic-"+a.prefix,deleteCssClass:"inline-deletelink",deleteText:a.deleteText,emptyCssClass:"empty-form",removed:k,added:function(a){h(a);"undefined"!==typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());n();k(a)},addButton:a.addButton});return c};b.fn.stackedFormset=function(d,a){var c=b(this),k=function(a){b(d).find(".inline_label").each(function(a){a+=1;b(this).html(b(this).html().replace(/(#\d+)/g,
"#"+a))})},n=function(){"undefined"!==typeof SelectFilter&&(b(".selectfilter").each(function(a,b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!1)}),b(".selectfilterstacked").each(function(a,b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!0)}))},h=function(a){a.find(".prepopulated_field").each(function(){var c=b(this).find("input, select, textarea"),d=c.data("dependency_list")||[],e=[];b.each(d,function(b,c){e.push("#"+a.find(".form-row .field-"+c).find("input, select, textarea").attr("id"))});
e.length&&c.prepopulate(e,c.attr("maxlength"))})};c.formset({prefix:a.prefix,addText:a.addText,formCssClass:"dynamic-"+a.prefix,deleteCssClass:"inline-deletelink",deleteText:a.deleteText,emptyCssClass:"empty-form",removed:k,added:function(a){h(a);"undefined"!==typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());n();k(a)},addButton:a.addButton});return c};b(document).ready(function(){b(".js-inline-admin-formset").each(function(){var d=b(this).data(),a=d.inlineFormset;
switch(d.inlineType){case "stacked":d=a.name+"-group .inline-related";b(d).stackedFormset(d,a.options);break;case "tabular":d=a.name+"-group .tabular.inline-related tbody:first > tr.form-row",b(d).tabularFormset(d,a.options)}})})})(django.jQuery);
(function(b){b.fn.formset=function(c){var a=b.extend({},b.fn.formset.defaults,c),e=b(this),l=e.parent(),m=function(a,d,h){var g=new RegExp("("+d+"-(\\d+|__prefix__))");d=d+"-"+h;b(a).prop("for")&&b(a).prop("for",b(a).prop("for").replace(g,d));a.id&&(a.id=a.id.replace(g,d));a.name&&(a.name=a.name.replace(g,d))},f=b("#id_"+a.prefix+"-TOTAL_FORMS").prop("autocomplete","off"),n=parseInt(f.val(),10),h=b("#id_"+a.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off"),q=b("#id_"+a.prefix+"-MIN_NUM_FORMS").prop("autocomplete",
"off"),t=function(g){g.preventDefault();g=b("#"+a.prefix+"-empty");var d=g.clone(!0);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+n);r(d);d.find("*").each(function(){m(this,a.prefix,f.val())});d.insertBefore(b(g));b(f).val(parseInt(f.val(),10)+1);n+=1;""!==h.val()&&0>=h.val()-f.val()&&k.parent().hide();p(d.closest(".inline-group"));a.added&&a.added(d);b(document).trigger("formset:added",[d,a.prefix])},r=function(b){b.is("tr")?b.children(":last").append('<div><a class="'+
a.deleteCssClass+'" href="#">'+a.deleteText+"</a></div>"):b.is("ul")||b.is("ol")?b.append('<li><a class="'+a.deleteCssClass+'" href="#">'+a.deleteText+"</a></li>"):b.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="#">'+a.deleteText+"</a></span>");b.find("a."+a.deleteCssClass).on("click",u.bind(this))},u=function(g){g.preventDefault();var d=b(g.target).closest("."+a.formCssClass);g=d.closest(".inline-group");var f=d.prev();f.length&&f.hasClass("row-form-errors")&&f.remove();
d.remove();--n;a.removed&&a.removed(d);b(document).trigger("formset:removed",[d,a.prefix]);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);(""===h.val()||0<h.val()-d.length)&&k.parent().show();p(g);f=function(){m(this,a.prefix,c)};var c=0;for(g=d.length;c<g;c++)m(b(d).get(c),a.prefix,c),b(d.get(c)).find("*").each(f)},p=function(a){""!==q.val()&&0<=q.val()-f.val()?a.find(".inline-deletelink").hide():a.find(".inline-deletelink").show()};e.each(function(c){b(this).not("."+a.emptyCssClass).addClass(a.formCssClass)});
e.filter("."+a.formCssClass+":not(.has_original):not(."+a.emptyCssClass+")").each(function(){r(b(this))});p(e);var k=a.addButton;(function(){if(null===k)if("TR"===e.prop("tagName")){var b=e.eq(-1).children().length;l.append('<tr class="'+a.addCssClass+'"><td colspan="'+b+'"><a href="#">'+a.addText+"</a></tr>");k=l.find("tr:last a")}else e.filter(":last").after('<div class="'+a.addCssClass+'"><a href="#">'+a.addText+"</a></div>"),k=e.filter(":last").next().find("a");k.on("click",t)})();c=""===h.val()||
0<h.val()-f.val();e.length&&c?k.parent().show():k.parent().hide();return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null,addButton:null};b.fn.tabularFormset=function(c,a){c=b(this);var e=function(){"undefined"!==typeof SelectFilter&&(b(".selectfilter").each(function(a,b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!1)}),b(".selectfilterstacked").each(function(a,
b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!0)}))},l=function(a){a.find(".prepopulated_field").each(function(){var c=b(this).find("input, select, textarea"),n=c.data("dependency_list")||[],h=[];b.each(n,function(b,c){h.push("#"+a.find(".field-"+c).find("input, select, textarea").attr("id"))});h.length&&c.prepopulate(h,c.attr("maxlength"))})};c.formset({prefix:a.prefix,addText:a.addText,formCssClass:"dynamic-"+a.prefix,deleteCssClass:"inline-deletelink",deleteText:a.deleteText,emptyCssClass:"empty-form",
added:function(a){l(a);"undefined"!==typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());e()},addButton:a.addButton});return c};b.fn.stackedFormset=function(c,a){var e=b(this),l=function(a){b(c).find(".inline_label").each(function(a){a+=1;b(this).html(b(this).html().replace(/(#\d+)/g,"#"+a))})},m=function(){"undefined"!==typeof SelectFilter&&(b(".selectfilter").each(function(a,b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!1)}),b(".selectfilterstacked").each(function(a,
b){a=b.name.split("-");SelectFilter.init(b.id,a[a.length-1],!0)}))},f=function(a){a.find(".prepopulated_field").each(function(){var c=b(this).find("input, select, textarea"),f=c.data("dependency_list")||[],e=[];b.each(f,function(b,c){e.push("#"+a.find(".form-row .field-"+c).find("input, select, textarea").attr("id"))});e.length&&c.prepopulate(e,c.attr("maxlength"))})};e.formset({prefix:a.prefix,addText:a.addText,formCssClass:"dynamic-"+a.prefix,deleteCssClass:"inline-deletelink",deleteText:a.deleteText,
emptyCssClass:"empty-form",removed:l,added:function(a){f(a);"undefined"!==typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());m();l(a)},addButton:a.addButton});return e};b(document).ready(function(){b(".js-inline-admin-formset").each(function(){var c=b(this).data(),a=c.inlineFormset;switch(c.inlineType){case "stacked":c=a.name+"-group .inline-related";b(c).stackedFormset(c,a.options);break;case "tabular":c=a.name+"-group .tabular.inline-related tbody:first > tr.form-row",
b(c).tabularFormset(c,a.options)}})})})(django.jQuery);

View File

@ -30,7 +30,7 @@
{% if result.form and result.form.non_field_errors %}
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">{% for item in result %}{{ item }}{% endfor %}</tr>
<tr>{% for item in result %}{{ item }}{% endfor %}</tr>
{% endfor %}
</tbody>
</table>

View File

@ -25,7 +25,7 @@
{% if inline_admin_form.form.non_field_errors %}
<tr class="row-form-errors"><td colspan="{{ inline_admin_form|cell_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
{% endif %}
<tr class="form-row {% cycle "row1" "row2" %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} empty-form{% endif %}"
<tr class="form-row {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} empty-form{% endif %}"
id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
<td class="original">
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>

View File

@ -500,6 +500,9 @@ Miscellaneous
* :djadmin:`migrate` management command now runs the ``database`` system checks
only for a database to migrate.
* The admin CSS classes ``row1`` and ``row2`` are removed in favor of
``:nth-child(odd)`` and ``:nth-child(even)`` pseudo-classes.
.. _deprecated-features-3.1:
Features deprecated in 3.1

View File

@ -28,14 +28,14 @@ QUnit.test('add form', function(assert) {
var addButton = this.table.find('.add-row a');
assert.equal(addButton.text(), this.addText);
addButton.click();
assert.ok(this.table.find('#first-1').hasClass('row2'));
assert.ok(this.table.find('#first-1'));
});
QUnit.test('added form has remove button', function(assert) {
var addButton = this.table.find('.add-row a');
assert.equal(addButton.text(), this.addText);
addButton.click();
assert.equal(this.table.find('#first-1.row2 .inline-deletelink').length, 1);
assert.equal(this.table.find('#first-1 .inline-deletelink').length, 1);
});
QUnit.test('add/remove form events', function(assert) {
@ -45,11 +45,11 @@ QUnit.test('add/remove form events', function(assert) {
var addButton = this.table.find('.add-row a');
$document.on('formset:added', function(event, $row, formsetName) {
assert.ok(true, 'event `formset:added` triggered');
assert.equal(true, $row.is($('.row2')));
assert.equal(true, $row.is('#first-1'));
assert.equal(formsetName, 'first');
});
addButton.click();
var deletedRow = $('.row2');
var deletedRow = $('#first-1');
var deleteLink = this.table.find('.inline-deletelink');
$document.on('formset:removed', function(event, $row, formsetName) {
assert.ok(true, 'event `formset:removed` triggered');
@ -74,7 +74,7 @@ QUnit.test('existing add button', function(assert) {
});
assert.equal(this.table.find('.add-row a').length, 0);
addButton.click();
assert.ok(this.table.find('#first-1').hasClass('row2'));
assert.ok(this.table.find('#first-1'));
});
@ -125,22 +125,6 @@ QUnit.test('removing a form-row also removed related row with non-field errors',
assert.notOk(this.table.find('.row-form-errors').length);
});
QUnit.test('removing and adding a row keeps cycling row1 and row2 classes', function(assert) {
var $ = django.jQuery;
var tr = this.inlineRows.slice(1, 2);
var deleteLink = tr.find('a.inline-deletelink');
var addLink = this.table.find('.add-row > td > a');
assert.ok(this.table.find('tr.form-row:even').hasClass('row1'));
assert.ok(this.table.find('tr.form-row:odd').hasClass('row2'));
deleteLink.trigger($.Event('click', {target: deleteLink}));
assert.ok(this.table.find('tr.form-row:even').hasClass('row1'));
assert.ok(this.table.find('tr.form-row:odd').hasClass('row2'));
addLink.trigger($.Event('click', {target: addLink}));
assert.ok(this.table.find('tr.form-row:even').hasClass('row1'));
assert.ok(this.table.find('tr.form-row:odd').hasClass('row2'));
});
QUnit.module('admin.inlines: tabular formsets with max_num', {
beforeEach: function() {
var $ = django.jQuery;

View File

@ -28,7 +28,7 @@
<input id="id_first-TOTAL_FORMS" value="1">
<input id="id_first-MAX_NUM_FORMS" value="">
<table class="inline">
<tr id="first-0" class="form-row row1">
<tr id="first-0" class="form-row">
<td class="field-test_field">
<input id="id_first-0-test_field">
</td>
@ -47,7 +47,7 @@
<input id="id_second-MAX_NUM_FORMS" value="">
<input id="id_second-MIN_NUM_FORMS" value="">
<table class="inline">
<tr id="second-0" class="form-row has_original row1">
<tr id="second-0" class="form-row has_original">
<td class="field-test_field">
<input id="id_second-0-test_field">
</td>
@ -62,7 +62,7 @@
</ul>
</td>
</tr>
<tr id="second-1" class="form-row row2">
<tr id="second-1" class="form-row">
<td class="field-test_field">
<input id="id_second-1-test_field">
</td>

View File

@ -40,7 +40,7 @@ from .models import (
def build_tbody_html(pk, href, extra_fields):
return (
'<tbody><tr class="row1">'
'<tbody><tr>'
'<td class="action-checkbox">'
'<input type="checkbox" name="_selected_action" value="{}" '
'class="action-select"></td>'

View File

@ -1205,20 +1205,6 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertEqual(len(self.selenium.find_elements_by_css_selector(
'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1)
def test_alternating_rows(self):
self.admin_login(username='super', password='secret')
self.selenium.get(self.live_server_url + reverse('admin:admin_inlines_profilecollection_add'))
# Add a few inlines
self.selenium.find_element_by_link_text('Add another Profile').click()
self.selenium.find_element_by_link_text('Add another Profile').click()
row_selector = 'form#profilecollection_form tr.dynamic-profile_set'
self.assertEqual(len(self.selenium.find_elements_by_css_selector(
"%s.row1" % row_selector)), 2, msg="Expect two row1 styled rows")
self.assertEqual(len(self.selenium.find_elements_by_css_selector(
"%s.row2" % row_selector)), 1, msg="Expect one row2 styled row")
def test_collapsed_inlines(self):
# Collapsed inlines have SHOW/HIDE links.
self.admin_login(username='super', password='secret')

View File

@ -30,7 +30,7 @@
{% if result.form.non_field_errors %}
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">{% for item in result %}{{ item }}{% endfor %}</tr>
<tr>{% for item in result %}{{ item }}{% endfor %}</tr>
{% endfor %}
</tbody>
</table>