Sorting does not work for StackedInline if widget of the field has <table>
igor-zmitrovich opened this issue · 2 comments
igor-zmitrovich commented
https://github.com/jrief/django-admin-sortable2/blob/2.1.10/client/admin-sortable2.ts#L214
This line selects the <thead>
of the field's widget <table>
and attaches the event listeners to it in if (tBody) {...}
This causes the UI to not handle the drag events correctly:
As a workaround, I've copy-pasted the compiled version of adminsortable2.js
to myproject/static/adminsortable2/js/adminsortable2.js
and modified the InlineSortable
's constructor as follows:
...
var InlineSortable = class {
constructor(inlineFieldSet) {
this.reversed = inlineFieldSet.classList.contains("reversed");
// Old code - selects any child (even tables inside fields' widgets)
// const tBody = inlineFieldSet.querySelector("table tbody");
// Fixed code - selects only direct child table
let tBody = null;
const table = inlineFieldSet.querySelector("table");
if (table && table.parentElement === inlineFieldSet) {
tBody = table.querySelector("tbody");
}
if (tBody) {
...
I am using:
Django==4.2.9
django-admin-sortable2==2.1.10
django-json-widget==1.1.1
jrief commented
Could you please dump a HTML code snippet containing at least one complete stacked inline form.
Then I will check what can be done by adopting the selectors.
igor-zmitrovich commented
Here is the snippet:
<div class="js-inline-admin-formset inline-group" id="example-3-group" data-inline-type="stacked" data-inline-formset="{"name": "#example-3", "options": {"prefix": "example-3", "addText": "Add another Appointment Screen", "deleteText": "Remove"}}">
<fieldset class="module collapse show sortable">
<h2>Section (<a id="fieldsetcollapser10" class="collapse-toggle" href="#">Hide</a>)</h2>
<input type="hidden" name="example-3-TOTAL_FORMS" value="3" id="id_example-3-TOTAL_FORMS" autocomplete="off"><input type="hidden" name="example-3-INITIAL_FORMS" value="3" id="id_example-3-INITIAL_FORMS"><input type="hidden" name="example-3-MIN_NUM_FORMS" value="0" id="id_example-3-MIN_NUM_FORMS" autocomplete="off"><input type="hidden" name="example-3-MAX_NUM_FORMS" value="1000" id="id_example-3-MAX_NUM_FORMS" autocomplete="off">
<div class="inline-related has_original dynamic-example-3 sortable-chosen" id="example-3-0" draggable="true" style="">
<h3><b>Section:</b> <span class="inline_label">some object label
</span>
<span class="delete"><input type="checkbox" name="example-3-0-DELETE" id="id_example-3-0-DELETE"> <label class="vCheckboxLabel inline" for="id_example-3-0-DELETE">Delete</label></span>
</h3>
<fieldset class="module aligned ">
<div class="form-row field-some_choice">
<div>
<div class="flex-container">
<label class="required" for="id_example-3-0-some_choice">Some choice:</label>
<select name="example-3-0-some_choice" id="id_example-3-0-some_choice">
<option value="whatever" selected="">Whatever</option>
</select>
</div>
</div>
</div>
<div class="form-row field-translations">
<div>
<div class="flex-container">
<label for="id_example-3-0-translations">Translations:</label>
<div style="height:auto;width:90%;display:inline-block;" id="id_example-3-0-translations">
<div class="jsoneditor jsoneditor-mode-form">
<div class="jsoneditor-menu">
<button type="button" class="jsoneditor-expand-all" title="Expand all fields"></button><button type="button" title="Collapse all fields" class="jsoneditor-collapse-all"></button><button type="button" class="jsoneditor-undo jsoneditor-separator" title="Undo last action (Ctrl+Z)" disabled=""></button><button type="button" class="jsoneditor-redo" title="Redo (Ctrl+Shift+Z)" disabled=""></button>
<div class="jsoneditor-modes" style="position: relative;"><button type="button" class="jsoneditor-modes jsoneditor-separator" title="Switch Editor Mode">Form ▾</button></div>
<div class="jsoneditor-search">
<div class="jsoneditor-results"></div>
<div class="jsoneditor-frame" title="Search fields and values"><button type="button" class="jsoneditor-refresh"></button><input type="text"><button type="button" title="Next result (Enter)" class="jsoneditor-next"></button><button type="button" title="Previous result (Shift + Enter)" class="jsoneditor-previous"></button></div>
</div>
<button type="button" class="jsoneditor-repair" title="Fill empty values with exemplary data"></button>
</div>
<div class="jsoneditor-outer has-main-menu-bar">
<div class="jsoneditor-tree">
<div class="jsoneditor-tree-inner">
<table class="jsoneditor-tree">
<colgroup>
<col width="24px">
<col>
</colgroup>
<tbody>
<tr class=" jsoneditor-expandable jsoneditor-expanded">
<td>
<table style="border-collapse: collapse; margin-left: 0px;" class="jsoneditor-values">
<tbody>
<tr>
<td class="jsoneditor-tree"><button type="button" class="jsoneditor-button jsoneditor-expanded" title="Click to expand/collapse this field (Ctrl+E).
Ctrl+Click to expand/collapse including all childs."></button></td>
<td class="jsoneditor-tree">
<div contenteditable="false" class="jsoneditor-readonly">Translations</div>
</td>
<td class="jsoneditor-tree"></td>
<td class="jsoneditor-tree">
<div class="jsoneditor-value jsoneditor-object" title="object containing 4 items">{4}</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table style="border-collapse: collapse; margin-left: 24px;" class="jsoneditor-values">
<tbody>
<tr>
<td class="jsoneditor-tree"><button type="button" class="jsoneditor-button jsoneditor-invisible" title=""></button></td>
<td class="jsoneditor-tree">
<div contenteditable="false" spellcheck="false" class="jsoneditor-field">title</div>
</td>
<td class="jsoneditor-separator">:</td>
<td class="jsoneditor-tree">
<div contenteditable="true" spellcheck="false" class="jsoneditor-value jsoneditor-string" title="">Translation</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table style="border-collapse: collapse; margin-left: 24px;" class="jsoneditor-values">
<tbody>
<tr>
<td class="jsoneditor-tree"><button type="button" class="jsoneditor-button jsoneditor-invisible" title=""></button></td>
<td class="jsoneditor-tree">
<div contenteditable="false" spellcheck="false" class="jsoneditor-field">header</div>
</td>
<td class="jsoneditor-separator">:</td>
<td class="jsoneditor-tree">
<div contenteditable="true" spellcheck="false" class="jsoneditor-value jsoneditor-string" title="">Translation</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table style="border-collapse: collapse; margin-left: 24px;" class="jsoneditor-values">
<tbody>
<tr>
<td class="jsoneditor-tree"><button type="button" class="jsoneditor-button jsoneditor-invisible" title=""></button></td>
<td class="jsoneditor-tree">
<div contenteditable="false" spellcheck="false" class="jsoneditor-field">cta_button</div>
</td>
<td class="jsoneditor-separator">:</td>
<td class="jsoneditor-tree">
<div contenteditable="true" spellcheck="false" class="jsoneditor-value jsoneditor-string" title="">Translation</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<table style="border-collapse: collapse; margin-left: 24px;" class="jsoneditor-values">
<tbody>
<tr>
<td class="jsoneditor-tree"><button type="button" class="jsoneditor-button jsoneditor-invisible" title=""></button></td>
<td class="jsoneditor-tree">
<div contenteditable="false" spellcheck="false" class="jsoneditor-field">description</div>
</td>
<td class="jsoneditor-separator">:</td>
<td class="jsoneditor-tree">
<div contenteditable="true" spellcheck="false" class="jsoneditor-value jsoneditor-string" title="">Translation</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr class="jsoneditor-append"></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<textarea id="id_example-3-0-translations_textarea" name="example-3-0-translations" required="" style="display: none">{"title": "Translation", "header": "Translation", "cta_button": "Translation", "descrition": "Translation"}</textarea>
<script id="widget-options-id_example-3-0-translations" type="application/json">"{\"modes\": [\"form\", \"code\"], \"mode\": \"form\", \"search\": true, \"name\": \"Translations\", \"enableTransform\": false, \"enableSort\": false, \"maxVisibleChilds\": 25, \"navigationBar\": false}"</script>
<script id="widget-value-id_example-3-0-translations" type="application/json">"{\"title\": \"Translation\", \"header\": \"Translation\", \"cta_button\": \"Translation\", \"description\": \"Translation\"}"</script>
<script>
(function() {
let container = document.getElementById("id_example-3-0-translations");
let textarea = document.getElementById("id_example-3-0-translations_textarea");
/* To prevent possible XSS attacks, parse text value of <script> tag to JSON.
We need to parse value twice as JSONEditorWidget already dumps options and value as strings. */
let options = JSON.parse(JSON.parse(document.getElementById('widget-options-id_example-3-0-translations').textContent));
let data = JSON.parse(JSON.parse(document.getElementById('widget-value-id_example-3-0-translations').textContent));
options.onChange = function () {
let json = editor.get();
textarea.value=JSON.stringify(json);
}
let editor = new JSONEditor(container, options);
container.jsoneditor = editor;
editor.set(data);
})();
</script>
</div>
<div class="help" id="id_example-3-0-translations_helptext">
<div>To edit translations used on newly added screen click "SAVE" and come back to this page again.</div>
</div>
</div>
</div>
<div class="form-row field-checkbox">
<div>
<div class="flex-container checkbox-row">
<input type="checkbox" name="example-3-0-checkbox" id="id_example-3-0-checkbox" checked=""><label class="vCheckboxLabel" for="id_example-3-0-checkbox">Checkbox</label>
</div>
<div class="help" id="id_example-3-0-checkbox_helptext">
<div>Label</div>
</div>
</div>
</div>
<div class="form-row hidden field-ordering">
<div>
<div class="flex-container">
<label for="id_example-3-0-ordering">Ordering:</label>
<input type="hidden" name="example-3-0-ordering" value="1" class="_reorder_" id="id_example-3-0-ordering">
</div>
</div>
</div>
</fieldset>
<input type="hidden" name="example-3-0-id" value="235" id="id_example-3-0-id">
<input type="hidden" name="example-3-0-example" value="231" id="id_example-3-0-example">
</div>
...
other forms
...
<div class="add-row"><a href="#">Add another Example</a></div>
</fieldset>
</div>