Tabbis theme component

AI-generated summary

A user is developing a theme component for Discourse to implement tabs using the external tabbis.js script. The component includes a JavaScript decorator to load the script and initialize tabs, along with CSS styling. The user initially faced issues with the script not loading properly but discovered that changing the post markup from <button> to <span> elements resolved the problem unfairest. Another user suggested that a plugin might be necessary, but the user clarified that a theme component is sufficient. The user shared their code on GitHub and received feedback from others, including suggestions to optimize script loading and improve specificity in the initialization call Falco.

Hello! I am trying to create a theme component to use the external script tabbis.js so I can create tabs in posts. I followed the walkthrough here: Embed widget within text in a topic - #2 by Johani

Here is what my theme component looks like so far.

Common > Header

// options
const TABBIS_SCRIPT_SRC = "https://cdn.jsdelivr.net/gh/jenstornell/tabbis.js/assets/js/src/tabbis.es6.js";

// we use the Discourse Load script lib to ensure scripts are loaded
// properly. Don't worry, this is smart enough to not duplicate the script
// if it's already loaded
const loadScript = require("discourse/lib/load-script").default;

// create a post decorator
api.decorateCookedElement(
  post => {
    // does this post have tabbis tabs?
    const tabbisTabs = post.querySelectorAll('[data-wrap="tabbis"]');

    // Yes, so let's do some work.
    if (tabbisTabs.length) {
      // for each tabbis widget
      tabbisTabs.forEach(tabbisTab => {

        // load the tabbis script.
        loadScript(TABBIS_SCRIPT_SRC).then(() => {

          // everything is ready, let's call the tabbis script
          tabbis();
        });
      });
    }
  },
  // add an id to the decorator to avoid memory leaks
  { id: "render-tabbis-tabs" }
);

</script>

Common > CSS (copied straight from the tabbis.js repo)

[data-panes] {
  --color: #000; }

[data-tabs] {
  background: var(--color);
  border: 2px solid var(--color);
  border-bottom: none;
  overflow-x: auto;
  display: flex; }
  [data-tabs] > [role="tab"] {
    all: unset;
    padding: .75rem 1.5rem;
    -webkit-user-select: none;
       -moz-user-select: none;
        -ms-user-select: none;
            user-select: none;
    font-weight: 600;
    color: #fff;
    border: 1px solid transparent;
    outline: none; }
    [data-tabs] > [role="tab"]:hover {
      background: rgba(255, 255, 255, 0.2); }
    [data-tabs] > [role="tab"]:active {
      background: rgba(255, 255, 255, 0.3); }
    [data-tabs] > [role="tab"]:focus {
      border: 1px dotted rgba(255, 255, 255, 0.5); }
    [data-tabs] > [role="tab"][aria-selected="true"] {
      background: #fff;
      color: #000; }
      [data-tabs] > [role="tab"][aria-selected="true"]:focus {
        border: 1px dotted rgba(0, 0, 0, 0.5); }

[data-panes] {
  border: 2px solid var(--color);
  border-top: none; }
  [data-panes] > * {
    background: #fff;
    padding: 2rem;
    font-weight: 600;
    outline: none;
    border: 1px solid transparent; }
    [data-panes] > *:focus {
      border: 1px dotted rgba(0, 0, 0, 0.5); }
    @media screen and (max-width: 460px) {
      [data-panes] > * {
        padding: 1rem; } }

Content Security Policy Script src: added https://cdn.jsdelivr.net

Post markup

[wrap="tabbis"]<div data-tabs>
<button>Tab1</button>
<button>Tab2</button>
<button>Tab3</button>
</div>

<div data-panes>
<div>Pane1</div>
<div>Pane2</div>
<div>Pane3</div>
</div>[/wrap]

The script seems to load for a split second after I edit the post, but once it loads fully it’s unusable.

This appears in the console but I have no idea how to fix it. Is there a conflict with other tabs in Discourse?

How can I get this working? I’m at a loss!

I think you’ll need a plugin to do that, but I could be wrong.

You might look at the math plugin or the plugin guides.

Why does this script require a plugin instead of a theme component? The single-page app structure is still very new to me.

Please share your whole theme-component source code (preferably as a GitHub repository) so that we can help you with it easily.

By the way this sounds like a pretty useful theme-component!

Thank you! I was surprised to see that there wasn’t already some kind of tabs plugin or component around here; it’s something I really want for my community so I’m giving it a shot. I am pretty new to anything except very basic javascript but I am trying to learn.

https://github.com/fairest/discourse-tabbis-beta

Changing the post markup to

[wrap="tabbis"]
  <div data-tabs>
    <span>Tab1</span>
    <span>Tab2</span>
    <span>Tab3</span>
  </div>

  <div data-panes>
    <div>Pane1</div>
    <div>Pane2</div>
    <div>Pane3</div>
  </div>
[/wrap]

makes it work :tada:

There are a few notes you could use to improve it further:

  1. Do not call loadScript multiple times. Check if it’s loaded already before calling it, otherwise it will be called multiple times.

  2. Pass a more specific selector to the tabbis() call, so you initialize the one you specifically have selected.