Skip to content

Commit

Permalink
feat(hc-drag-list): drag/drop component for making assignments
Browse files Browse the repository at this point in the history
Drag/drop component for making assignments. Targets are on the left, options are on the right.

re #2010
  • Loading branch information
anderslyman committed Feb 8, 2023
1 parent 60d5a51 commit 95cd2bd
Show file tree
Hide file tree
Showing 9 changed files with 631 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
<hc-drag-list></hc-drag-list>
<hc-drag-list
[assignments]="assignments"
[options]="options"
targetDisplayNameField="name"
targetHelperText="Targets"
targetTooltip="Assign options to targets below.
Example:
Target 1 = Option 3
Target 2 = Option 1
Target 3 = Option 3 (locked)"
optionsHelperText="Drag options to targets on the left"
optionDisplayNameField="name"
[requireAllAssignments]="false"
></hc-drag-list>
<div class="buttons">
<button hc-button buttonStyle="secondary" size="lg" (click)="dragList.resetChanges()">Reset Changes</button>
<button
hc-button
buttonStyle="primary"
size="lg"
(click)="saveSelections(dragList.selections)"
[disabled]="!(dragList || {}).submissionAllowed || false"
>
Save Selections
</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.buttons {
margin-top: 20px;
display: flex;
justify-content: flex-end;

button:first-of-type {
margin-right: 10px;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,44 @@
import { Component } from '@angular/core';
import {Component, ViewChild} from '@angular/core';
import {DragDropAssignment, DragListComponent} from 'projects/cashmere/src/lib/drag-list/drag-list.component';

/**
* @title Overview of Drag List functionality
*/
@Component({
selector: 'hc-drag-list-overview-example',
templateUrl: 'drag-list-overview-example.component.html'
templateUrl: 'drag-list-overview-example.component.html',
styleUrls: ['./drag-list-overview-example.component.scss']
})
export class DragListOverviewExampleComponent {
@ViewChild(DragListComponent) dragList: DragListComponent;

options: {id: number; name: string; title: string}[] = [
{id: 1, name: 'option 1', title: 'this is option 1'},
{id: 2, name: 'option 2', title: 'this is option 2'},
{id: 3, name: 'option 3', title: 'this is option 3'},
{id: 4, name: 'option 4', title: 'this is option 4'},
{id: 5, name: 'option 5', title: 'this is option 5'}
];
assignments: DragDropAssignment[] = [
{
target: {id: 1, name: 'target 1', title: 'this is target 1'},
assignment: null,
locked: false
},
{
target: {id: 2, name: 'target 2', title: 'this is target 2'},
assignment: null,
locked: false
},
{
target: {id: 3, name: 'target 3', title: 'this is target 3'},
assignment: this.options[4],
locked: true
}
];

saveSelections(selections: DragDropAssignment[]) {
console.log('SELECTIONS', selections);
window.alert('check console for received selections');
}
}
101 changes: 100 additions & 1 deletion projects/cashmere/src/lib/drag-list/drag-list.component.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,102 @@
<div class="hc-drag-list-container">
<h1>Hello World!</h1>
<div class="targets">
<div class="targets-info">
<span>{{ targetHelperText }}</span>
<hc-icon
*ngIf="!!targetTooltip"
fontSet="fa"
fontIcon="fa-info-circle"
[hcPop]="productStandardLevelNamesPop"
trigger="hover"
></hc-icon>
<hc-pop #productStandardLevelNamesPop>
<div class="preserve-whitespace" style="white-space: pre">{{ targetTooltip }}</div>
</hc-pop>
</div>
<div *ngFor="let assignment of modifiedAssignments" class="assignments">
<div class="target-name" [title]="assignment.target[targetHoverTextField] ?? ''">
<hc-icon
fontSet="fa"
fontIcon="fa-check"
*ngIf="!assignment.locked"
[style.visibility]="!!assignment.assignment ? 'visible' : 'hidden'"
></hc-icon>
<hc-icon fontSet="fa" fontIcon="fa-lock" [hcPop]="lockedText" trigger="hover" *ngIf="!!assignment.locked"></hc-icon>
<hc-pop #lockedText>{{ optionLockedText }}</hc-pop>
{{ assignment.target[targetDisplayNameField] }}
</div>

<div
class="assignment placeholder"
(dragenter)="dragenter($event)"
(dragleave)="dragleave($event)"
*ngIf="!assignment.assignment && !assignment.locked"
[attr.data-target-id]="assignment.target[targetIdField]"
(drop)="makeAssignment($event)"
(dragover)="dragover($event)"
>
{{ targetPlaceholder }}
</div>
<div
class="assignment"
(dragenter)="dragenter($event)"
(dragleave)="dragleave($event)"
*ngIf="!!assignment.assignment && !assignment.locked"
[attr.data-target-id]="assignment.target[targetIdField]"
[attr.data-option-id]="assignment.assignment[optionIdField]"
(drop)="makeAssignment($event)"
(dragover)="dragover($event)"
>
<div
class="option"
[attr.data-target-id]="assignment.target[targetIdField]"
[attr.data-option-id]="assignment.assignment[optionIdField]"
[title]="tooltipsByOptionId[assignment.assignment[optionIdField]]"
draggable="true"
(dragstart)="dragstart($event)"
>
<div class="option-name">
{{ assignment.assignment[optionDisplayNameField] }}
</div>
<div class="option-close" (click)="unassign(assignment)">
<svg width="10" height="10" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M1 7.03906L4 4.03906L7 7.03906M7 1.03906L3.99943 4.03906L1 1.03906"
stroke="#C0C5CC"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
</div>

<div class="assignment" *ngIf="!!assignment.assignment && !!assignment.locked">
<div class="option option-locked" [title]="tooltipsByOptionId[assignment.assignment[optionIdField]]">
<div class="option-name">
{{ assignment.assignment[optionDisplayNameField] }}
</div>
</div>
</div>
</div>
</div>
<div class="options">
<div class="options-info">{{ optionsHelperText }}</div>
<div class="options-container" (drop)="returnToOptionsList($event)" (dragover)="dragover($event)">
<div
*ngFor="let option of unassignedOptions"
class="option"
[attr.data-option-id]="option[optionIdField]"
[title]="tooltipsByOptionId[option[optionIdField]]"
draggable="true"
(dragstart)="dragstart($event)"
>
<div class="option-name">
{{ option[optionDisplayNameField] }}
</div>
<hc-icon fontSet="hc-icons" fontIcon="hci-grip"></hc-icon>
</div>
</div>
</div>
</div>
79 changes: 58 additions & 21 deletions projects/cashmere/src/lib/drag-list/drag-list.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,64 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { DragListModule } from './drag-list.module';
import { Component } from '@angular/core';

@Component({
template: `
<hc-drag-list></hc-drag-list>
`
})
export class DragListTestComponent {
}
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {DragDropAssignment, DragListComponent} from './drag-list.component';
import {ElementRef} from '@angular/core';

describe('DragListTestComponent', () => {
let formComponent: DragListTestComponent;
let formFixture: ComponentFixture<DragListTestComponent>;
let component: DragListComponent;
let fixture: ComponentFixture<DragListComponent>;
const el: ElementRef = new ElementRef({focus() {
// do nothing.
}});
let options: {id: number; name: string; title: string}[] = [
{id: 1, name: 'option 1', title: 'this is option 1'},
{id: 2, name: 'option 2', title: 'this is option 2'},
{id: 3, name: 'option 3', title: 'this is option 3'},
{id: 4, name: 'option 4', title: 'this is option 4'},
{id: 5, name: 'option 5', title: 'this is option 5'}
];
let assignments: DragDropAssignment[] = [
{
target: {id: 1, name: 'target 1', title: 'this is target 1'},
assignment: null,
locked: false
},
{
target: {id: 2, name: 'target 2', title: 'this is target 2'},
assignment: null,
locked: false
},
{
target: {id: 3, name: 'target 3', title: 'this is target 3'},
assignment: options[4],
locked: true
}
];

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [DragListTestComponent],
imports: [DragListModule]
beforeEach(async done => {
await TestBed.configureTestingModule({
declarations: [DragListComponent],
providers: [{provide: ElementRef, useValue: el}]
}).compileComponents();
fixture = TestBed.createComponent(DragListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
await fixture.whenStable();
done();
});
it('should create the component without error', () => {
expect(component).toBeTruthy();
});
it('submissionAllowed should be invalid if requireAllAssignments is true and there are unassigned targets', () => {
component.assignments = assignments;
component.options = options;
component.requireAllAssignments = true;

expect(component.submissionAllowed).toBeFalse();
});
it('submissionAllowed should be valid if requireAllAssignments is false and there are unassigned targets', () => {
component.assignments = assignments;
component.options = options;
component.requireAllAssignments = false;

formFixture = TestBed.createComponent(DragListTestComponent);
formComponent = formFixture.componentInstance;
formFixture.detectChanges();
}));
expect(component.submissionAllowed).toBeTrue();
});
});
Loading

0 comments on commit 95cd2bd

Please sign in to comment.