wagtail / wagtail

A Django content management system focused on flexibility and user experience
https://wagtail.org
BSD 3-Clause "New" or "Revised" License
18.38k stars 3.89k forks source link

TableBlock with custom TabbedInterface #2687

Open soar opened 8 years ago

soar commented 8 years ago

When new TableBlock is used with default TabbedInterface - all works fine:

from wagtail.contrib.table_block.blocks import TableBlock
from wagtail.wagtailadmin.edit_handlers import StreamFieldPanel
from wagtail.wagtailcore.fields import StreamField
from wagtail.wagtailcore.models import Page

class TestPage(Page):

    content = StreamField([
        ('table', TableBlock()),
    ])

    search_fields = Page.search_fields
    content_panels = Page.content_panels + [
        StreamFieldPanel('content')
    ]

I see editor as expected:

2016-06-07 11-03-33 wagtail - test page test page - google chrome

But when I try to create custom TabbedInterface:

from wagtail.contrib.table_block.blocks import TableBlock
from wagtail.wagtailadmin.edit_handlers import StreamFieldPanel, TabbedInterface, ObjectList
from wagtail.wagtailcore.fields import StreamField
from wagtail.wagtailcore.models import Page

class TestPage(Page):

    content = StreamField([
        ('table', TableBlock()),
    ])

    search_fields = Page.search_fields
    editor_content_panels = [
        StreamFieldPanel('content')
    ]

    edit_handler = TabbedInterface([
        ObjectList(Page.content_panels, heading="Default Editor"),
        ObjectList(Page.promote_panels, heading="Promote"),
        ObjectList(Page.settings_panels, heading="Settings", classname='settings'),
        ObjectList(editor_content_panels, heading="Editor"),
    ])

After creating new page with TableBlock and re-opening it for edition - table editor don't appears:

2016-06-07 11-06-11 wagtail - test page test page - google chrome

But... After opening "Developer Tools" (F12) magic happens:

2016-06-07 11-08-52 wagtail - test page test page - google chrome

I've checked this bug with latest Chrome and Firefox. Looks like some JavaScript window refresh is needed on Tab click event.

gasman commented 8 years ago

Thanks for the report @soar! My guess is that the table control is trying to find the size of its container element in order to resize itself correctly, and is getting a result of (0, 0) when the container is hidden. (It's probably also set up to redo the calculation on window.resize events, which is why it fixes itself when you open Developer Tools...)

Proper-Job commented 8 years ago

@gasman Do you want me to have a look at that?

gasman commented 8 years ago

@Proper-Job Yes please!

Proper-Job commented 8 years ago

ok, will do. I'll post any progress here.

SalahAdDin commented 8 years ago

Very hard.

Proper-Job commented 8 years ago

@gasman is there an "onTabChanged" JS event that I could subscribe to?

gasman commented 8 years ago

@Proper-Job We're using Bootstrap's tabs module, so the events should be as described here: http://getbootstrap.com/javascript/#tabs-events

Proper-Job commented 8 years ago

It's tricky, I'm still working on it.

agil commented 8 years ago

as a very fast and dirty workaround I've used

$(document).on('click', '.tab-nav a', function(e) {
    $('.wtHolder', $(this).attr('href')).scrollTop(1);
    $(window).resize(); // trigger resize to force handsontable relayout 
});
Proper-Job commented 8 years ago

Thanks. Using the onTabChanged listeners really wasn't working. I'll see if I can incorporate this somehow.

soar commented 8 years ago

@agil What is .wtHolder? I don't see such element in admin pages code.

I've tried something like:

            <script>
                $(document).on('click', '.tab-nav a', function(e) {
                    $('.wrapper', $(this).attr('href')).scrollTop(1);
                    $(window).resize();
                });
            </script>

But this not works.

soar commented 8 years ago

Oh, 2 hours spent and I finally found the solution:

@hooks.register('insert_global_admin_js')
def fix_editors_in_tabs():
    script = """
            <script>
                $(document).ready(function() {
                    $('.tab-nav a').on('shown.bs.tab', function (e) {
                        // Fix for TableBlock editor
                        $(window).trigger('resize');

                        // Fix for SimpleMDE editor
                        if (window.SimpleMDEInstances != null) {
                            $.each(window.SimpleMDEInstances, function(index, inst) {
                                inst.codemirror.refresh();
                            });
                        }
                    });
                });
            </script>
        """
    return script

It works for TableBlock and my MarkdownBlock.

BertrandBordage commented 7 years ago

The fix from @soar wasn’t working correctly for me, the table width was tiny when switching tabs.

I made another fix that instantiates again the table. The only downside is that we use the default TableBlock options. It should be OK in most cases, but if you changed the TableBlock, you may have to change this.

@hooks.register('insert_global_admin_js')
def fix_tables_in_tabs():
    return """
        <script>
            $(document).ready(function() {
                $('.tab-nav a').on('shown.bs.tab', function (e) {
                    $(e.target.hash + ' .handsontable').each(function () {
                        var inputId = $(this).siblings('input').attr('id');
                        if (typeof inputId !== 'undefined') {
                            initTable(inputId, %s);
                        }
                    });
                });
            });
        </script>
    """ % json.dumps(TableBlock().table_options)
danthedeckie commented 4 years ago

@BertrandBordage 's fix helped, but didn't work for me when the second tab is the active one when the page loads (on Chrome - Firefox was fine). I got this to work, but it's very ugly:

@hooks.register('insert_global_admin_js')
def fix_tables_in_tabs():
    return """
        <script>
            (function() {
                function fix_tables(){
                     $('.handsontable').each(function () {
                        var inputId = $(this).siblings('input').attr('id');
                        if (typeof inputId !== 'undefined') {
                            initTable(inputId, %s);
                        }
                    });
                };
                $(document).ready(function() {
                    setTimeout(fix_tables, 1000);
                    $('.tab-nav a').on('shown.bs.tab', fix_tables);
                });
            })();
        </script>
    """ % json.dumps(TableBlock().table_options)

There has to be a better way.

pySilver commented 3 years ago

$(window).resize(); called in the right moment is the answer. You can verify this by navigating to the tab where table is expected to be shown and calling this code from console (or simply resize window manually).

happygrizzly commented 1 month ago

Small update on how to fix. Wagtail v5.2.6.

@hooks.register('insert_editor_js')
def global_admin_js():
  return mark_safe(
    """
    <script>
      document.addEventListener('wagtail:tab-changed', _ => {
        window.dispatchEvent(new Event('resize'))
      })
    </script>
    """
  )