mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
75058: multiple badges fix, item alerts, tests
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<ng-template dsListableObject>
|
||||
</ng-template>
|
||||
<div #badges class="position-absolute ml-1">
|
||||
<div #badges>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<ul #buttons class="list-group list-group-flush">
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@@ -13,7 +12,6 @@ import { SharedModule } from '../../../../../shared/shared.module';
|
||||
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
||||
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
||||
import { ItemAdminSearchResultGridElementComponent } from './item-admin-search-result-grid-element.component';
|
||||
@@ -71,51 +69,4 @@ describe('ItemAdminSearchResultGridElementComponent', () => {
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when the item is not withdrawn', () => {
|
||||
beforeEach(() => {
|
||||
component.dso.isWithdrawn = false;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not show the withdrawn badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
|
||||
expect(badge).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is withdrawn', () => {
|
||||
beforeEach(() => {
|
||||
component.dso.isWithdrawn = true;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show the withdrawn badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
|
||||
expect(badge).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not private', () => {
|
||||
beforeEach(() => {
|
||||
component.dso.isDiscoverable = true;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should not show the private badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.private-badge'));
|
||||
expect(badge).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is private', () => {
|
||||
beforeEach(() => {
|
||||
component.dso.isDiscoverable = false;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show the private badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.private-badge'));
|
||||
expect(badge).not.toBeNull();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
@@ -1,12 +1,7 @@
|
||||
<div *ngIf="dso && !dso.isDiscoverable" class="private-badge">
|
||||
<span class="badge badge-danger">{{ "admin.search.item.private" | translate }}</span>
|
||||
</div>
|
||||
<div *ngIf="dso && dso.isWithdrawn" class="withdrawn-badge">
|
||||
<span class="badge badge-warning">{{ "admin.search.item.withdrawn" | translate }}</span>
|
||||
</div>
|
||||
<ds-listable-object-component-loader [object]="object"
|
||||
[viewMode]="viewModes.ListElement"
|
||||
[index]="index"
|
||||
[linkType]="linkType"
|
||||
[listID]="listID"></ds-listable-object-component-loader>
|
||||
[listID]="listID"
|
||||
[hideBadges]="true"></ds-listable-object-component-loader>
|
||||
<ds-item-admin-search-result-actions-element [item]="dso" [small]="false"></ds-item-admin-search-result-actions-element>
|
||||
|
@@ -1,11 +1,9 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
||||
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
||||
import { ItemAdminSearchResultListElementComponent } from './item-admin-search-result-list-element.component';
|
||||
@@ -51,51 +49,4 @@ describe('ItemAdminSearchResultListElementComponent', () => {
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when the item is not withdrawn', () => {
|
||||
beforeEach(() => {
|
||||
component.dso.isWithdrawn = false;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not show the withdrawn badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
|
||||
expect(badge).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is withdrawn', () => {
|
||||
beforeEach(() => {
|
||||
component.dso.isWithdrawn = true;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show the withdrawn badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
|
||||
expect(badge).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not private', () => {
|
||||
beforeEach(() => {
|
||||
component.dso.isDiscoverable = true;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should not show the private badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.private-badge'));
|
||||
expect(badge).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is private', () => {
|
||||
beforeEach(() => {
|
||||
component.dso.isDiscoverable = false;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show the private badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.private-badge'));
|
||||
expect(badge).not.toBeNull();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<div class="container" *ngVar="(itemRD$ | async) as itemRD">
|
||||
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
||||
<div *ngIf="itemRD?.payload as item">
|
||||
<ds-item-alerts [item]="item"></ds-item-alerts>
|
||||
<ds-item-versions-notice [item]="item"></ds-item-versions-notice>
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
||||
|
@@ -0,0 +1,8 @@
|
||||
<div>
|
||||
<div *ngIf="item && !item.isDiscoverable" class="private-warning">
|
||||
<ds-alert [type]="AlertTypeEnum.Warning" [content]="'item.alerts.private' | translate"></ds-alert>
|
||||
</div>
|
||||
<div *ngIf="item && item.isWithdrawn" class="withdrawn-warning">
|
||||
<ds-alert [type]="AlertTypeEnum.Warning" [content]="'item.alerts.withdrawn' | translate"></ds-alert>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,87 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ItemAlertsComponent } from './item-alerts.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('ItemAlertsComponent', () => {
|
||||
let component: ItemAlertsComponent;
|
||||
let fixture: ComponentFixture<ItemAlertsComponent>;
|
||||
let item: Item;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ItemAlertsComponent],
|
||||
imports: [TranslateModule.forRoot()],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ItemAlertsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('when the item is discoverable', () => {
|
||||
beforeEach(() => {
|
||||
item = Object.assign(new Item(), {
|
||||
isDiscoverable: true
|
||||
});
|
||||
component.item = item;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not display the private alert', () => {
|
||||
const privateWarning = fixture.debugElement.query(By.css('.private-warning'));
|
||||
expect(privateWarning).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not discoverable', () => {
|
||||
beforeEach(() => {
|
||||
item = Object.assign(new Item(), {
|
||||
isDiscoverable: false
|
||||
});
|
||||
component.item = item;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the private alert', () => {
|
||||
const privateWarning = fixture.debugElement.query(By.css('.private-warning'));
|
||||
expect(privateWarning).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is withdrawn', () => {
|
||||
beforeEach(() => {
|
||||
item = Object.assign(new Item(), {
|
||||
isWithdrawn: true
|
||||
});
|
||||
component.item = item;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the withdrawn alert', () => {
|
||||
const privateWarning = fixture.debugElement.query(By.css('.withdrawn-warning'));
|
||||
expect(privateWarning).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not withdrawn', () => {
|
||||
beforeEach(() => {
|
||||
item = Object.assign(new Item(), {
|
||||
isWithdrawn: false
|
||||
});
|
||||
component.item = item;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not display the withdrawn alert', () => {
|
||||
const privateWarning = fixture.debugElement.query(By.css('.withdrawn-warning'));
|
||||
expect(privateWarning).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
24
src/app/shared/item/item-alerts/item-alerts.component.ts
Normal file
24
src/app/shared/item/item-alerts/item-alerts.component.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { AlertType } from '../../alert/aletr-type';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-alerts',
|
||||
templateUrl: './item-alerts.component.html',
|
||||
styleUrls: ['./item-alerts.component.scss']
|
||||
})
|
||||
/**
|
||||
* Component displaying alerts for an item
|
||||
*/
|
||||
export class ItemAlertsComponent {
|
||||
/**
|
||||
* The Item to display alerts for
|
||||
*/
|
||||
@Input() item: Item;
|
||||
|
||||
/**
|
||||
* The AlertType enumeration
|
||||
* @type {AlertType}
|
||||
*/
|
||||
public AlertTypeEnum = AlertType;
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
<div #badges>
|
||||
<div *ngIf="objectAsAny && !objectAsAny.isDiscoverable" class="private-badge">
|
||||
<span class="badge badge-danger">{{ "admin.search.item.private" | translate }}</span>
|
||||
<div [ngClass]="{'d-none' : hideBadges}" #badges>
|
||||
<div *ngIf="privateBadge" class="private-badge">
|
||||
<span class="badge badge-danger">{{ "item.badge.private" | translate }}</span>
|
||||
</div>
|
||||
<div *ngIf="objectAsAny && objectAsAny.isWithdrawn" class="withdrawn-badge">
|
||||
<span class="badge badge-warning">{{ "admin.search.item.withdrawn" | translate }}</span>
|
||||
<div *ngIf="withdrawnBadge" class="withdrawn-badge">
|
||||
<span class="badge badge-warning">{{ "item.badge.withdrawn" | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template dsListableObject></ng-template>
|
||||
|
@@ -9,6 +9,9 @@ import * as listableObjectDecorators from './listable-object.decorator';
|
||||
import { PublicationListElementComponent } from '../../../object-list/item-list-element/item-types/publication/publication-list-element.component';
|
||||
import { ListableObjectDirective } from './listable-object.directive';
|
||||
import { spyOnExported } from '../../../testing/utils.test';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
|
||||
const testType = 'TestType';
|
||||
const testContext = Context.Search;
|
||||
@@ -26,7 +29,7 @@ describe('ListableObjectComponentLoaderComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [ListableObjectComponentLoaderComponent, PublicationListElementComponent, ListableObjectDirective],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
providers: [ComponentFactoryResolver]
|
||||
@@ -55,4 +58,63 @@ describe('ListableObjectComponentLoaderComponent', () => {
|
||||
expect(listableObjectDecorators.getListableObjectComponent).toHaveBeenCalledWith([testType], testViewMode, testContext);
|
||||
})
|
||||
});
|
||||
|
||||
describe('when the object is an item and viewMode is a list', () => {
|
||||
beforeEach(() => {
|
||||
comp.object = Object.assign(new Item());
|
||||
comp.viewMode = ViewMode.ListElement;
|
||||
});
|
||||
|
||||
describe('when the item is not withdrawn', () => {
|
||||
beforeEach(() => {
|
||||
(comp.object as any).isWithdrawn = false;
|
||||
comp.initBadges();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not show the withdrawn badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
|
||||
expect(badge).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is withdrawn', () => {
|
||||
beforeEach(() => {
|
||||
(comp.object as any).isWithdrawn = true;
|
||||
comp.initBadges();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show the withdrawn badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
|
||||
expect(badge).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not private', () => {
|
||||
beforeEach(() => {
|
||||
(comp.object as any).isDiscoverable = true;
|
||||
comp.initBadges();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should not show the private badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.private-badge'));
|
||||
expect(badge).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is private', () => {
|
||||
beforeEach(() => {
|
||||
(comp.object as any).isDiscoverable = false;
|
||||
comp.initBadges();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show the private badge', () => {
|
||||
const badge = fixture.debugElement.query(By.css('div.private-badge'));
|
||||
expect(badge).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -6,6 +6,7 @@ import { getListableObjectComponent } from './listable-object.decorator';
|
||||
import { GenericConstructor } from '../../../../core/shared/generic-constructor';
|
||||
import { ListableObjectDirective } from './listable-object.directive';
|
||||
import { CollectionElementLinkType } from '../../collection-element-link.type';
|
||||
import { hasValue } from '../../../empty.util';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-listable-object-component-loader',
|
||||
@@ -56,6 +57,11 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
||||
*/
|
||||
@Input() value: string;
|
||||
|
||||
/**
|
||||
* Whether or not informational badges (e.g. Private, Withdrawn) should be hidden
|
||||
*/
|
||||
@Input() hideBadges = false;
|
||||
|
||||
/**
|
||||
* Directive hook used to place the dynamic child component
|
||||
*/
|
||||
@@ -68,11 +74,14 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
||||
@ViewChild('badges', { static: true }) badges: ElementRef;
|
||||
|
||||
/**
|
||||
* The provided object as any
|
||||
* This is required to access the object's "isDiscoverable" and "isWithdrawn" properties from the template without
|
||||
* knowing the object's type
|
||||
* Whether or not the "Private" badge should be displayed for this listable object
|
||||
*/
|
||||
objectAsAny: any;
|
||||
privateBadge = false;
|
||||
|
||||
/**
|
||||
* Whether or not the "Withdrawn" badge should be displayed for this listable object
|
||||
*/
|
||||
withdrawnBadge = false;
|
||||
|
||||
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
|
||||
}
|
||||
@@ -81,7 +90,7 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
||||
* Setup the dynamic child component
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.objectAsAny = this.object as any;
|
||||
this.initBadges();
|
||||
|
||||
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent());
|
||||
|
||||
@@ -105,6 +114,19 @@ export class ListableObjectComponentLoaderComponent implements OnInit {
|
||||
(componentRef.instance as any).value = this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize which badges should be visible in the listable component
|
||||
*/
|
||||
initBadges() {
|
||||
let objectAsAny = this.object as any;
|
||||
if (hasValue(objectAsAny.indexableObject)) {
|
||||
objectAsAny = objectAsAny.indexableObject;
|
||||
}
|
||||
const objectExistsAndValidViewMode = hasValue(objectAsAny) && this.viewMode !== ViewMode.StandalonePage;
|
||||
this.privateBadge = objectExistsAndValidViewMode && hasValue(objectAsAny.isDiscoverable) && !objectAsAny.isDiscoverable;
|
||||
this.withdrawnBadge = objectExistsAndValidViewMode && hasValue(objectAsAny.isWithdrawn) && objectAsAny.isWithdrawn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the component depending on the item's relationship type, view mode and context
|
||||
* @returns {GenericConstructor<Component>}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||
<ds-truncatable [id]="dso.id">
|
||||
<div class="position-absolute ml-1">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
|
||||
class="card-img-top full-width">
|
||||
<div>
|
||||
|
@@ -218,6 +218,7 @@ import { AuthorizedCollectionSelectorComponent } from './dso-selector/dso-select
|
||||
import { DsoPageEditButtonComponent } from './dso-page/dso-page-edit-button/dso-page-edit-button.component';
|
||||
import { HoverClassDirective } from './hover-class.directive';
|
||||
import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component';
|
||||
import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component';
|
||||
|
||||
const MODULES = [
|
||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||
@@ -504,7 +505,8 @@ const ENTRY_COMPONENTS = [
|
||||
const SHARED_ITEM_PAGE_COMPONENTS = [
|
||||
MetadataFieldWrapperComponent,
|
||||
MetadataValuesComponent,
|
||||
DsoPageEditButtonComponent
|
||||
DsoPageEditButtonComponent,
|
||||
ItemAlertsComponent,
|
||||
];
|
||||
|
||||
const PROVIDERS = [
|
||||
|
@@ -462,14 +462,10 @@
|
||||
|
||||
"admin.search.item.move": "Move",
|
||||
|
||||
"admin.search.item.private": "Private",
|
||||
|
||||
"admin.search.item.reinstate": "Reinstate",
|
||||
|
||||
"admin.search.item.withdraw": "Withdraw",
|
||||
|
||||
"admin.search.item.withdrawn": "Withdrawn",
|
||||
|
||||
"admin.search.title": "Administrative Search",
|
||||
|
||||
|
||||
@@ -1329,12 +1325,24 @@
|
||||
|
||||
|
||||
|
||||
"item.alerts.private": "This item is private",
|
||||
|
||||
"item.alerts.withdrawn": "This item has been withdrawn",
|
||||
|
||||
|
||||
|
||||
"item.edit.authorizations.heading": "With this editor you can view and alter the policies of an item, plus alter policies of individual item components: bundles and bitstreams. Briefly, an item is a container of bundles, and bundles are containers of bitstreams. Containers usually have ADD/REMOVE/READ/WRITE policies, while bitstreams only have READ/WRITE policies.",
|
||||
|
||||
"item.edit.authorizations.title": "Edit item's Policies",
|
||||
|
||||
|
||||
|
||||
"item.badge.private": "Private",
|
||||
|
||||
"item.badge.withdrawn": "Private",
|
||||
|
||||
|
||||
|
||||
"item.bitstreams.upload.bundle": "Bundle",
|
||||
|
||||
"item.bitstreams.upload.bundle.placeholder": "Select a bundle",
|
||||
|
Reference in New Issue
Block a user