Repository navigation
feat(tabs): add the headerPosition input for the headers - #17884
viktorkombov wants to merge 12 commits into
Conversation
headerPosition places the tab headers in a row above the content (top, the default) or in a column on the left or right side of the content, also in right-to-left layouts. In a column, the headers are navigated with the Up and Down arrow keys, the scroll buttons scroll them up and down, tabAlignment aligns them along the column and the active indicator is on the side next to the content. A header focused with the keyboard in manual activation is scrolled into view, and so is the selected header when hidden tabs become visible. Closes #1982 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a556eb0 to
0b151c3
Compare
Add a Header position option to the tabs demo. A column of headers gets a fixed height, so that the alignment and the scroll buttons show. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Cancel the pending layout frame on destroy only when there is one, so that a server render no longer fails on cancelAnimationFrame. - Skip headers that are not displayed when scrolling with the buttons, so that Previous no longer jumps to the start after a hidden header. - Restore the inline transitions of the headers after a position change. - Simplify the code and the tests, and shorten the comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@desig9stein can you take a look at the changes this PR introduces as you are way more familiar with the Tab component? Thank you! |
desig9stein
left a comment
There was a problem hiding this comment.
In RTL apps, side navigation is normally mirrored: Material Design moves navigation drawers and rails to the right. Arabic and Hebrew users expect a column of tabs on the right, next to where reading starts.
- headerPosition="left" ties the app to one side. To get correct behavior in both directions, the app has to switch the value itself when the language changes.
- With the default top, the tabs already mirror in RTL (the first tab is on the right). A column that doesn't mirror is inconsistent with that.
Suggested fix for the PR: add logical values start and end that follow the direction, and possibly make one of them the documented recommendation. left and right can stay as physical values for the cases that really need a fixed side. This is a small change: in the styles, start is just flex-direction: row with no :dir(rtl) override, because the browser then mirrors it on its own. It's much easier to add before release than after, because changing what left means later would be a breaking change.
To add on top of what Marin said, we should avoid verbiage such as "left" and "right"... it should be "start" or "end" to cater for direction specifically. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@simeonoff, @desig9stein, |
There was a problem hiding this comment.
@viktorkombov Great job on this! Please note that we’ll need to mirror this update in Web Components to ensure both frameworks remain in sync.
@desig9stein, done! I’ve opened an issue in the |
simeonoff
left a comment
There was a problem hiding this comment.
@viktorkombov I left a comment on the scroll buttons, please take a look. It affects the default row of headers too, not just the column. Apart from that, the row looks the same as on master: the compiled CSS for all four themes doesn't change outside the new position classes.
mddragnev
left a comment
There was a problem hiding this comment.
Could you also check your other tests. I think that some of the tests can go because the behavior is already tested in others.
- Leave the display of the scroll buttons to the stylesheet again, so that application styles that hide them still apply. A column hides its buttons with a class until the headers are measured instead. - Make headerPosition an @input, like the other inputs of the tabs. - Pick the arrow keys of the next and previous tab once in keyDown. - Move the width check of a justified column into the column size test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Not introduced with this feature, but it would be nice to be fixed- in the tab dev demo, each tab is one-way bound in tabs-showcase.sample.html: <igx-tab-item [selected]="contact.id === selectedTabId". On a second click on the 'Select by index' button, this leads to unexpected behavior, so it would be nice to fix it by handling the selectedIndexChange event. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@teodosiah Done! It is fixed with the last commit. |
Closes #1982
The Tabs Specification now includes the new option.
Description
Adds
headerPositiontoigx-tabs.top, the default, keeps the headers in a row above the content, andstartandendplace them in a column before or after the content:In a column:
startandendfollow the text direction. In right-to-left layoutsstartis on the right, where side navigation usually is, so the column mirrors as the row of headers does.activationapplies as in a row.tabAlignmentaligns the headers along the column. Withjustify, they share the free height of the column.headerPositioncan change at runtime. The selection, the focus and the rendered headers and panels stay the same, and the focused header, or else the selected one, is scrolled into view.A few changes also apply to the default row of headers:
manualactivation, a header focused with the keyboard is now scrolled into view.display: noneno longer hides the scroll buttons.The
/tabsdev demo has a new Header position option in its properties panel. A column of headers gets a fixed height there, so that the alignment and the scroll buttons can be seen.Motivation / Context
Requested in #1982. The same feature is logged for Web Components in IgniteUI/igniteui-webcomponents#2553.
The keyboard freeze when all tabs are disabled is fixed separately in #17881, backported in #17882 and #17885. Both PRs change the keyboard handling of the tab headers, so the one merged second needs a small rebase. After that, Up and Down in a column use the fixed search for the next enabled tab as well.
Not changed here: after the headers are scrolled with the buttons, Tab can move the focus to a header that is out of view, and the previous button then scrolls the wrong way. That happens in the default row of headers too and is tracked in #17883.
Type of Change (check all that apply):
Component(s) / Area(s) Affected:
Tabs
How Has This Been Tested?
The new tests cover the column on both sides in LTR and RTL, changing the position at runtime, keyboard navigation, scrolling,
tabAlignment, the indicator, the theme-specific borders of all four themes in light and dark, hidden tabs, and zoneless change detection.Three of them cover edge cases found in review, and each fails without its fix:
transition: all, so the selected header was scrolled into view against the sizes at the start of the animation and ended up partly hidden. When the position changes, the headers are now measured without their transitions.ResizeObserver loop completed with undelivered notifications. A column now updates its scroll buttons in the next frame.A second review found three more problems:
ngOnDestroycalledcancelAnimationFrame, which the server does not have, so a server render failed with aReferenceError. It is now called only when a frame is pending. A browser test cannot show this, so I checked it withrenderApplicationfrom@angular/platform-serveragainst the production build, and the render now completes.display: nonehas an offset of 0, so Previous jumped to the start. The scroll buttons now skip hidden headers.transitionset inline on a header. It is now restored.The tests of the last two were extended and fail without the fix.
The existing RTL test "should position scroll buttons properly" now waits for the layout after it changes the width. The scroll buttons are set right after the first render now, instead of on the first resize notification, so they are hidden while the headers fit.
All tabs and bottom navigation tests pass, also in repeated runs.
npm run lint:lib,npm run test:stylesand the production build pass.Test Configuration:
Screenshots / Recordings
N/A
Checklist:
feature/README.MDupdates for the feature docsREADME.MDCHANGELOG.MDupdates for newly added functionalityng updatemigrations for the breaking changes (migrations guidelines)🤖 Generated with Claude Code