|
9 | 9 |
|
10 | 10 | import * as React from 'react';
|
11 | 11 | import ButtonPopover from '../../common/components/ButtonPopover';
|
12 |
| -import type {ErrorTarget} from '../externalLinks'; |
| 12 | +import type {ErrorT, LinkStateT} from '../externalLinks'; |
13 | 13 | import {ERROR_TARGETS} from '../URLCleanup';
|
14 | 14 |
|
15 | 15 | type PropsT = {
|
16 |
| - errorMessage: string, |
17 |
| - errorTarget: ErrorTarget, |
18 |
| - onCancel: () => void, |
19 |
| - onChange: (SyntheticEvent<HTMLInputElement>) => void, |
20 |
| - onToggle: (boolean) => void, |
21 |
| - rawUrl: string, |
22 |
| - url: string, |
| 16 | + cleanupUrl: (string) => string, |
| 17 | + link: LinkStateT, |
| 18 | + onConfirm: (string) => void, |
| 19 | + validateLink: (LinkStateT) => ErrorT | null, |
23 | 20 | };
|
24 | 21 |
|
25 | 22 | const URLInputPopover = (props: PropsT): React.MixedElement => {
|
26 | 23 | const popoverButtonRef = React.useRef(null);
|
27 |
| - const [isOpen, setIsOpen] = React.useState(false); |
| 24 | + const [isOpen, setIsOpen] = React.useState<boolean>(false); |
| 25 | + const [link, setLink] = React.useState<LinkStateT>(props.link); |
28 | 26 |
|
29 |
| - const toggle = (open) => { |
30 |
| - /* |
31 |
| - * Will be called by ButtonPopover when closed |
32 |
| - * either by losing focus or click 'Close' button |
33 |
| - */ |
| 27 | + React.useEffect(() => { |
| 28 | + setLink(props.link); |
| 29 | + }, [props.link]); |
| 30 | + |
| 31 | + const toggle = (open: boolean) => { |
| 32 | + // Will be called by ButtonPopover when closed by losing focus |
| 33 | + if (!open) { |
| 34 | + props.onConfirm(link.rawUrl); |
| 35 | + } |
34 | 36 | setIsOpen(open);
|
35 |
| - props.onToggle(open); |
36 | 37 | };
|
37 | 38 |
|
38 |
| - const handleCancel = () => { |
39 |
| - props.onCancel(); |
40 |
| - toggle(false); |
| 39 | + const handleUrlChange = (event: SyntheticEvent<HTMLInputElement>) => { |
| 40 | + const rawUrl = event.currentTarget.value; |
| 41 | + setLink({ |
| 42 | + ...link, |
| 43 | + rawUrl, |
| 44 | + url: props.cleanupUrl(rawUrl), |
| 45 | + }); |
| 46 | + }; |
| 47 | + |
| 48 | + const handleConfirm = (closeCallback: () => void) => { |
| 49 | + props.onConfirm(link.rawUrl); |
| 50 | + closeCallback(); |
41 | 51 | };
|
42 | 52 |
|
43 | 53 | const buildPopoverChildren = (
|
44 | 54 | closeAndReturnFocus,
|
45 |
| - ) => ( |
46 |
| - <form |
47 |
| - onSubmit={(event) => { |
48 |
| - event.preventDefault(); |
49 |
| - closeAndReturnFocus(); |
50 |
| - }} |
51 |
| - > |
52 |
| - <table> |
53 |
| - <tbody> |
54 |
| - <tr> |
55 |
| - <td className="section"> |
56 |
| - {addColonText(l('URL'))} |
57 |
| - </td> |
58 |
| - <td> |
59 |
| - <input |
60 |
| - className="value raw-url" |
61 |
| - onChange={props.onChange} |
62 |
| - style={{width: '336px'}} |
63 |
| - value={props.rawUrl} |
64 |
| - /> |
65 |
| - {props.errorMessage && |
66 |
| - props.errorTarget === ERROR_TARGETS.URL && |
67 |
| - <div |
68 |
| - className="error field-error target-url" |
69 |
| - data-visible="1" |
70 |
| - > |
71 |
| - {props.errorMessage} |
72 |
| - </div> |
73 |
| - } |
74 |
| - </td> |
75 |
| - </tr> |
76 |
| - {props.url && |
| 55 | + ) => { |
| 56 | + const error = props.validateLink(link); |
| 57 | + return ( |
| 58 | + <form |
| 59 | + onSubmit={(event) => { |
| 60 | + event.preventDefault(); |
| 61 | + handleConfirm(closeAndReturnFocus); |
| 62 | + }} |
| 63 | + > |
| 64 | + <table> |
| 65 | + <tbody> |
77 | 66 | <tr>
|
78 |
| - <td className="section" style={{whiteSpace: 'nowrap'}}> |
79 |
| - {addColonText(l('Cleaned up to'))} |
| 67 | + <td className="section"> |
| 68 | + {addColonText(l('URL'))} |
80 | 69 | </td>
|
81 | 70 | <td>
|
82 |
| - <a |
83 |
| - className="clean-url" |
84 |
| - href={props.url} |
85 |
| - rel="noreferrer" |
86 |
| - target="_blank" |
87 |
| - > |
88 |
| - {props.url} |
89 |
| - </a> |
| 71 | + <input |
| 72 | + className="value raw-url" |
| 73 | + onChange={handleUrlChange} |
| 74 | + style={{width: '336px'}} |
| 75 | + value={link.rawUrl} |
| 76 | + /> |
| 77 | + {error && |
| 78 | + error.target === ERROR_TARGETS.URL && |
| 79 | + <div |
| 80 | + className="error field-error target-url" |
| 81 | + data-visible="1" |
| 82 | + > |
| 83 | + {error.message} |
| 84 | + </div> |
| 85 | + } |
90 | 86 | </td>
|
91 | 87 | </tr>
|
92 |
| - } |
93 |
| - </tbody> |
94 |
| - </table> |
95 |
| - <div className="buttons" style={{display: 'block', marginTop: '1em'}}> |
96 |
| - <button |
97 |
| - className="negative" |
98 |
| - onClick={handleCancel} |
99 |
| - type="button" |
100 |
| - > |
101 |
| - {l('Cancel')} |
102 |
| - </button> |
103 |
| - <div |
104 |
| - className="buttons-right" |
105 |
| - style={{float: 'right', textAlign: 'right'}} |
106 |
| - > |
| 88 | + {link.url && |
| 89 | + <tr> |
| 90 | + <td className="section" style={{whiteSpace: 'nowrap'}}> |
| 91 | + {addColonText(l('Cleaned up to'))} |
| 92 | + </td> |
| 93 | + <td> |
| 94 | + {error ? link.url : ( |
| 95 | + <a |
| 96 | + className="clean-url" |
| 97 | + href={link.url} |
| 98 | + rel="noreferrer" |
| 99 | + target="_blank" |
| 100 | + > |
| 101 | + {link.url} |
| 102 | + </a>)} |
| 103 | + </td> |
| 104 | + </tr> |
| 105 | + } |
| 106 | + </tbody> |
| 107 | + </table> |
| 108 | + <div className="buttons" style={{display: 'block', marginTop: '1em'}}> |
107 | 109 | <button
|
108 |
| - className="positive" |
109 |
| - onClick={closeAndReturnFocus} |
| 110 | + className="negative" |
| 111 | + onClick={() => { |
| 112 | + // Reset input field value |
| 113 | + setLink(props.link); |
| 114 | + // Avoid calling toggle() otherwise changes will be saved |
| 115 | + setIsOpen(false); |
| 116 | + }} |
110 | 117 | type="button"
|
111 | 118 | >
|
112 |
| - {l('Done')} |
| 119 | + {l('Cancel')} |
113 | 120 | </button>
|
| 121 | + <div |
| 122 | + className="buttons-right" |
| 123 | + style={{float: 'right', textAlign: 'right'}} |
| 124 | + > |
| 125 | + <button |
| 126 | + className="positive" |
| 127 | + onClick={() => handleConfirm(closeAndReturnFocus)} |
| 128 | + type="button" |
| 129 | + > |
| 130 | + {l('Done')} |
| 131 | + </button> |
| 132 | + </div> |
114 | 133 | </div>
|
115 |
| - </div> |
116 |
| - </form> |
117 |
| - ); |
| 134 | + </form> |
| 135 | + ); |
| 136 | + }; |
118 | 137 |
|
119 | 138 | return (
|
120 | 139 | <ButtonPopover
|
|
0 commit comments