The Tabs module provides a set of APIs for working with tabs. Currently at least three modules use the Tabs module APIs:

While quicktabs began as a quick way of placing tabs on blocks, it's grown into a set of APIs for working with tabs that are independent of the specific block application. It provides many of the same API functionality as tabs module as well as some more advanced functionality.

It would be good to think through the relationship between quicktabs and tabs ahead of D7 upgrades. There's a fair bit of duplication of effort and also the potential for conflicting code and UI elements on sites running both. E.g., a "tabs" configuration menu item in tabs module sets default tab behaviour, but of course won't affect tabs created with quicktabs.

Personally I'd like to see a single API module that serves for quicktabs and other uses. I'm not sure however what the best path forward would be. What are thoughts from quicktabs maintainers?

Comments

katbailey’s picture

This really is the big, big question... here are my initial thoughts. To my mind, there are two facets to quicktabs: 1) the "tabbedness" of it, 2) the UI for choosing blocks, nodes or views to be loaded either via ajax or through a simple show/hide mechanism. Having put far more effort into the 2nd of these I can't help but see that as more central to what the module is all about. On the other hand, from a UX perspective, it's all just tabbed content, right?
But given that QT provides this whole mechanism for loading content via ajax, what would it even mean for CCK fieldgroups to be loaded via ajax? And as for panels, last I heard there was no API for loading a pane via ajax.
Though maybe that's the wrong way to look at it - there's been an issue in the queue for some time now about choosing the ajax option on a per-tab basis - so you could make it so that it's only an option for content that is ajax-loadable I guess.
Tabs module uses jQuery UI tabs, right? I know this has an ajax option but my understanding is that it just uses the href of the tab to go fetch the content at that location and load it into the div, which wouldn't quite work for views etc. Or am I underestimating the flexibility of jQuery UI Tabs?
Bottom line, I'm all for consolidation, I just need help visualising what form that consolidation could take, given the specifics of what QT does.

katbailey’s picture

A further note on the use of jQuery UI Tabs for Quicktabs...

I just had a look at the ajax option of UI Tabs and confirmed my suspicion that it simply uses the href of the link on the tab to pull html content from. The problem with this approach is that with Quicktabs you never want to load an entire Drupal page as such, so you need to use special callbacks that will return just the content you want, whether it's a node, a block or a view. And you can't have the paths to these callback as your hrefs because then it won't degrade gracefully without js. The way UI Tabs works without js is that it just goes to the page at the location of the href when you click on a tab. With Quicktabs, the current page gets reloaded, but with the clicked on tab as the active tab, which I think is a much better user experience.

nedjo’s picture

katbailey: thanks for taking the time to consider this and for your thoughts.

Definitely we need the advanced functionality that you've implemented in QT. The two big areas that aren't in the Tabs (jQuery UI Tabs) module are (a) AJAX with sensible non-js fallbacks as you've described and (b) skins. Probably there are others.

So I guess an initial question would be, how hard would it be to reimplement the QT implementations to Tabs? I'll have a look at this when I get a chance and report back on what would be involved.

sun’s picture

This would make a lot of sense. I'd add: also jQuery UI Tabs (now in core) constantly advances, and that plugin/library is used by gazillions of other developers on this planet. In D7, various JS/AJAX improvements are either already built-in, or will still find their way in. The purpose of modules likes Tabs and QuickTabs is therefore reduced to the challenge of properly integrating with various opportunities in which tabs may be used (i.e. blocks, panels, nodes, local tasks, etc).

D7 issues that are probably of interest for both of you:

#561858: [Tests added] Fix drupal_add_js() and drupal_add_css() to work for AJAX requests too by adding lazy-load to AJAX framework
#87994: Quit clobbering people's work when they click the filter tips link (introduces a default #attached handler for jQuery [UI] plugins)

katbailey’s picture

Version: 6.x-3.x-dev » 7.x-2.x-dev
Assigned: Unassigned » katbailey

OK, I can see how this would be possible in D7, leveraging the new ajax framework and having jQuery UI Tabs in core. Going to have a go at it over the next few days and see what I come up with...

sun’s picture

Also note that jquery* module maintainers have joined forces and are re-building advanced jQuery support in http://drupal.org/project/jquery

nedjo’s picture

@katbailey: thanks, please let me know if there's anything I can do to help!

Part of the broader question here is: what is the best way to provide APIs for jQuery plugins?

In tabs module I took a forms API based approach:

* In hook_elements(), declare elements for tab sets and tab pages.
* Build a set of tabs as an array of #type 'tabset' with nested 'tabpage' elements.
* Use a #process callback to process the tabset and child tab pages.
* Render with drupal_render().

I've found the approach useful as it provides a simple format to generate tabs whereever they're needed (content fieldgroups, views, panels, etc.) and a standard way to add new functionality through additional properties of the tabset or tabpage element.

But it's not an approach to integrating jQuery that seems to have caught on. There is no end of modules for integrating jQuery plugins that are basically similar to tabs (for slideshows, carousels, accordions, etc.), but I'm not aware of any of these modules that uses a forms API like approach. Are there some?

Maybe the more general question is: what should the general approach be to integrating jQuery plugins? Forms API? Something else? If forms API, where should this live? In the new jquery module, possibly as sub-modules, one per type of interface element? In a set of stand alone API modules like tabs (slideshow, carousel, etc.)?

None of this needs to hold up short term work, but it's worth thinking through. sun, katbailey, ideas? Should we maybe move this broader discussion to jquery module or g.d.o?

sun’s picture

Quick note: Of course, I can't foresee it's entire long-term future, but the first and foremost purpose of jquery module will merely be to register jquery libraries via Libraries API, and expose them to modules that want to use them. Libraries API follows the observer pattern, which means that modules are not the authority for choosing what exactly will be loaded, but instead, modules need to react on what is there. If that makes sense. Further details may be found in #719896: Add a hook_libraries_info()

Targeting D7, I think that a module like Tabs should introduce a new #attached handler, so as to allow to process any renderable array structure for tabs. Note that the aforementioned issue is a very large corner-stone for this question: #87994: Quit clobbering people's work when they click the filter tips link (don't get mistaken by the issue title)

katbailey’s picture

Just a quick update - I had some trouble making jQuery UI Tabs and the Ajax framework play nicely together. I want to use the Ajax framework for getting the actual content because it allows us to write one menu callback that behaves differently depending on whether 'nojs' is passed in as its first parameter. If it gets 'nojs', it reloads the current page with the clicked tab selected, otherwise it returns the requisite ajax commands. The first problem is that jQuery UI Tabs also wants to do its own loading of the content (and of course it will always use the nojs version of the path) - I can get around this by sending the abort method to it and this seems to work fine, I'm just not sure whether it's a nice way of doing it.
The second issue is that once a tab has been loaded via the ajax framework, it should behave from then on like a normal tab, i.e. it should just be hidden when you select a different tab, and shouldn't have to get loaded again when you re-select it.
Anyway, I'm sure that's workable-around.

My biggest concern at this point is my own lack of proper understanding of the kinds of issues sun is mentioning above - I haven't been following the relevant developments closely enough. I'm also concerned that a change this big will piss off a lot of existing users of Quicktabs because things won't work exactly the same way they did before. I mean surely we'd have to just get rid of the whole custom styles side of it, right? You'd do your theming the way you theme any other jQuery UI component.

I'll do some more work on it and put it in my sandbox when I have something that's ready to test out.

sun’s picture

If you identify any demand for a callback/hook that could apply in a generic way, please open a core issue for that and link to it from here. Drupal core's support for jQuery UI still is rudimentary, in particular because Drupal core is not really using it on its own. Aforementioned Filter tips modal popup issue tries at least to come up with a start of a generic implementation to allow for easy integration + loading of jQuery UI libraries + behaviors + applying them to your module's markup. Quite possibly, we are missing much more of such helper functions and/or hooks and callbacks in core.

katbailey’s picture

Well, here's one thing that would help for starters: #769258: AJAX behaviors cannot be bound once on links

nedjo’s picture

For a D7 version, we should consider emulating what is done in vertical tabs as closely as possible. Like the current tabs module, the vertical_tab implementation introduces an element type in hook_element_info(). However, there are important differences. Mainly, to add content to a tabset in the D6 tabs module, you assign the tab a child element of type 'tabpage':


  $form['example1'] = array(
    '#type' => 'tabset',
  );
  $form['example1']['tab1'] = array(
    // #type and #title are the minimum requirements.
    '#type' => 'tabpage',
    '#title' => t('One'),
    // This will be the content of the tab.
    '#content' => t('First tab content.'),
  );

In contrast, vertical tabs uses a #group property:


  $form['additional_settings'] = array(
    '#type' => 'vertical_tabs',
    '#weight' => 99,
  );

  $form['revision_information'] = array(
    '#type' => 'fieldset',
    '#title' => t('Revision information'),
    '#group' => 'additional_settings',
  );

So, to minimize confusion, we should consider adding the same #group property to the tabpage element. See http://api.drupal.org/api/function/form_process_fieldset/7 and http://api.drupal.org/api/function/form_process_vertical_tabs/7.

sun’s picture

The important part and processing of vertical tabs is a #pre_render function in D7 though. Those #process functions only prepare the vertical tab groups in $form_state.

Also note that vertical tabs only work with fieldsets. Therefore, #group is only supported on #type 'fieldset'. Also note that this is a horrible abuse of semantic HTML.

But in general, I'd agree that supporting any kind of renderable element as tab content would be a good thing to do. However, any other renderable array than forms does not have the benefit of $form_state, so there needs be a clever initialization of tabsets on a page.

katbailey’s picture

Version: 7.x-2.x-dev » 7.x-3.x-dev

Update: I've committed a 7.x-3.x branch (going to keep the 2.x version for people who really just want what they had in D6). It's an optimistic version that depends on #647228: Links are needlessly unable to fully participate in D7 AJAX framework features and #769258: AJAX behaviors cannot be bound once on links, which are by no means certain of getting in to D7. Also, I had wanted to use tpl files instead of theme functions but the renderable arrays confused the hell out of me so for now I'm just happy that it's functional. Nedjo, you now have commit access, so please feel free to make whatever changes you see fit.
You can see it in action here: http://174.143.25.221/

klonos’s picture

I know there hasn't been any updates here in almost a year, but I am really interested to see where this will go. ...subscribing.

katbailey’s picture

OK, here's an update. Last night I threw together an experimental version of Quicktabs that abstracts out the "tabification" (so that instead of using jQuery UI Tabs you can use jQuery UI Accordion, for example) and also the actual sourcing of the content. So, instead of taking the content from the Quicktabs table (which stores what people have added using the "Create Quicktab" form, you could create a QT block from any kind of content, e.g. the fieldgroups of a node, for example).

Nedjo had suggested something along these lines to me back in October but I wasn't in a position to work on it until now. It's still just a proof of concept but I'll play with it some more and once I'm sure that it provides everything the existing 7.x-3.x version does I'm going to commit it to that branch.

nedjo’s picture

klonos’s picture

Thanx for the update Katherine.

bryancasler’s picture

subscribe

Shadlington’s picture

Subbing

adam_b’s picture

subbubbing

Martin Mayer’s picture

subscribe