https://github.com/asciidoctor/asciidoctor-tabs
An extension for Asciidoctor that adds a tabs block to the AsciiDoc syntax.
https://github.com/asciidoctor/asciidoctor-tabs
Last synced: 9 days ago
JSON representation
An extension for Asciidoctor that adds a tabs block to the AsciiDoc syntax.
- Host: GitHub
- URL: https://github.com/asciidoctor/asciidoctor-tabs
- Owner: asciidoctor
- License: mit
- Created: 2022-10-01T05:54:33.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-09-30T18:45:53.000Z (7 months ago)
- Last Synced: 2024-10-30T00:00:39.704Z (6 months ago)
- Language: Ruby
- Size: 236 KB
- Stars: 39
- Watchers: 6
- Forks: 7
- Open Issues: 4
-
Metadata Files:
- Readme: README.adoc
- Changelog: CHANGELOG.adoc
- License: LICENSE
Awesome Lists containing this project
- stars - asciidoctor/asciidoctor-tabs - An extension for Asciidoctor that adds a tabs block to the AsciiDoc syntax. (Ruby)
README
= Asciidoctor Tabs
Dan Allen
v1.0.0-beta.6, 2023-08-01
:idprefix:
:idseparator: -
ifndef::env-github[:icons: font]
ifdef::env-github[]
:note-caption: :paperclip:
:tip-caption: :bulb:
endif::[]
:url-repo: https://github.com/asciidoctor/asciidoctor-tabsAn Asciidoctor extension that adds a tabs block to the AsciiDoc syntax.
NOTE: This extension is intended to be used with HTML backends (e.g., `html5`).
For all other backends (i.e., the filetype is not html), the custom block enclosure is discarded and its contents (a dlist) is converted normally.TIP: This extension is also published as an npm package named `@asciidoctor/tabs` for use with Asciidoctor.js, and hence, with Antora.
See the xref:js/README.adoc[README] for the npm package and its xref:docs/use-with-antora.adoc[Antora integration guide] for details.== Overview
Each set of tabs (i.e., a "`tabset`" or tabs block) is constructed from a description list (dlist) enclosed in an example block annotated with the tabs style (i.e., `[tab]`).
That nested combination of blocks gets translated by this extension into a single tabs block that is a specialization of an open block.The tabbed interface produced from this block can help organize information by code language, operating system, or product variant.
The benefit of organizing information in this way is that it condenses the use of vertical space by only showing what's relevant to the reader (and thus hiding information that's irrelevant or redundant).
The result is that readers enjoy a better user experience when reading your documentation.== Install
=== Using gem command
$ gem install --prerelease asciidoctor-tabs
=== Using Bundler
Create a [.path]_Gemfile_ in your project:
.Gemfile
[,ruby]
----
source 'https://rubygems.org'gem 'asciidoctor-tabs'
# or use the code directlly from GitHub
# gem 'asciidoctor-tabs', github: 'asciidoctor/asciidoctor-tabs'
----Then optionally configure Bundler to install gems locally:
$ bundle config --local path .bundle/gems
Then use Bundler to install the gem:
$ bundle
== Syntax
A tabset is defined using a description list (dlist) enclosed in an example block annotated with the tabs style.
.tabs.adoc
[,asciidoc]
----
[tabs]
====
Tab A:: Contents of Tab A.Tab B::
+
Contents of Tab B.Tab C::
+
--
Contents of Tab C.Contains more than one block.
--
====
----The tabs themselves are modeled as a dlist.
Each item in the dlist becomes a separate tab.
The term is used as the tab's label and the description is used as the tab's contents.
The contents can be defined as primary text, attached blocks, or both.
If the tab has a single attached block, and that block is an open block with no attributes, the open block enclosure itself is discarded upon conversion.You may choose to extend the block delimiter length from the typical 4 characters to 6 in order to avoid conflicts with any example blocks inside the tabs block (or just as a matter of style).
.tab-with-example-block.adoc
[,asciidoc]
----
[tabs]
======
Tab A::
+
====
Example block in Tab A.
====Tab B:: Just text.
======
----Using this technique, you can also create nested tabsets.
.tab-with-nested-tabs.adoc
[,asciidoc]
----
[tabs]
======
Tab A::
+
Selecting Tab A reveals a tabset with Tab Y and Tab Z.
+
[tabs]
====
Tab Y:: Contents of Tab Y, nested inside Tab A.
Tab Z:: Contents of Tab Z, nested inside Tab A.
====Tab B:: Just text.
======
----== Tabs Sync
If you want to synchronize (i.e., sync) the tab selection across tabsets, set the `tabs-sync-option` on the document.
.tabs-sync.adoc
[,asciidoc]
----
:tabs-sync-option:[tabs]
====
Tab A:: Triggers selection of Tab A in other congruent tabsets.
Tab B:: Triggers selection of Tab B in other congruent tabsets.
====...
[tabs]
====
Tab A:: Triggers selection of Tab A in other congruent tabsets.
Tab B:: Triggers selection of Tab B in other congruent tabsets.
====
----Only tabsets that have the same sync group ID are synchronized.
By default, the sync group ID is computed by taking the text of each tab, sorting that list, and joining it on `|` (e.g., `A|B`).
Each unique combination of tabs--or congruent tablist--implicitly creates a new sync group.You can override the sync group ID of a tabset using the `sync-group-id` attribute on the block.
This allows you to control the scope of the sync or to force a tabset to participate in a sync group even if its not congruent..tabs-with-custom-sync-groups.adoc
[,asciidoc]
----
:tabs-sync-option:[tabs,sync-group-id=group-1]
====
Tab A:: Triggers selection of Tab A in second tabset.
Tab B:: Triggers selection of Tab B in second tabset.
====[tabs,sync-group-id=group-1]
====
Tab A:: Triggers selection of Tab A in first tabset.
Tab B:: Triggers selection of Tab B in first tabset.
====[tabs,sync-group-id=group-2]
====
Tab A:: Triggers selection of Tab A in fourth tabset.
Tab B:: Triggers selection of Tab B in fourth tabset.
====[tabs,sync-group-id=group-2]
====
Tab A:: Triggers selection of Tab A in third tabset.
Tab B:: Triggers selection of Tab B in third tabset.
====
----Instead of enabling tabs sync globally, you can set the `sync` option on individual tabs blocks.
.tabs-with-sync-option.adoc
[,asciidoc]
----
[tabs%sync]
====
Tab A:: Triggers selection of Tab A in third tabset.
Tab B:: Triggers selection of Tab B in third tabset.
====[tabs]
====
Tab A:: Does not trigger selection of Tab A in other tabsets.
Tab B:: Does not trigger selection of Tab B in other tabsets.
====[tabs%sync]
====
Tab A:: Triggers selection of Tab A in first tabset.
Tab B:: Triggers selection of Tab B in first tabset.
====
----Conversely, if you want to delist a tabs block from the global sync, set the `nosync` option on that block.
.tabs-with-nosync-option.adoc
[,asciidoc]
----
:tabs-sync-option:[tabs]
====
Tab A:: Triggers selection of Tab A in third tabset.
Tab B:: Triggers selection of Tab B in third tabset.
====[tabs%nosync]
====
Tab A:: Does not trigger selection of Tab A in other tabsets.
Tab B:: Does not trigger selection of Tab B in other tabsets.
====[tabs]
====
Tab A:: Triggers selection of Tab A in first tabset.
Tab B:: Triggers selection of Tab B in first tabset.
====
----If you want to persist the sync selection, assign a value to the `data-sync-storage-key` attribute on the `` tag.
[,js]
----
<script data-sync-storage-key="preferred-tab">
----By default, the sync selection (per group) will be persisted to local storage (i.e., `data-sync-storage-scope="local"`) using the specified key.
You can set the `data-sync-storage-scope` attribute on the `<script>` tag to `session` to use session storage instead of local storage.[,js]
----
<script data-sync-storage-key="preferred-tab" data-sync-storage-scope="session">
----When using the extension on a standalone document (which will automatically embed the supporting script), you can configure these options using the `tabs-sync-storage-key` and `tabs-sync-storage-scope` document attributes, respectively.
[,asciidoc]
----
:tabs-sync-storage-key: tabs
:tabs-sync-storage-scope: session
----In this case, the converter will set the corresponding attributes on the `<script>` tag automatically.
== Usage
=== CLI
$ asciidoctor -r asciidoctor-tabs tabs.adoc
You can specify an alternate stylesheet for tabs using the `tabs-stylesheet` document attribute.
$ asciidoctor -r asciidoctor-tabs -a tabs-stylesheet=my-tabs.css tabs.adoc
The value of the `tabs-stylesheet` attribute is handled in the same way as the built-in `stylesheet` document attribute.
A relative path is resolved starting from the value of the `stylesdir` document attribute, which defaults to the directory of the document.=== API
There are two ways to use the extension with the Asciidoctor API.
In either case, you must require the Asciidoctor gem (`asciidoctor`) before requiring this one.You can require `asciidoctor/tabs` to register the extension as a global extension, just like with the CLI.
[,js]
----
require 'asciidoctor'
require 'asciidoctor/tabs'Asciidoctor.convert_file 'tabs.adoc', safe: :safe
----Or you can pass a registry instance to the `Extensions.register` method to register the extension with a scoped registry.
[,js]
----
require 'asciidoctor'
require 'asciidoctor/tabs/extensions'registry = Asciidoctor::Extensions.create
Asciidoctor::Tabs::Extensions.register registryAsciidoctor.convert_file 'tabs.adoc', extension_registry: registry, safe: :safe
----If you're not using other scoped extensions, you can pass in the extensions group without first creating a registry instance:
[,js]
----
Asciidoctor.convert_file 'tabs.adoc', extensions: Asciidoctor::Tabs::Extensions.group, safe: :safe
----== How it Works
This extension works by transforming the dlist inside the example block into a tabbed interface.
The example block enclosure is discarded.
The tabbed interface is supported by a stylesheet (style) and script (behavior) that are added to the HTML document by this extension.
(These assets can be found in the [.path]_data_ folder of the gem).NOTE: The stylesheet and script are only added when producing a standalone document.
The stylesheet is added to the end of the `<head>` tag and the script added to the end of the `<body>` tag.
By default, the contents of these assets are embedded into the HTML.
If the `linkcss` attribute is set by the API, the CLI, the document, or the safe mode, the HTML links to these assets.
*In this case, you must ensure the assets (found in the [.path]_data_ folder of the gem or project repository) get copied to the linked location.*The tabbed interface consists of two output elements.
The first element contains an unordered list of all the tab labels in document order.
The second element contains all the tab panes.
The labels and panes are correlated through the use of a unique ID.
Each tab is assigned an `id` attribute and each pane is assigned an `aria-labelledby` attribute that references the corresponding ID.
The added stylesheet sets up the appearance of the tabbed interface and the added script supports the interaction (i.e., tab selection).A tab can be selected when the page loads using a URL fragment (e.g., `#id-of-tab-here`).
Otherwise, the first tab is selected when the page loads.== Development
Follow the instructions below to learn how to get started developing on this project.
=== Retrieve the source code
Copy the {url-repo}[GitHub repository URL] and pass it to the `git clone` command:
[subs=attributes+]
$ git clone {url-repo}Next, switch to the project directory:
[subs=attributes+]
$ cd asciidoctor-tabs=== Install the dependencies
The development dependencies are defined in the [.path]_Gemfile_ at the root of the project.
Use the `bundle` command from Bundler to install these dependencies under the project directory:$ bundle --path=.bundle/gems
You must invoke `bundle` from the project's root directory so it can locate the [.path]_Gemfile_.
=== Run the tests
The test suite is located in the [.path]_spec_ directory.
The tests are based on RSpec.==== Run all tests
You can run all of the tests using Rake:
$ bundle exec rake spec
For more fine-grained control, you can also run the tests directly using RSpec:
$ bundle exec rspec
To run all tests in a single spec, pass the spec file to the `rpec` command:
$ bundle exec rspec spec/reducer_spec.rb
==== Run specific tests
If you only want to run a single test (or a group of tests), you can do so by first tagging the test cases, then filtering the test run using that tag.
Start by adding the `only` tag to one or more specifications:
[source,ruby]
----
it 'should do something new', only: true do
expect(true).to be true
end
----Next, run RSpec with the `only` flag enabled:
$ bundle exec rspec -t only
RSpec will only run the specifications that contain this flag.
You can also filter tests by keyword.
Let's assume we want to run all the tests that have `role` in their description.
Run RSpec with the example filter:$ bundle exec rspec -e role
RSpec will only run the specifications that have a description containing the text `only`.
=== Generate code coverage
To generate a code coverage report when running tests using simplecov, set the `COVERAGE` environment variable as follows when running the tests:
$ COVERAGE=deep bundle exec rake spec
You'll see a total coverage score, a detailed coverage report, and a link to HTML report in the output.
The HTML report helps you understand which lines and branches were missed, if any.== Authors
Asciidoctor Tabs was written by Dan Allen of OpenDevise Inc. and contributed to the Asciidoctor project.
== Copyright and License
Copyright (C) 2018-present Dan Allen (OpenDevise Inc.) and the individual contributors to this project.
Use of this software is granted under the terms of the MIT License.See the link:LICENSE[LICENSE] for the full license text.
== Trademarks
AsciiDoc(R) is a trademark of the Eclipse Foundation, Inc.