RFC-31: Introducing Multi-Bodied Macros for Content Formatting in Confluence Cloud

Hi developer community,

We are planning to introduce a new type of macro in Confluence Cloud. Please take some time to read our Request for Comment (RFC) and provide feedback on what you think about it. We would like to understand better how this new macro type can help you build solutions for content formatting, what challenges you may have, and what will help you overcome them.

RFCs are a way for Atlassian to share what we’re working on with our valued developer community.

It’s a document for building shared understanding of a topic. It expresses a technical solution, but can also communicate how it should be built or even document standards. The most important aspect of an RFC is that a written specification facilitates feedback and drives consensus. It is not a tool for approving or committing to ideas, but more so a collaborative practice to shape an idea and to find serious flaws early.

Please respect our community guidelines: keep it welcoming and safe by commenting on the idea, not the people (especially the author); keep it tidy by keeping on topic; empower the community by keeping comments constructive. Thanks!

Summary of Project:

With this project, we are introducing a new Connect macro type in Confluence Cloud that will allow you to store multiple rich-text bodies within a macro. Marketplace partners will have the ability to manipulate these bodies in their apps by adding, removing, and displaying them. Confluence users will be able to add any available Confluence elements to the bodies, including other macros. The rendering and editing of multi-bodied macros will provide a WYSIWYG experience for end users.

While multi-bodied macros offer many opportunities for content formatting, we will focus on the Tabs use case for the first release.

  • Publish: 27 Oct, 2023
  • Discuss: 27 Oct, 2023 - 10 Nov, 2023
  • Resolve: 17 Nov, 2023

Problem

Tabs are one of the most commonly used ways to format content. Users often use tabs on both Confluence Cloud and Confluence Server. The number of tab-related apps on the marketplace proves the popularity and demand for such functionality. On cloud alone, the 13 most popular apps are installed on 21K instances, with more than 10.2M users using them.

On Confluence server, most apps use nested-bodied macros to configure tabs. The outer macro represents the tab group, and the inner macros are tabs.

This approach has several issues:

  • Nested-bodied macros are not supported by the new editor. Partners have to create workaround solutions to implement tabs. Additionally, when users convert their pages to the new editor, any nested content can no longer be edited.
  • Users have different experiences with the user interface (UI) in the editor and renderer.
  • Marketplace partners are responsible for rendering the content of the body. They use a few approaches to do that, but none of them are ideal, and the result is far from what the editor natively renders.
  • Using nested-bodied macros as a layout for tabs creates confusion for users. It’s not obvious how to set up tabs, nor is it clear how tabs will be rendered. For large documents, it’s not easy to change focus from one tab to another.

Atlassian’s goal is to help our customers transition to the cloud and the new editor. The solution outlined in this RFC is crucial because it provides a more user-friendly and efficient way to build Tabs macros (as well as other content formatting macros) on the new editor. By enabling Tabs macros based on this solution, we (Atlassian) with you (Marketplace partners) can encourage customers to migrate from server to cloud, from the legacy editor to the new editor, and from old macros to the new ones.

Proposed Solution

In the proposed solution, we are going to build a new type of macro in Connect that Marketplace partners can use to create Tabs. For our initial release, we are planning to include the following scope:

  • a new multiBodiedExtension ADF node
  • a new macro type with bodyType = multi-rich-text
  • a new UX for the editor and the renderer
  • new AC JS functions to control bodies of the macro

New ADF Node

The nature of tabs is a multi-bodied container, where each tab’s content is represented and stored as a separate body. For this purpose, we will expand the ADF schema by adding a new node type called multiBodiedExtension. This node will allow for the storage of multiple bodies in separate entities, along with any other related data in macro parameters.

{
  "type": "multiBodiedExtension",
  "attrs": {
    "extensionType": "com.atlassian.confluence.macro.core",
    "extensionKey": "tabs-macro",
    "parameters": {
      "macroParams": {
        "tabs": ["Tab1 Title", "Tab2 Title"]
      }
    },
  },
  "content": [
    {
      "type": "extensionBody",
      "content": []
    },
    {
      "type": "extensionBody",
      "content": []
    }
  ]
}

A new macro type

A new macro type will be available in the dynamicContentMacro module. Marketplace partners will define it with the bodyType = multi-rich-text property. We don’t expect to change any other properties.

A new UX for the editor and the render

MultiBodiedExtension will be rendered differently from bodiedExtension.

For Tabs, it will consist of two parts:

  • Partner app (at the top)
  • The frame (at the bottom) where the content will be rendered. You can think of a frame as a viewport, where only one body is displayed at any given moment. In edit mode, it will represent the input field (same as for bodiedExtension) where the user can add most Confluence elements as well as bodiedExtensions from other partners. In view mode, the content will be handled and rendered by the Editor. The macro will be rendered in the same way in the editor and the renderer, providing users with a WYSIWYG experience. Marketplace partners will no longer be responsible for rendering the content. No more hassle with conversion :partying_face:. Instead, we provide a different API so marketplace partners can control which body is currently active/visible in a frame. This and other API functions are described in the next section.

Difference between bodiedExtension and multiBodiedExtension:

bodiedExtension multiBodiedExtension
Number of bodies One Many (the number can be limited depending on performance)
UX Two different representations in Editor and Renderer Very close to WYSIWYG experience
Body render by app developer by Editor

New AC JS functions to control bodies of the macro

Marketplace partners will have the ability to add or remove a new body, activate the body in a frame, and save additional app data (such as tab titles, active tabs, etc). This interaction will be accessible through the new ACJS API. Although we recognize the potential need for other functions, our initial milestone will focus on adding only the following:

  • addBody
  • removeBody
  • showBody / setActiveBody
  • getParams
  • updateParams

Let’s go through some of the flows of how it is going to work

Switching tabs:

  1. The user clicks a Tab2 element
  2. Behind the scenes, the app calls setActiveBody(1) // second tab

Adding a new tab:

  1. The user clicks a :heavy_plus_sign: button
  2. Behind the scenes, the app calls
  3. addBody()
  4. setActiveBody(2) // set active a new tab
  5. updateParams() // adding a new default title (Tab3) to tab titles

Removing a tab:

  1. The user hovers over the Tab element, the remove button :x: appears
  2. The user clicks a :x: button
  3. Behind the scenes, the app calls
  4. removeBody()
  5. setActiveBody(1) // set active the previous tab
  6. updateParams() // updating the list of tab titles

Migration from legacy to new editor

To improve the experience for customers who migrate from server to cloud, we recommend that partners migrate old Tabs macros to new multi-bodied Tabs after the page has been converted from the legacy to the new editor. In some cases, this may be the only option if the old Tabs were based on nested-bodied macros. To make it possible, both macros should be installed on the instance.

Currently, when a page is moved from server to cloud, it ends up in the legacy editor. Most partners have already built cloud apps that display tab macros appropriately as the legacy editor supports nested-bodied macros. However, when a page is converted to the new editor, these macros stop working as nested-bodied macros are converted to regular plain-text macros.

Instead of displaying a broken macro, we recommend that partners display an alternative view that explains why the macro is broken and how it can be fixed. This could be done through a button or a link that converts the existing macro to a multi-bodied macro using the update page API.

As part of this conversion flow, partners would need to:

  • Have both macros installed on the instance
  • Update an old Tabs macro to display an alternative UI when it’s rendered on the new editor
  • Implement the conversion of the node that corresponds to a legacy macro to a new node that is based on a multi-bodied macro.

This flow represents how it may look from the user’s perspective:

Other items under consideration

The following items are out of the scope of our initial release. However, we recognize the significant potential in multi-bodied macros that can be utilized for various use cases. To enable this, we’re considering supporting the following functionalities in future releases:

  • Ability to retrieve/update the content of the macro bodies
  • Positioning the frame in different locations
  • Rendering multiple frames for a single macro
  • Configuring the layout for multi-bodied macros

Asks

While we would appreciate any reactions you have to this RFC (even if it’s simply giving it a supportive “Agree, no serious flaws”), we’re especially interested in learning more about:

  1. How excited are you about multi-bodied macros? If you have (or if you might have) a Tabs macro, would you be interested in migrating it to a multi-bodied macro?
  2. Feature parity with your existing tab apps. What can’t be built but is very important for you?
  3. Is there any improvement you can see that would benefit the proposed approach?
  4. Although our current focus is on the Tabs use case, we are considering the possibility of using multi-bodied macros as a foundation for developing other content formatting macros. Would you be interested in exploring this option?

Thank you!

3 Likes

Two things I can think of that prevents me from using tabs ever, no matter how useful they can appear to be.

  1. How does the text of the tab title get into the search index? Right now, because these are normally just macro parameters to the nested macro, they never enter the search index, leading to poor UX when the user searches for the text in the tab title
  2. If I search for text on the second or subsequent tabs, and then click through from the search results to view the page, how is the correct tab selected so I can see the text? Currently, if the text is on the second tab, even if I do a CTRL+F to look for the text, it will not be displayed. There needs to be a mechanism to (possibly highlight the text and) select the correct tab so that the search text can be seen.

Unless you can solve these 2 problems, tabs will continue to be a disaster for user experience.

Btw: Someone suggested a solution to both of these problems back in 2013.

Also, try solving this problem for the expand macro while you’re at it. That would be great.


Aside: Oh man, tabs are a nightmare. People, please don’t ever use tabs unless you want to confuse your end users. Hiding content when you have too much is not the right answer, unless you don’t want your users to see that content. Restructuring your content into multiple pages is always better UX.

Or let them scroll :scroll:

2 Likes

How, if at all, would this be surfaced in the REST APIs?

4 Likes

I’m unopinionated on the tab idea, but it may impact us if other apps use them for:

  • Form apps. Say a customer wants a form with 20 fields, the vendor can have 20 multibodies.
  • Nested table apps. Say a customer wants a nested table tool, the vendor can develop a macro which takes as many multibodies as cells in the table.
  • Templates. Say my customer wants to apply a template on many pages, the vendor can design everything as a multibody.

Even though the first release focuses only on tabs, I am assuming that the paradigms established for this particular scenario should be applicable to other scenarios. So, how is this going to carry over to other types of use cases in the future?

1 Like

Hi @OleksandrBeztsinnyi

We’re genuinely excited about the introduction of this new macro type! It’s not just the migration experience that’s improving; the prospect of having a macro with multiple bodies opens up a world of possibilities. We know that many of our customers have been eagerly awaiting a more robust solution for content formatting, and this new macro type seems poised to deliver exactly that.

The ability to store and manipulate multiple rich-text bodies within a single macro is a game-changer. It not only simplifies migration for existing users but also promises to enhance the overall content creation and formatting experience in Confluence Cloud. This innovation aligns perfectly with the evolving needs of our users, and we can’t wait to get our hands on it and test its capabilities.

Thank you for the effort and innovation you’re bringing to Confluence Cloud! We’re looking forward to exploring the full potential of this new macro type.

Cheers
Oli

3 Likes

Hi @OleksandrBeztsinnyi,

Thanks for sharing this with the community, this looks very interesting. We have several apps which base their core functionality on retrieving the storage format of Confluence pages and using the “Convert content body” REST API endpoint to transform it into export_view or view format.

Therefore, I’m curious to know:

  • Will this also be represented in the storage format or only ADF?
  • How will this work with the “Convert content body” endpoint? I.e. what view and export_view format can we expect to be returned to us when we try to convert a multi-rich-text macro?

With the focus on apps implementing this in the frontend I’m worried that our apps will not be able to render macros like this properly - which for comparison worked very well with the old nested macros approach.

Cheers,
Sven

6 Likes

Hi @OleksandrBeztsinnyi ,
We’re concerned that exported pages (e.g. to PDF) can’t show these macros in full, and thus that exported pages show different information than what is shown on screen.
How is Atlassian going to handle export with the new macro?

3 Likes

To add to questions about exports. How will tabs be handled in email notifications for page edits?

4 Likes

Hey @david, thanks for your feedback.

I find it hard to disagree with your observation regarding the complexity that tabs introduce for end users. The challenge of searching for content that is presently hidden on another tab is indeed a valid concern, and not the only one at that.

It’s important to recognize that there will always be trade-offs when considering various content formatting solutions. However, it’s worth noting that users tend to appreciate and actively engage with tabs, as indicated by the statistics.

How does the text of the tab title get into the search index?

I think it will be possible with Content Properties.

If I search for text on the second or subsequent tabs, and then click through from the search results to view the page, how is the correct tab selected so I can see the text?

I wouldn’t expect the search to work on hidden content. It’s a known issue that also applies to the expand macro. One of the solutions may be a custom search, that overrides browser search. Any other thoughts?

You could move text off screen rather than hide it. Then if text is selected by the search cursor, you can focus the tab that contains the targeted text.

See JavaScript - detecting what the user searches on the page – Milan Laslop – C++, Android and .NET developer

It’s not bulletproof but it should work for many users. Marginal improvements are better than none.

2 Likes

Thanks @bobbergman1 for your question.

We don’t expect any major changes for the existing v1 and v2 REST APIs.

We are going to support different formats for multi-bodied macros that will be reflected on the next APIs:

Requesting or transforming the body of a page containing multi-bodied macros will return the corresponding response in the requested format.

Another API, the Get macro body by macro ID will not work with multi-bodied macros. In future releases, we may explore the possibility of introducing a similar API for multi-bodied macros

Let us know if we miss any other API that is affected by multi-bodied macros. Thanks!

1 Like

Hey @ademoss , thanks for your question.

The Tabs will be a foundation for other use cases that can be built based on multi-bodied macros. The features mentioned in Other items under consideration will provide the ability to display multiple bodies in different locations and will be determined by application developers.

Thank you, @SvenSchatter, for your feedback and questions.

We have plans to introduce various formats for multi-bodied macros, and storage is certainly one of them. However, implementing view and export_view might be a bit tricky due to the challenge of incorporating interactivity.

Sven, could you please share your specific use case? How do you intend to use multi-bodied macros and what is the reason behind your need to convert it via the API? Would it meet your requirements if the view format only provides an HTML snapshot of the macro with the default state, excluding any JavaScript logic?

1 Like

Hey @marc, you brought up an interesting topic to consider. I don’t have a definitive answer at the moment, but my expectation is that the exported page should mirror the user’s current view. In other words, if the second tab is selected, the exported result should reflect the content of that specific tab. From a user’s perspective, this is what I would anticipate.

You’ve made another excellent point, @BorisBerenberg. I’d anticipate that email notifications should include all changes made across different tabs. Nonetheless, achieving this might pose a significant challenge.

I’d expect email notifications, PDF exports etc to show all the tabs and all the content held within successively, otherwise, whats the point of adding content to a page if it’s just hidden.

On the HTML page, there is a valid and accessible way of getting to the hidden stuff in the other tabs. In a PDF/export and on an email notification, all content needs to be accessible somehow, so just displaying all content is the answer.

Otherwise, good luck making tabs work in a PDF or email.

4 Likes

Hi @OleksandrBeztsinnyi ,
I completely agree with @david here. Confluence is used in many compliance heavy use cases, and having “invisible” content on pages and in exports makes Confluence a hard/impossible to use.

3 Likes

Oh, and don’t break the experience on the Confluence mobile app.

Ship it as a fully working feature for all displays.

What is acceptable:

  • The tabs work as expected on mobile.

Examples of what is not acceptable:

  • Nothing is shown, the macro is a big secret to mobile users. It may as well not exist.
  • A box is displayed saying that tabs are not supported on mobile apps.
  • Tabs are displayed but they don’t work.
4 Likes

Hi @OleksandrBeztsinnyi,

That’s good to hear that you’ll be supporting storage!

Our products essentially take Confluence page trees and export them as PDF, Word, or Websites:

For our exporter apps we are essentially faced with the same challenge as you when it comes to PDF exports or email support. In order to make this feature useful to customers, I believe that - like others have also pointed out - all content of all tabs has to end up in the export. For our own exports, one approach I could see working is to get all the tab bodies and render them one after the other.

For Scroll Viewport, which makes websites out of Confluence content, it’s a little more complicated. Here, we would ideally want to support the same JavaScript logic as you do in Confluence. Unfortunately though, unless the export_view of these tab macros contains appropriate javascript which replicates the actual functionality of the given macro, there will be no way for us to make these macros work the same way on a Viewport site as they do in Confluence.

However, in both cases we’d definitely need access to all of the bodies. Returning just the default state (which might show only a single tab or no tab at all) would not be sufficient.

Cheers,
Sven


EDIT:

I forgot to mention, as long as these macros also support the renderModes feature of normal macros, you could probably push off some of the responsibility here to app developers and let them define how their macro should be displayed as static HTML.

However, for macros which do not implement this themselves, it would then still be very useful for us to be able to access all the rendered bodies, so we can at least try to render our own presentation of the macro as good as possible.

7 Likes