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 Aug 25, 2023
1 parent ba8b141 commit 9a20665
Show file tree
Hide file tree
Showing 16 changed files with 256 additions and 55 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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
[choices]="footerList"
(selectValue)="selectValue($event)"
[selected]="selectedValue"
[extraBtnClass]="'secondary min-w-full'"
ariaName="search-sort-by"
*ngIf="footerList.length > 0"
>
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
Expand Up @@ -5,13 +5,15 @@
<gn-ui-dropdown-selector
class="basis-1/4"
[choices]="typeChoices"
[extraBtnClass]="'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"
[choices]="xChoices$ | async"
[extraBtnClass]="'secondary min-w-full'"
(selectValue)="xProperty$.next($event)"
[selected]="xProperty$.value"
[title]="'chart.dropdown.xProperty' | translate"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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 @@ -64,6 +64,7 @@
#dropdown
[id]="wizardFieldConfig.id"
[title]="''"
[extraBtnClass]="'secondary min-w-full'"
[showTitle]="false"
[choices]="dropdownChoices"
[selected]="wizardFieldData"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class WizardFieldComponent implements AfterViewInit, OnDestroy {
return data ? new Date(Number(data)) : new Date()
}
case WizardFieldType.DROPDOWN: {
return data ? JSON.parse(data) : this.dropdownChoices[1]
return data ? JSON.parse(data) : this.dropdownChoices[0]?.value
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*ngIf="dropdownChoices$ | async as choices"
[title]="'table.select.data' | translate"
class="mb-7 w-auto ml-auto"
extraClass="!text-primary font-sans font-medium"
extraBtnClass="!text-primary font-sans font-medium"
[choices]="choices"
(selectValue)="selectLink($event)"
></gn-ui-dropdown-selector>
Expand Down
4 changes: 2 additions & 2 deletions libs/feature/record/src/lib/map-view/map-view.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="w-full h-full flex flex-col p-1">
<div class="flex w-full justify-end mb-7 mt-1">
<div class="w-full mb-7 mt-1">
<gn-ui-dropdown-selector
extraClass="!text-primary font-sans font-medium"
extraBtnClass="!text-primary font-sans font-medium"
[title]="'map.select.layer' | translate"
[choices]="dropdownChoices$ | async"
(selectValue)="selectLinkToDisplay($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
<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"
[minWidth]="'180px'"
[showTitle]="true"
(selectValue)="selectOrderToDisplay($event)"
></gn-ui-dropdown-selector>
</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
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,71 @@
<div class="flex flex-col items-start sm:flex-row sm:items-center relative">
<label
<div
class="flex items-start flex-col 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 min-w-0"
style="min-width: {{ minWidth }}"
extraClass="w-full !p-[8px] !pl-[16px] {{ extraBtnClass }}"
[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,89 @@ describe('DropdownSelectorComponent', () => {
{ label: 'B', value: 'b' },
{ label: 'C', value: 'c' },
]
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))
})
it('shows one element per item in the dropdown', () => {
expect(choicesEl.length).toBe(component.choices.length)
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('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('when an existing value is provided', () => {
beforeEach(() => {
component.selected = 'b'
})
it('selects the corresponding choice', () => {
expect(component.selectedChoice).toEqual({ label: 'B', value: 'b' })
})
})
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('when no selected value is provided', () => {
beforeEach(() => {
component.selected = undefined
})
it('selects the first choice', () => {
expect(component.selectedChoice).toEqual({ label: 'A', value: 'a' })
})
})

describe('when the selected value is not part of the choices', () => {
beforeEach(() => {
component.selected = 'blarg'
})
it('selects the first choice', () => {
expect(component.selectedChoice).toEqual({ label: 'A', value: 'a' })
})
})
})

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')
})
})
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 9a20665

Please sign in to comment.