mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge branch 'master' into 460-coll-pages
This commit is contained in:
@@ -81,7 +81,6 @@ const components = [
|
||||
SearchFilterService,
|
||||
SearchFixedFilterService,
|
||||
ConfigurationSearchPageGuard,
|
||||
SearchFilterService,
|
||||
SearchConfigurationService
|
||||
],
|
||||
entryComponents: [
|
||||
|
@@ -19,6 +19,7 @@ import { Observable } from 'rxjs/internal/Observable';
|
||||
import { FindAllOptions } from './request.models';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { PaginatedList } from './paginated-list';
|
||||
import { SearchParam } from '../cache/models/search-param.model';
|
||||
|
||||
@Injectable()
|
||||
export class CollectionDataService extends ComColDataService<Collection> {
|
||||
@@ -40,6 +41,36 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all collections the user is authorized to submit to
|
||||
*
|
||||
* @param options The [[FindAllOptions]] object
|
||||
* @return Observable<RemoteData<PaginatedList<Collection>>>
|
||||
* collection list
|
||||
*/
|
||||
getAuthorizedCollection(options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Collection>>> {
|
||||
const searchHref = 'findAuthorized';
|
||||
|
||||
return this.searchBy(searchHref, options).pipe(
|
||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all collections the user is authorized to submit to, by community
|
||||
*
|
||||
* @param communityId The community id
|
||||
* @param options The [[FindAllOptions]] object
|
||||
* @return Observable<RemoteData<PaginatedList<Collection>>>
|
||||
* collection list
|
||||
*/
|
||||
getAuthorizedCollectionByCommunity(communityId: string, options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Collection>>> {
|
||||
const searchHref = 'findAuthorizedByCommunity';
|
||||
options.searchParams = [new SearchParam('uuid', communityId)];
|
||||
|
||||
return this.searchBy(searchHref, options).pipe(
|
||||
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find whether there is a collection whom user has authorization to submit to
|
||||
*
|
||||
|
@@ -3,7 +3,7 @@ import { HttpHeaders } from '@angular/common/http';
|
||||
|
||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import { Observable, race as observableRace } from 'rxjs';
|
||||
import { filter, find, map, mergeMap, take } from 'rxjs/operators';
|
||||
import { filter, map, mergeMap, take } from 'rxjs/operators';
|
||||
import { cloneDeep, remove } from 'lodash';
|
||||
|
||||
import { AppState } from '../../app.reducer';
|
||||
@@ -262,8 +262,9 @@ export class RequestService {
|
||||
*/
|
||||
private clearRequestsOnTheirWayToTheStore(request: GetRequest) {
|
||||
this.getByHref(request.href).pipe(
|
||||
find((re: RequestEntry) => hasValue(re)))
|
||||
.subscribe((re: RequestEntry) => {
|
||||
filter((re: RequestEntry) => hasValue(re)),
|
||||
take(1)
|
||||
).subscribe((re: RequestEntry) => {
|
||||
if (!re.responsePending) {
|
||||
remove(this.requestsOnTheirWayToTheStore, (item) => item === request.href);
|
||||
}
|
||||
|
@@ -128,7 +128,10 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService
|
||||
// Iterate over all workspaceitem's sections
|
||||
Object.keys(item.sections)
|
||||
.forEach((sectionId) => {
|
||||
if (typeof item.sections[sectionId] === 'object' && isNotEmpty(item.sections[sectionId])) {
|
||||
if (typeof item.sections[sectionId] === 'object' && (isNotEmpty(item.sections[sectionId]) &&
|
||||
// When Upload section is disabled, add to submission only if there are files
|
||||
(!item.sections[sectionId].hasOwnProperty('files') || isNotEmpty((item.sections[sectionId] as any).files)))) {
|
||||
|
||||
const normalizedSectionData = Object.create({});
|
||||
// Iterate over all sections property
|
||||
Object.keys(item.sections[sectionId])
|
||||
|
@@ -8,3 +8,14 @@
|
||||
background-image: none !important;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.navbar ::ng-deep {
|
||||
a {
|
||||
color: $header-icon-color;
|
||||
|
||||
&:hover, &focus {
|
||||
color: darken($header-icon-color, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ import { isObject, uniqueId } from 'lodash';
|
||||
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||
import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
|
||||
import { ConfidenceType } from '../../../core/integration/models/confidence-type';
|
||||
import { PLACEHOLDER_PARENT_METADATA } from '../../form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model';
|
||||
|
||||
export interface ChipsItemIcon {
|
||||
metadata: string;
|
||||
@@ -62,7 +63,7 @@ export class ChipsItem {
|
||||
if (this._item.hasOwnProperty(icon.metadata)
|
||||
&& (((typeof this._item[icon.metadata] === 'string') && hasValue(this._item[icon.metadata]))
|
||||
|| (this._item[icon.metadata] as FormFieldMetadataValueObject).hasValue())
|
||||
&& !(this._item[icon.metadata] as FormFieldMetadataValueObject).hasPlaceholder()) {
|
||||
&& !this.hasPlaceholder(this._item[icon.metadata])) {
|
||||
if ((icon.visibleWhenAuthorityEmpty
|
||||
|| (this._item[icon.metadata] as FormFieldMetadataValueObject).confidence !== ConfidenceType.CF_UNSET)
|
||||
&& isNotEmpty(icon.style)) {
|
||||
@@ -109,4 +110,9 @@ export class ChipsItem {
|
||||
|
||||
this.display = value;
|
||||
}
|
||||
|
||||
private hasPlaceholder(value: any) {
|
||||
return (typeof value === 'string') ? (value === PLACEHOLDER_PARENT_METADATA) :
|
||||
(value as FormFieldMetadataValueObject).hasPlaceholder()
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,8 @@
|
||||
|
||||
<ng-container #componentViewContainer></ng-container>
|
||||
|
||||
<small *ngIf="hasHint" class="text-muted" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
|
||||
<small *ngIf="hasHint && (!showErrorMessages || errorMessages.length === 0)"
|
||||
class="text-muted" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
|
||||
|
||||
<div *ngIf="showErrorMessages" [ngClass]="[getClass('element', 'errors'), getClass('grid', 'errors')]">
|
||||
<small *ngFor="let message of errorMessages" class="invalid-feedback d-block">{{ message | translate:model.validators }}</small>
|
||||
|
@@ -1,4 +1,7 @@
|
||||
import { DynamicFormControlLayout, DynamicFormGroupModel, DynamicFormGroupModelConfig, serializable } from '@ng-dynamic-forms/core';
|
||||
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { isNotEmpty } from '../../../../empty.util';
|
||||
import { DsDynamicInputModel } from './ds-dynamic-input.model';
|
||||
import { FormFieldMetadataValueObject } from '../../models/form-field-metadata-value.model';
|
||||
@@ -16,12 +19,16 @@ export class DynamicConcatModel extends DynamicFormGroupModel {
|
||||
@serializable() separator: string;
|
||||
@serializable() hasLanguages = false;
|
||||
isCustomGroup = true;
|
||||
valueUpdates: Subject<string>;
|
||||
|
||||
constructor(config: DynamicConcatModelConfig, layout?: DynamicFormControlLayout) {
|
||||
|
||||
super(config, layout);
|
||||
|
||||
this.separator = config.separator + ' ';
|
||||
|
||||
this.valueUpdates = new Subject<string>();
|
||||
this.valueUpdates.subscribe((value: string) => this.value = value);
|
||||
}
|
||||
|
||||
get value() {
|
||||
|
@@ -28,6 +28,7 @@ export class DsDynamicInputModel extends DynamicInputModel {
|
||||
constructor(config: DsDynamicInputModelConfig, layout?: DynamicFormControlLayout) {
|
||||
super(config, layout);
|
||||
|
||||
this.hint = config.hint;
|
||||
this.readOnly = config.readOnly;
|
||||
this.value = config.value;
|
||||
this.language = config.language;
|
||||
@@ -57,11 +58,7 @@ export class DsDynamicInputModel extends DynamicInputModel {
|
||||
}
|
||||
|
||||
get hasLanguages(): boolean {
|
||||
if (this.languageCodes && this.languageCodes.length > 1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return this.languageCodes && this.languageCodes.length > 1;
|
||||
}
|
||||
|
||||
get language(): string {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { DynamicFormControlLayout, DynamicFormGroupModel, DynamicInputModelConfig, serializable } from '@ng-dynamic-forms/core';
|
||||
import { DsDynamicInputModel, DsDynamicInputModelConfig } from './ds-dynamic-input.model';
|
||||
import { DynamicFormControlLayout, DynamicFormGroupModel, serializable } from '@ng-dynamic-forms/core';
|
||||
import { DsDynamicInputModel } from './ds-dynamic-input.model';
|
||||
import { Subject } from 'rxjs';
|
||||
import { DynamicFormGroupModelConfig } from '@ng-dynamic-forms/core/src/model/form-group/dynamic-form-group.model';
|
||||
import { LanguageCode } from '../../models/form-field-language-value.model';
|
||||
@@ -12,6 +12,7 @@ export interface DsDynamicQualdropModelConfig extends DynamicFormGroupModelConfi
|
||||
languageCodes?: LanguageCode[];
|
||||
language?: string;
|
||||
readOnly: boolean;
|
||||
hint?: string;
|
||||
}
|
||||
|
||||
export class DynamicQualdropModel extends DynamicFormGroupModel {
|
||||
@@ -20,6 +21,7 @@ export class DynamicQualdropModel extends DynamicFormGroupModel {
|
||||
@serializable() languageUpdates: Subject<string>;
|
||||
@serializable() hasLanguages = false;
|
||||
@serializable() readOnly: boolean;
|
||||
@serializable() hint: string;
|
||||
isCustomGroup = true;
|
||||
|
||||
constructor(config: DsDynamicQualdropModelConfig, layout?: DynamicFormControlLayout) {
|
||||
@@ -33,6 +35,8 @@ export class DynamicQualdropModel extends DynamicFormGroupModel {
|
||||
this.languageUpdates.subscribe((lang: string) => {
|
||||
this.language = lang;
|
||||
});
|
||||
|
||||
this.hint = config.hint;
|
||||
}
|
||||
|
||||
get value() {
|
||||
|
@@ -20,11 +20,10 @@
|
||||
[disabled]="isInputDisabled()"
|
||||
[placeholder]="model.placeholder | translate"
|
||||
[readonly]="model.readOnly"
|
||||
(change)="$event.preventDefault()"
|
||||
(change)="onChange($event)"
|
||||
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
|
||||
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
|
||||
(click)="$event.stopPropagation(); $event.stopPropagation(); sdRef.close();"
|
||||
(input)="onInput($event)">
|
||||
(click)="$event.stopPropagation(); $event.stopPropagation(); sdRef.close();">
|
||||
</div>
|
||||
|
||||
<!--Lookup-name, second field-->
|
||||
@@ -40,11 +39,10 @@
|
||||
[disabled]="firstInputValue.length === 0 || isInputDisabled()"
|
||||
[placeholder]="model.secondPlaceholder | translate"
|
||||
[readonly]="model.readOnly"
|
||||
(change)="$event.preventDefault()"
|
||||
(change)="onChange($event)"
|
||||
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
|
||||
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
|
||||
(click)="$event.stopPropagation(); sdRef.close();"
|
||||
(input)="onInput($event)">
|
||||
(click)="$event.stopPropagation(); sdRef.close();">
|
||||
</div>
|
||||
<div class="col-auto text-center">
|
||||
<button ngbDropdownAnchor
|
||||
|
@@ -237,6 +237,12 @@ describe('Dynamic Lookup component', () => {
|
||||
|
||||
it('should init component properly', () => {
|
||||
expect(lookupComp.firstInputValue).toBe('');
|
||||
const de = lookupFixture.debugElement.queryAll(By.css('button'));
|
||||
const searchBtnEl = de[0].nativeElement;
|
||||
const editBtnEl = de[1].nativeElement;
|
||||
expect(searchBtnEl.disabled).toBe(true);
|
||||
expect(editBtnEl.disabled).toBe(true);
|
||||
expect(editBtnEl.textContent.trim()).toBe('form.edit');
|
||||
});
|
||||
|
||||
it('should return search results', fakeAsync(() => {
|
||||
@@ -283,7 +289,7 @@ describe('Dynamic Lookup component', () => {
|
||||
lookupComp.firstInputValue = 'test';
|
||||
lookupFixture.detectChanges();
|
||||
|
||||
lookupComp.onInput(new Event('input'));
|
||||
lookupComp.onChange(new Event('change'));
|
||||
expect(lookupComp.model.value).toEqual(new FormFieldMetadataValueObject('test'))
|
||||
|
||||
}));
|
||||
@@ -293,10 +299,11 @@ describe('Dynamic Lookup component', () => {
|
||||
lookupComp.firstInputValue = 'test';
|
||||
lookupFixture.detectChanges();
|
||||
|
||||
lookupComp.onInput(new Event('input'));
|
||||
lookupComp.onChange(new Event('change'));
|
||||
expect(lookupComp.model.value).not.toBeDefined();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('and init model value is not empty', () => {
|
||||
@@ -318,6 +325,19 @@ describe('Dynamic Lookup component', () => {
|
||||
it('should init component properly', () => {
|
||||
expect(lookupComp.firstInputValue).toBe('test');
|
||||
});
|
||||
|
||||
it('should have search button disabled on edit mode', () => {
|
||||
lookupComp.editMode = true;
|
||||
lookupFixture.detectChanges();
|
||||
|
||||
const de = lookupFixture.debugElement.queryAll(By.css('button'));
|
||||
const searchBtnEl = de[0].nativeElement;
|
||||
const saveBtnEl = de[1].nativeElement;
|
||||
expect(searchBtnEl.disabled).toBe(true);
|
||||
expect(saveBtnEl.disabled).toBe(false);
|
||||
expect(saveBtnEl.textContent.trim()).toBe('form.save');
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -340,7 +360,14 @@ describe('Dynamic Lookup component', () => {
|
||||
});
|
||||
it('should render two input element', () => {
|
||||
const de = lookupFixture.debugElement.queryAll(By.css('input.form-control'));
|
||||
const deBtn = lookupFixture.debugElement.queryAll(By.css('button'));
|
||||
const searchBtnEl = deBtn[0].nativeElement;
|
||||
const editBtnEl = deBtn[1].nativeElement;
|
||||
|
||||
expect(de.length).toBe(2);
|
||||
expect(searchBtnEl.disabled).toBe(true);
|
||||
expect(editBtnEl.disabled).toBe(true);
|
||||
expect(editBtnEl.textContent.trim()).toBe('form.edit');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -418,6 +445,19 @@ describe('Dynamic Lookup component', () => {
|
||||
expect(lookupComp.firstInputValue).toBe('Name');
|
||||
expect(lookupComp.secondInputValue).toBe('Lastname');
|
||||
});
|
||||
|
||||
it('should have search button disabled on edit mode', () => {
|
||||
lookupComp.editMode = true;
|
||||
lookupFixture.detectChanges();
|
||||
|
||||
const de = lookupFixture.debugElement.queryAll(By.css('button'));
|
||||
const searchBtnEl = de[0].nativeElement;
|
||||
const saveBtnEl = de[1].nativeElement;
|
||||
expect(searchBtnEl.disabled).toBe(true);
|
||||
expect(saveBtnEl.disabled).toBe(false);
|
||||
expect(saveBtnEl.textContent.trim()).toBe('form.save');
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -123,6 +123,15 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
||||
}
|
||||
}
|
||||
|
||||
protected updateModel(value) {
|
||||
this.group.markAsDirty();
|
||||
this.model.valueUpdates.next(value);
|
||||
this.setInputsValue(value);
|
||||
this.change.emit(value);
|
||||
this.optionsList = null;
|
||||
this.pageInfo = null;
|
||||
}
|
||||
|
||||
public formatItemForInput(item: any, field: number): string {
|
||||
if (isUndefined(item) || isNull(item)) {
|
||||
return '';
|
||||
@@ -159,7 +168,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
||||
}
|
||||
|
||||
public isSearchDisabled() {
|
||||
return isEmpty(this.firstInputValue);
|
||||
return isEmpty(this.firstInputValue) || this.editMode;
|
||||
}
|
||||
|
||||
public onBlurEvent(event: Event) {
|
||||
@@ -170,12 +179,13 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
||||
this.focus.emit(event);
|
||||
}
|
||||
|
||||
public onInput(event) {
|
||||
public onChange(event) {
|
||||
event.preventDefault();
|
||||
if (!this.model.authorityOptions.closed) {
|
||||
if (isNotEmpty(this.getCurrentValue())) {
|
||||
const currentValue = new FormFieldMetadataValueObject(this.getCurrentValue());
|
||||
if (!this.editMode) {
|
||||
this.onSelect(currentValue);
|
||||
this.updateModel(currentValue);
|
||||
}
|
||||
} else {
|
||||
this.remove();
|
||||
@@ -191,12 +201,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
||||
}
|
||||
|
||||
public onSelect(event) {
|
||||
this.group.markAsDirty();
|
||||
this.model.valueUpdates.next(event);
|
||||
this.setInputsValue(event);
|
||||
this.change.emit(event);
|
||||
this.optionsList = null;
|
||||
this.pageInfo = null;
|
||||
this.updateModel(event);
|
||||
}
|
||||
|
||||
public openChange(isOpened: boolean) {
|
||||
@@ -219,7 +224,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
||||
display: this.getCurrentValue(),
|
||||
value: this.getCurrentValue()
|
||||
});
|
||||
this.onSelect(newValue);
|
||||
this.updateModel(newValue);
|
||||
} else {
|
||||
this.remove();
|
||||
}
|
||||
|
@@ -129,9 +129,11 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent
|
||||
|| this.selectedChipItem.item[model.name].value === PLACEHOLDER_PARENT_METADATA)
|
||||
? null
|
||||
: this.selectedChipItem.item[model.name];
|
||||
if (isNotNull(value)) {
|
||||
model.valueUpdates.next(this.formBuilderService.isInputModel(model) ? value.value : value);
|
||||
}
|
||||
|
||||
const nextValue = (this.formBuilderService.isInputModel(model) && isNotNull(value) && (typeof value !== 'string')) ?
|
||||
value.value : value;
|
||||
model.valueUpdates.next(nextValue);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } fro
|
||||
import { FormGroup } from '@angular/forms';
|
||||
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { catchError, first, tap } from 'rxjs/operators';
|
||||
import { catchError, distinctUntilChanged, first, tap } from 'rxjs/operators';
|
||||
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
|
||||
import {
|
||||
DynamicFormControlComponent,
|
||||
@@ -71,7 +71,13 @@ export class DsDynamicScrollableDropdownComponent extends DynamicFormControlComp
|
||||
}
|
||||
this.pageInfo = object.pageInfo;
|
||||
this.cdr.detectChanges();
|
||||
})
|
||||
});
|
||||
|
||||
this.group.get(this.model.id).valueChanges.pipe(distinctUntilChanged())
|
||||
.subscribe((value) => {
|
||||
this.setCurrentValue(value);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
inputFormatter = (x: AuthorityValue): string => x.display || x.value;
|
||||
|
@@ -28,7 +28,8 @@
|
||||
aria-hidden="true"
|
||||
[authorityValue]="currentValue"
|
||||
(whenClickOnConfidenceNotAccepted)="whenClickOnConfidenceNotAccepted($event)"></i>
|
||||
<input class="form-control"
|
||||
<input #instance="ngbTypeahead"
|
||||
class="form-control"
|
||||
[attr.autoComplete]="model.autoComplete"
|
||||
[class.is-invalid]="showErrorMessages"
|
||||
[dynamicId]="bindId && model.id"
|
||||
|
@@ -156,7 +156,7 @@ describe('DsDynamicTypeaheadComponent test suite', () => {
|
||||
inputElement.value = 'test value';
|
||||
inputElement.dispatchEvent(new Event('input'));
|
||||
|
||||
expect((typeaheadComp.model as any).value).toEqual(new FormFieldMetadataValueObject('test value'))
|
||||
expect(typeaheadComp.inputValue).toEqual(new FormFieldMetadataValueObject('test value'))
|
||||
|
||||
});
|
||||
|
||||
@@ -173,19 +173,56 @@ describe('DsDynamicTypeaheadComponent test suite', () => {
|
||||
|
||||
});
|
||||
|
||||
it('should emit blur Event onBlur', () => {
|
||||
it('should emit blur Event onBlur when popup is closed', () => {
|
||||
spyOn(typeaheadComp.blur, 'emit');
|
||||
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false);
|
||||
typeaheadComp.onBlur(new Event('blur'));
|
||||
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit change Event onBlur when AuthorityOptions.closed is false', () => {
|
||||
it('should not emit blur Event onBlur when popup is opened', () => {
|
||||
spyOn(typeaheadComp.blur, 'emit');
|
||||
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(true);
|
||||
const input = typeaheadFixture.debugElement.query(By.css('input'));
|
||||
|
||||
input.nativeElement.blur();
|
||||
expect(typeaheadComp.blur.emit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit change Event onBlur when AuthorityOptions.closed is false and inputValue is changed', () => {
|
||||
typeaheadComp.inputValue = 'test value';
|
||||
typeaheadFixture.detectChanges();
|
||||
spyOn(typeaheadComp.blur, 'emit');
|
||||
spyOn(typeaheadComp.change, 'emit');
|
||||
typeaheadComp.onBlur(new Event('blur'));
|
||||
// expect(typeaheadComp.change.emit).toHaveBeenCalled();
|
||||
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false);
|
||||
typeaheadComp.onBlur(new Event('blur', ));
|
||||
expect(typeaheadComp.change.emit).toHaveBeenCalled();
|
||||
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not emit change Event onBlur when AuthorityOptions.closed is false and inputValue is not changed', () => {
|
||||
typeaheadComp.inputValue = 'test value';
|
||||
typeaheadComp.model = new DynamicTypeaheadModel(TYPEAHEAD_TEST_MODEL_CONFIG);
|
||||
(typeaheadComp.model as any).value = 'test value';
|
||||
typeaheadFixture.detectChanges();
|
||||
spyOn(typeaheadComp.blur, 'emit');
|
||||
spyOn(typeaheadComp.change, 'emit');
|
||||
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false);
|
||||
typeaheadComp.onBlur(new Event('blur', ));
|
||||
expect(typeaheadComp.change.emit).not.toHaveBeenCalled();
|
||||
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not emit change Event onBlur when AuthorityOptions.closed is false and inputValue is null', () => {
|
||||
typeaheadComp.inputValue = null;
|
||||
typeaheadComp.model = new DynamicTypeaheadModel(TYPEAHEAD_TEST_MODEL_CONFIG);
|
||||
(typeaheadComp.model as any).value = 'test value';
|
||||
typeaheadFixture.detectChanges();
|
||||
spyOn(typeaheadComp.blur, 'emit');
|
||||
spyOn(typeaheadComp.change, 'emit');
|
||||
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false);
|
||||
typeaheadComp.onBlur(new Event('blur', ));
|
||||
expect(typeaheadComp.change.emit).not.toHaveBeenCalled();
|
||||
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
|
||||
import {
|
||||
@@ -8,14 +8,13 @@ import {
|
||||
} from '@ng-dynamic-forms/core';
|
||||
import { catchError, debounceTime, distinctUntilChanged, filter, map, merge, switchMap, tap } from 'rxjs/operators';
|
||||
import { Observable, of as observableOf, Subject } from 'rxjs';
|
||||
import { NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NgbTypeahead, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
||||
import { DynamicTypeaheadModel } from './dynamic-typeahead.model';
|
||||
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
||||
import { isEmpty, isNotEmpty } from '../../../../../empty.util';
|
||||
import { isEmpty, isNotEmpty, isNotNull } from '../../../../../empty.util';
|
||||
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
|
||||
|
||||
import { ConfidenceType } from '../../../../../../core/integration/models/confidence-type';
|
||||
|
||||
@Component({
|
||||
@@ -32,6 +31,8 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp
|
||||
@Output() change: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() focus: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@ViewChild('instance') instance: NgbTypeahead;
|
||||
|
||||
searching = false;
|
||||
searchOptions: IntegrationSearchOptions;
|
||||
searchFailed = false;
|
||||
@@ -105,16 +106,26 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp
|
||||
onInput(event) {
|
||||
if (!this.model.authorityOptions.closed && isNotEmpty(event.target.value)) {
|
||||
this.inputValue = new FormFieldMetadataValueObject(event.target.value);
|
||||
this.model.valueUpdates.next(this.inputValue);
|
||||
}
|
||||
}
|
||||
|
||||
onBlur(event: Event) {
|
||||
if (!this.instance.isPopupOpen()) {
|
||||
if (!this.model.authorityOptions.closed && isNotEmpty(this.inputValue)) {
|
||||
if (isNotNull(this.inputValue) && this.model.value !== this.inputValue) {
|
||||
this.model.valueUpdates.next(this.inputValue);
|
||||
this.change.emit(this.inputValue);
|
||||
}
|
||||
this.inputValue = null;
|
||||
}
|
||||
this.blur.emit(event);
|
||||
} else {
|
||||
// prevent on blur propagation if typeahed suggestions are showed
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
// set focus on input again, this is to avoid to lose changes when no suggestion is selected
|
||||
(event.target as HTMLInputElement).focus();
|
||||
}
|
||||
}
|
||||
|
||||
onChange(event: Event) {
|
||||
@@ -141,4 +152,5 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp
|
||||
this.click$.next(this.formatter(this.currentValue));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -47,6 +47,7 @@ export class ConcatFieldParser extends FieldParser {
|
||||
|
||||
const input1ModelConfig: DynamicInputModelConfig = this.initModel(id + CONCAT_FIRST_INPUT_SUFFIX, label, false, false);
|
||||
const input2ModelConfig: DynamicInputModelConfig = this.initModel(id + CONCAT_SECOND_INPUT_SUFFIX, label, true, false);
|
||||
input2ModelConfig.hint = ' ';
|
||||
|
||||
if (this.configData.mandatory) {
|
||||
input1ModelConfig.required = true;
|
||||
|
@@ -190,6 +190,8 @@ export abstract class FieldParser {
|
||||
|
||||
controlModel.placeholder = this.configData.label;
|
||||
|
||||
controlModel.hint = this.configData.hints;
|
||||
|
||||
if (this.configData.mandatory && setErrors) {
|
||||
this.markAsRequired(controlModel);
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ export class OneboxFieldParser extends FieldParser {
|
||||
const clsGroup = {
|
||||
element: {
|
||||
control: 'form-row',
|
||||
hint: 'ds-form-qualdrop-hint'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,8 +55,10 @@ export class OneboxFieldParser extends FieldParser {
|
||||
inputSelectGroup.id = newId.replace(/\./g, '_') + QUALDROP_GROUP_SUFFIX;
|
||||
inputSelectGroup.group = [];
|
||||
inputSelectGroup.legend = this.configData.label;
|
||||
inputSelectGroup.hint = this.configData.hints;
|
||||
|
||||
const selectModelConfig: DynamicSelectModelConfig<any> = this.initModel(newId + QUALDROP_METADATA_SUFFIX, label);
|
||||
selectModelConfig.hint = null;
|
||||
this.setOptions(selectModelConfig);
|
||||
if (isNotEmpty(fieldValue)) {
|
||||
selectModelConfig.value = fieldValue.metadata;
|
||||
@@ -63,6 +66,7 @@ export class OneboxFieldParser extends FieldParser {
|
||||
inputSelectGroup.group.push(new DynamicSelectModel(selectModelConfig, clsSelect));
|
||||
|
||||
const inputModelConfig: DsDynamicInputModelConfig = this.initModel(newId + QUALDROP_VALUE_SUFFIX, label, true);
|
||||
inputModelConfig.hint = null;
|
||||
this.setValues(inputModelConfig, fieldValue);
|
||||
|
||||
inputSelectGroup.readOnly = selectModelConfig.disabled && inputModelConfig.readOnly;
|
||||
|
@@ -42,3 +42,8 @@
|
||||
.right-addon input {
|
||||
padding-right: $spacer * 2.25;
|
||||
}
|
||||
|
||||
.ds-form-qualdrop-hint {
|
||||
top: -$spacer;
|
||||
position: relative;
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<a [class.disabled]="!(object.workflowitem | async)?.hasSucceeded"
|
||||
class="btn btn-primary mt-1 mb-3"
|
||||
ngbTooltip="{{'submission.workflow.tasks.claimed.edit_help' | translate}}"
|
||||
[routerLink]="['/workflowitems/' + (object.workflowitem | async)?.payload.id + '/' + object.id + '/edit']"
|
||||
[routerLink]="['/workflowitems/' + (object.workflowitem | async)?.payload.id + '/edit']"
|
||||
role="button">
|
||||
<i class="fa fa-edit"></i> {{'submission.workflow.tasks.claimed.edit' | translate}}
|
||||
</a>
|
||||
|
@@ -15,11 +15,11 @@
|
||||
<ng-container *ngVar="(bitstreams$ | async) as bitstreams">
|
||||
<ds-metadata-field-wrapper [label]="('item.page.files' | translate)">
|
||||
<div *ngIf="bitstreams?.length > 0" class="file-section">
|
||||
<a *ngFor="let file of bitstreams; let last=last;" [href]="file?.content" target="_blank" [download]="file?.name">
|
||||
<button class="btn btn-link" *ngFor="let file of bitstreams; let last=last;" (click)="downloadBitstreamFile(file?.uuid)">
|
||||
<span>{{file?.name}}</span>
|
||||
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
||||
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<ng-container *ngIf="bitstreams?.length === 0">
|
||||
<span class="text-muted">{{('mydspace.results.no-files' | translate)}}</span>
|
||||
|
@@ -12,10 +12,20 @@ import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
|
||||
import { ItemDetailPreviewFieldComponent } from './item-detail-preview-field/item-detail-preview-field.component';
|
||||
import { FileSizePipe } from '../../../utils/file-size-pipe';
|
||||
import { VarDirective } from '../../../utils/var.directive';
|
||||
import { FileService } from '../../../../core/shared/file.service';
|
||||
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||
import { HALEndpointServiceStub } from '../../../testing/hal-endpoint-service-stub';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
|
||||
function getMockFileService(): FileService {
|
||||
return jasmine.createSpyObj('FileService', {
|
||||
downloadFile: jasmine.createSpy('downloadFile'),
|
||||
getFileNameFromResponseContentDisposition: jasmine.createSpy('getFileNameFromResponseContentDisposition')
|
||||
});
|
||||
}
|
||||
|
||||
let component: ItemDetailPreviewComponent;
|
||||
let fixture: ComponentFixture<ItemDetailPreviewComponent>;
|
||||
|
||||
@@ -62,6 +72,10 @@ describe('ItemDetailPreviewComponent', () => {
|
||||
}),
|
||||
],
|
||||
declarations: [ItemDetailPreviewComponent, ItemDetailPreviewFieldComponent, TruncatePipe, FileSizePipe, VarDirective],
|
||||
providers: [
|
||||
{ provide: FileService, useValue: getMockFileService() },
|
||||
{ provide: HALEndpointService, useValue: new HALEndpointServiceStub('workspaceitems') }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(ItemDetailPreviewComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
|
@@ -1,12 +1,15 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||
import { fadeInOut } from '../../../animations/fade';
|
||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||
import { MyDSpaceResult } from '../../../../+my-dspace-page/my-dspace-result.model';
|
||||
import { FileService } from '../../../../core/shared/file.service';
|
||||
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||
|
||||
/**
|
||||
* This component show metadata for the given item object in the detail view.
|
||||
@@ -54,6 +57,16 @@ export class ItemDetailPreviewComponent {
|
||||
*/
|
||||
public thumbnail$: Observable<Bitstream>;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {FileService} fileService
|
||||
* @param {HALEndpointService} halService
|
||||
*/
|
||||
constructor(private fileService: FileService,
|
||||
private halService: HALEndpointService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all instance variables
|
||||
*/
|
||||
@@ -62,4 +75,15 @@ export class ItemDetailPreviewComponent {
|
||||
this.bitstreams$ = this.item.getFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform bitstream download
|
||||
*/
|
||||
public downloadBitstreamFile(uuid: string) {
|
||||
this.halService.getEndpoint('bitstreams').pipe(
|
||||
first())
|
||||
.subscribe((url) => {
|
||||
const fileUrl = `${url}/${uuid}/content`;
|
||||
this.fileService.downloadFile(fileUrl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -10,10 +10,10 @@
|
||||
class="btn btn-outline-primary"
|
||||
(blur)="onClose()"
|
||||
(click)="onClose()"
|
||||
[disabled]="(disabled$ | async)"
|
||||
[disabled]="(disabled$ | async) || (processingChange$ | async)"
|
||||
ngbDropdownToggle>
|
||||
<span *ngIf="(disabled$ | async)"><i class='fas fa-circle-notch fa-spin'></i></span>
|
||||
<span *ngIf="!(disabled$ | async)">{{ selectedCollectionName$ | async }}</span>
|
||||
<span *ngIf="(processingChange$ | async)"><i class='fas fa-circle-notch fa-spin'></i></span>
|
||||
<span *ngIf="!(processingChange$ | async)">{{ selectedCollectionName$ | async }}</span>
|
||||
</button>
|
||||
|
||||
<div ngbDropdownMenu
|
||||
|
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
CUSTOM_ELEMENTS_SCHEMA,
|
||||
DebugElement,
|
||||
SimpleChange
|
||||
} from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, DebugElement, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
@@ -14,12 +8,10 @@ import { filter } from 'rxjs/operators';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
|
||||
import { SubmissionServiceStub } from '../../../shared/testing/submission-service-stub';
|
||||
import {
|
||||
mockSubmissionId,
|
||||
mockSubmissionRestResponse
|
||||
} from '../../../shared/mocks/mock-submission';
|
||||
import { mockSubmissionId, mockSubmissionRestResponse } from '../../../shared/mocks/mock-submission';
|
||||
import { SubmissionService } from '../../submission.service';
|
||||
import { SubmissionFormCollectionComponent } from './submission-form-collection.component';
|
||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||
@@ -27,16 +19,35 @@ import { SubmissionJsonPatchOperationsService } from '../../../core/submission/s
|
||||
import { SubmissionJsonPatchOperationsServiceStub } from '../../../shared/testing/submission-json-patch-operations-service-stub';
|
||||
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
|
||||
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||
import { Community } from '../../../core/shared/community.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import {
|
||||
createTestComponent
|
||||
} from '../../../shared/testing/utils';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { createTestComponent } from '../../../shared/testing/utils';
|
||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||
|
||||
const subcommunities = [Object.assign(new Community(), {
|
||||
name: 'SubCommunity 1',
|
||||
id: '123456789-1',
|
||||
metadata: [
|
||||
{
|
||||
key: 'dc.title',
|
||||
language: 'en_US',
|
||||
value: 'SubCommunity 1'
|
||||
}]
|
||||
}),
|
||||
Object.assign(new Community(), {
|
||||
name: 'SubCommunity 1',
|
||||
id: '123456789s-1',
|
||||
metadata: [
|
||||
{
|
||||
key: 'dc.title',
|
||||
language: 'en_US',
|
||||
value: 'SubCommunity 1'
|
||||
}]
|
||||
})
|
||||
];
|
||||
|
||||
const mockCommunity1Collection1 = Object.assign(new Collection(), {
|
||||
name: 'Community 1-Collection 1',
|
||||
@@ -82,20 +93,54 @@ const mockCommunity2Collection2 = Object.assign(new Collection(), {
|
||||
}]
|
||||
});
|
||||
|
||||
const collectionResults = [mockCommunity1Collection1, mockCommunity1Collection2, mockCommunity2Collection1, mockCommunity2Collection2].map((collection: Collection) => Object.assign(new SearchResult<Collection>(), { indexableObject: collection }));
|
||||
const searchService = {
|
||||
search: () => {
|
||||
return observableOf(new RemoteData(true, true, true,
|
||||
undefined, new PaginatedList(new PageInfo(), collectionResults)))
|
||||
}
|
||||
};
|
||||
const mockCommunity = Object.assign(new Community(), {
|
||||
name: 'Community 1',
|
||||
id: '123456789-1',
|
||||
metadata: [
|
||||
{
|
||||
key: 'dc.title',
|
||||
language: 'en_US',
|
||||
value: 'Community 1'
|
||||
}],
|
||||
collections: observableOf(new RemoteData(true, true, true,
|
||||
undefined, new PaginatedList(new PageInfo(), [mockCommunity1Collection1, mockCommunity1Collection2]))),
|
||||
subcommunities: observableOf(new RemoteData(true, true, true,
|
||||
undefined, new PaginatedList(new PageInfo(), subcommunities))),
|
||||
});
|
||||
|
||||
const mockCommunity2 = Object.assign(new Community(), {
|
||||
name: 'Community 2',
|
||||
id: '123456789-2',
|
||||
metadata: [
|
||||
{
|
||||
key: 'dc.title',
|
||||
language: 'en_US',
|
||||
value: 'Community 2'
|
||||
}],
|
||||
collections: observableOf(new RemoteData(true, true, true,
|
||||
undefined, new PaginatedList(new PageInfo(), [mockCommunity2Collection1, mockCommunity2Collection2]))),
|
||||
subcommunities: observableOf(new RemoteData(true, true, true,
|
||||
undefined, new PaginatedList(new PageInfo(), []))),
|
||||
});
|
||||
|
||||
const mockCommunity1Collection1Rd = observableOf(new RemoteData(true, true, true,
|
||||
undefined, mockCommunity1Collection1));
|
||||
|
||||
const mockCommunityList = observableOf(new RemoteData(true, true, true,
|
||||
undefined, new PaginatedList(new PageInfo(), [mockCommunity, mockCommunity2])));
|
||||
|
||||
const mockCommunityCollectionList = observableOf(new RemoteData(true, true, true,
|
||||
undefined, new PaginatedList(new PageInfo(), [mockCommunity1Collection1, mockCommunity1Collection2])));
|
||||
|
||||
const mockCommunity2CollectionList = observableOf(new RemoteData(true, true, true,
|
||||
undefined, new PaginatedList(new PageInfo(), [mockCommunity2Collection1, mockCommunity2Collection2])));
|
||||
|
||||
const mockCollectionList = [
|
||||
{
|
||||
communities: [
|
||||
{
|
||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
||||
name: 'Submission test'
|
||||
id: '123456789-1',
|
||||
name: 'Community 1'
|
||||
}
|
||||
],
|
||||
collection: {
|
||||
@@ -106,8 +151,8 @@ const mockCollectionList = [
|
||||
{
|
||||
communities: [
|
||||
{
|
||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
||||
name: 'Submission test'
|
||||
id: '123456789-1',
|
||||
name: 'Community 1'
|
||||
}
|
||||
],
|
||||
collection: {
|
||||
@@ -118,8 +163,8 @@ const mockCollectionList = [
|
||||
{
|
||||
communities: [
|
||||
{
|
||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
||||
name: 'Submission test'
|
||||
id: '123456789-2',
|
||||
name: 'Community 2'
|
||||
}
|
||||
],
|
||||
collection: {
|
||||
@@ -130,8 +175,8 @@ const mockCollectionList = [
|
||||
{
|
||||
communities: [
|
||||
{
|
||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
||||
name: 'Submission test'
|
||||
id: '123456789-2',
|
||||
name: 'Community 2'
|
||||
}
|
||||
],
|
||||
collection: {
|
||||
@@ -158,6 +203,12 @@ describe('SubmissionFormCollectionComponent Component', () => {
|
||||
const communityDataService: any = jasmine.createSpyObj('communityDataService', {
|
||||
findAll: jasmine.createSpy('findAll')
|
||||
});
|
||||
|
||||
const collectionDataService: any = jasmine.createSpyObj('collectionDataService', {
|
||||
findById: jasmine.createSpy('findById'),
|
||||
getAuthorizedCollectionByCommunity: jasmine.createSpy('getAuthorizedCollectionByCommunity')
|
||||
});
|
||||
|
||||
const store: any = jasmine.createSpyObj('store', {
|
||||
dispatch: jasmine.createSpy('dispatch'),
|
||||
select: jasmine.createSpy('select')
|
||||
@@ -179,15 +230,12 @@ describe('SubmissionFormCollectionComponent Component', () => {
|
||||
TestComponent
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: SubmissionJsonPatchOperationsService,
|
||||
useClass: SubmissionJsonPatchOperationsServiceStub
|
||||
},
|
||||
{ provide: CollectionDataService, useValue: collectionDataService },
|
||||
{ provide: SubmissionJsonPatchOperationsService, useClass: SubmissionJsonPatchOperationsServiceStub },
|
||||
{ provide: SubmissionService, useClass: SubmissionServiceStub },
|
||||
{ provide: CommunityDataService, useValue: communityDataService },
|
||||
{ provide: JsonPatchOperationsBuilder, useValue: jsonPatchOpBuilder },
|
||||
{ provide: Store, useValue: store },
|
||||
{ provide: SearchService, useValue: searchService },
|
||||
ChangeDetectorRef,
|
||||
SubmissionFormCollectionComponent
|
||||
],
|
||||
@@ -252,17 +300,21 @@ describe('SubmissionFormCollectionComponent Component', () => {
|
||||
});
|
||||
|
||||
it('should init collection list properly', () => {
|
||||
communityDataService.findAll.and.returnValue(mockCommunityList);
|
||||
collectionDataService.findById.and.returnValue(mockCommunity1Collection1Rd);
|
||||
collectionDataService.getAuthorizedCollectionByCommunity.and.returnValues(mockCommunityCollectionList, mockCommunity2CollectionList);
|
||||
|
||||
comp.ngOnChanges({
|
||||
currentCollectionId: new SimpleChange(null, collectionId, true)
|
||||
});
|
||||
|
||||
expect(comp.searchListCollection$).toBeObservable(cold('(b)', {
|
||||
expect(comp.searchListCollection$).toBeObservable(cold('(ab)', {
|
||||
a: [],
|
||||
b: mockCollectionList
|
||||
}));
|
||||
|
||||
expect(comp.selectedCollectionName$).toBeObservable(cold('(ab|)', {
|
||||
a: '',
|
||||
b: 'Community 1-Collection 1'
|
||||
expect(comp.selectedCollectionName$).toBeObservable(cold('(a|)', {
|
||||
a: 'Community 1-Collection 1'
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -394,8 +446,6 @@ class TestComponent {
|
||||
definitionId = 'traditional';
|
||||
submissionId = mockSubmissionId;
|
||||
|
||||
onCollectionChange = () => {
|
||||
return;
|
||||
}
|
||||
onCollectionChange = () => { return; }
|
||||
|
||||
}
|
||||
|
@@ -17,13 +17,16 @@ import {
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
find,
|
||||
flatMap,
|
||||
map,
|
||||
mergeMap,
|
||||
reduce,
|
||||
startWith
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||
import { Community } from '../../../core/shared/community.model';
|
||||
import { hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||
@@ -32,12 +35,8 @@ import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { SubmissionService } from '../../submission.service';
|
||||
import { SubmissionObject } from '../../../core/submission/models/submission-object.model';
|
||||
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
|
||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||
import { FindAllOptions } from '../../../core/data/request.models';
|
||||
|
||||
/**
|
||||
* An interface to represent a collection entry
|
||||
@@ -95,6 +94,12 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
||||
*/
|
||||
public disabled$ = new BehaviorSubject<boolean>(true);
|
||||
|
||||
/**
|
||||
* A boolean representing if a collection change operation is processing
|
||||
* @type {BehaviorSubject<boolean>}
|
||||
*/
|
||||
public processingChange$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* The search form control
|
||||
* @type {FormControl}
|
||||
@@ -148,17 +153,17 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
||||
*
|
||||
* @param {ChangeDetectorRef} cdr
|
||||
* @param {CommunityDataService} communityDataService
|
||||
* @param {CollectionDataService} collectionDataService
|
||||
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
||||
* @param {SubmissionJsonPatchOperationsService} operationsService
|
||||
* @param {SubmissionService} submissionService
|
||||
* @param {SearchService} searchService
|
||||
*/
|
||||
constructor(protected cdr: ChangeDetectorRef,
|
||||
private communityDataService: CommunityDataService,
|
||||
private collectionDataService: CollectionDataService,
|
||||
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||
private operationsService: SubmissionJsonPatchOperationsService,
|
||||
private submissionService: SubmissionService,
|
||||
private searchService: SearchService) {
|
||||
private submissionService: SubmissionService) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,40 +200,37 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
||||
&& hasValue(changes.currentCollectionId.currentValue)) {
|
||||
this.selectedCollectionId = this.currentCollectionId;
|
||||
|
||||
// // @TODO replace with search/top browse endpoint
|
||||
// // @TODO implement community/subcommunity hierarchy
|
||||
// const communities$ = this.communityDataService.findAll().pipe(
|
||||
// find((communities: RemoteData<PaginatedList<Community>>) => isNotEmpty(communities.payload)),
|
||||
// mergeMap((communities: RemoteData<PaginatedList<Community>>) => communities.payload.page));
|
||||
|
||||
const listCollection$: Observable<CollectionListEntry[]> = this.searchService.search(
|
||||
new PaginatedSearchOptions({
|
||||
dsoType: DSpaceObjectType.COLLECTION,
|
||||
pagination: new PaginationComponentOptions(),
|
||||
scope: 'c0e4de93-f506-4990-a840-d406f6f2ada7'
|
||||
})
|
||||
).pipe(
|
||||
getSucceededRemoteData(),
|
||||
map((collections: RemoteData<PaginatedList<SearchResult<Collection>>>) => collections.payload.page),
|
||||
filter((collectionData: Array<SearchResult<Collection>>) => isNotEmpty(collectionData)),
|
||||
map((collectionData: Array<SearchResult<Collection>>) => {
|
||||
return collectionData.map((collection: SearchResult<Collection>) => {
|
||||
return {
|
||||
communities: [{
|
||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
||||
name: 'Submission test'
|
||||
}],
|
||||
collection: { id: collection.indexableObject.id, name: collection.indexableObject.name }
|
||||
}
|
||||
})
|
||||
})
|
||||
this.selectedCollectionName$ = this.collectionDataService.findById(this.currentCollectionId).pipe(
|
||||
find((collectionRD: RemoteData<Collection>) => isNotEmpty(collectionRD.payload)),
|
||||
map((collectionRD: RemoteData<Collection>) => collectionRD.payload.name)
|
||||
);
|
||||
|
||||
this.selectedCollectionName$ = listCollection$.pipe(
|
||||
map((collectionData: CollectionListEntry[]) => collectionData.find((entry: CollectionListEntry) => entry.collection.id === this.selectedCollectionId)),
|
||||
filter((entry: CollectionListEntry) => hasValue(entry.collection)),
|
||||
map((entry: CollectionListEntry) => entry.collection.name),
|
||||
startWith('')
|
||||
const findOptions: FindAllOptions = {
|
||||
elementsPerPage: 1000
|
||||
};
|
||||
|
||||
// Retrieve collection list only when is the first change
|
||||
if (changes.currentCollectionId.isFirstChange()) {
|
||||
// @TODO replace with search/top browse endpoint
|
||||
// @TODO implement community/subcommunity hierarchy
|
||||
const communities$ = this.communityDataService.findAll(findOptions).pipe(
|
||||
find((communities: RemoteData<PaginatedList<Community>>) => isNotEmpty(communities.payload)),
|
||||
mergeMap((communities: RemoteData<PaginatedList<Community>>) => communities.payload.page));
|
||||
|
||||
const listCollection$ = communities$.pipe(
|
||||
flatMap((communityData: Community) => {
|
||||
return this.collectionDataService.getAuthorizedCollectionByCommunity(communityData.uuid, findOptions).pipe(
|
||||
find((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending && collections.hasSucceeded),
|
||||
mergeMap((collections: RemoteData<PaginatedList<Collection>>) => collections.payload.page),
|
||||
filter((collectionData: Collection) => isNotEmpty(collectionData)),
|
||||
map((collectionData: Collection) => ({
|
||||
communities: [{ id: communityData.id, name: communityData.name }],
|
||||
collection: { id: collectionData.id, name: collectionData.name }
|
||||
}))
|
||||
);
|
||||
}),
|
||||
reduce((acc: any, value: any) => [...acc, ...value], []),
|
||||
startWith([])
|
||||
);
|
||||
|
||||
const searchTerm$ = this.searchField.valueChanges.pipe(
|
||||
@@ -248,6 +250,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all instance variables
|
||||
@@ -271,7 +274,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
||||
*/
|
||||
onSelect(event) {
|
||||
this.searchField.reset();
|
||||
this.disabled$.next(true);
|
||||
this.processingChange$.next(true);
|
||||
this.operationsBuilder.replace(this.pathCombiner.getPath(), event.collection.id, true);
|
||||
this.subs.push(this.operationsService.jsonPatchByResourceID(
|
||||
this.submissionService.getSubmissionObjectLinkName(),
|
||||
@@ -283,7 +286,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
||||
this.selectedCollectionName$ = observableOf(event.collection.name);
|
||||
this.collectionChange.emit(submissionObject[0]);
|
||||
this.submissionService.changeSubmissionCollection(this.submissionId, event.collection.id);
|
||||
this.disabled$.next(false);
|
||||
this.processingChange$.next(false);
|
||||
this.cdr.detectChanges();
|
||||
})
|
||||
);
|
||||
|
@@ -361,7 +361,7 @@ const addError = (state: SubmissionObjectState, action: InertSectionErrorsAction
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an RemoveSectionErrorsAction
|
||||
* a RemoveSectionErrorsAction
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the section's errors updated.
|
||||
*/
|
||||
@@ -416,7 +416,7 @@ function initSubmission(state: SubmissionObjectState, action: InitSubmissionForm
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an ResetSubmissionFormAction
|
||||
* a ResetSubmissionFormAction
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the section removed.
|
||||
*/
|
||||
@@ -439,7 +439,7 @@ function resetSubmission(state: SubmissionObjectState, action: ResetSubmissionFo
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an CompleteInitSubmissionFormAction
|
||||
* a CompleteInitSubmissionFormAction
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the section removed.
|
||||
*/
|
||||
@@ -461,7 +461,7 @@ function completeInit(state: SubmissionObjectState, action: CompleteInitSubmissi
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an SaveSubmissionFormAction | SaveSubmissionSectionFormAction
|
||||
* a SaveSubmissionFormAction | SaveSubmissionSectionFormAction
|
||||
* | SaveForLaterSubmissionFormAction | SaveAndDepositSubmissionAction
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the flag set to true.
|
||||
@@ -491,7 +491,7 @@ function saveSubmission(state: SubmissionObjectState,
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an SaveSubmissionFormSuccessAction | SaveForLaterSubmissionFormSuccessAction
|
||||
* a SaveSubmissionFormSuccessAction | SaveForLaterSubmissionFormSuccessAction
|
||||
* | SaveSubmissionSectionFormSuccessAction | SaveSubmissionFormErrorAction
|
||||
* | SaveForLaterSubmissionFormErrorAction | SaveSubmissionSectionFormErrorAction
|
||||
* @return SubmissionObjectState
|
||||
@@ -521,7 +521,7 @@ function completeSave(state: SubmissionObjectState,
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an DepositSubmissionAction
|
||||
* a DepositSubmissionAction
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the deposit flag changed.
|
||||
*/
|
||||
@@ -544,7 +544,7 @@ function startDeposit(state: SubmissionObjectState, action: DepositSubmissionAct
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an DepositSubmissionSuccessAction or DepositSubmissionErrorAction
|
||||
* a DepositSubmissionSuccessAction or a DepositSubmissionErrorAction
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the deposit flag changed.
|
||||
*/
|
||||
@@ -586,7 +586,7 @@ function changeCollection(state: SubmissionObjectState, action: ChangeSubmission
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an SetActiveSectionAction
|
||||
* a SetActiveSectionAction
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the active section.
|
||||
*/
|
||||
@@ -676,7 +676,7 @@ function updateSectionData(state: SubmissionObjectState, action: UpdateSectionDa
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an DisableSectionAction
|
||||
* a DisableSectionAction
|
||||
* @param enabled
|
||||
* enabled or disabled section.
|
||||
* @return SubmissionObjectState
|
||||
@@ -705,7 +705,7 @@ function changeSectionState(state: SubmissionObjectState, action: EnableSectionA
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an SectionStatusChangeAction
|
||||
* a SectionStatusChangeAction
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the section new validity status.
|
||||
*/
|
||||
@@ -769,7 +769,7 @@ function newFile(state: SubmissionObjectState, action: NewUploadedFileAction): S
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* a EditFileDataAction action
|
||||
* an EditFileDataAction action
|
||||
* @return SubmissionObjectState
|
||||
* the new state, with the edited file.
|
||||
*/
|
||||
|
@@ -64,6 +64,12 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
|
||||
*/
|
||||
public isLoading = true;
|
||||
|
||||
/**
|
||||
* A map representing all field on their way to be removed
|
||||
* @type {Map}
|
||||
*/
|
||||
protected fieldsOnTheirWayToBeRemoved: Map<string, number[]> = new Map();
|
||||
|
||||
/**
|
||||
* The form config
|
||||
* @type {SubmissionFormsModel}
|
||||
@@ -295,6 +301,7 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
|
||||
}),
|
||||
distinctUntilChanged())
|
||||
.subscribe((sectionState: SubmissionSectionObject) => {
|
||||
this.fieldsOnTheirWayToBeRemoved = new Map();
|
||||
this.updateForm(sectionState.data as WorkspaceitemSectionFormObject, sectionState.errors);
|
||||
})
|
||||
)
|
||||
@@ -348,11 +355,24 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
|
||||
* the [[DynamicFormControlEvent]] emitted
|
||||
*/
|
||||
onRemove(event: DynamicFormControlEvent): void {
|
||||
const fieldId = this.formBuilderService.getId(event.model);
|
||||
const fieldIndex = this.formOperationsService.getArrayIndexFromEvent(event);
|
||||
|
||||
// Keep track that this field will be removed
|
||||
if (this.fieldsOnTheirWayToBeRemoved.has(fieldId)) {
|
||||
const indexes = this.fieldsOnTheirWayToBeRemoved.get(fieldId);
|
||||
indexes.push(fieldIndex);
|
||||
this.fieldsOnTheirWayToBeRemoved.set(fieldId, indexes);
|
||||
} else {
|
||||
this.fieldsOnTheirWayToBeRemoved.set(fieldId, [fieldIndex]);
|
||||
}
|
||||
|
||||
this.formOperationsService.dispatchOperationsFromEvent(
|
||||
this.pathCombiner,
|
||||
event,
|
||||
this.previousValue,
|
||||
this.hasStoredValue(this.formBuilderService.getId(event.model), this.formOperationsService.getArrayIndexFromEvent(event)));
|
||||
this.hasStoredValue(fieldId, fieldIndex));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,9 +385,23 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
|
||||
*/
|
||||
hasStoredValue(fieldId, index): boolean {
|
||||
if (isNotEmpty(this.sectionData.data)) {
|
||||
return this.sectionData.data.hasOwnProperty(fieldId) && isNotEmpty(this.sectionData.data[fieldId][index]);
|
||||
return this.sectionData.data.hasOwnProperty(fieldId) &&
|
||||
isNotEmpty(this.sectionData.data[fieldId][index]) &&
|
||||
!this.isFieldToRemove(fieldId, index);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the specified field is on the way to be removed
|
||||
*
|
||||
* @param fieldId
|
||||
* the section data retrieved from the serverù
|
||||
* @param index
|
||||
* the section data retrieved from the server
|
||||
*/
|
||||
isFieldToRemove(fieldId, index) {
|
||||
return this.fieldsOnTheirWayToBeRemoved.has(fieldId) && this.fieldsOnTheirWayToBeRemoved.get(fieldId).includes(index);
|
||||
}
|
||||
}
|
||||
|
@@ -155,14 +155,14 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
|
||||
filter((submissionObject: SubmissionObjectEntry) => isUndefined(this.collectionId) || this.collectionId !== submissionObject.collection),
|
||||
tap((submissionObject: SubmissionObjectEntry) => this.collectionId = submissionObject.collection),
|
||||
flatMap((submissionObject: SubmissionObjectEntry) => this.collectionDataService.findById(submissionObject.collection)),
|
||||
find((rd: RemoteData<Collection>) => isNotUndefined((rd.payload))),
|
||||
filter((rd: RemoteData<Collection>) => isNotUndefined((rd.payload))),
|
||||
tap((collectionRemoteData: RemoteData<Collection>) => this.collectionName = collectionRemoteData.payload.name),
|
||||
flatMap((collectionRemoteData: RemoteData<Collection>) => {
|
||||
return this.resourcePolicyService.findByHref(
|
||||
(collectionRemoteData.payload as any)._links.defaultAccessConditions
|
||||
);
|
||||
}),
|
||||
find((defaultAccessConditionsRemoteData: RemoteData<ResourcePolicy>) =>
|
||||
filter((defaultAccessConditionsRemoteData: RemoteData<ResourcePolicy>) =>
|
||||
defaultAccessConditionsRemoteData.hasSucceeded),
|
||||
tap((defaultAccessConditionsRemoteData: RemoteData<ResourcePolicy>) => {
|
||||
if (isNotEmpty(defaultAccessConditionsRemoteData.payload)) {
|
||||
@@ -171,7 +171,6 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
|
||||
}
|
||||
}),
|
||||
flatMap(() => config$),
|
||||
take(1),
|
||||
flatMap((config: SubmissionUploadsModel) => {
|
||||
this.availableAccessConditionOptions = isNotEmpty(config.accessConditionOptions) ? config.accessConditionOptions : [];
|
||||
|
||||
|
@@ -197,7 +197,11 @@ export class SubmissionService {
|
||||
* The submission id
|
||||
*/
|
||||
dispatchSave(submissionId) {
|
||||
this.getSubmissionSaveProcessingStatus(submissionId).pipe(
|
||||
find((isPending: boolean) => !isPending)
|
||||
).subscribe(() => {
|
||||
this.store.dispatch(new SaveSubmissionFormAction(submissionId));
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -67,7 +67,7 @@ export function startServer(bootstrap: Type<{}> | NgModuleFactory<{}>) {
|
||||
|
||||
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
|
||||
if (!res._headerSent) {
|
||||
console.warn('Error in SSR, serving for direct CSR');
|
||||
console.warn('Error in SSR, serving for direct CSR. Error details : ', error);
|
||||
res.sendFile('index.csr.html', { root: './src' });
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +1,16 @@
|
||||
@import '_themed_bootstrap_variables.scss';
|
||||
|
||||
/** Help Variables **/
|
||||
$fa-fixed-width: 1.25rem;
|
||||
$icon-padding: 1rem;
|
||||
$collapsed-sidebar-width: calculatePx($fa-fixed-width + (2 * $icon-padding));
|
||||
$sidebar-items-width: 250px;
|
||||
$total-sidebar-width: $collapsed-sidebar-width + $sidebar-items-width;
|
||||
$fa-fixed-width: 1.25rem !default;
|
||||
$icon-padding: 1rem !default;
|
||||
$collapsed-sidebar-width: calculatePx($fa-fixed-width + (2 * $icon-padding)) !default;
|
||||
$sidebar-items-width: 250px !default;
|
||||
$total-sidebar-width: $collapsed-sidebar-width + $sidebar-items-width !default;
|
||||
|
||||
/* Fonts */
|
||||
$fa-font-path: "../assets/fonts";
|
||||
$fa-font-path: "../assets/fonts" !default;
|
||||
/* Images */
|
||||
$image-path: "../assets/images";
|
||||
$image-path: "../assets/images" !default;
|
||||
|
||||
/** Bootstrap Variables **/
|
||||
/* Colors */
|
||||
@@ -44,8 +44,8 @@ $link-color: map-get($theme-colors, info) !default;
|
||||
|
||||
$navbar-dark-color: rgba(white, .5) !default;
|
||||
$navbar-light-color: rgba(black, .5) !default;
|
||||
$navbar-dark-toggler-icon-bg: url("data%3Aimage%2Fsvg+xml%3Bcharset%3Dutf8%2C%3Csvg+viewBox%3D%270+0+30+30%27+xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath+stroke%3D%27#{$navbar-dark-color}%27+stroke-width%3D%272%27+stroke-linecap%3D%27round%27+stroke-miterlimit%3D%2710%27+d%3D%27M4+7h22M4+15h22M4+23h22%27%2F%3E%3C%2Fsvg%3E");
|
||||
$navbar-light-toggler-icon-bg: url("data%3Aimage%2Fsvg+xml%3Bcharset%3Dutf8%2C%3Csvg+viewBox%3D%270+0+30+30%27+xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath+stroke%3D%27#{$navbar-light-color}%27+stroke-width%3D%272%27+stroke-linecap%3D%27round%27+stroke-miterlimit%3D%2710%27+d%3D%27M4+7h22M4+15h22M4+23h22%27%2F%3E%3C%2Fsvg%3E");
|
||||
$navbar-dark-toggler-icon-bg: url("data%3Aimage%2Fsvg+xml%3Bcharset%3Dutf8%2C%3Csvg+viewBox%3D%270+0+30+30%27+xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath+stroke%3D%27#{$navbar-dark-color}%27+stroke-width%3D%272%27+stroke-linecap%3D%27round%27+stroke-miterlimit%3D%2710%27+d%3D%27M4+7h22M4+15h22M4+23h22%27%2F%3E%3C%2Fsvg%3E") !default;
|
||||
$navbar-light-toggler-icon-bg: url("data%3Aimage%2Fsvg+xml%3Bcharset%3Dutf8%2C%3Csvg+viewBox%3D%270+0+30+30%27+xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath+stroke%3D%27#{$navbar-light-color}%27+stroke-width%3D%272%27+stroke-linecap%3D%27round%27+stroke-miterlimit%3D%2710%27+d%3D%27M4+7h22M4+15h22M4+23h22%27%2F%3E%3C%2Fsvg%3E") !default;
|
||||
|
||||
$enable-shadows: true !default;
|
||||
|
||||
|
@@ -1,38 +1,39 @@
|
||||
@import '_themed_custom_variables.scss';
|
||||
|
||||
$content-spacing: $spacer * 1.5;
|
||||
$content-spacing: $spacer * 1.5 !default;
|
||||
|
||||
$button-height: $input-btn-padding-y * 2 + $input-btn-line-height + calculateRem($input-btn-border-width*2);
|
||||
$button-height: $input-btn-padding-y * 2 + $input-btn-line-height + calculateRem($input-btn-border-width*2) !default;
|
||||
|
||||
$card-height-percentage:98%;
|
||||
$card-thumbnail-height:240px;
|
||||
$dropdown-menu-max-height: 200px;
|
||||
$drop-zone-area-height: 44px;
|
||||
$drop-zone-area-z-index: 1025;
|
||||
$drop-zone-area-inner-z-index: 1021;
|
||||
$login-logo-height:72px;
|
||||
$login-logo-width:72px;
|
||||
$submission-header-z-index: 1001;
|
||||
$submission-footer-z-index: 1000;
|
||||
$card-height-percentage:98% !default;
|
||||
$card-thumbnail-height:240px !default;
|
||||
$dropdown-menu-max-height: 200px !default;
|
||||
$drop-zone-area-height: 44px !default;
|
||||
$drop-zone-area-z-index: 1025 !default;
|
||||
$drop-zone-area-inner-z-index: 1021 !default;
|
||||
$login-logo-height:72px !default;
|
||||
$login-logo-width:72px !default;
|
||||
$submission-header-z-index: 1001 !default;
|
||||
$submission-footer-z-index: 999 !default;
|
||||
|
||||
$main-z-index: 0;
|
||||
$nav-z-index: 10;
|
||||
$sidebar-z-index: 20;
|
||||
$main-z-index: 0 !default;
|
||||
$nav-z-index: 10 !default;
|
||||
$sidebar-z-index: 20 !default;
|
||||
|
||||
$header-logo-height: 80px;
|
||||
$header-logo-height-xs: 50px;
|
||||
$header-logo-height: 80px !default;
|
||||
$header-logo-height-xs: 50px !default;
|
||||
$header-icon-color: $link-color !default;
|
||||
|
||||
$admin-sidebar-bg: darken(#2B4E72, 17%);
|
||||
$admin-sidebar-active-bg: darken($admin-sidebar-bg, 3%);
|
||||
$admin-sidebar-header-bg: darken($admin-sidebar-bg, 7%);
|
||||
$admin-sidebar-bg: darken(#2B4E72, 17%) !default;
|
||||
$admin-sidebar-active-bg: darken($admin-sidebar-bg, 3%) !default;
|
||||
$admin-sidebar-header-bg: darken($admin-sidebar-bg, 7%) !default;
|
||||
|
||||
$dark-scrollbar-background: $admin-sidebar-active-bg;
|
||||
$dark-scrollbar-foreground: #47495d;
|
||||
$dark-scrollbar-background: $admin-sidebar-active-bg !default;
|
||||
$dark-scrollbar-foreground: #47495d !default;
|
||||
|
||||
$submission-sections-margin-bottom: .5rem !default;
|
||||
|
||||
$edit-item-button-min-width: 100px;
|
||||
$edit-item-metadata-field-width: 190px;
|
||||
$edit-item-language-field-width: 43px;
|
||||
$edit-item-button-min-width: 100px !default;
|
||||
$edit-item-metadata-field-width: 190px !default;
|
||||
$edit-item-language-field-width: 43px !default;
|
||||
|
||||
$thumbnail-max-width: 175px;
|
||||
$thumbnail-max-width: 175px !default;
|
||||
|
Reference in New Issue
Block a user