Skip to content

Commit

Permalink
Validation and errors
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffdaley committed Jul 10, 2023
1 parent 426efff commit 9f84566
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 44 deletions.
10 changes: 1 addition & 9 deletions web/app/components/document/sidebar/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Component from "@glimmer/component";
import ConfigService from "hermes/services/config";
import { inject as service } from "@ember/service";
import { HermesDocument } from "hermes/types/document";
import isValidURL from "hermes/utils/is-valid-u-r-l";

interface DocumentSidebarHeaderComponentSignature {
Args: {
Expand All @@ -12,15 +13,6 @@ interface DocumentSidebarHeaderComponentSignature {
};
}

export function isValidURL(input: string) {
try {
new URL(input);
return true;
} catch {
return false;
}
}

export default class DocumentSidebarHeaderComponent extends Component<DocumentSidebarHeaderComponentSignature> {
@service("config") declare configSvc: ConfigService;

Expand Down
15 changes: 5 additions & 10 deletions web/app/components/document/sidebar/related-resources/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
RelatedHermesDocument,
} from "hermes/components/document/sidebar/related-resources";
import Ember from "ember";
import isValidURL from "hermes/utils/is-valid-u-r-l";

interface DocumentSidebarRelatedResourcesAddComponentSignature {
Element: null;
Expand Down Expand Up @@ -48,7 +49,6 @@ export default class DocumentSidebarRelatedResourcesAddComponent extends Compone

@tracked externalLinkTitle = "";


get noMatchesFound(): boolean {
const objectEntriesLengthIsZero =
Object.entries(this.args.shownDocuments).length === 0;
Expand Down Expand Up @@ -127,6 +127,7 @@ export default class DocumentSidebarRelatedResourcesAddComponent extends Compone
return link.url === externalLink.url;
});


if (isDuplicate) {
this.showDuplicateMessage();
} else {
Expand Down Expand Up @@ -218,15 +219,9 @@ export default class DocumentSidebarRelatedResourcesAddComponent extends Compone
});

protected checkURL = restartableTask(async () => {
const url = this.query;
try {
this.queryIsURL = Boolean(new URL(url));
} catch (e) {
this.queryIsURL = false;
} finally {
if (this.queryIsURL) {
void this.fetchURLInfo.perform();
}
this.queryIsURL = await isValidURL(this.query);
if (this.queryIsURL) {
void this.fetchURLInfo.perform();
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,22 @@
</div>
{{else}}
<div class="w-full" data-test-add-external-resource-form>
<form {{on "submit" @onSubmit}} class="mb-2.5">
<form {{on "submit" @onSubmit}} class="mb-3">
<Hds::Form::TextInput::Field
{{on "input" @onInput}}
@value={{@title}}
placeholder="Enter a title"
placeholder="Optional"
required={{true}}
class="external-resource-title-input"
as |F|
/>
>
<F.Label>Title</F.Label>
</Hds::Form::TextInput::Field>
</form>
<div class="external-resource-url">
<div
class="truncate"
data-test-add-external-resource-truncated-url
>{{@url}}</div>
<div class="truncate" data-test-add-external-resource-truncated-url>
{{@url}}
</div>
<div class="external-resource-favicon">
<FlightIcon @name={{get-link-icon @url}} />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
/>
</LinkTo>
{{else}}
{{log @resource}}
<ExternalLink
data-test-item-type="external-resource"
href={{this.externalResourceURL}}
Expand All @@ -41,6 +40,5 @@
@resource={{this.externalLink}}
@hideModal={{this.hideModal}}
@onSave={{this.saveChanges}}
@url={{this.externalResourceURL}}
/>
{{/if}}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
>
<M.Header>Edit resource</M.Header>
<M.Body>
<form {{did-insert this.registerForm}} class="px-6 pb-6" {{on "submit" this.onSave}}>
<form
{{did-insert this.registerForm}}
class="px-6 pb-6"
{{on "submit" (perform this.onSave)}}
>
<Hds::Form::TextInput::Field
{{on "input" this.updateFormValues}}
@value={{this.title}}
placeholder="Optional"
required={{true}}
name="title"
class="external-resource-title-input mb-4"
Expand All @@ -25,6 +30,11 @@
as |F|
>
<F.Label>URL</F.Label>
{{#if this.errorMessageIsShown}}
<F.Error class="mt-3">
A valid URL is required.
</F.Error>
{{/if}}
</Hds::Form::TextInput::Field>
{{! This adds enter-to-submit functionality }}
<input type="submit" class="hidden" />
Expand All @@ -37,13 +47,19 @@
@text="Save changes"
@color="primary"
{{! Should it show a flashMessage? }}
{{on "click" this.onSave}}
{{on "click" (perform this.onSave)}}
/>
<Hds::Button
@text="Cancel"
@color="secondary"
{{on "click" @hideModal}}
/>
<Hds::Button
@text="Delete"
@color="tertiary"
@icon="trash"
class="text-color-foreground-critical hover:text-color-foreground-critical-on-surface"
/>
</Hds::ButtonSet>
</M.Footer>
</Hds::Modal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { assert } from "@ember/debug";
import { action } from "@ember/object";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { restartableTask } from "ember-concurrency";
import { RelatedExternalLink } from "hermes/components/document/sidebar/related-resources";
import isValidURL from "hermes/utils/is-valid-u-r-l";

interface DocumentSidebarRelatedResourcesListItemEditComponentSignature {
Args: {
resource: RelatedExternalLink;
hideModal: () => void;
onSave: (resource: RelatedExternalLink) => void;
// Temporary workaround until this is an attribute of the resource
url: string;
};
Blocks: {
default: [];
Expand All @@ -20,11 +20,15 @@ interface DocumentSidebarRelatedResourcesListItemEditComponentSignature {
export default class DocumentSidebarRelatedResourcesListItemEditComponent extends Component<DocumentSidebarRelatedResourcesListItemEditComponentSignature> {
@tracked resource = this.args.resource;

@tracked url = this.args.url;
@tracked url = this.args.resource.url;
@tracked title = this.args.resource.title;

@tracked errorMessageIsShown = false;

@tracked _form: HTMLFormElement | null = null;

@tracked urlIsValid = false;

@action protected registerForm(form: HTMLFormElement): void {
this._form = form;
}
Expand All @@ -45,20 +49,34 @@ export default class DocumentSidebarRelatedResourcesListItemEditComponent extend

this.title = title;
this.url = url;

void this.validateURL.perform();
}

@action protected onSave(): void {
protected validateURL = restartableTask(async () => {
this.urlIsValid = await isValidURL(this.url);
this.errorMessageIsShown = !this.urlIsValid;
});

protected onSave = restartableTask(async (e: Event) => {
// prevent the form from submitting on enter
e.preventDefault();

let newResource = this.args.resource;
newResource.url = this.url;
newResource.title = this.title;

// TODO: validate fields
// if the title is empty, use a fallback (url, domain)
// validate that the url is a valid url
// if the url is invalid, show an error message and start eager validation
await this.validateURL.perform();

this.args.onSave(newResource);
}
if (this.urlIsValid) {
if (!this.args.resource.title.length) {
newResource.title = this.url;
}
this.args.onSave(newResource);
} else {
this.errorMessageIsShown = true;
}
});
}

declare module "@glint/environment-ember-loose/registry" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{{else}}
<FlightIcon
@name={{get-link-icon this.url}}
class={{if (eq (get-link-icon this.url) "external-link") "opacity-50"}}
class={{if (eq (get-link-icon this.url) "file") "opacity-40"}}
/>
{{/if}}
</div>
Expand All @@ -18,7 +18,7 @@
@tagName="h4"
class="text-body-200 text-color-foreground-strong font-medium"
>
{{@resource.title}}
{{this.title}}
</TruncatedText>
{{#if this.resourceIsDocument}}
<div class="text-color-foreground-faint text-body-100 mt-px">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { action } from "@ember/object";
import Component from "@glimmer/component";
import { RelatedHermesDocument, RelatedResource } from "../../related-resources";
import {
RelatedHermesDocument,
RelatedResource,
} from "../../related-resources";
import { assert } from "@ember/debug";

interface DocumentSidebarRelatedResourcesListItemResourceComponentSignature {
Expand All @@ -25,6 +28,13 @@ export default class DocumentSidebarRelatedResourcesListItemResourceComponent ex
return this.args.resource.documentNumber;
}

protected get title() {
if ("url" in this.args.resource) {
return this.args.resource.title || this.url;
}
return this.args.resource.title;
}

assertResourceIsDocument(
document: RelatedResource
): asserts document is RelatedHermesDocument {
Expand Down
2 changes: 1 addition & 1 deletion web/app/helpers/get-link-icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const getLinkIconHelper = helper<GetLinkIconSignature>(([url]) => {
}
}
}
return "external-link";
return "file";
});

export default getLinkIconHelper;
Expand Down
4 changes: 4 additions & 0 deletions web/app/styles/components/document/related-resources.scss
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@

.external-resource-title-input {
@apply text-display-300 font-semibold h-11 px-2.5;

&::placeholder {
@apply opacity-50;
}
}

.external-resource-url {
Expand Down
8 changes: 8 additions & 0 deletions web/app/utils/is-valid-u-r-l.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default async function isValidURL(string: string): Promise<boolean> {
try {
new URL(string);
return true;
} catch {
return false;
}
}

0 comments on commit 9f84566

Please sign in to comment.