Skip to content
Permalink
Browse files
feat(hint): display hint about fulltext search (DEV-901) (#734)
* docs(data): update documentation

* docs(results): display search syntax hint

* docs(search): display search syntax hint

* feat(hint): new component to display hints

* style: resolve responsive style issues

* feat(search): display fulltext search hint

* style(hint): resolve highlight style

* style(search): resolve responsive design issue
  • Loading branch information
kilchenmann committed May 12, 2022
1 parent 404f1f6 commit f54dafc4498a3bec43049a32bd9d91cc2bce6572
Show file tree
Hide file tree
Showing 16 changed files with 198 additions and 34 deletions.
@@ -35,14 +35,15 @@ Full text search performs queries including one or more terms or phrases, and re

![Filter your full text search by specific project](../assets/images/search-fulltext-filterByProject.png)*<https://admin.dasch.swiss> - Search 1: Full text search*

When clicking on the search bar, the search history panel is displayed. The **10** last searches are registered. It is also possible to clear the search history list (*Clear list* button at the bottom of the panel).
When clicking on the search bar, the search history panel is displayed. The **10** last searches are registered. It is also possible to clear the search history list (*Clear list* button at the bottom of the panel or the *x* at the end of each line).

![Search history panel](../assets/images/search-history.png)*Search history list is accessible for the full text search from any webpage.*

Special syntax:

- asterisk* can be used as a wildcard symbol
- "quotation marks" searches for the whole pattern
- question mark? can be used as wildcard symbol for a single character.
- asterisk* can be used as a wildcard symbol for zero, one or multiple characters.
- "quotation marks" searches for the whole pattern.

---

@@ -54,6 +55,7 @@ The advanced search allows for the creation of complex queries using a graphical
The widget's contents are then turned into a string representing a Gravsearch (SPARQL) query to be sent to DSP-API.

A query consists of the following elements:

- data model selection
- selection of a resource class belonging to the selected data model (optional)
- specification of properties, comparison operators, and values (optional).
@@ -106,14 +108,14 @@ This search finds "Jacob" as well as "Jakob".

Used with a linking property, the `matches` operator lets the user search for a linked resource that matches the specified properties.
In this example, the user writes a query looking for all letters that have an author that:

1. was born after January 1st 1650
2. whose family name is "Bernoulli"
1. whose family name is "Bernoulli"

This is different from the "is equal to" operator that lets the user specify a certain person (selected from a list).

![Advanced search panel with a search example](../assets/images/advanced-search-linked-resource.png)


---

### Expert search
@@ -166,7 +168,7 @@ Each column of the table corresponds to one metadata.
Once you have found the desired sources, you can (re)view them and annotate the source itself, the media file, or single metadata values. If you select more than one source, you can compare them in a side-by-side view, link them, edit them all at once, or save them in a collection. A collection is similar to a playlist in a music app or shopping basket in an online store.

### Display a source
&#9888; *WORK IN PROGRESS*
<!-- &#9888; *WORK IN PROGRESS* -->

The DSP-APP offers different source views for different media types. There's a viewer for still images, moving images, audio and document files. You can open them from the list of search results. Depending on the media type, DSP-APP offers different tools to work on the source.

@@ -187,17 +189,17 @@ Additionally, you can work on the source directly, e.g, transcribe a moving imag
---

### Select more than one source
&#9888; *NOT YET IMPLEMENTED*
<!-- &#9888; *NOT YET IMPLEMENTED* -->

![Three sources selected; what do you want to do with them?](../assets/images/source-selected-three.png)*Three sources are selected; what do you want to do with them?*

By selecting more than one source, you will be able to edit them all at once, add them to a collection, share or connect them.
By selecting more than one source, you will be able to edit them all at once, add them to a collection, share or connect them.
Or you could compare the sources (see [Compare the sources](/user-guide/data/#compare-the-sources)).

---

### Compare the sources
&#9888; *NOT YET IMPLEMENTED*
<!-- &#9888; *NOT YET IMPLEMENTED* -->

You will be able to compare from two to six source objects at the same time side by side.

@@ -206,7 +208,7 @@ You will be able to compare from two to six source objects at the same time side
---

### Annotate and connect your data (sources and/or metadata)
&#9888; *NOT YET IMPLEMENTED*
<!-- &#9888; *NOT YET IMPLEMENTED* -->

A main feature of the flexible data storage that DSP-APP uses is the possibility to annotate and link sources and their metadata. An annotation can be a small note about a date like "Not sure about the birthdate of this person. There's another date mentioned in the source XYZ". Inside the note, it will be possible to link to another source.

@@ -159,6 +159,7 @@ import { SearchSelectOntologyComponent } from './workspace/search/advanced-searc
import { ExpertSearchComponent } from './workspace/search/expert-search/expert-search.component';
import { FulltextSearchComponent } from './workspace/search/fulltext-search/fulltext-search.component';
import { SearchPanelComponent } from './workspace/search/search-panel/search-panel.component';
import { HintComponent } from './main/action/hint/hint.component';

// translate: AoT requires an exported function for factories
export function httpLoaderFactory(httpClient: HttpClient) {
@@ -304,7 +305,8 @@ export function httpLoaderFactory(httpClient: HttpClient) {
UsersComponent,
UsersListComponent,
VideoComponent,
VideoPreviewComponent
VideoPreviewComponent,
HintComponent
],
imports: [
AngularSplitModule.forRoot(),
@@ -0,0 +1,5 @@
<div class="hint" [innerHtml]="content"></div>

<a mat-button [href]="documentation" target="_blank" color="primary" class="external-url">
Read more in the user guide<mat-icon class="suffix">launch</mat-icon>
</a>
@@ -0,0 +1,7 @@
a {
margin: 16px auto;

.mat-icon.suffix {
margin-left: 16px;
}
}
@@ -0,0 +1,31 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatIconModule } from '@angular/material/icon';

import { HintComponent } from './hint.component';

describe('HintComponent', () => {
let component: HintComponent;
let fixture: ComponentFixture<HintComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
HintComponent
],
imports: [
MatIconModule
]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(HintComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,52 @@
import { Component, Input, OnInit } from '@angular/core';

@Component({
selector: 'app-hint',
templateUrl: './hint.component.html',
styleUrls: ['./hint.component.scss']
})
export class HintComponent implements OnInit {

@Input() topic: string;

content: string;

documentation: string;

constructor() { }

ngOnInit(): void {
this.content = this._getHint(this.topic);
}

/**
* get correct hint depending on the topic
* @param topic
* @returns hint as html
*
* todo: the hint should be written in markdown in different languages. Maybe stored in docs.
*/
private _getHint(topic: string): string {
switch(topic) {
case 'search':
this.documentation = 'https://docs.dasch.swiss/DSP-APP/user-guide/data/#search-and-browse';
return `<p>Use special syntax:</p>
<ul>
<li>question mark<strong>?</strong> can be used as wildcard symbol for a single character.<br>
<code class="">Example: <i>be?r</i> will find <i>beer</i> but also <i>bear</i></code>
</li><br>
<li>asterisk<strong>*</strong> can be used as a wildcard symbol for zero, one or multiple characters.<br>
<code class="">Example: <i>b*r</i> will find <i>beer</i> but also <i>bear</i></code>
</li><br>
<li><strong>"</strong>quotation marks<strong>"</strong> searches for the whole pattern.<br>
<code class="">Example: <i>"Lorem ipsum"</i> will find texts with exact content <i>Lorem ipsum</i></code>
</li>
</ul>`;
break;

default:
return `There's no hint implemented for the topic <strong>${topic}<strong>.`;
}
}

}
@@ -53,7 +53,7 @@
a {
margin: 0 auto;

.mat-icon {
.mat-icon.suffix {
margin-left: 16px;
}
}
@@ -55,6 +55,8 @@
<li>Try more general keywords.</li>
<li>Try fewer keywords.</li>
</ul>
<mat-divider></mat-divider>
<app-hint [topic]="'search'"></app-hint>
</div>

</div>
@@ -32,10 +32,6 @@ button.active {
background-color: $black-12-opacity;
}

.no-results {
margin: 64px;
}

.link:hover {
background-color: $black-12-opacity;
}
@@ -35,7 +35,6 @@

<!-- full-text search menu -->
<ng-template #fulltextSearchMenu>

<div class="app-search-menu" [class.with-project-filter]="projectfilter">
<div class="app-menu-content">
<mat-list class="app-previous-search-list">
@@ -103,8 +102,7 @@ <h4 mat-line (click)="doPrevSearch(item)" class="app-previous-search-item">
<input #fulltextSearchInputMobile class="app-fulltext-search-input" type="search" [(ngModel)]="searchQuery"
name="fulltext-search" minlength="3" autocomplete="off" [placeholder]="'Search'" (click)="setFocus()"
(keyup.esc)="resetSearch()" (keyup.enter)="doSearch()">
<button mat-button color="primary" class="app-fulltext-search-button"
(click)="doSearch()" type="submit">
<button mat-button color="primary" class="app-fulltext-search-button" (click)="doSearch()" type="submit">
Search
</button>
</div>
@@ -7,14 +7,26 @@
</app-fulltext-search>

<!-- Do not display advanced and expert search on mobile devices smaller than tablet -->
<div class="advanced-expert-buttons">
<div class="advanced-expert-buttons" [class.with-project-filter]="projectfilter">
<button mat-flat-button class="hint" [class.active]="showHint"
(click)="openPanelWithBackdrop('hint')">
<mat-icon matPrefix>lightbulb</mat-icon>
<span class="desktop-only">How to search</span>
</button>
<span class="fill-remaining-space"></span>
<!-- advanced search button: if advanced === true -->
<button mat-flat-button *ngIf="advanced" [class.active]="showAdvanced"
(click)="openPanelWithBackdrop('advanced')">advanced</button>
(click)="openPanelWithBackdrop('advanced')">
<mat-icon matPrefix>manage_search</mat-icon>
<span class="desktop-only">advanced</span>
</button>

<!-- expert search button: if expert === true -->
<button mat-flat-button *ngIf="expert" [class.active]="showExpert"
(click)="openPanelWithBackdrop('expert')">expert</button>
(click)="openPanelWithBackdrop('expert')">
<mat-icon matPrefix>school</mat-icon>
<span class="desktop-only">expert</span>
</button>
</div>

</div>
@@ -24,8 +36,11 @@
<div class="app-search-menu with-advanced-search" [class.with-project-filter]="projectfilter">
<div class="app-menu-header">
<span class="app-menu-title">
<h4 *ngIf="showAdvanced">Advanced search</h4>
<h4 *ngIf="!showAdvanced">Expert search</h4>
<h4 [ngSwitch]="true">
<span *ngSwitchCase="showAdvanced">Advanced search</span>
<span *ngSwitchCase="showExpert">Expert search</span>
<span *ngSwitchCase="showHint">How to search</span>
</h4>
</span>
<span class="fill-remaining-space"></span>
<span class="app-menu-close">
@@ -34,9 +49,10 @@ <h4 *ngIf="!showAdvanced">Expert search</h4>
</button>
</span>
</div>
<div class="app-menu-content">
<app-advanced-search *ngIf="showAdvanced" [limitToProject]="limitToProject" (search)="emitSearch($event)"></app-advanced-search>
<app-expert-search *ngIf="!showAdvanced" (search)="emitSearch($event)"></app-expert-search>
<div class="app-menu-content" [ngSwitch]="true">
<app-advanced-search *ngSwitchCase="showAdvanced" [limitToProject]="limitToProject" (search)="emitSearch($event)"></app-advanced-search>
<app-expert-search *ngSwitchCase="showExpert" (search)="emitSearch($event)"></app-expert-search>
<app-hint *ngSwitchCase="showHint" [topic]="'search'"></app-hint>
</div>
</div>
</ng-template>
@@ -5,8 +5,8 @@
height: 72px;

.advanced-expert-buttons {
display: table;
height: 20px;
display: flex;
height: 24px;
margin-left: auto;

button {
@@ -18,10 +18,26 @@
border-top-right-radius: 0;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
padding-top: 4px;

&.active {
background-color: $black-12-opacity;
}

&.hint {
color: $accent_200;

&.active {
color: $dark;
}
}

.mat-icon {
margin-top: -2px;
font-size: 18px;
width: 18px;
height: 18px;
}
}
}

@@ -61,6 +61,7 @@ export class SearchPanelComponent implements OnInit {
// show advanced or expert search
showAdvanced: boolean;
showExpert: boolean;
showHint: boolean;

constructor(
private _overlay: Overlay,
@@ -75,6 +76,7 @@ export class SearchPanelComponent implements OnInit {

this.showAdvanced = (type === 'advanced');
this.showExpert = (type === 'expert');
this.showHint = (type === 'hint');

const config = new OverlayConfig({
hasBackdrop: true,
@@ -88,6 +90,7 @@ export class SearchPanelComponent implements OnInit {
this.overlayRef.backdropClick().subscribe(() => {
this.showAdvanced = false;
this.showExpert = false;
this.showHint = false;
this.overlayRef.detach();
});
}
@@ -124,6 +127,7 @@ export class SearchPanelComponent implements OnInit {
closeMenu(): void {
this.showAdvanced = false;
this.showExpert = false;
this.showHint = false;
if (this.overlayRef) {
this.overlayRef.detach();
}
@@ -396,8 +396,10 @@ a,
}

.no-results {
margin: 64px auto;
width: 400px;
margin: 64px;
min-width: 400px;
max-width: calc(70vw - 128px);
line-height: 1.5;
}

// --------------------------------------
@@ -43,7 +43,7 @@ $grid-breakpoints: (
}
.advanced-expert-buttons {
font-size: smaller !important;
margin-top: -6px !important;
margin-top: -2px !important;
}
}

0 comments on commit f54dafc

Please sign in to comment.