Apply sticky table headers for markdown-rendered table views

What does this MR do and why?

Add sticky table headers for markdown tables in view mode

Wrap markdown tables with a bounded-height scroll container (max-height: 70vh) and apply position: sticky to thead cells. This keeps column headers visible when scrolling long tables.

Changelog: added

Screenshots or screen recordings

Tested in Chrome browser:

Before After

Browsers

Browser Recording
Safari
Firefox
iPhone 14 Pro Max emulator

How to set up and validate locally

The Ultimate Markdown Test Document # The Ultimate Markdown Test Document

This document is designed to test frontend markdown rendering across a wide variety of syntax and edge cases.


1. Headings

H1 — The Quick Brown Fox

H2 — Jumps Over

H3 — The Lazy Dog

H4 — And Then Some

H5 — Going Deeper
H6 — As Deep As It Gets

2. Text Formatting

Regular paragraph text sits here, flowing naturally across the line. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Bold text and also bold appear in many places.

Italic text and also italic are slightly different but similar.

Bold and italic combined together.

Strikethrough text for deleted or deprecated content.

Inline code looks like this inline.

This is a line with a manual
line break (two trailing spaces).

This is a new paragraph after a blank line.


3. Blockquotes

This is a simple blockquote. It contains a single line of quoted content.

This is a longer blockquote. It spans multiple lines. Each line starts with a > character.

Blockquote with heading

Blockquotes can also contain other markdown elements like bold, italic, and even lists:

  • Item one
  • Item two
  • Item three

Pretty neat, right?

Nested blockquote level one

Nested blockquote level two

Nested blockquote level three


4. Lists

Unordered Lists

  • Alpha
  • Beta
  • Gamma
    • Gamma Sub-item 1
    • Gamma Sub-item 2
      • Deep nested item
      • Another deep item
    • Gamma Sub-item 3
  • Delta

Ordered Lists

  1. First item
  2. Second item
  3. Third item
    1. Sub-item 3.1
    2. Sub-item 3.2
  4. Fourth item
  5. Fifth item

Mixed Lists

  1. Top-level ordered
    • Unordered child
    • Another unordered child
      1. Back to ordered
      2. Still ordered
  2. Back to top-level ordered

Task Lists

  • Set up the project
  • Write unit tests
  • Deploy to staging
  • Write documentation
  • Deploy to production
  • Celebrate 🎉

5. Code Blocks

Inline Code

Use const instead of var whenever possible. Call the function like myFunc(arg1, arg2).

Fenced Code Block (JavaScript)

// A simple async fetch example
async function fetchData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (err) {
    console.error('Fetch failed:', err);
    throw err;
  }
}

const result = await fetchData('https://api.example.com/data');
console.log(result);

Fenced Code Block (Python)

from typing import Optional
import httpx

class APIClient:
    def __init__(self, base_url: str, api_key: Optional[str] = None):
        self.base_url = base_url
        self.headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}

    async def get(self, endpoint: str) -> dict:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{self.base_url}/{endpoint}",
                headers=self.headers
            )
            response.raise_for_status()
            return response.json()

# Usage
client = APIClient("https://api.example.com", api_key="secret-key")
data = await client.get("users/42")
print(data)

Fenced Code Block (SQL)

SELECT
    u.id,
    u.name,
    u.email,
    COUNT(o.id) AS total_orders,
    SUM(o.amount) AS total_spent
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.created_at >= '2024-01-01'
  AND u.is_active = TRUE
GROUP BY u.id, u.name, u.email
HAVING total_spent > 500
ORDER BY total_spent DESC
LIMIT 25;

Fenced Code Block (Bash)

#!/bin/bash

set -euo pipefail

APP_NAME="my-app"
VERSION=$(git describe --tags --always)
IMAGE="$APP_NAME:$VERSION"

echo "Building Docker image: $IMAGE"
docker build -t "$IMAGE" .

echo "Running tests..."
docker run --rm "$IMAGE" npm test

echo "Pushing to registry..."
docker tag "$IMAGE" "registry.example.com/$IMAGE"
docker push "registry.example.com/$IMAGE"

echo "Done! Version $VERSION deployed."

No-language Code Block

Plain preformatted text
  with some indentation
    and more indentation
No syntax highlighting here.
    Tabs and spaces preserved.

6. Tables

Simple Table

Name Age City
Alice 30 New York
Bob 25 Los Angeles
Charlie 35 Chicago
Diana 28 Houston

Aligned Table

Left-Aligned Center-Aligned Right-Aligned
Apple Red $1.20
Banana Yellow $0.50
Blueberry Blue $3.99
Watermelon Green/Red $5.00

Wide Table

Feature Starter Pro Enterprise
Users 5 25 Unlimited
Storage 10 GB 100 GB 10 TB
API Calls/month 1,000 50,000 Unlimited
Support Email Chat Dedicated
Custom Domains
SSO
SLA None 99.9% 99.99%
Price/month Free $49 Contact Us

7. Horizontal Rules

Three hyphens:


Three asterisks:


Three underscores:



Basic hyperlink

Link with title

Reference-style link

Auto-link: https://www.example.com

Auto-email: hello@example.com


9. Images

A placeholder image

Smaller placeholder


10. Footnotes

Here is a sentence with a footnote.1

Here is another sentence with a different footnote.2


11. Definition Lists (Extended Markdown)

Markdown
A lightweight markup language for creating formatted text.
HTML
HyperText Markup Language, the standard language for web pages.
CSS
Cascading Style Sheets, used for styling web documents.

12. Mathematical Notation (LaTeX-style)

Inline math: E = mc^2

Block math:

\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}

\frac{\partial f}{\partial x} = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}


13. Miscellaneous

Escaping Characters

*Not italic* **Not bold** `Not code`

Long Unbroken String

averylongstringthatmightcauseoverflowissuesifnothandledproperlybythefrontendrenderer_averylongstringthatmightcauseoverflowissues

Emoji

Markdown renderers often support emoji shortcodes: 🚀 🎉 🔥

Or raw unicode emoji: 🚀 🎉 🔥 💡 📦 🛠️ 🧪 🎨

Special HTML Characters in Text

AT&T, 2 < 3, 5 > 4, "quoted text", 100% done.


14. Nested & Complex Structures

Blockquote containing a code block and a list

Here's a quoted section with embedded content:

const x = 42;
console.log(x);

And a list inside the quote:

  1. Step one
  2. Step two
  3. Step three

List containing blockquotes

  • First point is straightforward.
  • Second point includes a quote:

    "We choose to go to the moon." — JFK

  • Third point continues normally.

Table inside a section with other content

Below is a comparison of sorting algorithms:

Algorithm Best Average Worst Space Stable Best Average Worst Space Stable
Bubble Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Bubble Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Bubble Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Bubble Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Bubble Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Bubble Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Bubble Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Bubble Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes O(n) O(n²) O(n²) O(1) Yes

Choose your algorithm based on the constraints of your specific use case.


15. Long Prose Section

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vehicula, arcu non facilisis tincidunt, purus turpis consequat augue, sit amet tincidunt turpis leo ac nisl. Fusce euismod turpis at diam cursus, in tincidunt nisi facilisis. Sed non velit ut orci dictum cursus.

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra.

Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

A subsection within long prose

This subsection breaks up the long prose with a heading and continues with more content. The quick brown fox jumps over the lazy dog. The five boxing wizards jump quickly. How vexingly quick daft zebras jump! Pack my box with five dozen liquor jugs.

Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis molestie dictum semper, diam lectus blandit dui, ut semper ipsum eros vel sapien. Duis commodo nisi lorem, a interdum lorem pharetra vel. Proin euismod augue in felis lacinia, vel pretium enim hendrerit.

query: assignee = currentUser()
fields: title, createdAt, milestone, assignee
title: Issues assigned to current user

End of test document. If you can see this, the markdown rendered correctly! 🎉

  1. Toggle on the Feature Flag editor_sticky_table_headers
  2. Go to a wiki page, e.g. http://gdk.test:3000/flightjs/Flight/-/wikis/home. If it does not exist yet, create the wiki
  3. Copy / paste the markdown above into it
  4. Scroll table at the bottom horizontally and vertically

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #585265

  1. This is the first footnote, appearing at the bottom.

  2. This is a named footnote. It can contain bold, italic, and code.

Edited by Vanessa Otto

Merge request reports

Loading