Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export class ButtonGroupButtonDirective implements AfterViewInit {
[
"tedi-button",
"tedi-button-group-button",
"no-print",
`tedi-button--${this.effectiveVariant()}`,
`tedi-button--${this.effectiveSize()}`,
].join(" "),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,4 +431,8 @@ describe("ButtonGroupComponent", () => {
expect(getRoot().getAttribute("role")).toBe("group");
}));
});

it("should hide button group buttons when printing", () => {
getItems().forEach((item) => expect(item.classList).toContain("no-print"));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class BaseButtonDirective implements AfterContentChecked {
}

classes = computed(() => {
const classList = [this.classNamePrefix()];
const classList = [this.classNamePrefix(), "no-print"];

if (this.iconOnly()) {
classList.push(`${this.classNamePrefix()}--icon-only`);
Expand Down
77 changes: 77 additions & 0 deletions src/tedi/components/buttons/button/button.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { IconComponent } from "../../base/icon/icon.component";
import { ButtonComponent, ButtonSize, ButtonVariant } from "./button.component";
import { Component, input } from "@angular/core";
import { By } from "@angular/platform-browser";
import { PrintDirective, PrintVisibility } from "../../../directives/print";

@Component({
standalone: true,
imports: [ButtonComponent, PrintDirective],
template: `
<button tedi-button tediPrint="show">Printed</button>
<button tedi-button tediPrint breakInside="avoid">Break only</button>
<button tedi-button [tediPrint]="visibility()">Toggled</button>
`,
})
class PrintOverrideHost {
visibility = input<PrintVisibility | "">("hide");
}

describe("ButtonComponent", () => {
let fixture: ComponentFixture<ButtonComponent>;
Expand Down Expand Up @@ -63,4 +79,65 @@ describe("ButtonComponent", () => {
it("should not contain 'undefined' in class list", () => {
expect(buttonElement.classList).not.toContain("undefined");
});

it("should hide the button when printing", () => {
expect(buttonElement.classList).toContain("no-print");
});

it("should keep show-print when the consumer opts back into printing", () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [PrintOverrideHost],
});

const hostFixture = TestBed.createComponent(PrintOverrideHost);
hostFixture.detectChanges();

const button = hostFixture.debugElement.queryAll(
By.directive(ButtonComponent),
)[0].nativeElement as HTMLElement;

expect(button.classList).toContain("tedi-button");
expect(button.classList).toContain("no-print");
expect(button.classList).toContain("show-print");
});

it("should keep the button hidden when only break inputs are set", () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [PrintOverrideHost],
});

const hostFixture = TestBed.createComponent(PrintOverrideHost);
hostFixture.detectChanges();

const button = hostFixture.debugElement.queryAll(
By.directive(ButtonComponent),
)[1].nativeElement as HTMLElement;

expect(button.classList).toContain("no-print");
expect(button.classList).toContain("break-inside-avoid");
expect(button.classList).not.toContain("show-print");
});

it("should keep the button's own no-print when tediPrint is cleared", () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [PrintOverrideHost],
});

const hostFixture = TestBed.createComponent(PrintOverrideHost);
hostFixture.detectChanges();

const button = hostFixture.debugElement.queryAll(
By.directive(ButtonComponent),
)[2].nativeElement as HTMLElement;

expect(button.classList).toContain("no-print");

hostFixture.componentRef.setInput("visibility", "");
hostFixture.detectChanges();

expect(button.classList).toContain("no-print");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,9 @@ describe("CardButtonComponent", () => {
fixture.detectChanges();
expect(button().disabled).toBe(true);
});

it("should stay printable, matching React's CardButton", () => {
expect(anchor().classList).not.toContain("no-print");
expect(button().classList).not.toContain("no-print");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type ClosingButtonIconSize = 18 | 24;
host: {
"[attr.title]": "showTitle() ? ariaLabel() || _defaultLabel() : null",
"[attr.aria-label]": "ariaLabel() || _defaultLabel()",
class: "no-print",
"[class.tedi-closing-button]": "true",
"[class.tedi-closing-button--small]": "size() === 'small'",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,8 @@ describe("ClosingButtonComponent", () => {
expect(buttonElement.getAttribute("title")).toBeNull();
expect(buttonElement.getAttribute("aria-label")).toBe("Sulge");
});

it("should hide the closing button when printing", () => {
expect(buttonElement.classList).toContain("no-print");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,9 @@ describe("CollapseButtonComponent", () => {
const button: HTMLButtonElement = fixture.nativeElement;
expect(button.getAttribute("type")).toBe("button");
});

it("should hide the collapse button when printing", () => {
const button: HTMLButtonElement = fixture.nativeElement;
expect(button.classList).toContain("no-print");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export class CollapseButtonComponent {
});

protected readonly hostClasses = computed(() => {
const classes = ["tedi-collapse-button"];
const classes = ["tedi-collapse-button", "no-print"];
if (this.open()) classes.push("tedi-collapse-button--open");
if (this.size() === "small") classes.push("tedi-collapse-button--small");
if (this.inverted() && this.arrowType() !== "secondary") {
Expand Down
16 changes: 16 additions & 0 deletions src/tedi/components/buttons/collapse/collapse.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,19 @@ $transition-duration: 300ms;
}
}
}

@media print {
.tedi-collapse__content,
.tedi-collapse__extender {
transition: none;
}

.tedi-collapse__content {
grid-template-rows: 1fr;
overflow: visible;
}

.tedi-collapse__extender {
visibility: visible;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ describe("InfoButtonComponent", () => {
"tedi-info-button--inverted",
);
});

it("should hide the info button when printing", () => {
expect(fixture.nativeElement.classList).toContain("no-print");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { TediTranslationService } from "../../../services";
encapsulation: ViewEncapsulation.None,
host: {
type: "button",
class: "tedi-info-button",
class: "tedi-info-button no-print",
"[class.tedi-info-button--inverted]": "color() === 'inverted'",
"[attr.aria-label]": "ariaLabel() || _defaultLabel()",
},
Expand Down
8 changes: 8 additions & 0 deletions src/tedi/components/layout/footer/footer.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,12 @@ describe("FooterComponent", () => {
);
expect(containerElement.classList).toContain("tedi-footer--mobile");
});

it("should hide the footer when printing", () => {
mockBreakpointService.isBelowBreakpoint.mockReturnValue(signal(false));
const fixture = TestBed.createComponent(FooterComponent);
fixture.detectChanges();

expect(fixture.nativeElement.classList).toContain("no-print");
});
});
3 changes: 3 additions & 0 deletions src/tedi/components/layout/footer/footer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import { BreakpointService } from "../../../services/breakpoint/breakpoint.servi
selector: "tedi-footer",
templateUrl: "./footer.component.html",
styleUrl: "./footer.component.scss",
host: {
class: "no-print",
},
})
export class FooterComponent {
breakpointService = inject(BreakpointService);
Expand Down
35 changes: 35 additions & 0 deletions src/tedi/components/layout/header/header.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { TestBed } from "@angular/core/testing";
import { Component } from "@angular/core";
import { By } from "@angular/platform-browser";
import { HeaderComponent } from "./header.component";

@Component({
standalone: true,
imports: [HeaderComponent],
template: `<header tedi-header></header>`,
})
class HostComponent {}

describe("HeaderComponent", () => {
it("should apply the base class", () => {
TestBed.configureTestingModule({ imports: [HostComponent] });
const fixture = TestBed.createComponent(HostComponent);
fixture.detectChanges();

const header = fixture.debugElement.query(By.directive(HeaderComponent))
.nativeElement as HTMLElement;

expect(header.classList).toContain("tedi-header");
});

it("should hide the header when printing", () => {
TestBed.configureTestingModule({ imports: [HostComponent] });
const fixture = TestBed.createComponent(HostComponent);
fixture.detectChanges();

const header = fixture.debugElement.query(By.directive(HeaderComponent))
.nativeElement as HTMLElement;

expect(header.classList).toContain("no-print");
});
});
2 changes: 1 addition & 1 deletion src/tedi/components/layout/header/header.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
class: "tedi-header",
class: "tedi-header no-print",
},
})
export class HeaderComponent {}
4 changes: 4 additions & 0 deletions src/tedi/components/layout/sidenav/sidenav.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,8 @@ describe("SideNavComponent", () => {
expect(document.body.style.overflow).toBe("");
});
});

it("should hide the sidenav when printing", () => {
expect(sidenavElement.classList).toContain("no-print");
});
});
6 changes: 5 additions & 1 deletion src/tedi/components/layout/sidenav/sidenav.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ export class SideNavComponent {
}

classes = computed(() => {
const classList = ["tedi-sidenav", `tedi-sidenav--${this.size()}`];
const classList = [
"tedi-sidenav",
"no-print",
`tedi-sidenav--${this.size()}`,
];

if (this.dividers()) {
classList.push("tedi-sidenav--dividers");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,4 +269,12 @@ describe("Breadcrumbs", () => {
expect(separators(fixture)).toHaveLength(2);
});
});

it("stays printable — breadcrumbs are context, not a control", () => {
const fixture = setup();
const breadcrumbs = fixture.debugElement.query(By.css("tedi-breadcrumbs"))
.nativeElement as HTMLElement;

expect(breadcrumbs.classList).not.toContain("no-print");
});
});
4 changes: 4 additions & 0 deletions src/tedi/components/navigation/link/link.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@ describe("Link Component", () => {

expect(linkElement.classList).toContain("tedi-link--no-underline");
});

it("should stay printable, unlike buttons", () => {
expect(linkElement.classList).not.toContain("no-print");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { TediTranslationPipe } from "../../../../services/translation/translatio
import { TabsComponent } from "../tabs.component";
import { TabsTriggerComponent } from "../tabs-trigger/tabs-trigger.component";
import { TabsOverflowContentDirective } from "./tabs-overflow-content.directive";
import { PrintVisibility } from "../../../../directives/print";

export type TabsOverflowMode = "dropdown" | "scroll";

Expand Down Expand Up @@ -57,6 +58,7 @@ interface OverflowItem {
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
class: "tedi-tabs-list",
"[class.no-print]": "printVisibility() === 'hide'",
"[class.tedi-tabs-list--fade-start]":
"overflowMode() === 'scroll' && canScrollStart()",
"[class.tedi-tabs-list--fade-end]":
Expand All @@ -83,6 +85,15 @@ export class TabsListComponent implements AfterViewInit, OnDestroy {
readonly overflowMode = input<TabsOverflowMode>("dropdown");
/** Label for the overflow dropdown trigger. Defaults to the `more` translation. */
readonly dropdownLabel = input<string>();
/**
* Controls the visibility of the tablist when printing.
* - `show`: the tablist is printed (default), no print class is applied
* - `hide`: the tablist is left out of the printed output
*
* `tediPrint` on the same element composes with this: `tediPrint="show"`
* wins over `printVisibility="hide"`, matching the core `show-print` rule.
*/
readonly printVisibility = input<PrintVisibility>("show");

private readonly listRef =
viewChild.required<ElementRef<HTMLDivElement>>("list");
Expand Down
Loading
Loading