Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Datahub] Add an API form generator for OGC API Records #687

Merged
merged 11 commits into from
Dec 22, 2023
119 changes: 119 additions & 0 deletions apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,122 @@ describe('record with file distributions', () => {
.should('deep.eq', ['csv (csv)', 'json (json)', 'geojson (geojson)'])
})
})

describe('api cards', () => {
beforeEach(() => {
cy.visit('/dataset/accroche_velos')
cy.get('gn-ui-api-card').first().as('firstCard')
})

it('should display the open panel button', () => {
cy.get('@firstCard')
.find('button')
.children('mat-icon')
.should('have.text', 'more_horiz')
})
it('should open and close the panel on click on open panel button', () => {
cy.get('@firstCard').click()
cy.get('gn-ui-record-api-form').should('be.visible')
cy.get('@firstCard').click()
cy.get('gn-ui-record-api-form').should('not.be.visible')
})
})

describe('api form', () => {
beforeEach(() => {
cy.visit('/dataset/accroche_velos')
cy.get('gn-ui-api-card').first().find('button').click()
cy.get('gn-ui-record-api-form').children('div').as('apiForm')
})
it('should have request inputs', () => {
cy.get('@apiForm').find('gn-ui-text-input').should('have.length', 2)
cy.get('@apiForm').find('gn-ui-dropdown-selector').should('have.length', 1)
cy.get('@apiForm')
.children('div')
.first()
.children('div')
.first()
.find('button')
.should('have.length', 1)
cy.get('@apiForm').find('gn-ui-copy-text-button').should('have.length', 1)
})
it('should change url on input change', () => {
cy.get('@apiForm')
.find('gn-ui-copy-text-button')
.find('input')
.invoke('val')
.then((url) => {
cy.get('@apiForm').find('gn-ui-text-input').first().clear()
cy.get('@apiForm').find('gn-ui-text-input').first().type('54')
cy.get('@apiForm')
.find('gn-ui-copy-text-button')
.find('input')
.invoke('val')
.then((newUrl) => {
expect(newUrl).to.not.eq(url)
expect(newUrl).to.include('54')
})
})
})
it('should set limit to zero on click on "All" button', () => {
cy.get('@apiForm').find('gn-ui-text-input').first().clear()
cy.get('@apiForm').find('gn-ui-text-input').first().type('54')
cy.get('@apiForm').find('input[type="checkbox"]').check()
cy.get('@apiForm').find('gn-ui-text-input').first().should('have.value', '')
})
it('should reset all 3 inputs and link on click', () => {
cy.get('@apiForm').find('gn-ui-text-input').first().as('firstInput')
cy.get('@firstInput').clear()
cy.get('@firstInput').type('54')

cy.get('@apiForm').find('gn-ui-text-input').eq(1).as('secondInput')
cy.get('@secondInput').clear()
cy.get('@secondInput').type('87')

cy.get('@apiForm').find('gn-ui-dropdown-selector').click()
cy.get('button[data-cy-value="csv"]').click()

cy.get('@apiForm')
.find('gn-ui-copy-text-button')
.find('input')
.invoke('val')
.should('include', 'offset=87&limit=54&f=csv')

cy.get('@apiForm').children('div').first().find('button').first().click()

cy.get('@firstInput').find('input').should('have.value', '')
cy.get('@secondInput').find('input').should('have.value', '')
cy.get('@apiForm')
.find('gn-ui-dropdown-selector')
.find('button')
.children('div')
.should('have.text', ' JSON ')
cy.get('@apiForm')
.find('gn-ui-copy-text-button')
.find('input')
.invoke('val')
.should('include', 'f=json')
})
it('should close the panel on click', () => {
cy.get('gn-ui-record-api-form').prev().find('button').click()
cy.get('gn-ui-record-api-form').should('not.be.visible')
})
it('should switch to other card url if card already open', () => {
cy.get('@apiForm')
.find('gn-ui-copy-text-button')
.find('input')
.invoke('val')
.then((url) => {
cy.get('@apiForm').find('gn-ui-text-input').first().clear()
cy.get('@apiForm').find('gn-ui-text-input').first().type('54')
cy.get('gn-ui-api-card').eq(1).find('button').click()
cy.get('@apiForm')
.find('gn-ui-copy-text-button')
.find('input')
.invoke('val')
.then((newUrl) => {
expect(newUrl).to.not.eq(url)
})
})
})
})
2 changes: 1 addition & 1 deletion apps/datahub-e2e/src/e2e/home.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable cypress/no-unnecessary-waiting */
import 'cypress-real-events'

describe('header', () => {
describe('home', () => {
beforeEach(() => cy.visit('/'))

describe('general display', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.tab-header-label {
@apply uppercase text-sm text-primary opacity-75 hover:text-primary-darker;
}
89 changes: 65 additions & 24 deletions apps/datahub/src/app/record/record-apis/record-apis.component.html
Original file line number Diff line number Diff line change
@@ -1,25 +1,66 @@
<p
class="font-title text-[28px] font-medium mt-8 mb-5 text-title text-center sm:mt-12 sm:mb-[-22px] sm:text-left"
translate
>
record.metadata.api
</p>
<gn-ui-carousel
containerClass="gap-4 py-10"
stepsContainerClass="right-[var(--container-outside-width)] bottom-[calc(100%-12px)] top-auto"
>
<gn-ui-api-card
*ngFor="
let link of facade.apiLinks$ | async;
let first = first;
let last = last
"
[link]="link"
class="w-80"
[ngClass]="{
'mr-[var(--container-outside-width)]': last,
'ml-[var(--container-outside-width)]': first
}"
<div class="container-lg px-4 lg:mx-auto">
<p
class="font-title text-[28px] font-medium mt-8 mb-5 text-title text-center sm:mt-12 sm:mb-[-22px] sm:text-left"
translate
>
</gn-ui-api-card>
</gn-ui-carousel>
record.metadata.api
</p>
<gn-ui-carousel
containerClass="gap-4 py-10"
stepsContainerClass="right-[var(--container-outside-width)] bottom-[calc(100%-12px)] top-auto"
>
<gn-ui-api-card
*ngFor="
let link of facade.apiLinks$ | async;
let first = first;
let last = last
"
[link]="link"
[currentLink]="selectedApiLink"
class="w-80"
[ngClass]="{
'mr-[var(--container-outside-width)]': last,
'ml-[var(--container-outside-width)]': first,
'card-shadow': link !== selectedApiLink || !selectedApiLink,
'bg-neutral-100': link === selectedApiLink
}"
(openRecordApiForm)="openRecordApiForm($event)"
>
</gn-ui-api-card>
</gn-ui-carousel>
</div>
<div
class="content overflow-hidden transition-all duration-300"
[ngClass]="selectedApiLink ? 'ease-in' : 'ease-out'"
[style.maxHeight]="maxHeight"
[style.opacity]="opacity"
>
<div class="bg-primary-opacity-10 py-8">
<div class="flex flex-col px-4 gap-8 container-lg lg:mx-auto">
<div class="flex flex-wrap justify-between sm:mb-2 ng-star-inserted">
<p class="text-[21px] text-title font-title" translate>
record.metadata.api.form.title
</p>
<button
type="button"
class="flex items-center gap-0.5 text-primary group"
(click)="closeRecordApiForm()"
>
<div
class="text-sm font-medium opacity-50 group-hover:opacity-100 uppercase tracking-wide mt-0.5"
translate
>
record.metadata.api.form.closeButton
</div>
<mat-icon
class="!w-5 !h-5 text-xl font-bold material-symbols-outlined !flex items-center"
>close</mat-icon
>
</button>
</div>
<gn-ui-record-api-form
[apiLink]="selectedApiLink"
></gn-ui-record-api-form>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { Subject } from 'rxjs'
import { RecordApisComponent } from './record-apis.component'
import { TranslateModule } from '@ngx-translate/core'
import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
import { MdViewFacade } from '@geonetwork-ui/feature/record'
import { BehaviorSubject } from 'rxjs'

class MdViewFacadeMock {
apiLinks$ = new Subject()
selectedApiLink$ = new BehaviorSubject([])
}

describe('DataApisComponent', () => {
const serviceDistributionMock = {
type: 'service',
url: new URL('http://myogcapifeatures.test'),
accessServiceProtocol: 'ogcFeatures',
} as DatasetServiceDistribution

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

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [RecordApisComponent],
imports: [TranslateModule.forRoot()],
providers: [
{
provide: MdViewFacade,
useClass: MdViewFacadeMock,
},
],
}).compileComponents()
})

beforeEach(() => {
fixture = TestBed.createComponent(RecordApisComponent)
component = fixture.componentInstance
fixture.detectChanges()
Expand All @@ -32,4 +39,32 @@ describe('DataApisComponent', () => {
it('should create', () => {
expect(component).toBeTruthy()
})

describe('#openRecordApiForm', () => {
beforeEach(() => {
component.openRecordApiForm(serviceDistributionMock)
})
it('should update selectedApiLink', () => {
expect(component.selectedApiLink).toEqual(serviceDistributionMock)
})
it('should update maxHeight for transition', () => {
expect(component.maxHeight).toEqual('500px')
})
it('should update opacity for transition', () => {
expect(component.opacity).toEqual(1)
})
})

describe('#closeRecordApiForm', () => {
it('should update selectedApiLink', () => {
component.closeRecordApiForm()
expect(component.selectedApiLink).toBeUndefined()
})
it('should update maxHeight for transition', () => {
expect(component.maxHeight).toEqual('0px')
})
it('should update opacity for transition', () => {
expect(component.opacity).toEqual(0)
})
})
})
28 changes: 26 additions & 2 deletions apps/datahub/src/app/record/record-apis/record-apis.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, ChangeDetectionStrategy } from '@angular/core'
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'
import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
import { MdViewFacade } from '@geonetwork-ui/feature/record'

@Component({
Expand All @@ -7,6 +8,29 @@ import { MdViewFacade } from '@geonetwork-ui/feature/record'
styleUrls: ['./record-apis.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RecordApisComponent {
export class RecordApisComponent implements OnInit {
maxHeight = '0px'
opacity = 0
selectedApiLink: DatasetServiceDistribution
constructor(public facade: MdViewFacade) {}

ngOnInit(): void {
this.setStyle(undefined)
this.selectedApiLink = undefined
}

openRecordApiForm(link: DatasetServiceDistribution) {
this.selectedApiLink = link
this.setStyle(link)
}

closeRecordApiForm() {
this.selectedApiLink = undefined
this.setStyle(undefined)
}

setStyle(link: DatasetServiceDistribution) {
this.maxHeight = link === undefined ? '0px' : '500px'
this.opacity = link === undefined ? 0 : 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,16 @@
<datahub-record-downloads class="block mt-5"></datahub-record-downloads>
</div>

<div id="links" class="container-lg px-4 lg:mx-auto">
<div *ngIf="displayOtherLinks | async">
<div id="links" class="lg:mx-auto">
<div class="container-lg px-4 lg:mx-auto" *ngIf="displayOtherLinks | async">
<datahub-record-otherlinks class="block mt-5"></datahub-record-otherlinks>
</div>

<div *ngIf="displayApi$ | async">
<datahub-record-apis class="block mt-6"></datahub-record-apis>
<datahub-record-apis
[isPanelOpen]="isPanelOpen"
class="block mt-6"
></datahub-record-apis>
</div>
</div>

Expand Down
Loading
Loading