Skip to content

Commit

Permalink
Merge pull request #327 from amosproj/search-highlight
Browse files Browse the repository at this point in the history
Search highlight
  • Loading branch information
PianoRollRepresentation authored Jun 22, 2021
2 parents 2f3b1e4 + 263a3de commit 17e76ec
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 51 deletions.
5 changes: 4 additions & 1 deletion frontend/cypress/unit/stores/colors/EdgeColorStore.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { EdgeDescriptor } from '../../../../src/shared/entities';
import { EntityStyleStore } from '../../../../src/stores/colors';
import SearchSelectionStore from '../../../../src/stores/SearchSelectionStore';

describe('EdgeColorStore', () => {
let entityStyleStore: EntityStyleStore;
let searchSelectionStore: SearchSelectionStore;
const dummies: EdgeDescriptor[] = [
{ id: 1, type: 'HELLO', from: 1, to: 1 },
{ id: 2, type: 'WORLD', from: 2, to: 2 },
Expand All @@ -11,7 +13,8 @@ describe('EdgeColorStore', () => {
];

beforeEach(() => {
entityStyleStore = new EntityStyleStore();
searchSelectionStore = new SearchSelectionStore();
entityStyleStore = new EntityStyleStore(searchSelectionStore);
});

it('should return colors for types', () => {
Expand Down
25 changes: 0 additions & 25 deletions frontend/cypress/unit/stores/colors/EntityColorStore.test.ts

This file was deleted.

101 changes: 101 additions & 0 deletions frontend/cypress/unit/stores/colors/EntityStyleStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
EdgeDescriptor,
NodeDescriptor,
} from '../../../../src/shared/entities';
import { EntityStyleStore } from '../../../../src/stores/colors';
import SearchSelectionStore, {
SelectedSearchResult,
} from '../../../../src/stores/SearchSelectionStore';

describe('EntityStyleStore', () => {
let entityStyleStore: EntityStyleStore;
let searchSelectionStore: SearchSelectionStore;
const entityDummies: (EdgeDescriptor | NodeDescriptor)[] = [
{ id: 1, type: 'EDGE', from: 1, to: 1 },
{ id: 2, type: 'EDGE', from: 2, to: 2 },
{ id: 1, types: ['NODE'] },
{ id: 2, types: ['NODE'] },
];

const searchSelectionDummies: SelectedSearchResult[] = [
{ id: 1, type: 'EDGE', from: 1, to: 1, interfaceType: 'EdgeDescriptor' },
{ id: 2, type: 'EDGE', from: 2, to: 2, interfaceType: 'EdgeDescriptor' },
{ id: 1, types: ['NODE'], interfaceType: 'NodeDescriptor' },
{ id: 2, types: ['NODE'], interfaceType: 'NodeDescriptor' },
{ name: 'EDGE', interfaceType: 'EdgeTypeDescriptor' },
{ name: 'NODE', interfaceType: 'NodeTypeDescriptor' },
];

beforeEach(() => {
searchSelectionStore = new SearchSelectionStore();
entityStyleStore = new EntityStyleStore(searchSelectionStore);
});

it('should return different colors for different entity types', () => {
const styleProvider = entityStyleStore.getValue();
const color1 = styleProvider.getStyle(entityDummies[0] as EdgeDescriptor);
const color2 = styleProvider.getStyle(entityDummies[2] as NodeDescriptor);

expect(color1).not.to.be.deep.eq(color2);
});

describe('change stroke width', () => {
it('should change stroke width for a single edge search selection update', () => {
searchSelectionStore.setState(searchSelectionDummies[0]);

const styleProvider = entityStyleStore.getValue();

const edgeStyle = styleProvider.getStyle(
entityDummies[0] as EdgeDescriptor
);

expect(edgeStyle.stroke.width).to.be.eq(5);
});

it('should change stroke width for a single node search selection update', () => {
searchSelectionStore.setState(searchSelectionDummies[2]);

const styleProvider = entityStyleStore.getValue();

const nodeStyle = styleProvider.getStyle(
entityDummies[2] as NodeDescriptor
);

expect(nodeStyle.stroke.width).to.be.eq(5);
});

it('should change stroke width for an edge type search selection update', () => {
searchSelectionStore.setState(searchSelectionDummies[4]);

const styleProvider = entityStyleStore.getValue();

const edgeStyle1 = styleProvider.getStyle(
entityDummies[0] as NodeDescriptor
);

const edgeStyle2 = styleProvider.getStyle(
entityDummies[1] as NodeDescriptor
);

expect(edgeStyle1.stroke.width).to.be.eq(5);
expect(edgeStyle2.stroke.width).to.be.eq(5);
});

it('should change stroke width for a node type search selection update', () => {
searchSelectionStore.setState(searchSelectionDummies[5]);

const styleProvider = entityStyleStore.getValue();

const nodeStyle1 = styleProvider.getStyle(
entityDummies[2] as NodeDescriptor
);

const nodeStyle2 = styleProvider.getStyle(
entityDummies[3] as NodeDescriptor
);

expect(nodeStyle1.stroke.width).to.be.eq(5);
expect(nodeStyle2.stroke.width).to.be.eq(5);
});
});
});
5 changes: 4 additions & 1 deletion frontend/cypress/unit/stores/colors/NodeColorStore.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { NodeDescriptor } from '../../../../src/shared/entities';
import { EntityStyleStore } from '../../../../src/stores/colors';
import SearchSelectionStore from '../../../../src/stores/SearchSelectionStore';

describe('NodeColorStore', () => {
let entityStyleStore: EntityStyleStore;
let searchSelectionStore: SearchSelectionStore;
const dummies: NodeDescriptor[] = [
{ id: 1, types: ['HELLO'] },
{ id: 2, types: ['WORLD'] },
{ id: 3, types: ['COMPOSED', 'KEY'] },
];

beforeEach(() => {
entityStyleStore = new EntityStyleStore();
searchSelectionStore = new SearchSelectionStore();
entityStyleStore = new EntityStyleStore(searchSelectionStore);
});

it('should return colors for types', () => {
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/configureServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,14 @@ export default function configureServices(container: Container): void {
container.bind(FilterStateStore).to(FilterStateStore).inSingletonScope();
container.bind(FilterQueryStore).to(FilterQueryStore).inSingletonScope();
container.bind(QueryResultStore).to(QueryResultStore).inSingletonScope();
container.bind(EntityStyleStore).to(EntityStyleStore).inSingletonScope();
container
.bind(EntityStyleStore)
.toDynamicValue(
(context) =>
new EntityStyleStore(context.container.get(SearchSelectionStore))
)
.inSingletonScope();

container.bind(ShortestPathStateStore).toSelf().inSingletonScope();
container.bind(ExplorationStore).toSelf().inSingletonScope();
container.bind(SearchSelectionStore).toSelf().inSingletonScope();
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/search/SearchEntryConverter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default function convertSearchResultToSearchResultList(
{formatEntry(n)}
</div>
),
entity: n,
entity: { ...n, interfaceType: 'NodeDescriptor' as const },
})),
},
{
Expand All @@ -67,7 +67,7 @@ export default function convertSearchResultToSearchResultList(
&nbsp;{formatEntry(e)}
</div>
),
entity: e,
entity: { ...e, interfaceType: 'EdgeDescriptor' as const },
})),
},
{
Expand All @@ -81,7 +81,7 @@ export default function convertSearchResultToSearchResultList(
color={styleProvider.getStyle({ id: -1, types: [t.name] }).color}
/>
),
entity: t,
entity: { ...t, interfaceType: 'NodeTypeDescriptor' as const },
})),
},
{
Expand All @@ -98,7 +98,7 @@ export default function convertSearchResultToSearchResultList(
}
/>
),
entity: t,
entity: { ...t, interfaceType: 'EdgeTypeDescriptor' as const },
})),
},
]
Expand Down
9 changes: 2 additions & 7 deletions frontend/src/search/SearchResultList.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import { EdgeDescriptor, NodeDescriptor } from '../shared/entities';
import { EdgeTypeDescriptor, NodeTypeDescriptor } from '../shared/schema';
import { SelectedSearchResult } from '../stores/SearchSelectionStore';

export default interface SearchResultList {
key: number | string;
header: string;
elements: {
key: number | string;
element: JSX.Element;
entity:
| EdgeDescriptor
| EdgeTypeDescriptor
| NodeDescriptor
| NodeTypeDescriptor;
entity: SelectedSearchResult;
}[];
}
29 changes: 22 additions & 7 deletions frontend/src/stores/SearchSelectionStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,34 @@ import SimpleStore from './SimpleStore';
import { EdgeDescriptor, NodeDescriptor } from '../shared/entities';
import { EdgeTypeDescriptor, NodeTypeDescriptor } from '../shared/schema';

// enables discriminated union types https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions
interface TypedEdgeDescriptor extends EdgeDescriptor {
interfaceType: 'EdgeDescriptor';
}
interface TypedEdgeTypeDescriptor extends EdgeTypeDescriptor {
interfaceType: 'EdgeTypeDescriptor';
}
interface TypedNodeDescriptor extends NodeDescriptor {
interfaceType: 'NodeDescriptor';
}
interface TypedNodeTypeDescriptor extends NodeTypeDescriptor {
interfaceType: 'NodeTypeDescriptor';
}

export type SelectedSearchResult =
| EdgeDescriptor
| EdgeTypeDescriptor
| NodeDescriptor
| NodeTypeDescriptor
| undefined;
| TypedEdgeDescriptor
| TypedEdgeTypeDescriptor
| TypedNodeDescriptor
| TypedNodeTypeDescriptor;

/**
* Contains the selected search result (if any).
* It's the search result that was clicked on in the search bar.
*/
export default class SearchSelectionStore extends SimpleStore<SelectedSearchResult> {
protected getInitialValue(): SelectedSearchResult {
export default class SearchSelectionStore extends SimpleStore<
SelectedSearchResult | undefined
> {
protected getInitialValue(): SelectedSearchResult | undefined {
return undefined;
}
}
1 change: 1 addition & 0 deletions frontend/src/stores/colors/EntityStyle.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
/**
* Described the stroke style of an entity.
*/
Expand Down
1 change: 1 addition & 0 deletions frontend/src/stores/colors/EntityStyleProvider.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import { NodeStyle, EdgeStyle } from './EntityStyle';
import { EdgeDescriptor, NodeDescriptor } from '../../shared/entities';

Expand Down
45 changes: 42 additions & 3 deletions frontend/src/stores/colors/EntityStyleProviderImpl.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'reflect-metadata';
import { common } from '@material-ui/core/colors';
import { NodeStyle, EdgeStyle } from './EntityStyle';
import { EdgeStyle, NodeStyle } from './EntityStyle';
import { EntityStyleProvider } from './EntityStyleProvider';
import getNthColor from './getNthColor';
import { EdgeDescriptor, NodeDescriptor } from '../../shared/entities';
import { ArgumentError } from '../../shared/errors';
import { QueryNodeResult, QueryEdgeResult } from '../../shared/queries';
import { QueryEdgeResult, QueryNodeResult } from '../../shared/queries';
import getTextColor from './getTextColor';
import EntityStyleStore from './EntityStyleStore';

Expand Down Expand Up @@ -36,7 +36,7 @@ export class EntityStyleProviderImpl implements EntityStyleProvider {
color: common.black,
text: { color: common.black },
stroke: {
width: 1,
width: this.isSelected(entity) ? 5 : 1,
dashes: false,
color: common.black,
},
Expand Down Expand Up @@ -121,6 +121,45 @@ export class EntityStyleProviderImpl implements EntityStyleProvider {
private isVirtual(entity: QueryEntityResult): boolean {
return entity.virtual === true;
}

/**
* Returns true if entity corresponds to the entity that is selected via search.
* @param entity - is compared with the selection
* @private
*/
private isSelected(entity: QueryEntityResult): boolean {
const selection = this.entityStyleStore.getEntitySelectionInfo();
if (selection === undefined) {
return false;
}

// check entity type
if (
!(
(selection.kind === 'EDGE' && isEdgeDescriptor(entity)) ||
(selection.kind === 'NODE' && isNodeDescriptor(entity))
)
) {
return false;
}

if ('id' in selection) {
// single entity selected => check if entity is that entity
return entity.id === selection.id;
}

if ('type' in selection) {
// type selected => check if entity is of that type
if (isNodeDescriptor(entity)) {
return entity.types.some((type) => type === selection.type);
}
if (isEdgeDescriptor(entity)) {
return entity.type === selection.type;
}
}

return false;
}
}

export default EntityStyleProviderImpl;
Loading

0 comments on commit 17e76ec

Please sign in to comment.