Skip to content

Commit

Permalink
feat: update select dropdown to match multiselect one
Browse files Browse the repository at this point in the history
  • Loading branch information
f-necas committed Jul 5, 2023
1 parent 6487b30 commit f89ac8b
Show file tree
Hide file tree
Showing 14 changed files with 266 additions and 56 deletions.
2 changes: 2 additions & 0 deletions apps/datafeeder/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { SummarizePageComponent } from './presentation/pages/summarize-page/summ
import { SummarizeIllustrationComponent } from './presentation/components/svg/summarize-illustration/summarize-illustration.component'
import { SummarizeBackgroundComponent } from './presentation/components/svg/summarize-background/summarize-background.component'
import { DATAFEEDER_STATE_KEY, reducer } from './store/datafeeder.reducer'
import { MatIconModule } from '@angular/material/icon'

export function apiConfigurationFactory() {
return new Configuration({
Expand Down Expand Up @@ -71,6 +72,7 @@ export function apiConfigurationFactory() {
UiInputsModule,
UiWidgetsModule,
HttpClientModule,
MatIconModule,
UtilI18nModule,
FeatureEditorModule,
ApiModule.forRoot(apiConfigurationFactory),
Expand Down
4 changes: 4 additions & 0 deletions apps/datafeeder/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&family=Permanent+Marker&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined"
rel="stylesheet"
/>

<script src="assets/env.js"></script>
</head>
Expand Down
5 changes: 5 additions & 0 deletions apps/datafeeder/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ gn-ui-button button[type='button'].secondary {
border-color: var(--color-primary);
border-width: 1px;
}

gn-ui-dropdown-selector gn-ui-button button[type='button'].secondary {
border-width: 2px;
}

gn-ui-button button[type='button'].secondary:hover {
background: var(--color-primary-darker);
color: white;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
<div class="w-full h-full flex flex-col">
<div
class="flex flex-col space-y-2 sm:flex-row sm:space-y-0 sm:space-x-2 justify-between text-[13px]"
class="flex flex-col flex-wrap space-y-2 sm:flex-row sm:space-y-0 sm:space-x-2 justify-between text-[13px] gap-4"
>
<gn-ui-dropdown-selector
class="basis-1/4"
class="basis-1/4 grow"
[choices]="typeChoices"
[extraClass]="'secondary min-w-full'"
(selectValue)="chartType$.next($event)"
[selected]="chartType$.value"
[title]="'chart.dropdown.type' | translate"
></gn-ui-dropdown-selector>
<gn-ui-dropdown-selector
class="basis-1/4"
class="basis-1/4 grow"
[choices]="xChoices$ | async"
[extraClass]="'secondary min-w-full'"
(selectValue)="xProperty$.next($event)"
[selected]="xProperty$.value"
[title]="'chart.dropdown.xProperty' | translate"
></gn-ui-dropdown-selector>
<gn-ui-dropdown-selector
class="basis-1/4"
class="basis-1/4 grow"
*ngIf="!isCountAggregation"
[choices]="yChoices$ | async"
(selectValue)="yProperty$.next($event)"
Expand All @@ -26,7 +28,7 @@
class="select-y-prop"
></gn-ui-dropdown-selector>
<gn-ui-dropdown-selector
class="basis-1/4"
class="basis-1/4 grow"
[choices]="aggregationChoices"
class="aggregation-choices"
(selectValue)="aggregation$.next($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class MockChartComponent {
template: '<div></div>',
})
export class MockDropdownSelectorComponent {
@Input() selected: any
@Input() choices: unknown[]
@Output() selectValue = new EventEmitter<any>()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ChartViewComponent } from './chart-view.component'
import { ChartComponent, UiDatavizModule } from '@geonetwork-ui/ui/dataviz'
import { UiWidgetsModule } from '@geonetwork-ui/ui/widgets'
import { MetadataLinkType } from '@geonetwork-ui/util/shared'
import { OverlayModule } from '@angular/cdk/overlay'

export default {
title: 'Smart/Dataviz/ChartView',
Expand All @@ -21,6 +22,7 @@ export default {
imports: [
ChartComponent,
HttpClientModule,
OverlayModule,
UiDatavizModule,
UiWidgetsModule,
BrowserAnimationsModule,
Expand Down
2 changes: 2 additions & 0 deletions libs/feature/dataviz/src/lib/feature-dataviz.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ChartViewComponent } from './chart-view/chart-view.component'
import { TranslateModule } from '@ngx-translate/core'
import { UiWidgetsModule } from '@geonetwork-ui/ui/widgets'
import { UiInputsModule } from '@geonetwork-ui/ui/inputs'
import { OverlayModule } from '@angular/cdk/overlay'

@NgModule({
imports: [
Expand All @@ -25,6 +26,7 @@ import { UiInputsModule } from '@geonetwork-ui/ui/inputs'
UiWidgetsModule,
TranslateModule,
ChartComponent,
OverlayModule,
UiInputsModule,
],
declarations: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@
#dropdown
[id]="wizardFieldConfig.id"
[title]="''"
[extraClass]="'secondary min-w-full'"
[showTitle]="false"
[choices]="dropdownChoices"
[selectedValueExpectedAsObject]="true"
[selected]="wizardFieldData"
ariaName="search-sort-by"
></gn-ui-dropdown-selector>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
<p translate>organisation.sort.intro</p>
</span>
<span class="flex flex-wrap sm:flex-nowrap sm:shrink-0">
<span class="mt-2 mr-2 opacity-75" translate>
organisation.sort.sortBy
</span>
<gn-ui-dropdown-selector
[title]="''"
[title]="'organisation.sort.sortBy' | translate"
class="shrink"
[choices]="choices"
[showTitle]="false"
[showTitle]="true"
(selectValue)="selectOrderToDisplay($event)"
></gn-ui-dropdown-selector>
</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'

import { OrganisationsSortComponent } from './organisations-sort.component'
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { TranslateModule } from '@ngx-translate/core'

@Component({
selector: 'gn-ui-dropdown-selector',
template: '',
})
class DropdownSelectorMockComponent {
@Input() showTitle: unknown
@Input() choices: {
value: unknown
label: string
}[]
@Input() selected: unknown
@Output() selectValue = new EventEmitter<unknown>()
}

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

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OrganisationsSortComponent],
declarations: [OrganisationsSortComponent, DropdownSelectorMockComponent],
imports: [TranslateModule.forRoot()],
}).compileComponents()

fixture = TestBed.createComponent(OrganisationsSortComponent)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,68 @@
<div class="flex flex-col items-start sm:flex-row sm:items-center relative">
<label
<div class="flex items-start sm:flex-row sm:items-center relative w-full">
<span
*ngIf="showTitle"
class="tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap"
[attr.for]="id"
>
{{ title }}
</label>
<select
[id]="id"
(change)="this.selectValue.emit($event.target.value)"
class="w-full min-w-[66px] truncate text-main bg-white border border-gray-300 py-[10px] px-2 rounded-[0.25em] cursor-pointer leading-tight focus:outline-none hover:text-primary-darker hover:border-primary-lighter focus:ring-primary-lightest focus:ring-4"
[ngClass]="extraClass"
[class.w-full]="!showTitle"
</span>
<gn-ui-button
type="outline"
class="grow"
extraClass="w-full !p-[8px] !pl-[16px] {{ extraClass }}"
[title]="title"
[attr.aria-owns]="id"
(buttonClick)="openOverlay()"
cdkOverlayOrigin
#overlayOrigin="cdkOverlayOrigin"
>
<option
<div class="grow flex items-center mr-2 gap-2 overflow-hidden">
<div class="text-left font-medium truncate py-1">
{{ getChoiceLabel() | translate }}
</div>
</div>
<mat-icon class="shrink-0 opacity-40">
<ng-container *ngIf="overlayOpen">expand_less</ng-container>
<ng-container *ngIf="!overlayOpen">expand_more</ng-container>
</mat-icon>
</gn-ui-button>
</div>

<ng-template
cdkConnectedOverlay
cdkConnectedOverlayHasBackdrop
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
[cdkConnectedOverlayOrigin]="overlayOrigin"
[cdkConnectedOverlayOpen]="overlayOpen"
[cdkConnectedOverlayPositions]="overlayPositions"
[cdkConnectedOverlayFlexibleDimensions]="true"
(backdropClick)="closeOverlay()"
(detach)="closeOverlay()"
>
<div
class="bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container"
[style.max-height]="overlayMaxHeight"
[style.width]="overlayWidth"
role="listbox"
tabindex="-1"
[attr.id]="id"
[attr.aria-multiselectable]="true"
[attr.aria-label]="title"
>
<label
*ngFor="let choice of choices"
[value]="choice.value"
[selected]="isSelected(choice)"
[title]="choice.label"
class="flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors"
>
{{ choice.label | translate }}
</option>
</select>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-800"
></div>
</div>
<input
class="w-[0px] h-[18px] align-text-top shrink-0"
type="text"
[checked]="isSelected(choice)"
(click)="onSelectValue(choice)"
/>
<span class="text-[14px]">
{{ choice.label | translate }}
</span>
</label>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { TranslateModule } from '@ngx-translate/core'
import { ButtonComponent } from '../button/button.component'

import { DropdownSelectorComponent } from './dropdown-selector.component'
import { OverlayModule } from '@angular/cdk/overlay'
import { MatIconModule } from '@angular/material/icon'

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

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
imports: [OverlayModule, MatIconModule, TranslateModule.forRoot()],
declarations: [DropdownSelectorComponent, ButtonComponent],
}).compileComponents()
})
Expand All @@ -24,37 +25,74 @@ describe('DropdownSelectorComponent', () => {
{ label: 'B', value: 'b' },
{ label: 'C', value: 'c' },
]
component.selectedValueExpectedAsObject = false
fixture.detectChanges()
})

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

describe('items array', () => {
let choicesEl
let selectEl
describe('items selection', () => {
let emitted
beforeEach(() => {
component.selected = 'b'
fixture.detectChanges()
choicesEl = fixture.nativeElement.querySelectorAll('option')
selectEl = fixture.nativeElement.querySelector('select')
emitted = null
component.selectValue.subscribe((v) => (emitted = v))
})
describe('when clicking an item with selectedValueExpectedAsObject', () => {
it('emits the correct item as Json object', () => {
component.onSelectValue({ label: 'A', value: 'a' })
expect(emitted).toEqual('a')
})
})
it('shows one element per item in the dropdown', () => {
expect(choicesEl.length).toBe(component.choices.length)

describe('when clicking an item with selectedValueExpectedAsObject', () => {
beforeEach(() => {
component.selectedValueExpectedAsObject = true
})
it('emits the correct item as Json object', () => {
component.selectedValueExpectedAsObject = true
component.onSelectValue({ label: 'A', value: 'a' })
expect(emitted).toEqual(JSON.stringify({ label: 'A', value: 'a' }))
})
})
it('displays the active element as such', () => {
expect(selectEl.value).toBe('b')
expect(choicesEl[0].selected).toBeFalsy()
expect(choicesEl[1].selected).toBeTruthy()
expect(choicesEl[2].selected).toBeFalsy()
})

describe('overlay sizing', () => {
describe('width', () => {
beforeEach(() => {
const originEl: HTMLElement =
component.overlayOrigin.elementRef.nativeElement
originEl.getBoundingClientRect = () =>
({
width: 25,
height: 20,
} as any)
component.openOverlay()
})
it('sets the width according to the toggle element', () => {
expect(component.overlayWidth).toBe('25px')
})
})
it('emits the value of the clicked item', () => {
let emitted
component.selectValue.subscribe((v) => (emitted = v))
selectEl.value = component.choices[0].value
selectEl.dispatchEvent(new Event('change'))
expect(emitted).toBe(component.choices[0].value)
describe('max height (with maxRows set)', () => {
beforeEach(() => {
component.maxRows = 10
component.openOverlay()
})
it('sets the max height according to the max rows input', () => {
expect(component.overlayMaxHeight).toMatch('350px')
})
})
describe('max height (with maxRows unset)', () => {
beforeEach(() => {
component.maxRows = undefined
component.openOverlay()
})
it('sets the max height according to the max rows input', () => {
// we don't need the exact measurement, just to make sure it's an actual value
expect(component.overlayMaxHeight).toBe('none')
})
})
})
})
Loading

0 comments on commit f89ac8b

Please sign in to comment.