Skip to content
This repository was archived by the owner on Jun 18, 2018. It is now read-only.

Implement wildcard source type #31

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 10 additions & 16 deletions src/HandlerRegistry.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import invariant from 'invariant';
import isArray from 'lodash/isArray';
import getNextUniqueId from './utils/getNextUniqueId';
import isValidSourceType from './utils/isValidSourceType';
import isValidTargetType from './utils/isValidTargetType';
import { addSource, addTarget, removeSource, removeTarget } from './actions/registry';
import asap from 'asap';

@@ -21,20 +23,6 @@ function validateTargetContract(target) {
invariant(typeof target.drop === 'function', 'Expected beginDrag to be a function.');
}

function validateType(type, allowArray) {
if (allowArray && isArray(type)) {
type.forEach(t => validateType(t, false));
return;
}

invariant(
typeof type === 'string' || typeof type === 'symbol',
allowArray ?
'Type can only be a string, a symbol, or an array of either.' :
'Type can only be a string or a symbol.'
);
}

function getNextHandlerId(role) {
const id = getNextUniqueId().toString();
switch (role) {
@@ -70,7 +58,10 @@ export default class HandlerRegistry {
}

addSource(type, source) {
validateType(type);
invariant(
isValidSourceType(type),
'Source type can only be a string, a symbol, a boolean, or an array thereof.'
);
validateSourceContract(source);

const sourceId = this.addHandler(HandlerRoles.SOURCE, type, source);
@@ -79,7 +70,10 @@ export default class HandlerRegistry {
}

addTarget(type, target) {
validateType(type, true);
invariant(
isValidTargetType(type),
'Target type can only be a string or a symbol.'
);
validateTargetContract(target);

const targetId = this.addHandler(HandlerRoles.TARGET, type, target);
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export { default as DragDropManager } from './DragDropManager';
export { default as DragSource } from './DragSource';
export { default as DropTarget } from './DropTarget';
export { default as createTestBackend } from './backends/createTestBackend';
export { default as createTestBackend } from './backends/createTestBackend';
export { default as isValidTargetType } from './utils/isValidTargetType';
export { default as isValidSourceType } from './utils/isValidSourceType';
5 changes: 5 additions & 0 deletions src/utils/isValidSourceType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function isValidSourceType(sourceType) {
const type = typeof sourceType;
return type === 'string' || type === 'symbol';
}

18 changes: 18 additions & 0 deletions src/utils/isValidTargetType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import isArray from 'lodash/isArray';

export default function isValidTargetType(targetType) {
if (isArray(targetType)) {
for (let n = 0, len = targetType.length; n < len; n++) {
const type = typeof targetType[n];
if (type !== 'boolean' && type !== 'string' && type !== 'symbol') {
return false;
}
}

return true;
} else {
const type = typeof targetType;
return type === 'boolean' || type === 'string' || type === 'symbol';
}
}

4 changes: 2 additions & 2 deletions src/utils/matchesType.js
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@ import isArray from 'lodash/isArray';

export default function matchesType(targetType, draggedItemType) {
if (isArray(targetType)) {
return targetType.some(t => t === draggedItemType);
return targetType.some(t => t === true || t === draggedItemType);
} else {
return targetType === draggedItemType;
return targetType === true || targetType === draggedItemType;
}
}
16 changes: 16 additions & 0 deletions test/API.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import expect from 'expect.js';
import { isValidSourceType } from '../src';
import { isValidTargetType } from '../src';

describe('API', () => {
describe('utils', (done) => {
it('exposes isValidSourceType', () => {
expect(isValidSourceType).to.be.a("function");
});

it('exposes isValidTargetType', () => {
expect(isValidTargetType).to.be.a("function");
});
});
});

54 changes: 44 additions & 10 deletions test/DragDropManager.spec.js
Original file line number Diff line number Diff line change
@@ -94,6 +94,21 @@ describe('DragDropManager', () => {
expect(() => registry.addTarget([Symbol(), Symbol()], target)).to.not.throwError();
});

// The target type true is a wildcard and matches all target types. The
// target type false matches no target types.
//
// Using true or false as a source type doesn't make sense and isn't
// supported.
it('accepts boolean as type for DropTargets, not for DragSources', () => {
const source = new NormalSource();
const target = new NormalTarget();

expect(() => registry.addSource(true, source)).to.throwError();
expect(() => registry.addTarget(true, target)).to.not.throwError();
expect(() => registry.addTarget(false, target)).to.not.throwError();
expect(() => registry.addTarget([true, true, false], target)).to.not.throwError();
});

it('throws on invalid type', () => {
const source = new NormalSource();
const target = new NormalTarget();
@@ -358,13 +373,16 @@ describe('DragDropManager', () => {
it('ignores drop() if target has a different type', () => {
const source = new NormalSource();
const sourceId = registry.addSource(Types.FOO, source);
const target = new NormalTarget();
const targetId = registry.addTarget(Types.BAR, target);
const targetA = new NormalTarget();
const targetAId = registry.addTarget(Types.BAR, targetA);
const targetB = new NormalTarget();
const targetBId = registry.addTarget(Types.NIL, targetB);

backend.simulateBeginDrag([sourceId]);
backend.simulateHover([targetId]);
backend.simulateHover([targetAId, targetBId]);
backend.simulateDrop();
expect(target.didCallDrop).to.equal(false);
expect(targetA.didCallDrop).to.equal(false);
expect(targetB.didCallDrop).to.equal(false);
});

it('throws in drop() if it is called outside a drag operation', () => {
@@ -422,16 +440,20 @@ describe('DragDropManager', () => {
const targetAId = registry.addTarget(Types.FOO, targetA);
const targetB = new NormalTarget({ number: 16 });
const targetBId = registry.addTarget(Types.BAR, targetB);
const targetC = new NormalTarget({ number: 42 });
const targetCId = registry.addTarget(Types.FOO, targetC);
const targetC = new NormalTarget({ number: 29 });
const targetCId = registry.addTarget(Types.NIL, targetC);
const targetD = new NormalTarget({ number: 42 });
const targetDId = registry.addTarget(Types.FOO, targetD);

backend.simulateBeginDrag([sourceId]);
backend.simulateHover([targetAId, targetBId, targetCId]);
backend.simulateHover([targetAId, targetBId, targetCId, targetDId]);
backend.simulateDrop();
backend.simulateEndDrag();
expect(targetA.didCallDrop).to.equal(true);
expect(targetB.didCallDrop).to.equal(false);
expect(targetC.didCallDrop).to.equal(true);
console.log("C dropped?", targetC.didCallDrop);
expect(targetC.didCallDrop).to.equal(false);
expect(targetD.didCallDrop).to.equal(true);
expect(source.recordedDropResult).to.eql({ number: 42 });
});

@@ -587,13 +609,22 @@ describe('DragDropManager', () => {
const targetCId = registry.addTarget(Types.FOO, targetC);
const targetD = new NormalTarget();
const targetDId = registry.addTarget([Types.BAZ, Types.FOO], targetD);
const targetE = new NormalTarget();
const targetEId = registry.addTarget(Types.WILDCARD, targetE);
const targetF = new NormalTarget();
const targetFId = registry.addTarget(Types.NIL, targetF);
const targetG = new NormalTarget();
const targetGId = registry.addTarget([Types.WILDCARD, Types.NIL], targetG);

backend.simulateBeginDrag([sourceId]);
backend.simulateHover([targetAId, targetBId, targetCId, targetDId]);
backend.simulateHover([targetAId, targetBId, targetCId, targetDId, targetEId, targetFId, targetGId]);
expect(targetA.didCallHover).to.equal(true);
expect(targetB.didCallHover).to.equal(false);
expect(targetC.didCallHover).to.equal(true);
expect(targetD.didCallHover).to.equal(true);
expect(targetE.didCallHover).to.equal(true);
expect(targetF.didCallHover).to.equal(false);
expect(targetG.didCallHover).to.equal(true);
});

it('includes non-droppable targets when dispatching hover', () => {
@@ -603,11 +634,14 @@ describe('DragDropManager', () => {
const targetAId = registry.addTarget(Types.FOO, targetA);
const targetB = new TargetWithNoDropResult();
const targetBId = registry.addTarget(Types.FOO, targetB);
const targetC = new TargetWithNoDropResult();
const targetCId = registry.addTarget(Types.WILDCARD, targetC);

backend.simulateBeginDrag([sourceId]);
backend.simulateHover([targetAId, targetBId]);
backend.simulateHover([targetAId, targetBId, targetCId]);
expect(targetA.didCallHover).to.equal(true);
expect(targetB.didCallHover).to.equal(true);
expect(targetC.didCallHover).to.equal(true);
});

it('throws in hover() if it contains the same target twice', () => {
2 changes: 2 additions & 0 deletions test/types.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const FOO = 'FOO';
export const BAR = 'BAR';
export const BAZ = 'BAZ';
export const WILDCARD = true;
export const NIL = false;