Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow specifying icons for header objectDetails #101

Merged
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
@@ -1,4 +1,4 @@
<div class="onecx-page-header" data-testid="ocx-page-header-wrapper">
<div class="onecx-page-header" name="ocx-page-header-wrapper">
<ng-container *ngIf="showBreadcrumbs">
<p-breadcrumb
[model]="items"
Expand Down Expand Up @@ -47,7 +47,7 @@ <h4>{{ subheader }}</h4>
(onClick)="action.actionCallback()"
[title]="(action.titleKey ? (action.titleKey | translate) : action.title) || (action.labelKey ? (action.labelKey | translate) : action.label)"
[disabled]="action.disabled ? action.disabled : false"
[attr.data-testid]="action.icon ? 'ocx-page-header-inline-action-icon-button' : 'ocx-page-header-inline-action-button'"
[attr.name]="action.icon ? 'ocx-page-header-inline-action-icon-button' : 'ocx-page-header-inline-action-button'"
></p-button>
</ng-container>
</div>
Expand All @@ -62,7 +62,7 @@ <h4>{{ subheader }}</h4>
title="{{ 'OCX_PAGE_HEADER.MORE_ACTIONS' | translate }}"
class="more-actions-menu-button action-button ml-2"
(click)="menu.toggle($event)"
data-testid="ocx-page-header-overflow-action-button"
name="ocx-page-header-overflow-action-button"
></button>
<p-menu #menu [popup]="true" [model]="overflowActions" appendTo="body"></p-menu>
</div>
Expand All @@ -82,9 +82,16 @@ <h4>{{ subheader }}</h4>
class="object-info flex flex-row md:flex-column align-items-baseline md:align-items-center justify-content-between"
*ngFor="let item of objectDetails"
>
<label class="flex font-medium text-600">{{ item.label | dynamicPipe:item.labelPipe }}</label>
<span class="flex text-900 mt-2" [title]="item.tooltip || ''"
>{{ item.value | dynamicPipe:item.valuePipe:item.valuePipeArgs}}</span
<label class="flex font-medium text-600" name="object-detail-label">{{ item.label | dynamicPipe:item.labelPipe }}</label>
<span
*ngIf="item.icon || item.value"
class="flex text-900 mt-2"
[ngClass]="item.icon ? 'gap-1 align-items-center' : null"
[title]="item.tooltip || ''"
name="object-detail-value"
>
<i *ngIf="item.icon" class="{{item.icon}}" name="object-detail-icon"></i>
{{ item.value | dynamicPipe:item.valuePipe:item.valuePipeArgs}}</span
>
</div>
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { Action, PageHeaderComponent } from './page-header.component'
import { RouterTestingModule } from '@angular/router/testing'
import { ConfigurationService } from '../../../services/configuration.service'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { Component } from '@angular/core'
import { TranslateTestingModule } from 'ngx-translate-testing'
import { BreadcrumbModule } from 'primeng/breadcrumb'
import { MenuModule } from 'primeng/menu'
import { ButtonModule } from 'primeng/button'
import { AppStateService } from '../../../services/app-state.service'
import { UserService } from '../../../services/user.service'
import { MockUserService } from '../../../../../mocks/mock-user-service'
import { PageHeaderHarness, TestbedHarnessEnvironment } from '../../../../../testing'
import { DynamicPipe } from '../../pipes/dynamic.pipe'
import { PrimeIcons } from 'primeng/api'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'

const mockActions: Action[] = [
Expand Down Expand Up @@ -41,15 +43,6 @@ const mockActions: Action[] = [
},
]

// Mock host component that's used in our testBed instead of ocx-page-header
// Using this mock host allows us to simulate Angular @Input mechanisms
@Component({
template: '<ocx-page-header [actions]="actions"></ocx-page-header>',
})
class TestHostComponent {
actions: Action[] | undefined
}

describe('PageHeaderComponent', () => {
const origAddEventListener = window.addEventListener
const origPostMessage = window.postMessage
Expand All @@ -73,13 +66,14 @@ describe('PageHeaderComponent', () => {
window.postMessage = origPostMessage
})

let component: TestHostComponent
let fixture: ComponentFixture<TestHostComponent>
let component: PageHeaderComponent
let fixture: ComponentFixture<PageHeaderComponent>
let pageHeaderHarness: PageHeaderHarness
let userServiceSpy: jest.SpyInstance<boolean, [permissionKey: string], any>

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [PageHeaderComponent, TestHostComponent],
declarations: [PageHeaderComponent, PageHeaderComponent, DynamicPipe],
imports: [
RouterTestingModule,
HttpClientTestingModule,
Expand All @@ -104,124 +98,197 @@ describe('PageHeaderComponent', () => {
})
})

beforeEach(() => {
fixture = TestBed.createComponent(TestHostComponent)
beforeEach(async () => {
fixture = TestBed.createComponent(PageHeaderComponent)
component = fixture.componentInstance
fixture.detectChanges()
pageHeaderHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, PageHeaderHarness)
const userService = fixture.debugElement.injector.get(UserService)
jest.restoreAllMocks()
userServiceSpy = jest.spyOn(userService, 'hasPermission')
})

it('should create', () => {
it('should create', async () => {
expect(component).toBeTruthy()
expect(fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-wrapper"]')).toHaveLength(
1
)
const pageHeaderWrapper = await pageHeaderHarness.getPageHeaderWrapperHarness()
expect(pageHeaderWrapper).toBeTruthy()
})

it('should check permissions and render buttons accordingly', () => {
it('should check permissions and render buttons accordingly', async () => {
expect(
fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-inline-action-button"]')
await pageHeaderHarness.getInlineActionButtons()
).toHaveLength(0)
expect(
fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-overflow-action-button"]')
await pageHeaderHarness.getOverflowActionButtons()
).toHaveLength(0)

component.actions = mockActions
fixture.detectChanges()

expect(
fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-inline-action-button"]')
await pageHeaderHarness.getInlineActionButtons()
).toHaveLength(1)
expect(fixture.debugElement.nativeElement.querySelector('[title="My Test Action"]')).toBeTruthy()
expect(await pageHeaderHarness.getElementByTitle('My Test Action')).toBeTruthy()
expect(
fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-overflow-action-button"]')
await pageHeaderHarness.getOverflowActionButtons()
).toHaveLength(1)
expect(fixture.debugElement.nativeElement.querySelector('[title="More actions"]')).toBeTruthy()
expect(userServiceSpy).toHaveBeenCalledTimes(6)
expect(await pageHeaderHarness.getElementByTitle('More actions')).toBeTruthy()
expect(userServiceSpy).toHaveBeenCalledTimes(3)
})

it("should check permissions and not render button that user isn't allowed to see", () => {
it("should check permissions and not render button that user isn't allowed to see", async () => {
userServiceSpy.mockClear()

userServiceSpy.mockReturnValue(false)

expect(
fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-inline-action-button"]')
await pageHeaderHarness.getInlineActionButtons()
).toHaveLength(0)
expect(
fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-overflow-action-button"]')
await pageHeaderHarness.getOverflowActionButtons()
).toHaveLength(0)

component.actions = mockActions
fixture.detectChanges()

expect(
fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-inline-action-button"]')
await pageHeaderHarness.getInlineActionButtons()
).toHaveLength(0)
expect(fixture.debugElement.nativeElement.querySelector('[title="My Test Action"]')).toBeFalsy()
expect(await pageHeaderHarness.getElementByTitle('My Test Action')).toBeFalsy()
expect(
fixture.debugElement.nativeElement.querySelectorAll('[data-testid="ocx-page-header-overflow-action-button"]')
await pageHeaderHarness.getOverflowActionButtons()
).toHaveLength(0)
expect(fixture.debugElement.nativeElement.querySelector('[title="More actions"]')).toBeFalsy()
expect(userServiceSpy).toHaveBeenCalledTimes(6)
expect(await pageHeaderHarness.getElementByTitle('More actions')).toBeFalsy()
expect(userServiceSpy).toHaveBeenCalledTimes(3)
})

it('should show overflow actions when menu overflow button clicked', () => {
it("should render objectDetails as object info in the page header", async () => {
const objectDetailsWithoutIcons = [
{
label: 'Venue',
value: 'AIE Munich',
},
{
label: 'Status',
value: 'Confirmed',
},
]
expect((await pageHeaderHarness.getObjectInfos()).length).toEqual(0)

component.objectDetails = objectDetailsWithoutIcons

expect((await pageHeaderHarness.getObjectInfos()).length).toEqual(2)
const objectDetailLabels = await pageHeaderHarness.getObjectDetailLabels()
const objectDetailValues = await pageHeaderHarness.getObjectDetailValues()
const objectDetailIcons = await pageHeaderHarness.getObjectDetailIcons()
expect(objectDetailLabels.length).toEqual(2)
expect(objectDetailValues.length).toEqual(2)
expect(objectDetailIcons.length).toEqual(0)

objectDetailLabels.forEach(async (label, i) => {
expect(await label.text()).toEqual(objectDetailsWithoutIcons[i].label)
})

objectDetailValues.forEach(async (value, i) => {
expect(await value.text()).toEqual(objectDetailsWithoutIcons[i].value)
})
})

it("should render objectDetails with icons as object info in the page header", async () => {
const objectDetailsWithIcons = [
{
label: 'Venue',
value: 'AIE Munich',
},
{
label: 'Status',
value: 'Confirmed',
icon: PrimeIcons.CHECK
},
{
label: 'Done?',
icon: PrimeIcons.EXCLAMATION_CIRCLE
},
{
label: 'Empty'
}
]
expect((await pageHeaderHarness.getObjectInfos()).length).toEqual(0)

component.objectDetails = objectDetailsWithIcons

expect((await pageHeaderHarness.getObjectInfos()).length).toEqual(4)
const objectDetailLabels = await pageHeaderHarness.getObjectDetailLabels()
const objectDetailValues = await pageHeaderHarness.getObjectDetailValues()
const objectDetailIcons = await pageHeaderHarness.getObjectDetailIcons()
expect(objectDetailLabels.length).toEqual(4)
expect(objectDetailValues.length).toEqual(3)
expect(objectDetailIcons.length).toEqual(2)

objectDetailLabels.forEach(async (label, i) => {
expect(await label.text()).toEqual(objectDetailsWithIcons[i].label)
})

objectDetailValues.forEach(async (value, i) => {
if(objectDetailsWithIcons[i].value) {
expect(await value.text()).toEqual(objectDetailsWithIcons[i].value)
}
})

expect(await objectDetailIcons[0].getAttribute('class')).toEqual(objectDetailsWithIcons[1].icon)
expect(await objectDetailIcons[1].getAttribute('class')).toEqual(objectDetailsWithIcons[2].icon)
})

it('should show overflow actions when menu overflow button clicked', async () => {
component.actions = mockActions
fixture.detectChanges()

const menuOverflowButton = fixture.debugElement.nativeElement.querySelector(
'[data-testid="ocx-page-header-overflow-action-button"]'
)
expect(menuOverflowButton).toBeTruthy()
menuOverflowButton.click()
fixture.detectChanges()
const menuOverflowButtons = await pageHeaderHarness.getOverflowActionButtons()

expect(menuOverflowButtons).toBeTruthy()
expect(menuOverflowButtons.length).toBe(1)
await menuOverflowButtons[0].click()

expect(fixture.debugElement.nativeElement.querySelector('[title="My Test Overflow Action"]')).toBeTruthy()
expect(fixture.debugElement.nativeElement.querySelector('[title="My Test Overflow Disabled Action"]')).toBeTruthy()
const menuItems = await pageHeaderHarness.getOverFlowMenuItems()
expect(menuItems.length).toBe(2)
expect(await menuItems[0].getText()).toBe("My Test Overflow Action")
expect(await menuItems[1].getText()).toBe("My Test Overflow Disabled Action")
})

it('should use provided action callback on overflow button click', () => {
it('should use provided action callback on overflow button click', async () => {
jest.spyOn(console, 'log')

component.actions = mockActions
fixture.detectChanges()

const menuOverflowButton = fixture.debugElement.nativeElement.querySelector(
'[data-testid="ocx-page-header-overflow-action-button"]'
)
expect(menuOverflowButton).toBeTruthy()
menuOverflowButton.click()
fixture.detectChanges()
const menuOverflowButtons = await pageHeaderHarness.getOverflowActionButtons()

expect(menuOverflowButtons).toBeTruthy()
expect(menuOverflowButtons.length).toBe(1)
await menuOverflowButtons[0].click()

const enabledActionElement = fixture.debugElement.nativeElement.querySelector('[title="My Test Overflow Action"]')
expect(enabledActionElement).toBeTruthy()
expect(enabledActionElement.classList).not.toContain('p-disabled')
enabledActionElement.click()
const menuItems = await pageHeaderHarness.getOverFlowMenuItems()
expect(menuItems.length).toBe(2)
const enabledActionElement = await menuItems[0].host()
expect(await(enabledActionElement.hasClass('p-disabled'))).toBe(false)
await enabledActionElement.click()
expect(console.log).toHaveBeenCalledTimes(1)
})

it('should disable overflow button when action is disabled', () => {
it('should disable overflow button when action is disabled', async () => {
jest.spyOn(console, 'log')

component.actions = mockActions
fixture.detectChanges()

const menuOverflowButton = fixture.debugElement.nativeElement.querySelector(
'[data-testid="ocx-page-header-overflow-action-button"]'
)
const menuOverflowButton = await pageHeaderHarness.getOverflowActionButtons()
expect(menuOverflowButton).toBeTruthy()
menuOverflowButton.click()
fixture.detectChanges()
expect(menuOverflowButton.length).toBe(1)
menuOverflowButton[0].click()

const disabledActionElement = fixture.debugElement.nativeElement.querySelector(
'[title="My Test Overflow Disabled Action"]'
)
const overFlowMenuItems = await pageHeaderHarness.getOverFlowMenuItems()
const disabledActionElement = overFlowMenuItems[1]

expect(overFlowMenuItems).toBeTruthy()
expect(overFlowMenuItems?.length).toBe(2)
expect(disabledActionElement).toBeTruthy()
expect(disabledActionElement.classList).toContain('p-disabled')
disabledActionElement.click()
expect(await (await disabledActionElement.host()).hasClass('p-disabled')).toBe(true)
await (await disabledActionElement.host()).click()
expect(console.log).toHaveBeenCalledTimes(0)
})
})
Loading
Loading