Skip to content

Commit

Permalink
feat(editor): Added field contacts for metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
Romuald Caplier committed Aug 26, 2024
1 parent b0bcda8 commit cd18cb7
Show file tree
Hide file tree
Showing 9 changed files with 545 additions and 10 deletions.
2 changes: 1 addition & 1 deletion libs/feature/editor/src/lib/+state/editor.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const initialEditorState: EditorState = {
saveError: null,
changedSinceSave: false,
editorConfig: DEFAULT_CONFIGURATION,
currentPage: 0,
currentPage: 2, //todo: remove before merge
}

const reducer = createReducer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,20 @@
</gn-ui-autocomplete>

<ng-container *ngIf="contactsForRessourceByRole.get(role) as contacts">
<ng-container *ngIf="contacts.length > 1">
<gn-ui-sortable-list
[elements]="contactsAsDynElemByRole.get(role)"
(elementsChange)="handleContactsChanged($event)"
></gn-ui-sortable-list>
</ng-container>
<ng-container *ngIf="contacts.length === 1">
<ng-container *ngFor="let contact of contacts">
<gn-ui-contact-card
[contact]="contact"
(contactRemoved)="removeContact(index)"
></gn-ui-contact-card> </ng-container
></ng-container>

<ng-container *ngIf="contacts.length > 1">
<gn-ui-sortable-list
[elements]="contactsAsDynElemByRole.get(role)"
(elementsChange)="handleContactsChanged($event)"
></gn-ui-sortable-list>
</ng-container>
</ng-container>

<hr class="border-t-[#D6D3D1] mt-4 mb-6" *ngIf="!isLast" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<div class="flex flex-col gap-3">
<div class="flex flex-row flex-wrap gap-2" data-test="rolesToPick">
<!-- <ng-container *ngFor="let role of rolesToPick">-->
<!-- <gn-ui-button-->
<!-- extraClass="px-2 py-1.5"-->
<!-- (buttonClick)="addRoleToDisplay(role)"-->
<!-- >-->
<!-- <div class="flex flex-row gap-1 items-center">-->
<!-- <span class="text-primary text-[20px] leading-[0] font-bold pb-[5px]"-->
<!-- >&#8330;</span-->
<!-- >-->
<!-- <span class="font-bold" translate>{{ roleToLabel(role) }}</span>-->
<!-- </div>-->
<!-- </gn-ui-button>-->
<!-- </ng-container>-->
</div>
<!-- <div-->
<!-- class="mt-8"-->
<!-- *ngIf="-->
<!-- roleSectionsToDisplay && roleSectionsToDisplay.length > 0;-->
<!-- else noContact-->
<!-- "-->
<!-- >-->
<!-- <div-->
<!-- *ngFor="-->
<!-- let role of roleSectionsToDisplay;-->
<!-- let index = index;-->
<!-- let isLast = last-->
<!-- "-->
<!-- class="flex flex-col gap-4"-->
<!-- >-->
<!-- <div class="flex flex-row justify-between">-->
<!-- <span class="font-bold text-base" translate>{{-->
<!-- roleToLabel(role)-->
<!-- }}</span>-->
<!-- </div>-->

<gn-ui-autocomplete
[placeholder]="'Choose a contact'"
[action]="autoCompleteAction"
(itemSelected)="addContact($event)"
[displayWithFn]="displayWithFn"
[minCharacterCount]="1"
[clearOnSelection]="true"
>
</gn-ui-autocomplete>
<ng-container *ngIf="contacts.length > 0; else noContact">
<ng-container *ngIf="contacts.length === 1">
<ng-container *ngFor="let contact of contacts; let index = index">
<gn-ui-contact-card
[contact]="contact"
(contactRemoved)="removeContact()"
></gn-ui-contact-card> </ng-container
></ng-container>

<ng-container *ngIf="contacts.length > 1">
<gn-ui-sortable-list
[elements]="contactsAsDynElem"
(elementsChange)="handleContactsChanged($event)"
></gn-ui-sortable-list>
</ng-container>
</ng-container>
</div>
<ng-template #noContact>
<div
class="p-4 border border-primary bg-primary-lightest rounded-lg"
translate
>
editor.record.form.field.contacts.noContact
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormFieldContactsComponent } from './form-field-contacts.component'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
import { BehaviorSubject } from 'rxjs'
import {
Individual,
Organization,
Role,
} from '@geonetwork-ui/common/domain/model/record'
import { ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'
import { UserModel } from '@geonetwork-ui/common/domain/model/user'
import { CommonModule } from '@angular/common'
import { TranslateModule } from '@ngx-translate/core'
import { ContactCardComponent } from '../../../contact-card/contact-card.component'
import {
AutocompleteComponent,
DropdownSelectorComponent,
UiInputsModule,
} from '@geonetwork-ui/ui/inputs'
import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
import { FormControl } from '@angular/forms'

const organizationBarbie: Organization = {
name: 'Barbie Inc.',
}

const organizationGoogle: Organization = {
name: 'Google',
}

class MockPlatformServiceInterface {
getUsers = jest.fn(() => new BehaviorSubject([]))
}

class MockOrganizationsServiceInterface {
organisations$ = new BehaviorSubject([organizationBarbie, organizationGoogle])
}

describe('FormFieldContactsForResourceComponent', () => {
let component: FormFieldContactsComponent
let fixture: ComponentFixture<FormFieldContactsComponent>

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
FormFieldContactsComponent,
CommonModule,
TranslateModule.forRoot(),
UiInputsModule,
ContactCardComponent,
DropdownSelectorComponent,
],
providers: [
{
provide: PlatformServiceInterface,
useClass: MockPlatformServiceInterface,
},
{
provide: OrganizationsServiceInterface,
useClass: MockOrganizationsServiceInterface,
},
ChangeDetectorRef,
],
})
.overrideComponent(AutocompleteComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default },
})
.compileComponents()

fixture = TestBed.createComponent(FormFieldContactsComponent)
component = fixture.componentInstance
component.control = new FormControl<Individual[]>([])
fixture.detectChanges()
})

it('should create the component', () => {
expect(component).toBeTruthy()
})

describe('ngOnInit', () => {
it('should initialize organizations', async () => {
await component.ngOnInit()

expect(component.allOrganizations.size).toBe(2)
})
})

describe('addRoleToDisplay', () => {
it('should add role to display and filter roles to pick', () => {
const initialRolesToPick = [...component.rolesToPick]
const roleToAdd = initialRolesToPick[0]

component.addRoleToDisplay(roleToAdd)

expect(component.roleSectionsToDisplay).toContain(roleToAdd)
expect(component.rolesToPick).not.toContain(roleToAdd)
})
})

describe('filterRolesToPick', () => {
it('should filter roles already in roleSectionsToDisplay', () => {
component.rolesToPick = ['custodian', 'owner'] as Role[]
component.roleSectionsToDisplay = ['custodian'] as Role[]

component.filterRolesToPick()

expect(component.rolesToPick).toEqual(['owner'])
})
})

describe('updateContactsForRessource', () => {
it('should update contactsForRessourceByRole and contactsAsDynElemByRole', () => {
const mockContact: Individual = {
role: 'owner',
organization: { name: 'Org1' } as Organization,
} as Individual

component.allOrganizations.set('Org1', { name: 'Org1' } as Organization)
component.control.setValue([mockContact])

component.updateContacts()

expect(component.contactsForRessourceByRole.get('owner')).toEqual([
mockContact,
])
expect(component.contactsAsDynElemByRole.get('owner').length).toBe(1)
})
})

describe('manageRoleSectionsToDisplay', () => {
it('should add new roles to roleSectionsToDisplay', () => {
const mockContact: Individual = {
role: 'owner',
organization: { name: 'Org1' } as Organization,
} as Individual

component.manageRoleSectionsToDisplay([mockContact])

expect(component.roleSectionsToDisplay).toContain('owner')
})
})

describe('removeContact', () => {
it('should remove contact at specified index', () => {
const mockContacts: Individual[] = [
{
role: 'owner',
organization: { name: 'Org1' } as Organization,
} as Individual,
{
role: 'custodian',
organization: { name: 'Org2' } as Organization,
} as Individual,
]

component.control.setValue(mockContacts)
component.removeContact(0)

expect(component.control.value.length).toBe(1)
expect(component.control.value[0]).toEqual(mockContacts[1])
})
})

describe('handleContactsChanged', () => {
it('should update contacts based on reordered dynamic elements', () => {
const mockContacts: Individual[] = [
{
role: 'owner',
organization: { name: 'Org1' } as Organization,
} as Individual,
{
role: 'owner',
organization: { name: 'Org2' } as Organization,
} as Individual,
]

component.contactsForRessourceByRole.set('owner', [mockContacts[0]])
component.contactsForRessourceByRole.set('owner', [mockContacts[1]])

const reorderedElements = [
{ inputs: { contact: mockContacts[1] } } as any,
{ inputs: { contact: mockContacts[0] } } as any,
]

component.handleContactsChanged(reorderedElements)

const newControlValue = component.control.value
expect(newControlValue[0]).toEqual(mockContacts[1])
expect(newControlValue[1]).toEqual(mockContacts[0])
})
})

describe('addContact', () => {
it('should add a new contact to the control value', () => {
const mockUser: UserModel = {
username: 'user1',
name: 'John',
surname: 'Doe',
organisation: 'Org1',
} as UserModel

component.allOrganizations.set('Org1', { name: 'Org1' } as Organization)
const initialContacts = component.control.value.length

component.addContact(mockUser, 'owner')

expect(component.control.value.length).toBe(initialContacts + 1)
expect(component.control.value[initialContacts].role).toBe('owner')
expect(component.control.value[initialContacts].organization.name).toBe(
'Org1'
)
})
})

describe('ngOnDestroy', () => {
it('should unsubscribe from all subscriptions', () => {
const subscriptionSpy = jest.spyOn(component.subscription, 'unsubscribe')

component.ngOnDestroy()

expect(subscriptionSpy).toHaveBeenCalled()
})
})
})
Loading

0 comments on commit cd18cb7

Please sign in to comment.