diff --git a/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.html b/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.html
index 46734ec3a..a1a27b3e8 100644
--- a/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.html
+++ b/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.html
@@ -3,13 +3,13 @@
class="mt-[-8px]"
[label]="label"
[hint]="hint"
- [value]="constraint.text ?? ''"
+ [value]="constraint_.text ?? ''"
(valueChange)="handleConstraintTextChange($event)"
>
-
+
@@ -18,9 +18,9 @@
diff --git a/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.spec.ts b/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.spec.ts
index 87b6df5d1..ef6377e2a 100644
--- a/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.spec.ts
+++ b/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.spec.ts
@@ -27,15 +27,18 @@ describe('ConstraintCardComponent', () => {
describe('showUrlInput', () => {
it('returns true if url is not nullish', () => {
- component.constraint.url = new URL('https://example.com/my-license.pdf')
- expect(component.showUrlInput).toBe(true)
+ component.constraint = {
+ text: 'abcd',
+ url: new URL('https://example.com/my-license.pdf'),
+ }
+ expect(component.showUrl).toBe(true)
})
it('returns true if showUrl button was clicked once', () => {
- component.showUrlBtnClicked = true
- expect(component.showUrlInput).toBe(true)
+ component.showUrl = true
+ expect(component.showUrl).toBe(true)
})
it('returns false otherwise', () => {
- expect(component.showUrlInput).toBe(false)
+ expect(component.showUrl).toBe(false)
})
})
})
diff --git a/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts b/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts
index 21b43d0c4..a723afb9d 100644
--- a/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts
+++ b/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts
@@ -44,25 +44,26 @@ import { iconoirPlus } from '@ng-icons/iconoir'
})
export class ConstraintCardComponent {
@Input() label: string
- @Input() constraint: Constraint
+ constraint_: Constraint
+ @Input() set constraint(v: Constraint) {
+ this.constraint_ = v
+ this.showUrl = this.showUrl || !!v.url
+ }
@Output() constraintChange = new EventEmitter()
hint = 'editor.record.form.constraint.markdown.placeholder' // TODO: get text and translate
- showUrlBtnClicked = false
- get showUrlInput() {
- return this.showUrlBtnClicked || !!this.constraint.url?.toString()
- }
+ showUrl = false
handleConstraintTextChange(text: string) {
this.constraintChange.emit({
- ...this.constraint,
+ ...this.constraint_,
text,
})
}
handleURLChange(url: string | null) {
this.constraintChange.emit({
- text: this.constraint.text,
+ text: this.constraint_.text,
...(url && { url: new URL(url) }),
})
}
diff --git a/libs/ui/inputs/src/lib/url-input/url-input.component.html b/libs/ui/inputs/src/lib/url-input/url-input.component.html
index 55e89c46d..5d31d70a4 100644
--- a/libs/ui/inputs/src/lib/url-input/url-input.component.html
+++ b/libs/ui/inputs/src/lib/url-input/url-input.component.html
@@ -4,7 +4,7 @@
class="gn-ui-text-input px-[var(--text-padding)]"
[ngClass]="extraClass"
type="url"
- [value]="value?.toString() ?? ''"
+ [value]="inputValue"
(input)="handleInput($event)"
(keydown.enter)="handleUpload(input)"
[placeholder]="placeholder"
diff --git a/libs/ui/inputs/src/lib/url-input/url-input.component.spec.ts b/libs/ui/inputs/src/lib/url-input/url-input.component.spec.ts
index f26351f14..722e8c6b5 100644
--- a/libs/ui/inputs/src/lib/url-input/url-input.component.spec.ts
+++ b/libs/ui/inputs/src/lib/url-input/url-input.component.spec.ts
@@ -120,24 +120,52 @@ describe('UrlInputComponent', () => {
it('is disabled if parent set it as disabled', () => {
component.disabled = true
inputEl.value = ''
+ inputEl.dispatchEvent(new Event('input'))
fixture.detectChanges()
expect(button.componentInstance.disabled).toBe(true)
})
it('is disabled if value is empty', () => {
inputEl.value = ''
+ inputEl.dispatchEvent(new Event('input'))
fixture.detectChanges()
expect(button.componentInstance.disabled).toBe(true)
})
it('is disabled if value is not an URL', () => {
inputEl.value = 'hello'
+ inputEl.dispatchEvent(new Event('input'))
fixture.detectChanges()
expect(button.componentInstance.disabled).toBe(true)
})
it('is not disabled otherwise', () => {
inputEl.value = 'http://hello.org'
+ inputEl.dispatchEvent(new Event('input'))
fixture.detectChanges()
expect(button.componentInstance.disabled).toBeFalsy()
})
})
+
+ describe('input value', () => {
+ it('changes if the component input resolves to a different url', () => {
+ inputEl.value = 'http://aaa.com/1234'
+ inputEl.dispatchEvent(new Event('input'))
+ component.value = 'http://aaa.com/bcd'
+ fixture.detectChanges()
+ expect(inputEl.value).toEqual('http://aaa.com/bcd')
+ })
+ it('does not change if the component input is different that the current value but resolves to the same url', () => {
+ inputEl.value = 'http://aaa.com/1234 5678'
+ inputEl.dispatchEvent(new Event('input'))
+ component.value = 'http://aaa.com/1234%205678'
+ fixture.detectChanges()
+ expect(inputEl.value).toEqual('http://aaa.com/1234 5678')
+ })
+ it('does not change if both the component input and the current input are not valid urls', () => {
+ inputEl.value = 'blargz'
+ inputEl.dispatchEvent(new Event('input'))
+ component.value = undefined
+ fixture.detectChanges()
+ expect(inputEl.value).toEqual('blargz')
+ })
+ })
})
})
diff --git a/libs/ui/inputs/src/lib/url-input/url-input.component.ts b/libs/ui/inputs/src/lib/url-input/url-input.component.ts
index c48585c16..d725c4bc1 100644
--- a/libs/ui/inputs/src/lib/url-input/url-input.component.ts
+++ b/libs/ui/inputs/src/lib/url-input/url-input.component.ts
@@ -1,4 +1,5 @@
import {
+ ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
@@ -26,9 +27,21 @@ import { iconoirArrowUp, iconoirLink } from '@ng-icons/iconoir'
size: '1.5em',
}),
],
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UrlInputComponent {
- @Input() value?: string
+ @Input() set value(v: string) {
+ // we're making sure to only update the input if the URL representation of it has changed; otherwise we keep it identical
+ // to avoid glitches when starting to write a URL and having some characters added/replaced automatically
+ if (!v || !this.isValidUrl(v)) return
+ if (
+ this.isValidUrl(this.inputValue) &&
+ new URL(v).toString() === new URL(this.inputValue).toString()
+ )
+ return
+ this.inputValue = v
+ this.cd.markForCheck()
+ }
@Input() extraClass = ''
@Input() placeholder = 'https://'
@Input() disabled: boolean
@@ -40,10 +53,13 @@ export class UrlInputComponent {
@Output() valueChange = new EventEmitter()
@Output() uploadClick = new EventEmitter()
+ inputValue = ''
+
constructor(private cd: ChangeDetectorRef) {}
handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value
+ this.inputValue = value
if (!value || !this.isValidUrl(value)) {
this.valueChange.next(null)
return