Skip to content

feat: add NcTabs component - #9143

Open
remko48 wants to merge 4 commits into
mainfrom
feat/9126/nc-tabs
Open

remko48 wants to merge 4 commits into
mainfrom
feat/9126/nc-tabs

Conversation

@remko48

@remko48 remko48 commented Oct 30, 2026

Copy link
Copy Markdown

☑️ Resolves

Note:

🖼️ Screenshots

00-page-01

🏁 Checklist

  • ⛑️ Tests are included or are not applicable
  • 📘 Component documentation has been extended, updated or is not applicable
  • 2️⃣ Backport to stable8 for maintained Vue 2 version or not applicable

AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Styleguidist caches a stylesheet by component name, and the Markdown table and the props table are both called TableRenderer. Whichever rendered first in a session styled both, so after a page with a table in its documentation every props, events and slots table lost its padding, borders and column widths. The Markdown table now renders under its own name with the same styles.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
Renders a row of tabs and the panel of the selected tab, for switching between views in the app content. The tabs are passed as data and the selected one is bound with v-model; each panel is a panel-<id> slot and only the selected one is rendered. A tab can have an icon, a count and be disabled. Follows the ARIA tabs pattern: arrow keys, Home and End move between the enabled tabs and select them.

The tabs look like the sidebar tabs, with the icon above the name and the tabs sharing the width of the row, including their older look before Nextcloud 34. The row scrolls when the tabs do not fit.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
…h the shown tab

The row of tabs scrolls, so it clipped the focus outline of the tabs, almost entirely in the look before Nextcloud 34. It now leaves room for the outline. In forced colors the selected tab looked like the others; its bar is now drawn in the system highlight color. When the model is a disabled, unknown or removed tab, the first enabled tab is shown and now also becomes the model. The tablist can be named by a visible heading with the new ariaLabelledby prop. The ellipsis rules that never applied are removed.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
The element ids were built from the tab ids, so a tab id with whitespace broke aria-controls and aria-labelledby, which are lists of ids. They are now built from the position of the tab. The docs also say that a panel starts afresh each time its tab is selected.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
@remko48 remko48 added enhancement New feature or request 3. to review Waiting for reviews component Component discussion and/or suggestion labels Oct 30, 2026
@remko48
remko48 requested a review from a team as a code owner October 30, 2026 13:07
@codecov

codecov Bot commented Oct 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.11321% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 59.59%. Comparing base (44c25ae) to head (1f78f8f).
⚠️ Report is 16 commits behind head on main.

Files with missing lines Patch % Lines
src/components/NcTabs/NcTabs.vue 98.11% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9143      +/-   ##
==========================================
+ Coverage   59.11%   59.59%   +0.48%     
==========================================
  Files         125      126       +1     
  Lines        4520     4574      +54     
  Branches     1347     1370      +23     
==========================================
+ Hits         2672     2726      +54     
  Misses       1570     1570              
  Partials      278      278              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment on lines +139 to +146
defineEmits<{
/**
* Another tab was selected.
*
* @param id - The `id` of the selected tab
*/
'update:modelValue': [id: string]
}>()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// When the model is a disabled, unknown or removed tab, another tab is shown: make that
// the model, so the parent, and for example the URL, hold the tab that is shown
watch(activeTab, (tab) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we really write the model back? If a tab is disabled while loading, a ?tab=sharing deep link would get lost. Maybe just show the fallback and let the parent handle it?

:aria-selected="tab === activeTab ? 'true' : 'false'"
:aria-controls="tab === activeTab ? panelElementId(tab) : undefined"
:tabindex="tab === activeTab ? 0 : -1"
:disabled="tab.disabled"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the native disabled, keyboard and screen reader users never land on a disabled tab, so they don't know it's there. Would aria-disabled="true" and keeping it focusable work better? That's what the APG suggests.

<div
v-if="activeTab"
:id="panelElementId(activeTab)"
:key="activeTab.id"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arrowing through the tabs remounts every panel on the way, which could mean a request per keypress for heavy panels. What about arrows only moving focus, and Enter/Space selecting?

@susnux
susnux requested a review from a team November 2, 2026 16:22

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews component Component discussion and/or suggestion enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants