Skip to content
This repository has been 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
Show file tree
Hide file tree
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';

Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
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
Expand Up @@ -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
Expand Up @@ -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();
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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 });
});

Expand Down Expand Up @@ -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', () => {
Expand All @@ -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', () => {
Expand Down
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;