Skip to content

Commit 56e9d0f

Browse files
bdfranckchrisolsen
authored andcommitted
feat(#2478): add work side menu component
1 parent 39df355 commit 56e9d0f

File tree

18 files changed

+1905
-0
lines changed

18 files changed

+1905
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./work-side-menu/work-side-menu";
2+
export * from "./work-side-menu-item/work-side-menu-item";
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { ComponentFixture, TestBed, fakeAsync, tick } from "@angular/core/testing";
2+
import { GoabxWorkSideMenuItem } from "./work-side-menu-item";
3+
import { Component } from "@angular/core";
4+
import { By } from "@angular/platform-browser";
5+
6+
@Component({
7+
standalone: true,
8+
imports: [GoabxWorkSideMenuItem],
9+
template: `
10+
<goabx-work-side-menu-item
11+
[label]="label"
12+
[url]="url"
13+
[badge]="badge"
14+
[current]="current"
15+
[divider]="divider"
16+
[icon]="icon"
17+
[testId]="testId"
18+
[type]="type"
19+
>
20+
</goabx-work-side-menu-item>
21+
`,
22+
})
23+
class TestWorkSideMenuItemComponent {
24+
label = "Test label";
25+
url = "/test";
26+
badge = "Test badge";
27+
current = true;
28+
divider = true;
29+
icon = "triangle";
30+
testId = "test-id";
31+
type = "normal";
32+
}
33+
34+
describe("GoabxWorkSideMenuItem", () => {
35+
let fixture: ComponentFixture<TestWorkSideMenuItemComponent>;
36+
let component: TestWorkSideMenuItemComponent;
37+
38+
beforeEach(fakeAsync(() => {
39+
TestBed.configureTestingModule({
40+
imports: [TestWorkSideMenuItemComponent],
41+
}).compileComponents();
42+
43+
fixture = TestBed.createComponent(TestWorkSideMenuItemComponent);
44+
component = fixture.componentInstance;
45+
46+
fixture.detectChanges();
47+
tick(); // Wait for setTimeout in ngOnInit
48+
fixture.detectChanges();
49+
}));
50+
51+
it("should render and set the props correctly", () => {
52+
const menuItemElement = fixture.debugElement.query(
53+
By.css("goa-work-side-menu-item"),
54+
).nativeElement;
55+
expect(menuItemElement.getAttribute("label")).toBe("Test label");
56+
expect(menuItemElement.getAttribute("url")).toBe("/test");
57+
expect(menuItemElement.getAttribute("badge")).toBe("Test badge");
58+
expect(menuItemElement.getAttribute("current")).toBe("true");
59+
expect(menuItemElement.getAttribute("divider")).toBe("true");
60+
expect(menuItemElement.getAttribute("icon")).toBe("triangle");
61+
expect(menuItemElement.getAttribute("testid")).toBe("test-id");
62+
expect(menuItemElement.getAttribute("type")).toBe("normal");
63+
});
64+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { GoabWorkSideMenuItemType } from "@abgov/ui-components-common";
2+
import {
3+
CUSTOM_ELEMENTS_SCHEMA,
4+
Component,
5+
Input,
6+
OnInit,
7+
ChangeDetectorRef,
8+
} from "@angular/core";
9+
import { CommonModule } from "@angular/common";
10+
11+
@Component({
12+
standalone: true,
13+
selector: "goabx-work-side-menu-item", // eslint-disable-line
14+
imports: [CommonModule],
15+
template: `
16+
<goa-work-side-menu-item
17+
*ngIf="isReady"
18+
[attr.label]="label"
19+
[attr.url]="url"
20+
[attr.badge]="badge"
21+
[attr.current]="current"
22+
[attr.divider]="divider"
23+
[attr.icon]="icon"
24+
[attr.testid]="testId"
25+
[attr.type]="type"
26+
>
27+
<ng-content />
28+
</goa-work-side-menu-item>
29+
`,
30+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
31+
})
32+
export class GoabxWorkSideMenuItem implements OnInit {
33+
@Input({ required: true }) label!: string;
34+
@Input({ required: true }) url!: string;
35+
@Input() badge?: string;
36+
@Input() current?: boolean;
37+
@Input() divider?: boolean;
38+
@Input() icon?: string;
39+
@Input() testId?: string;
40+
@Input() type?: GoabWorkSideMenuItemType = "normal";
41+
42+
isReady = false;
43+
44+
constructor(private cdr: ChangeDetectorRef) {}
45+
46+
ngOnInit(): void {
47+
// For Angular 20, we need to delay rendering the web component
48+
// to ensure all attributes are properly bound before the component initializes
49+
setTimeout(() => {
50+
this.isReady = true;
51+
this.cdr.detectChanges();
52+
}, 0);
53+
}
54+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { ComponentFixture, TestBed, tick, fakeAsync } from "@angular/core/testing";
2+
import { GoabxWorkSideMenu } from "./work-side-menu";
3+
import { Component } from "@angular/core";
4+
import { By } from "@angular/platform-browser";
5+
6+
@Component({
7+
standalone: true,
8+
imports: [GoabxWorkSideMenu],
9+
template: `
10+
<goabx-work-side-menu
11+
[open]="open"
12+
[heading]="heading"
13+
[url]="url"
14+
[userName]="userName"
15+
[userSecondaryText]="userSecondaryText"
16+
[testId]="testId"
17+
[primaryContent]="primaryTemplate"
18+
[secondaryContent]="secondaryTemplate"
19+
[accountContent]="accountTemplate"
20+
>
21+
</goabx-work-side-menu>
22+
<ng-template #primaryTemplate>
23+
<div>Primary content</div>
24+
</ng-template>
25+
<ng-template #secondaryTemplate>
26+
<div>Secondary content</div>
27+
</ng-template>
28+
<ng-template #accountTemplate>
29+
<div>Account content</div>
30+
</ng-template>
31+
`,
32+
})
33+
class TestWorkSideMenuComponent {
34+
open = true;
35+
heading = "Test heading";
36+
url = "/test";
37+
userName = "Test User";
38+
userSecondaryText = "test@example.com";
39+
testId = "test-id";
40+
}
41+
describe("GoabxBWorkSideMenu", () => {
42+
let fixture: ComponentFixture<TestWorkSideMenuComponent>;
43+
let component: TestWorkSideMenuComponent;
44+
45+
beforeEach(fakeAsync(() => {
46+
TestBed.configureTestingModule({
47+
imports: [TestWorkSideMenuComponent],
48+
}).compileComponents();
49+
50+
fixture = TestBed.createComponent(TestWorkSideMenuComponent);
51+
component = fixture.componentInstance;
52+
fixture.detectChanges();
53+
tick(); // Wait for setTimeout in ngOnInit
54+
fixture.detectChanges();
55+
}));
56+
57+
it("should render and set the props correctly", () => {
58+
const menuElement = fixture.debugElement.query(
59+
By.css("goa-work-side-menu"),
60+
).nativeElement;
61+
expect(menuElement.getAttribute("heading")).toBe("Test heading");
62+
expect(menuElement.getAttribute("url")).toBe("/test");
63+
expect(menuElement.getAttribute("user-name")).toBe("Test User");
64+
expect(menuElement.getAttribute("user-secondary-text")).toBe("test@example.com");
65+
expect(menuElement.getAttribute("testId")).toBe("test-id");
66+
});
67+
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {
2+
booleanAttribute,
3+
CUSTOM_ELEMENTS_SCHEMA,
4+
Component,
5+
Input,
6+
Output,
7+
TemplateRef,
8+
EventEmitter,
9+
OnInit,
10+
ChangeDetectorRef,
11+
} from "@angular/core";
12+
import { CommonModule, NgTemplateOutlet } from "@angular/common";
13+
@Component({
14+
standalone: true,
15+
selector: "goabx-work-side-menu", // eslint-disable-line
16+
imports: [NgTemplateOutlet, CommonModule],
17+
template: `
18+
<goa-work-side-menu
19+
*ngIf="isReady"
20+
[open]="open ?? false"
21+
[attr.heading]="heading"
22+
[attr.url]="url"
23+
[attr.user-name]="userName"
24+
[attr.user-secondary-text]="userSecondaryText"
25+
[attr.testid]="testId"
26+
(_toggle)="_onToggle()"
27+
>
28+
<div slot="primary">
29+
<ng-container [ngTemplateOutlet]="primaryContent"></ng-container>
30+
</div>
31+
<div slot="secondary">
32+
<ng-container [ngTemplateOutlet]="secondaryContent"></ng-container>
33+
</div>
34+
<div slot="account">
35+
<ng-container [ngTemplateOutlet]="accountContent"></ng-container>
36+
</div>
37+
</goa-work-side-menu>
38+
`,
39+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
40+
})
41+
export class GoabxWorkSideMenu implements OnInit {
42+
@Input({ required: true }) heading!: string;
43+
@Input({ required: true }) url!: string;
44+
@Input() userName?: string;
45+
@Input() userSecondaryText?: string;
46+
@Input({ transform: booleanAttribute }) open?: boolean;
47+
@Input() testId?: string;
48+
@Input() primaryContent!: TemplateRef<any>;
49+
@Input() secondaryContent!: TemplateRef<any>;
50+
@Input() accountContent!: TemplateRef<any>;
51+
@Output() onToggle = new EventEmitter();
52+
53+
isReady = false;
54+
55+
constructor(private cdr: ChangeDetectorRef) {}
56+
57+
ngOnInit(): void {
58+
// For Angular 20, we need to delay rendering the web component
59+
// to ensure all attributes are properly bound before the component initializes
60+
setTimeout(() => {
61+
this.isReady = true;
62+
this.cdr.detectChanges();
63+
}, 0);
64+
}
65+
66+
_onToggle() {
67+
this.onToggle.emit();
68+
}
69+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./lib/angular-components.module";
22
export * from "./lib/components";
3+
export * from "./experimental";
34
export * from "@abgov/ui-components-common";

libs/common/src/lib/common.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,3 +1169,6 @@ export type GoabPublicFormTaskStatus = "completed" | "not-started" | "cannot-sta
11691169
export type GoabDrawerPosition = "bottom" | "left" | "right" | undefined;
11701170
export type GoabDrawerSizeUnit = "px" | "rem" | "ch" | "vh" | "vw";
11711171
export type GoabDrawerSize = `${number}${GoabDrawerSizeUnit}` | undefined;
1172+
1173+
// Work side menu
1174+
export type GoabWorkSideMenuItemType = "normal" | "emergency" | "success";

0 commit comments

Comments
 (0)