diff --git a/src/app/notes/header/language-rail/language-rail.component.scss b/src/app/notes/header/language-rail/language-rail.component.scss index 5dcd9da..3e4a9ce 100644 --- a/src/app/notes/header/language-rail/language-rail.component.scss +++ b/src/app/notes/header/language-rail/language-rail.component.scss @@ -8,6 +8,8 @@ border-bottom: 1px solid var(--line-soft); flex-shrink: 0; overflow-x: auto; + // Same fade as the tag rail: a row that scrolls should say so. + mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); } .language-rail-label { diff --git a/src/app/notes/header/tag-rail/tag-rail.component.html b/src/app/notes/header/tag-rail/tag-rail.component.html index d8d489a..e6a1791 100644 --- a/src/app/notes/header/tag-rail/tag-rail.component.html +++ b/src/app/notes/header/tag-rail/tag-rail.component.html @@ -1,9 +1,12 @@ @if (tags().length > 0) {
{{ 'notes.tags' | transloco }} - @for (tag of tags(); track tag) { - - } + +
+ @for (tag of tags(); track tag) { + + } +
diff --git a/src/app/notes/header/tag-rail/tag-rail.component.scss b/src/app/notes/header/tag-rail/tag-rail.component.scss index 62469cd..972fc0c 100644 --- a/src/app/notes/header/tag-rail/tag-rail.component.scss +++ b/src/app/notes/header/tag-rail/tag-rail.component.scss @@ -5,7 +5,21 @@ padding: 10px 24px; border-bottom: 1px solid var(--line-soft); flex-shrink: 0; +} + +// ⚠️ Only the pills scroll. With the Manage button inside the scrolling row, its +// `margin-left: auto` had no free space left to claim once the tags overflowed, and it +// followed them out of the viewport. +.tag-rail-scroll { + display: flex; + align-items: center; + gap: 7px; + flex: 1; + // Or the row pushes the button out again instead of scrolling. + min-width: 0; overflow-x: auto; + // Falls on empty space while everything fits, so it only shows when there is more. + mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); } .tag-rail-label { @@ -18,7 +32,6 @@ .tag-rail-manage { all: unset; box-sizing: border-box; - margin-left: auto; flex-shrink: 0; padding: 3px 9px; border: 1px solid var(--line-soft); diff --git a/src/app/notes/header/tag-rail/tag-rail.component.spec.ts b/src/app/notes/header/tag-rail/tag-rail.component.spec.ts index a9bad30..358af39 100644 --- a/src/app/notes/header/tag-rail/tag-rail.component.spec.ts +++ b/src/app/notes/header/tag-rail/tag-rail.component.spec.ts @@ -42,6 +42,22 @@ describe('TagRailComponent', () => { expect(rail.getAttribute('aria-label')).toBe('Filtrer par tag'); }); + /** + * ⚠️ Structural, because jsdom lays nothing out. Inside the scrolling row the Manage + * button's `margin-left: auto` had no free space to claim once the tags overflowed, so + * it followed them out of the viewport — at forty tags it sat at x=2710 in a rail 1920 + * wide. What keeps it reachable is being outside that row. + */ + it('keeps the Manage button out of the row that scrolls', async () => { + fixture.componentRef.setInput('tags', ['alpha', 'beta']); + await fixture.whenStable(); + + const scroll = fixture.nativeElement.querySelector('.tag-rail-scroll'); + expect(scroll.querySelectorAll('app-tag-pill')).toHaveLength(2); + expect(scroll.querySelector('.tag-rail-manage')).toBeNull(); + expect(fixture.nativeElement.querySelector('.tag-rail > .tag-rail-manage')).not.toBeNull(); + }); + it('forwards the toggled event from a tag pill as tagToggled', async () => { fixture.componentRef.setInput('tags', ['alpha']); await fixture.whenStable();