Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search highlight #327

Merged
merged 12 commits into from
Jun 22, 2021
Merged
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
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