Skip to content

Commit

Permalink
Merge pull request #935 from griffithlab/tag-input-multi
Browse files Browse the repository at this point in the history
multi-tag field display & behavior improved
  • Loading branch information
jmcmichael authored Dec 1, 2023
2 parents bcf0c30 + e1f1981 commit 0992e0f
Show file tree
Hide file tree
Showing 18 changed files with 494 additions and 386 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { CvcPipesModule } from '@app/core/pipes/pipes.module';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyNzFormFieldModule } from '@ngx-formly/ng-zorro-antd/form-field';
import { NzFormModule } from 'ng-zorro-antd/form';
import { NzIconModule } from 'ng-zorro-antd/icon';
import { NzInputModule } from 'ng-zorro-antd/input';
Expand All @@ -18,7 +17,6 @@ import { CvcEnumSelectComponent } from './enum-select.component';
CommonModule,
ReactiveFormsModule,
FormlyModule.forChild(),
FormlyNzFormFieldModule,
NzIconModule,
NzFormModule,
NzSelectModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const formFieldConfig: FormlyFieldConfig[] = [
type: 'gene-select',
props: {
description: 'Enter an Entrez Gene for this Variant',
required: true,
},
},
{
Expand Down Expand Up @@ -78,20 +79,21 @@ const formFieldConfig: FormlyFieldConfig[] = [
type: 'clinvar-multi-input',
wrappers: ['form-field'],
props: {
label: "ClinVar IDs",
description: 'Specify if Clinvar IDs exist, or if they are not applicable for this variant.'
}
label: 'ClinVar IDs',
description:
'Specify if Clinvar IDs exist, or if they are not applicable for this variant.',
},
},
{
key: 'variantTypeIds',
type: 'variant-type-multi-select',
wrappers: ['form-field']
wrappers: ['form-field'],
},
{
template: "<h4><u>Primary (5') Coordinates</u></h4>",
props: {
colSpan: 24
}
colSpan: 24,
},
},
{
key: 'referenceBuild',
Expand All @@ -103,13 +105,15 @@ const formFieldConfig: FormlyFieldConfig[] = [
wrappers: ['form-field'],
validators: {
nccnVersionNumber: {
expression: (c: AbstractControl) => c.value ? /^\d{2,3}$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) => `"${field.formControl?.value}" does not appear to be an Ensembl version number`,
expression: (c: AbstractControl) =>
c.value ? /^\d{2,3}$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) =>
`"${field.formControl?.value}" does not appear to be an Ensembl version number`,
},
},
props: {
label: 'Ensembl Version',
description: "Enter a valid Ensembl database version (e.g. 75)"
description: 'Enter a valid Ensembl database version (e.g. 75)',
},
},
{
Expand All @@ -118,13 +122,16 @@ const formFieldConfig: FormlyFieldConfig[] = [
wrappers: ['form-field'],
validators: {
nccnVersionNumber: {
expression: (c: AbstractControl) => c.value ? /^[ACTG\\]+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) => `"${field.formControl?.value}" contains invalid characters.`,
expression: (c: AbstractControl) =>
c.value ? /^[ACTG\\]+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) =>
`"${field.formControl?.value}" contains invalid characters.`,
},
},
props: {
label: 'Reference Bases',
description: "The nucleotide(s) of the reference genome affected by the variant. Only used for SNVs and Indels (otherwise leave blank)"
description:
'The nucleotide(s) of the reference genome affected by the variant. Only used for SNVs and Indels (otherwise leave blank)',
},
},
{
Expand All @@ -133,13 +140,16 @@ const formFieldConfig: FormlyFieldConfig[] = [
wrappers: ['form-field'],
validators: {
nccnVersionNumber: {
expression: (c: AbstractControl) => c.value ? /^[ACTG\\]+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) => `"${field.formControl?.value}" contains invalid characters.`,
expression: (c: AbstractControl) =>
c.value ? /^[ACTG\\]+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) =>
`"${field.formControl?.value}" contains invalid characters.`,
},
},
props: {
label: 'Variant Bases',
description: "The nucleotide(s) of the variant allele. Only used for SNVs and Indels (otherwise leave blank)"
description:
'The nucleotide(s) of the variant allele. Only used for SNVs and Indels (otherwise leave blank)',
},
},
{
Expand All @@ -149,22 +159,26 @@ const formFieldConfig: FormlyFieldConfig[] = [
props: {
label: 'Chromosome',
options: Chromosomes,
description: 'Specify the chromosome in which this variant occurs (e.g. 17).'
}
description:
'Specify the chromosome in which this variant occurs (e.g. 17).',
},
},
{
key: 'start',
type: 'input',
wrappers: ['form-field'],
validators: {
isNumeric: {
expression: (c: AbstractControl) => c.value ? /^\d+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) => 'Start coordinate must be numeric',
expression: (c: AbstractControl) =>
c.value ? /^\d+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) =>
'Start coordinate must be numeric',
},
},
props: {
label: 'Start',
description: "Enter the left/first coordinate of this variant. Must be ≤ the Stop coordinate. Coordinate must be compatible with the selected reference build."
description:
'Enter the left/first coordinate of this variant. Must be ≤ the Stop coordinate. Coordinate must be compatible with the selected reference build.',
},
},
{
Expand All @@ -173,13 +187,16 @@ const formFieldConfig: FormlyFieldConfig[] = [
wrappers: ['form-field'],
validators: {
isNumeric: {
expression: (c: AbstractControl) => c.value ? /^\d+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) => 'Stop coordinate must be numeric',
expression: (c: AbstractControl) =>
c.value ? /^\d+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) =>
'Stop coordinate must be numeric',
},
},
props: {
label: 'Stop',
description: "Provide the right/second coordinate of this variant. Must be ≥ the Start coordinate. Coordinate must be compatible with the selected reference build."
description:
'Provide the right/second coordinate of this variant. Must be ≥ the Start coordinate. Coordinate must be compatible with the selected reference build.',
},
},
{
Expand All @@ -188,14 +205,15 @@ const formFieldConfig: FormlyFieldConfig[] = [
wrappers: ['form-field'],
props: {
label: 'Representative Transcript',
description: "Specify a transcript ID, including version number (e.g. ENST00000348159.4, the canonical transcript defined by Ensembl)."
description:
'Specify a transcript ID, including version number (e.g. ENST00000348159.4, the canonical transcript defined by Ensembl).',
},
},
{
template: "<h4><u>Secondary (3') Coordinates</u></h4>",
props: {
colSpan: 24
}
colSpan: 24,
},
},
{
key: 'chromosome2',
Expand All @@ -204,22 +222,26 @@ const formFieldConfig: FormlyFieldConfig[] = [
props: {
label: 'Chromosome',
options: Chromosomes,
description: 'If this variant is a fusion (e.g. BCR-ABL1), specify the chromosome name, coordinates, and representative transcript for the 3-prime partner.'
}
description:
'If this variant is a fusion (e.g. BCR-ABL1), specify the chromosome name, coordinates, and representative transcript for the 3-prime partner.',
},
},
{
key: 'start2',
type: 'input',
wrappers: ['form-field'],
validators: {
isNumeric: {
expression: (c: AbstractControl) => c.value ? /^\d+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) => 'Start coordinate must be numeric',
expression: (c: AbstractControl) =>
c.value ? /^\d+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) =>
'Start coordinate must be numeric',
},
},
props: {
label: 'Start',
description: "Enter the left/first coordinate of this 3-prime partner fusion variant. Must be ≤ the Stop coordinate. Coordinate must be compatible with the selected reference build."
description:
'Enter the left/first coordinate of this 3-prime partner fusion variant. Must be ≤ the Stop coordinate. Coordinate must be compatible with the selected reference build.',
},
},
{
Expand All @@ -228,13 +250,16 @@ const formFieldConfig: FormlyFieldConfig[] = [
wrappers: ['form-field'],
validators: {
isNumeric: {
expression: (c: AbstractControl) => c.value ? /^\d+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) => 'Stop coordinate must be numeric',
expression: (c: AbstractControl) =>
c.value ? /^\d+$/.test(c.value) : true,
message: (_: any, field: FormlyFieldConfig) =>
'Stop coordinate must be numeric',
},
},
props: {
label: 'Stop',
description: "Provide the right/second coordinate of this 3-prime partner fusion variant. Must be ≥ the Start coordinate. Coordinate must be compatible with the selected reference build."
description:
'Provide the right/second coordinate of this 3-prime partner fusion variant. Must be ≥ the Start coordinate. Coordinate must be compatible with the selected reference build.',
},
},
{
Expand All @@ -243,7 +268,8 @@ const formFieldConfig: FormlyFieldConfig[] = [
wrappers: ['form-field'],
props: {
label: 'Representative Transcript',
description: "Specify a transcript ID, including version number (e.g. ENST00000348159.4, the canonical transcript defined by Ensembl)."
description:
'Specify a transcript ID, including version number (e.g. ENST00000348159.4, the canonical transcript defined by Ensembl).',
},
},
],
Expand All @@ -262,7 +288,7 @@ const formFieldConfig: FormlyFieldConfig[] = [
},
},
{
type: 'cvc-cancel-button'
type: 'cvc-cancel-button',
},
{
key: 'organizationId',
Expand All @@ -277,7 +303,4 @@ const formFieldConfig: FormlyFieldConfig[] = [
},
]
export const variantReviseFields: FormlyFieldConfig[] =
assignFieldConfigDefaultValues(
formFieldConfig,
variantReviseFormInitialModel
)
assignFieldConfigDefaultValues(formFieldConfig, variantReviseFormInitialModel)
12 changes: 11 additions & 1 deletion client/src/app/forms/mixins/base/base-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BaseState } from '@app/forms/states/base.state'
import { Maybe } from '@app/generated/civic.apollo'
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'
import { FieldType, FieldTypeConfig } from '@ngx-formly/core'
import { BehaviorSubject, filter, map, Observable } from 'rxjs'
import { BehaviorSubject, filter, first, map, Observable } from 'rxjs'
import { pluck } from 'rxjs-etc/operators'
import { tag } from 'rxjs-spy/operators'

Expand Down Expand Up @@ -56,6 +56,9 @@ export function BaseFieldType<
this.onValueChange$ = new BehaviorSubject<Maybe<V>>(undefined)
}

// // DEBUG
// // uncomment next line to limit logging to a single field
// if(this.field.key === 'evidenceDirection')
// this.onValueChange$
// .pipe(tag(`${this.field.key} base-field onValueChange$`))
// .subscribe()
Expand All @@ -65,6 +68,12 @@ export function BaseFieldType<
this.onValueChange$.next(v)
})

// trigger markAsTouched once for defined values, to trigger
// field validation for revise forms' pre-populated model values
this.onValueChange$.pipe(first()).subscribe((v) => {
if (v !== undefined) this.formControl.markAsTouched()
})

if (
this.field.options?.formState &&
this.field.options.formState.fields
Expand All @@ -81,6 +90,7 @@ export function BaseFieldType<
this.field.key
)
}

// it is assumed entity state field names are
// field key string + '$', e.g. field key 'geneId'
// will emit value changes from state.field.geneId$
Expand Down
15 changes: 0 additions & 15 deletions client/src/app/forms/mixins/string-input-field.mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,14 @@ export function StringTagField<TBase extends MixinConstructor<FieldType>>(
) {
@Injectable()
abstract class StringTagFieldMixin extends Base {
// BASE FIELD TYPE SOURCE STREAMS
onValueChange$?: Subject<Maybe<string | number>>

// LOCAL SOURCE STREAMS
onTagClose$!: Subject<MouseEvent> // emits on entity tag closed btn click

// LOCAL PRESENTATION STREAMS
tagLabel$!: Subject<Maybe<string>> // emits label for tag

configureStringTagField(): void {
if (!this.onValueChange$) {
console.error(
`${this.field.id} cannot find onValueChange$ Subject, ensure configureBaseField() has been called before configureDisplayStringTag in its AfterViewInit hook.`
)
return
}

this.tagLabel$ = new Subject<Maybe<string>>()
this.onValueChange$
.pipe(untilDestroyed(this))
.subscribe((str: Maybe<string | number>) => {
this.tagLabel$.next(str ? str.toString() : undefined)
})

this.onTagClose$ = new Subject<MouseEvent>()

Expand Down
48 changes: 24 additions & 24 deletions client/src/app/forms/test-pages/layout-tests/layout-forms.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,28 +191,28 @@ export const noStateFormsFieldConfig: FormlyFieldConfig[] = [
},
],
},
// {
// wrappers: ['field-grid'],
// props: <CvcFieldGridWrapperConfig>{
// grid: {
// cols: 2,
// },
// },
// fieldGroup: [
// <CvcBaseInputFieldOptions>{
// key: 'tag',
// type: 'tag-input',
// props: {
// label: 'Input Test',
// },
// },
// <CvcBaseInputFieldOptions>{
// key: 'tags',
// type: 'tag-multi-input',
// props: {
// label: 'Input Multi Test',
// },
// },
// ],
// },
{
wrappers: ['field-grid'],
props: <CvcFieldGridWrapperConfig>{
grid: {
cols: 2,
},
},
fieldGroup: [
<CvcBaseInputFieldOptions>{
key: 'tag',
type: 'tag-input',
props: {
label: 'Input Test',
},
},
<CvcBaseInputFieldOptions>{
key: 'tags',
type: 'tag-multi-input',
props: {
label: 'Input Multi Test',
},
},
],
},
]
Loading

0 comments on commit 0992e0f

Please sign in to comment.