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

Resolve all frontend linting issues #1356

Merged
merged 3 commits into from
Jan 4, 2024
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
76 changes: 36 additions & 40 deletions frontend/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,42 @@
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "ia",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"prefix": "ia"
}
],
"@typescript-eslint/member-ordering": "warn",
"@typescript-eslint/naming-convention": "warn",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/explicit-member-accessibility": [
"off",
{
"accessibility": "explicit"
}
],
"brace-style": [
"error",
"1tbs"
],
"id-blacklist": "off",
"id-match": "off",
"no-underscore-dangle": "off",
"valid-typeof": "error",
"quotes": [
"warn",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
]
"@angular-eslint/no-output-on-prefix": "off",
"@angular-eslint/component-selector": [
"error",
{
"type": ["element", "attribute"],
"prefix": "ia",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"prefix": "ia"
}
],
"@typescript-eslint/member-ordering": "warn",
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "objectLiteralProperty",
"format": ["camelCase", "snake_case", "PascalCase"],
"leadingUnderscore": "allow"
}
],
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/dot-notation": "off",
"id-blacklist": "off",
"no-underscore-dangle": "off",
"quotes": [
"warn",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
]
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { AuthService } from './services/auth.service';
import { environment } from '../environments/environment';

@Component({
selector: 'app-root',
selector: 'ia-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
public iframe: boolean
public iframe: boolean;

constructor(private authService: AuthService) {
this.authService.setInitialAuth();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<tr *ngIf="document.relevance">
<th>Relevance</th>
<td>
<search-relevance [value]="document.relevance"></search-relevance>
<ia-search-relevance [value]="document.relevance"></ia-search-relevance>
</td>
</tr>
<tr>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/document-view/document-view.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export class DocumentViewComponent implements OnChanges {
let highlighted = this.document.fieldValues[field.name];
if (this.document.highlight && this.document.highlight.hasOwnProperty(field.name) &&
this.selectedFieldsContain(field)) { // only apply highlights to selected search fields
for (let highlight of this.document.highlight[field.name]) {
for (const highlight of this.document.highlight[field.name]) {
const stripped_highlight = this.stripTags(highlight);
highlighted = highlighted.replace(stripped_highlight, highlight);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<div class="relevance-score">
<span>Relevance: {{document.relevance * 100 | number:'1.0-0' }}%</span>
</div>
<search-relevance [value]="document.relevance"></search-relevance>
<ia-search-relevance [value]="document.relevance"></ia-search-relevance>
</ng-container>
<button *ngIf="document.fieldValues.image_path" class="button scan-button is-primary is-inverted"
iaBalloon="View Scan" aria-label="view scan"
Expand Down
99 changes: 59 additions & 40 deletions frontend/src/app/download/download.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { Corpus, CorpusField, DownloadOptions, PendingDownload, QueryModel } fro
const highlightFragmentSize = 50;

@Component({
selector: 'ia-download',
templateUrl: './download.component.html',
styleUrls: ['./download.component.scss']
selector: 'ia-download',
templateUrl: './download.component.html',
styleUrls: ['./download.component.scss'],
})
export class DownloadComponent implements OnChanges {
@Input() public corpus: Corpus;
Expand All @@ -31,13 +31,18 @@ export class DownloadComponent implements OnChanges {

private downloadsPageLink = {
text: 'view downloads',
route: ['/download-history']
route: ['/download-history'],
};

constructor(private downloadService: DownloadService, private notificationService: NotificationService) { }
constructor(
private downloadService: DownloadService,
private notificationService: NotificationService
) {}

ngOnChanges() {
this.availableCsvFields = Object.values(this.corpus.fields).filter(field => field.downloadable);
this.availableCsvFields = Object.values(this.corpus.fields).filter(
(field) => field.downloadable
);
const highlight = this.queryModel.highlightSize;
// 'Query in context' becomes an extra option if any field in the corpus has been marked as highlightable
if (highlight !== undefined) {
Expand All @@ -54,7 +59,7 @@ export class DownloadComponent implements OnChanges {
downloadable: true,
filterOptions: null,
mappingType: null,
} as unknown as CorpusField) ;
} as unknown as CorpusField);
}
}

Expand All @@ -70,50 +75,64 @@ export class DownloadComponent implements OnChanges {
}
}

/** results can be downloaded directly: show menu to pick file options */
private directDownload() {
this.pendingDownload = { download_type: 'search_results' };
}

/** download short file directly */
public confirmDirectDownload(options: DownloadOptions) {
this.isDownloading = true;
this.downloadService.download(
this.corpus, this.queryModel, this.getCsvFields(), this.resultsCount, this.route, highlightFragmentSize, options
).catch( error => {
this.notificationService.showMessage(error);
}).then(() => {
this.isDownloading = false;
this.pendingDownload = undefined;
}
);
}

/** start backend task to create csv file */
private longDownload() {
this.downloadService.downloadTask(
this.corpus, this.queryModel, this.getCsvFields(), this.route, highlightFragmentSize
).then( results => {
this.notificationService.showMessage(
'Downloading CSV file... A link will be sent to your email address shortly.', 'success',
this.downloadsPageLink,
);
}).catch( error => {
this.notificationService.showMessage(error, 'danger');
});
this.downloadService
.download(
this.corpus,
this.queryModel,
this.getCsvFields(),
this.resultsCount,
this.route,
highlightFragmentSize,
options
)
.catch((error) => {
this.notificationService.showMessage(error);
})
.then(() => {
this.isDownloading = false;
this.pendingDownload = undefined;
});
}

public selectCsvFields(selection: CorpusField[]) {
this.selectedCsvFields = selection;
}

/** results can be downloaded directly: show menu to pick file options */
private directDownload() {
this.pendingDownload = { download_type: 'search_results' };
}

/** start backend task to create csv file */
private longDownload() {
this.downloadService
.downloadTask(
this.corpus,
this.queryModel,
this.getCsvFields(),
this.route,
highlightFragmentSize
)
.then((results) => {
this.notificationService.showMessage(
'Downloading CSV file... A link will be sent to your email address shortly.',
'success',
this.downloadsPageLink
);
})
.catch((error) => {
this.notificationService.showMessage(error, 'danger');
});
}

private getCsvFields(): CorpusField[] {
if (this.selectedCsvFields === undefined) {
return this.corpus.fields.filter(field => field.csvCore);
return this.corpus.fields.filter((field) => field.csvCore);
} else {
return this.selectedCsvFields;
}
return this.selectedCsvFields;
}
}


}
54 changes: 32 additions & 22 deletions frontend/src/app/dropdown/dropdown.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { Component, ElementRef, EventEmitter, HostListener, Input, Output, OnDestroy } from '@angular/core';
import {
Component,
ElementRef,
EventEmitter,
HostListener,
Input,
Output,
OnDestroy,
HostBinding,
} from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import {debounceTime} from 'rxjs/operators';
import { debounceTime } from 'rxjs/operators';
import * as _ from 'lodash';


@Component({
selector: 'ia-dropdown',
templateUrl: './dropdown.component.html',
styleUrls: ['./dropdown.component.scss'],
host: { class: 'control' }
})
export class DropdownComponent<T> implements OnDestroy {
@HostBinding('class') classes = 'control';
@Input()
public canDeselect = false;

Expand Down Expand Up @@ -47,23 +55,9 @@ export class DropdownComponent<T> implements OnDestroy {
constructor(private elementRef: ElementRef) {
// don't trigger a lot of events when a user is quickly looping through the options
// for example using the keyboard arrows
this.changeSubscription = this.changeSubject.pipe(debounceTime(100)).subscribe(value => this.onChange.next(value));
}

ngOnDestroy() {
this.changeSubscription.unsubscribe();
}

public toggleDropdown() {
this.showDropdown = !this.showDropdown;
}

public select(option: T | undefined, hide = true) {
this.value = option;
if (hide) {
this.showDropdown = false;
}
this.changeSubject.next(option);
this.changeSubscription = this.changeSubject
.pipe(debounceTime(100))
.subscribe((value) => this.onChange.next(value));
}

@HostListener('document:click', ['$event'])
Expand Down Expand Up @@ -98,9 +92,25 @@ export class DropdownComponent<T> implements OnDestroy {
}
}
}

ngOnDestroy() {
this.changeSubscription.unsubscribe();
}

public toggleDropdown() {
this.showDropdown = !this.showDropdown;
}

public select(option: T | undefined, hide = true) {
this.value = option;
if (hide) {
this.showDropdown = false;
}
this.changeSubject.next(option);
}
}

enum KeyCode {
Up = 38,
Down = 40
Down = 40,
}
23 changes: 12 additions & 11 deletions frontend/src/app/history/search-history/search-history.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,32 @@ import { findByName } from '../../utils/utils';
import { faLink } from '@fortawesome/free-solid-svg-icons';

@Component({
selector: 'search-history',
selector: 'ia-search-history',
templateUrl: './search-history.component.html',
styleUrls: ['./search-history.component.scss']
styleUrls: ['./search-history.component.scss'],
})
export class SearchHistoryComponent extends HistoryDirective implements OnInit {
public queries: QueryDb[];
public displayCorpora = false;
public linkIcon = faLink;
constructor(
corpusService: CorpusService,
private queryService: QueryService,
private router: Router,
private router: Router
) {
super(corpusService);
}

linkIcon = faLink;

async ngOnInit() {
this.retrieveCorpora();
this.queryService.retrieveQueries().then(
searchHistory => {
const sortedQueries = this.sortByDate(searchHistory);
// not using _.sortedUniqBy as sorting and filtering takes place w/ different aspects
this.queries = _.uniqBy(sortedQueries, query => query.query_json).map(this.addQueryModel.bind(this));
});
this.queryService.retrieveQueries().then((searchHistory) => {
const sortedQueries = this.sortByDate(searchHistory);
// not using _.sortedUniqBy as sorting and filtering takes place w/ different aspects
this.queries = _.uniqBy(
sortedQueries,
(query) => query.query_json
).map(this.addQueryModel.bind(this));
});
}

addQueryModel(query?: QueryDb) {
Expand Down
Loading
Loading