Commit d920184b authored by Jérémy Destin's avatar Jérémy Destin Committed by Guillaume Cornut
Browse files

feat: Add links in the navbar. Add the spinner during loading. Minor fixes. GNP-5430.

parent a7cec011
This diff is collapsed.
......@@ -27,13 +27,14 @@
"bootstrap": "4.1.3",
"core-js": "2.5.7",
"font-awesome": "4.7.0",
"ngx-spinner": "6.1.2",
"jquery": "^3.3.1",
"leaflet": "^1.3.4",
"leaflet.markercluster": "^1.4.1",
"popper": "^1.0.1",
"rxjs": "6.3.3",
"zone.js": "0.8.26",
"trait-ontology-widget": "git+https://github.com/gnpis/trait-ontology-widget.git#npm_import"
"trait-ontology-widget": "git+https://github.com/gnpis/trait-ontology-widget.git#npm_import",
"zone.js": "0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.12.2",
......
......@@ -13,13 +13,15 @@ import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NavbarComponent } from './navbar/navbar.component';
import { MapComponent } from './map/map.component';
import { NgbAlertModule, NgbPaginationModule, NgbTabsetModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { ReactiveFormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SuggestionFieldComponent } from './form/suggestion-field/suggestion-field.component';
import { DocumentComponent } from './result-page/document/document.component';
import { ErrorComponent } from './error/error.component';
import { ErrorInterceptorService } from './error-interceptor.service';
import { TraitOntologyWidgetComponent } from './form/trait-ontology-widget/trait-ontology-widget.component';
import { FacetsComponent } from './result-page/facets/facets.component';
import { NgxSpinnerModule } from 'ngx-spinner';
@NgModule({
declarations: [
......@@ -45,8 +47,11 @@ import { FacetsComponent } from './result-page/facets/facets.component';
NgbPaginationModule,
NgbTabsetModule,
NgbAlertModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule
HttpClientModule,
NgxSpinnerModule
],
providers: [
{ provide: HTTP_INTERCEPTORS, useExisting: ErrorInterceptorService, multi: true }
......
......@@ -5,7 +5,7 @@ import { ReactiveFormsModule } from '@angular/forms';
import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { GnpisService } from '../../gnpis.service';
import { BehaviorSubject, of } from 'rxjs';
import { emptyCriteria } from '../../model/data-discovery.model';
import { DataDiscoveryCriteriaUtils } from '../../model/data-discovery.model';
describe('SuggestionFieldComponent', () => {
......@@ -33,13 +33,13 @@ describe('SuggestionFieldComponent', () => {
});
it('should create', () => {
component.criteria$ = new BehaviorSubject(emptyCriteria());
component.criteria$ = new BehaviorSubject(DataDiscoveryCriteriaUtils.emptyCriteria());
fixture.detectChanges();
expect(component).toBeTruthy();
});
it('should fetch suggestion on text change', async(() => {
const criteria = emptyCriteria();
const criteria = DataDiscoveryCriteriaUtils.emptyCriteria();
component.criteria$ = new BehaviorSubject(criteria);
component.criteriaField = 'crops';
fixture.detectChanges();
......@@ -59,7 +59,7 @@ describe('SuggestionFieldComponent', () => {
it('should display the selected criteria as pills', () => {
component.criteriaField = 'crops';
const criteria = { ...emptyCriteria(), crops: ['Zea', 'Wheat'] };
const criteria = { ...DataDiscoveryCriteriaUtils.emptyCriteria(), crops: ['Zea', 'Wheat'] };
component.criteria$ = new BehaviorSubject(criteria);
fixture.detectChanges();
......@@ -75,7 +75,7 @@ describe('SuggestionFieldComponent', () => {
it('should fetch suggestion', async(() => {
component.criteriaField = 'crops';
const selectedCrops = ['Zea', 'Wheat'];
const criteria = { ...emptyCriteria(), crops: selectedCrops };
const criteria = { ...DataDiscoveryCriteriaUtils.emptyCriteria(), crops: selectedCrops };
component.criteria$ = new BehaviorSubject(criteria);
const allSuggestions = ['Zea', 'Wheat', 'Vitis', 'Grapevine'];
......
......@@ -3,7 +3,7 @@ import { TestBed } from '@angular/core/testing';
import { CropOntologyWidgetFactory, TraitOntologyWidgetComponent } from './trait-ontology-widget.component';
import { GnpisService } from '../../gnpis.service';
import { BehaviorSubject } from 'rxjs';
import { emptyCriteria } from '../../model/data-discovery.model';
import { DataDiscoveryCriteriaUtils } from '../../model/data-discovery.model';
describe('TraitOntologyWidgetComponent', () => {
const service = jasmine.createSpyObj(
......@@ -61,14 +61,14 @@ describe('TraitOntologyWidgetComponent', () => {
it('should create', () => {
component.criteria$ = new BehaviorSubject(emptyCriteria());
component.criteria$ = new BehaviorSubject(DataDiscoveryCriteriaUtils.emptyCriteria());
fixture.detectChanges();
expect(component).toBeTruthy();
});
it('should initialize selection from criteria', () => {
const criteria = emptyCriteria();
const criteria = DataDiscoveryCriteriaUtils.emptyCriteria();
criteria.topSelectedTraitOntologyIds = fakeWidget.jsTreePanel.jstree.get_top_selected();
const expectedBottomIds = fakeWidget.jsTreePanel.jstree.get_bottom_selected();
......
......@@ -3,7 +3,7 @@ import { TestBed } from '@angular/core/testing';
import { GnpisService } from './gnpis.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { BrapiMetaData, BrapiResults } from './model/brapi.model';
import { DataDiscoveryCriteria, DataDiscoverySource } from './model/data-discovery.model';
import { DataDiscoveryCriteria, DataDiscoveryResults, DataDiscoverySource } from './model/data-discovery.model';
describe('GnpisService', () => {
let service: GnpisService;
......@@ -11,7 +11,7 @@ describe('GnpisService', () => {
const source1: DataDiscoverySource = {
'@id': 'id1',
'@type': ['source'],
'@type': ['schema:DataCatalog'],
'schema:identifier': 'ID 1',
'schema:name': 'source 1',
'schema:url': 'http://source1.com',
......@@ -19,7 +19,7 @@ describe('GnpisService', () => {
};
const source2: DataDiscoverySource = {
'@id': 'id2',
'@type': ['source'],
'@type': ['schema:DataCatalog'],
'schema:identifier': 'ID 2',
'schema:name': 'source 2',
'schema:url': 'http://source2.com',
......@@ -80,7 +80,7 @@ describe('GnpisService', () => {
it('should search documents with criteria', () => {
const expectedResult: BrapiResults<any> = {
const expectedResult: DataDiscoveryResults = {
metadata: {} as BrapiMetaData,
result: {
data: [{
......@@ -90,7 +90,7 @@ describe('GnpisService', () => {
'schema:name': 'doc_name',
'schema:url': 'http://dco/url',
'schema:description': 'description',
'schema:includedInDataCatalog': 'id1'
'schema:includedInDataCatalog': source1
}, {
'@type': ['Phenotyping Study'],
'@id': 'urn',
......@@ -98,9 +98,10 @@ describe('GnpisService', () => {
'schema:name': 'doc_name',
'schema:url': 'http://dco/url',
'schema:description': 'description',
'schema:includedInDataCatalog': 'id2'
'schema:includedInDataCatalog': source2
}]
}
},
facets: []
};
const criteria = { crops: ['d'] } as DataDiscoveryCriteria;
......
......@@ -9,7 +9,30 @@
<div id="navbar" class="collapse navbar-collapse" [class.collapse]="navbarCollapsed">
<ul class="navbar-nav mr-auto">
<li class="nav-item" *ngFor="let link of navbar.links">
<a class="nav-link d-flex align-items-center" [href]="link.url" target="_blank">{{ link.label }}</a>
<div *ngIf="!link.subMenu">
<a class="nav-link d-flex align-items-center"
[href]="link.url" target="_blank">{{ link.label }}</a>
</div>
<!-- Dropdown -->
<div *ngIf="link.subMenu" class="dropdown-container">
<a class="nav-link d-flex align-items-center dropdown-toggle" data-toggle="dropdown"
[href]="link.url"
role="button"
aria-haspopup="true"
aria-expanded="false"
target="_blank">{{ link.label }}</a>
<div class="dropdown-menu">
<div *ngFor="let subItem of link.subMenu">
<a class="dropdown-item"
href="{{ subItem.url }}">{{ subItem.label }}</a>
</div>
</div>
</div>
</li>
</ul>
</div>
......
......@@ -19,3 +19,7 @@
background-color: $theme-navbar-hover-bg-color;
}
}
.dropdown-container {
position: relative;
}
......@@ -39,7 +39,7 @@
<span class="col-4" id="result-part">
Results :
</span>
<span *ngIf="pagination.totalResult" class="col-8 text-right small text-muted pt-1">
<span *ngIf="pagination.totalResult" class="col-8 text-right small text-muted mt-3">
From {{ pagination.startResult | number }} to {{ pagination.endResult | number }}
over {{ pagination.totalResult | number }} documents
<span *ngIf="pagination.totalResult > pagination.maxResults">
......@@ -86,6 +86,15 @@
</div>
</div>
<ngx-spinner
bdColor="rgba(51,51,51,0.6)"
size="large"
color="#fff"
type="ball-spin-clockwise"
></ngx-spinner>
<!-- else we display a simple message -->
<div *ngIf="pagination.totalResult == 0" id="no-results" class="text-center">
<div class="no-result-icon">
......
......@@ -6,13 +6,14 @@
}
.result {
border-top: 3px solid #c2c2c2;
border-top: 4.5px solid #c2c2c2;
border-bottom: 1px solid #c2c2c2;
}
#result-part {
font-size: 1.2rem;
font-weight: bold;
margin-top: 0.5em;
display: block;
}
......
......@@ -74,14 +74,15 @@ describe('ResultPageComponent', () => {
],
schemas: [NO_ERRORS_SCHEMA],
});
fixture = TestBed.createComponent(ResultPageComponent);
component = fixture.componentInstance;
}));
it('should generate criteria from URL', () => {
fixture.detectChanges();
fixture.detectChanges();
component.criteria$.subscribe(criteria => {
expect(criteria.crops).toEqual([params.crops]);
expect(criteria.germplasmLists).toEqual(params.germplasmLists);
......@@ -116,6 +117,5 @@ describe('ResultPageComponent', () => {
expect(component.documents).not.toBe(null);
});
})
;
......@@ -12,6 +12,7 @@ import { BehaviorSubject } from 'rxjs';
import { GnpisService } from '../gnpis.service';
import { filter } from 'rxjs/operators';
import { FormComponent } from '../form/form.component';
import { NgxSpinnerService } from 'ngx-spinner';
@Component({
......@@ -38,7 +39,8 @@ export class ResultPageComponent implements OnInit {
constructor(private route: ActivatedRoute,
private router: Router,
private gnpisService: GnpisService
private gnpisService: GnpisService,
private spinner: NgxSpinnerService
) {
}
......@@ -49,7 +51,8 @@ export class ResultPageComponent implements OnInit {
this.documents = result.data;
this.updatePagination(metadata.pagination);
this.facets = facets;
});
this.spinner.hide();
}, error => this.spinner.hide());
}
private updatePagination({ currentPage, pageSize, totalCount, totalPages }) {
......@@ -62,6 +65,7 @@ export class ResultPageComponent implements OnInit {
}
ngOnInit(): void {
this.spinner.show();
const queryParams = this.route.snapshot.queryParams;
// Parse criteria from URL query params
......
......@@ -6,7 +6,65 @@ export const environment = {
production: false,
navbar: {
title: 'GnpIS Plant Data Search',
links: [{ label: 'URGI', url: 'http://urgi.versailles.inra.fr' }]
links: [
{ label: 'INRA', url: 'http://www.inra.fr/' },
{
label: 'URGI',
url: '#',
subMenu: [
{ label: 'Home', url: 'https://urgi.versailles.inra.fr' },
{ label: 'News', url: 'https://urgi.versailles.inra.fr/About-us/News' },
{ label: 'About us', url: 'https://urgi.versailles.inra.fr/About-us' }
]
},
{
label: 'Taxon/Germplasm',
url: '#',
subMenu: [
{ label: 'Taxon', url: 'https://urgi.versailles.inra.fr/siregal/common/taxon/form.do' },
{ label: 'Accession Simple', url: 'https://urgi.versailles.inra.fr/gnpis-core' },
{ label: 'Accession passport', url: 'https://urgi.versailles.inra.fr/siregal/siregal/accessionForm.do' },
{ label: 'Collections CRB', url: 'https://urgi.versailles.inra.fr/siregal/siregal/grc.do' },
]
},
{ label: 'Phenotyping', url: 'https://urgi.versailles.inra.fr/ephesis/ephesis/viewer.do' },
{
label: 'Polymorphism',
url: '#',
subMenu: [
{ label: 'Genotyping', url: 'https://urgi.versailles.inra.fr/GnpSNP/snp/genotyping/form.do' },
{ label: 'SNP Discovery', url: 'https://urgi.versailles.inra.fr/GnpSNP/snp/welcome.do' },
]
},
{ label: 'Association', url: 'https://urgi.versailles.inra.fr/association/association/viewer.do#form' },
{
label: 'Map/Marker/QTL',
url: '#',
subMenu: [
{ label: 'Map', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/searchMap.do' },
{ label: 'Loci', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/loci/queryLociSelect.do' },
{ label: 'QTL', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/qtl/queryQtlSelect.do' },
{ label: 'MetaQTLs', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/metaqtl/form.do' },
{ label: 'Marker', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/marker/markerForm.do' },
{ label: 'Pool', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/pool/poolForm.do' },
{ label: 'Traits', url: 'https://urgi.versailles.inra.fr/GnpMap/mapping/queryTraitSelect.do' },
{ label: 'Biomercator', url: 'https://urgi.versailles.inra.fr/Tools/BioMercator-V4' },
]
},
{ label: 'Genomes', url: 'https://urgi.versailles.inra.fr/Data/Genome/Genome-data-access' },
{ label: 'Synteny', url: 'https://urgi.versailles.inra.fr/synteny/synteny/viewer.do#dataset' },
{
label: 'Sequence',
url: '#',
subMenu: [
{ label: 'Sequence', url: 'https://urgi.versailles.inra.fr/sequence/sequence/sequence/form.do' },
{ label: 'Experiment', url: 'https://urgi.versailles.inra.fr/sequence/sequence/experiment/form.do' },
{ label: 'Analysis', url: 'https://urgi.versailles.inra.fr/sequence/sequence/analysis/form.do' },
{ label: 'Project', url: 'https://urgi.versailles.inra.fr/sequence/sequence/project/form.do' },
]
}
]
}
};
......
......@@ -10,5 +10,15 @@
</head>
<body>
<gpds-root></gpds-root>
<!-- Required for the dropdown menu see : https://getbootstrap.com/docs/4.0/getting-started/introduction/#js -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
</body>
</html>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment