Note: if you don't have example sequence data or the STAR index files, see [setup](/setup.html).
-<div>
-{% tabs input %}
-
-{% tab input generic %}
+{% assign tabs = "generic, arvados" | split: ", " %}
+{% capture generic_tab_content %}
main-input.yaml
```
fq:
> logging. The logging you see, how access other logs, and how to
> track workflow progress will depend on your CWL runner platform.
{: .challenge }
+{% endcapture %}
-{% endtab %}
-
-{% tab input arvados %}
+{% capture arvados_tab_content %}
main-input.yaml
```
fq:
> then use the `Run CWL Workflow on Arvados` task.
>
{: .challenge }
-{% endtab %}
-{% endtabs %}
+{% endcapture %}
+
+<div class="tabbed">
+ <ul class="tab">
+ {% for tab in tabs %}
+ <li><a href="#section-{{ tab }}">{{ tab }}</a></li>
+ {% endfor %}
+ </ul>
+
+ <section id="section-generic">{{ generic_tab_content | markdownify}}</section>
+ <section id="section-arvados">{{ arvados_tab_content | markdownify}}</section>
</div>
# Debugging the workflow
This has a similar structure as `main-input.yaml`. The each output
parameter is listed, with the `location` field of each `File` object
indicating where the output file can be found.
+
+<script>
+ (function() {
+ // Get relevant elements and collections
+ const tabbed = document.querySelector('.tabbed');
+ const tablist = tabbed.querySelector('ul');
+ const tabs = tablist.querySelectorAll('a');
+ const panels = tabbed.querySelectorAll('[id^="section"]');
+
+ // The tab switching function
+ const switchTab = (oldTab, newTab) => {
+ newTab.focus();
+ // Make the active tab focusable by the user (Tab key)
+ newTab.removeAttribute('tabindex');
+ // Set the selected state
+ newTab.setAttribute('aria-selected', 'true');
+ newTab.classList.add('active');
+ oldTab.removeAttribute('aria-selected');
+ oldTab.setAttribute('tabindex', '-1');
+ oldTab.classList.remove('active');
+ // Get the indices of the new and old tabs to find the correct
+ // tab panels to show and hide
+ let index = Array.prototype.indexOf.call(tabs, newTab);
+ let oldIndex = Array.prototype.indexOf.call(tabs, oldTab);
+ panels[oldIndex].hidden = true;
+ panels[index].hidden = false;
+ }
+
+ // Add the tablist role to the first <ul> in the .tabbed container
+ tablist.setAttribute('role', 'tablist');
+
+ // Add semantics are remove user focusability for each tab
+ Array.prototype.forEach.call(tabs, (tab, i) => {
+ tab.setAttribute('role', 'tab');
+ tab.setAttribute('id', 'tab' + (i + 1));
+ tab.setAttribute('tabindex', '-1');
+ // tab.setAttribute('class', 'active');
+ tab.parentNode.setAttribute('role', 'presentation');
+
+ // Handle clicking of tabs for mouse users
+ tab.addEventListener('click', e => {
+ e.preventDefault();
+ let currentTab = tablist.querySelector('[aria-selected]');
+ if (e.currentTarget !== currentTab) {
+ switchTab(currentTab, e.currentTarget);
+ }
+ });
+
+ // Handle keydown events for keyboard users
+ tab.addEventListener('keydown', e => {
+ // Get the index of the current tab in the tabs node list
+ let index = Array.prototype.indexOf.call(tabs, e.currentTarget);
+ // Work out which key the user is pressing and
+ // Calculate the new tab's index where appropriate
+ let dir = e.which === 37 ? index - 1 : e.which === 39 ? index + 1 : e.which === 40 ? 'down' : null;
+ if (dir !== null) {
+ e.preventDefault();
+ // If the down key is pressed, move focus to the open panel,
+ // otherwise switch to the adjacent tab
+ dir === 'down' ? panels[i].focus() : tabs[dir] ? switchTab(e.currentTarget, tabs[dir]) : void 0;
+ }
+ });
+ });
+
+ // Add tab panel semantics and hide them all
+ Array.prototype.forEach.call(panels, (panel, i) => {
+ panel.setAttribute('role', 'tabpanel');
+ panel.setAttribute('tabindex', '-1');
+ let id = panel.getAttribute('id');
+ panel.setAttribute('aria-labelledby', tabs[i].id);
+ panel.hidden = true;
+ });
+
+ // Initially activate the first tab and reveal the first tab panel
+ tabs[0].removeAttribute('tabindex');
+ tabs[0].setAttribute('aria-selected', 'true');
+ tabs[0].setAttribute('class', 'active');
+ panels[0].hidden = false;
+ })();
+</script>
-const removeActiveClasses = function (ulElement) {
- const lis = ulElement.querySelectorAll('li');
- Array.prototype.forEach.call(lis, function(li) {
- li.classList.remove('active');
- });
- }
+window.addEventListener('load', function() {
+ // Get relevant elements and collections
+ const tabbed = document.querySelector('.tabbed');
+ const tablist = tabbed.querySelector('ul');
+ const tabs = tablist.querySelectorAll('a');
+ const panels = tabbed.querySelectorAll('[id^="section"]');
- const getChildPosition = function (element) {
- var parent = element.parentNode;
- var i = 0;
- for (var i = 0; i < parent.children.length; i++) {
- if (parent.children[i] === element) {
- return i;
- }
- }
+ // The tab switching function
+ const switchTab = (oldTab, newTab) => {
+ newTab.focus();
+ // Make the active tab focusable by the user (Tab key)
+ newTab.removeAttribute('tabindex');
+ // Set the selected state
+ newTab.setAttribute('aria-selected', 'true');
+ newTab.classList.add('active');
+ oldTab.removeAttribute('aria-selected');
+ oldTab.setAttribute('tabindex', '-1');
+ oldTab.classList.remove('active');
+ // Get the indices of the new and old tabs to find the correct
+ // tab panels to show and hide
+ let index = Array.prototype.indexOf.call(tabs, newTab);
+ let oldIndex = Array.prototype.indexOf.call(tabs, oldTab);
+ panels[oldIndex].hidden = true;
+ panels[index].hidden = false;
+ }
- throw new Error('No parent found');
- }
+ // Add the tablist role to the first <ul> in the .tabbed container
+ tablist.setAttribute('role', 'tablist');
-window.addEventListener('load', function () {
- const tabLinks = document.querySelectorAll('ul.tab li a');
+ // Add semantics are remove user focusability for each tab
+ Array.prototype.forEach.call(tabs, (tab, i) => {
+ tab.setAttribute('role', 'tab');
+ tab.setAttribute('id', 'tab' + (i + 1));
+ tab.setAttribute('tabindex', '-1');
+// tab.setAttribute('class', 'active');
+ tab.parentNode.setAttribute('role', 'presentation');
- Array.prototype.forEach.call(tabLinks, function(link) {
- link.addEventListener('click', function (event) {
- event.preventDefault();
+ // Handle clicking of tabs for mouse users
+ tab.addEventListener('click', e => {
+ e.preventDefault();
+ let currentTab = tablist.querySelector('[aria-selected]');
+ if (e.currentTarget !== currentTab) {
+ switchTab(currentTab, e.currentTarget);
+ }
+ });
- liTab = link.parentNode;
- ulTab = liTab.parentNode;
- position = getChildPosition(liTab);
- if (liTab.className.includes('active')) {
- return;
- }
+ // Handle keydown events for keyboard users
+ tab.addEventListener('keydown', e => {
+ // Get the index of the current tab in the tabs node list
+ let index = Array.prototype.indexOf.call(tabs, e.currentTarget);
+ // Work out which key the user is pressing and
+ // Calculate the new tab's index where appropriate
+ let dir = e.which === 37 ? index - 1 : e.which === 39 ? index + 1 : e.which === 40 ? 'down' : null;
+ if (dir !== null) {
+ e.preventDefault();
+ // If the down key is pressed, move focus to the open panel,
+ // otherwise switch to the adjacent tab
+ dir === 'down' ? panels[i].focus() : tabs[dir] ? switchTab(e.currentTarget, tabs[dir]) : void 0;
+ }
+ });
+ });
- removeActiveClasses(ulTab);
- tabContentId = ulTab.getAttribute('data-tab');
- tabContentElement = document.getElementById(tabContentId);
- removeActiveClasses(tabContentElement);
+ // Add tab panel semantics and hide them all
+ Array.prototype.forEach.call(panels, (panel, i) => {
+ panel.setAttribute('role', 'tabpanel');
+ panel.setAttribute('tabindex', '-1');
+ let id = panel.getAttribute('id');
+ panel.setAttribute('aria-labelledby', tabs[i].id);
+ panel.hidden = true;
+ });
- tabContentElement.querySelectorAll('li')[position].classList.add('active');
- liTab.classList.add('active');
- }, false);
- });
+ // Initially activate the first tab and reveal the first tab panel
+ tabs[0].removeAttribute('tabindex');
+ tabs[0].setAttribute('aria-selected', 'true');
+ tabs[0].setAttribute('class', 'active');
+ panels[0].hidden = false;
});