Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sorting does not work for StackedInline if widget of the field has <table> #384

Open
igor-zmitrovich opened this issue Jan 3, 2024 · 2 comments

Comments

@igor-zmitrovich
Copy link

igor-zmitrovich commented Jan 3, 2024

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) {...}

image

This causes the UI to not handle the drag events correctly:
ezgif-5-393cf1496a

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
Copy link
Owner

jrief commented May 17, 2024

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
Copy link
Author

Here is the snippet:

<div class="js-inline-admin-formset inline-group" id="example-3-group" data-inline-type="stacked" data-inline-formset="{&quot;name&quot;: &quot;#example-3&quot;, &quot;options&quot;: {&quot;prefix&quot;: &quot;example-3&quot;, &quot;addText&quot;: &quot;Add another Appointment Screen&quot;, &quot;deleteText&quot;: &quot;Remove&quot;}}">
  <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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants