Skip to content

Commit

Permalink
Link content validation errors with corresponding inputs (#6042)
Browse files Browse the repository at this point in the history
  • Loading branch information
acelaya authored Feb 9, 2024
1 parent f9ee2f6 commit a599a7d
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 17 deletions.
6 changes: 4 additions & 2 deletions lms/static/scripts/frontend_apps/components/BookSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Thumbnail,
} from '@hypothesis/frontend-shared';
import classNames from 'classnames';
import { useEffect, useRef, useState } from 'preact/hooks';
import { useEffect, useId, useRef, useState } from 'preact/hooks';

import type { Book } from '../api-types';
import { formatErrorMessage } from '../errors';
Expand Down Expand Up @@ -46,6 +46,7 @@ export default function BookSelector({
const vsService = useService(VitalSourceService);

const inputRef = useRef<HTMLInputElement | null>(null);
const errorId = useId();

// is a request in-flight via the vitalsource service?
const [isLoadingBook, setIsLoadingBook] = useState(false);
Expand Down Expand Up @@ -183,6 +184,7 @@ export default function BookSelector({
placeholder="e.g. https://bookshelf.vitalsource.com/#/books/012345678..."
readOnly={isLoadingBook}
spellcheck={false}
aria-describedby={errorId}
/>
<IconButton
icon={ArrowRightIcon}
Expand All @@ -199,7 +201,7 @@ export default function BookSelector({
)}

{error && (
<UIMessage status="error" data-testid="error-message">
<UIMessage status="error" data-testid="error-message" id={errorId}>
{error}
</UIMessage>
)}
Expand Down
4 changes: 2 additions & 2 deletions lms/static/scripts/frontend_apps/components/ErrorModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, ModalDialog } from '@hypothesis/frontend-shared';
import type { ModalDialogProps } from '@hypothesis/frontend-shared/lib/components/feedback/ModalDialog';
import type { PanelModalDialogProps } from '@hypothesis/frontend-shared/lib/components/feedback/ModalDialog';
import type { ComponentChildren } from 'preact';
import { useRef } from 'preact/hooks';

Expand Down Expand Up @@ -61,7 +61,7 @@ type ErrorModalBaseProps = {
};

/** `title` is optional for this component but required by `Modal` */
export type ErrorModalProps = Omit<ModalDialogProps, 'title'> &
export type ErrorModalProps = Omit<PanelModalDialogProps, 'title'> &
ErrorModalBaseProps;

/**
Expand Down
9 changes: 7 additions & 2 deletions lms/static/scripts/frontend_apps/components/JSTORPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Thumbnail,
} from '@hypothesis/frontend-shared';
import classnames from 'classnames';
import { useRef, useState } from 'preact/hooks';
import { useId, useRef, useState } from 'preact/hooks';

import type { JSTORMetadata, JSTORThumbnail } from '../api-types';
import { formatErrorMessage } from '../errors';
Expand Down Expand Up @@ -41,6 +41,7 @@ export default function JSTORPicker({
onSelectURL,
}: JSTORPickerProps) {
const [error, setError] = useState<string | null>(null);
const errorId = useId();

// Selected JSTOR article ID or DOI, updated when the user confirms what
// they have pasted/typed into the input field.
Expand Down Expand Up @@ -189,11 +190,13 @@ export default function JSTORPicker({
elementRef={inputRef}
id={inputId}
name="jstorURL"
feedback={renderedError ? 'error' : undefined}
onChange={() => onURLChange()}
onInput={() => resetCurrentArticle()}
onKeyDown={onKeyDown}
placeholder="e.g. https://www.jstor.org/stable/1234"
spellcheck={false}
aria-describedby={errorId}
/>
<IconButton
icon={ArrowRightIcon}
Expand Down Expand Up @@ -237,7 +240,9 @@ export default function JSTORPicker({
)}

{renderedError && (
<UIMessage status="error">{renderedError}</UIMessage>
<UIMessage status="error" id={errorId}>
{renderedError}
</UIMessage>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@hypothesis/frontend-shared';
import classnames from 'classnames';
import type { ComponentChildren, RefObject } from 'preact';
import { useId } from 'preact/hooks';

import { useUniqueId } from '../utils/hooks';
import UIMessage from './UIMessage';
Expand Down Expand Up @@ -52,6 +53,7 @@ export default function URLFormWithPreview({
}: URLFormWithPreviewProps) {
const orientation = thumbnail?.orientation ?? 'square';
const inputId = useUniqueId('url');
const errorId = useId();
const onChange = () => onURLChange(inputRef.current!.value);
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Enter') {
Expand Down Expand Up @@ -103,11 +105,13 @@ export default function URLFormWithPreview({
elementRef={inputRef}
id={inputId}
name="URL"
feedback={error ? 'error' : undefined}
onChange={onChange}
onKeyDown={onKeyDown}
onInput={onInput}
placeholder={urlPlaceholder}
spellcheck={false}
aria-describedby={errorId}
/>
<IconButton
icon={ArrowRightIcon}
Expand All @@ -120,7 +124,7 @@ export default function URLFormWithPreview({
{children}

{error && (
<UIMessage status="error" data-testid="error-message">
<UIMessage status="error" data-testid="error-message" id={errorId}>
{error}
</UIMessage>
)}
Expand Down
10 changes: 8 additions & 2 deletions lms/static/scripts/frontend_apps/components/URLPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, ModalDialog, Input } from '@hypothesis/frontend-shared';
import { useRef, useState } from 'preact/hooks';
import { useId, useRef, useState } from 'preact/hooks';

import { isYouTubeURL } from '../utils/youtube';
import UIMessage from './UIMessage';
Expand Down Expand Up @@ -31,6 +31,7 @@ export default function URLPicker({
// Holds an error message corresponding to client-side validation of the
// input field
const [error, setError] = useState<string | null>(null);
const errorId = useId();

const submit = (event: Event) => {
event.preventDefault();
Expand Down Expand Up @@ -99,12 +100,17 @@ export default function URLPicker({
name="url"
placeholder="e.g. https://example.com/article.pdf"
required
aria-describedby={errorId}
/>
</form>
{/** setting a height here "preserves space" for this error display
* and prevents the dialog size from jumping when an error is rendered */}
<div className="h-4 min-h-4">
{!!error && <UIMessage status="error">{error}</UIMessage>}
{!!error && (
<UIMessage status="error" id={errorId}>
{error}
</UIMessage>
)}
</div>
</div>
</ModalDialog>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@hypothesis/frontend-build": "^3.0.0",
"@hypothesis/frontend-shared": "^7.1.0",
"@hypothesis/frontend-shared": "^7.4.0",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
Expand Down
40 changes: 33 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1968,15 +1968,15 @@ __metadata:
languageName: node
linkType: hard

"@hypothesis/frontend-shared@npm:^7.1.0":
version: 7.1.0
resolution: "@hypothesis/frontend-shared@npm:7.1.0"
"@hypothesis/frontend-shared@npm:^7.4.0":
version: 7.4.0
resolution: "@hypothesis/frontend-shared@npm:7.4.0"
dependencies:
highlight.js: ^11.6.0
wouter-preact: ^2.10.0-alpha.1
wouter-preact: ^3.0.0
peerDependencies:
preact: ^10.4.0
checksum: 3dc7b2c82df68d7b17fa166c8ac78ad8317924129b241207ee5ae91a9e2688bcac5be7262735c98e8ec64aaa2654cdcfa4dd1476191b67b3bb2e8b96174339e2
checksum: 5744240cc37d0f2d30ff34f240cb95606c0f846fda8b52fba18d60ec5e402fde39629c3ff32593b36db3949a9556dc7c2f7985d2c281215d7877d87151d5b458
languageName: node
linkType: hard

Expand Down Expand Up @@ -7651,7 +7651,7 @@ __metadata:
"@babel/preset-react": ^7.23.3
"@babel/preset-typescript": ^7.23.3
"@hypothesis/frontend-build": ^3.0.0
"@hypothesis/frontend-shared": ^7.1.0
"@hypothesis/frontend-shared": ^7.4.0
"@hypothesis/frontend-testing": ^1.2.0
"@rollup/plugin-babel": ^6.0.4
"@rollup/plugin-commonjs": ^25.0.7
Expand Down Expand Up @@ -8177,6 +8177,13 @@ __metadata:
languageName: node
linkType: hard

"mitt@npm:^3.0.1":
version: 3.0.1
resolution: "mitt@npm:3.0.1"
checksum: b55a489ac9c2949ab166b7f060601d3b6d893a852515ae9eca4e11df01c013876df777ea109317622b5c1c60e8aae252558e33c8c94e14124db38f64a39614b1
languageName: node
linkType: hard

"mixin-deep@npm:^1.2.0":
version: 1.3.2
resolution: "mixin-deep@npm:1.3.2"
Expand Down Expand Up @@ -9563,6 +9570,13 @@ __metadata:
languageName: node
linkType: hard

"regexparam@npm:^3.0.0":
version: 3.0.0
resolution: "regexparam@npm:3.0.0"
checksum: c8649af1538ccc12b5c5d250525f61bd370227dce41f4fb908433a9651e18b7be21dd8f8518c322dd9ebd75f7caaaea4921e374c39a469c11d4f9d0c738043e0
languageName: node
linkType: hard

"regexpu-core@npm:^5.1.0":
version: 5.1.0
resolution: "regexpu-core@npm:5.1.0"
Expand Down Expand Up @@ -11523,7 +11537,7 @@ __metadata:
languageName: node
linkType: hard

"wouter-preact@npm:^2.10.0-alpha.1, wouter-preact@npm:^2.11.0":
"wouter-preact@npm:^2.11.0":
version: 2.11.0
resolution: "wouter-preact@npm:2.11.0"
peerDependencies:
Expand All @@ -11532,6 +11546,18 @@ __metadata:
languageName: node
linkType: hard

"wouter-preact@npm:^3.0.0":
version: 3.0.0
resolution: "wouter-preact@npm:3.0.0"
dependencies:
mitt: ^3.0.1
regexparam: ^3.0.0
peerDependencies:
preact: ^10.0.0
checksum: 253927b4dbf0906220f71f103f3934e649debc4a5f2c94c84e3097a9072c3c2d967b559421b626acb555437ab3aa416ed0faabedd2cf95fb3c61fdbb83370bca
languageName: node
linkType: hard

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0":
version: 7.0.0
resolution: "wrap-ansi@npm:7.0.0"
Expand Down

0 comments on commit a599a7d

Please sign in to comment.