dce-reactkit 3.9.0-beta.1 → 3.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cjs/index.js +564 -257
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/Dropdown.d.ts +32 -0
  4. package/dist/cjs/types/components/Modal/ModalProps.d.ts +0 -2
  5. package/dist/cjs/types/helpers/validators/ChicagoTitleCase.d.ts +9 -0
  6. package/dist/cjs/types/helpers/validators/validURL.d.ts +8 -0
  7. package/dist/cjs/types/helpers/visitEndpointOnAnotherServer/index.d.ts +24 -0
  8. package/dist/cjs/types/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.d.ts +33 -0
  9. package/dist/cjs/types/index.d.ts +4 -1
  10. package/dist/cjs/types/types/DropdownItemType.d.ts +6 -0
  11. package/dist/cjs/types/types/ReactKitErrorCode.d.ts +4 -1
  12. package/dist/esm/index.js +562 -259
  13. package/dist/esm/index.js.map +1 -1
  14. package/dist/esm/types/components/Dropdown.d.ts +32 -0
  15. package/dist/esm/types/components/Modal/ModalProps.d.ts +0 -2
  16. package/dist/esm/types/helpers/validators/ChicagoTitleCase.d.ts +9 -0
  17. package/dist/esm/types/helpers/validators/validURL.d.ts +8 -0
  18. package/dist/esm/types/helpers/visitEndpointOnAnotherServer/index.d.ts +24 -0
  19. package/dist/esm/types/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.d.ts +33 -0
  20. package/dist/esm/types/index.d.ts +4 -1
  21. package/dist/esm/types/types/DropdownItemType.d.ts +6 -0
  22. package/dist/esm/types/types/ReactKitErrorCode.d.ts +4 -1
  23. package/dist/index.d.ts +107 -46
  24. package/package.json +2 -1
  25. package/src/components/Dropdown.tsx +317 -0
  26. package/src/components/Modal/ModalProps.ts +0 -4
  27. package/src/components/Modal/index.tsx +3 -39
  28. package/src/components/MultiSwitch.tsx +1 -1
  29. package/src/helpers/validators/ChicagoTitleCase.test.ts +85 -0
  30. package/src/helpers/validators/ChicagoTitleCase.ts +32 -0
  31. package/src/helpers/validators/findURL.test.ts +83 -0
  32. package/src/helpers/validators/findURL.ts +43 -0
  33. package/src/helpers/validators/validURL.test.ts +90 -0
  34. package/src/helpers/validators/validURL.ts +18 -0
  35. package/src/helpers/visitEndpointOnAnotherServer/index.ts +93 -0
  36. package/src/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.ts +164 -0
  37. package/src/index.ts +6 -0
  38. package/src/types/DropdownItemType.ts +11 -0
  39. package/src/types/ReactKitErrorCode.tsx +6 -1
@@ -0,0 +1,317 @@
1
+ /**
2
+ * A simple dropdown menu
3
+ * @author Alessandra De Lucas
4
+ * @author Yuen Ler Chow
5
+ * @author Gabe Abrams
6
+ */
7
+
8
+ // Import React
9
+ import React, { useEffect, useReducer, useRef } from 'react';
10
+
11
+ // Import helpers
12
+ import combineClassNames from '../helpers/combineClassNames';
13
+
14
+ // Import shared types
15
+ import Variant from '../types/Variant';
16
+ import DropdownItemType from '../types/DropdownItemType';
17
+
18
+ // Check dark mode
19
+ import { isDarkModeOn } from '../client/initClient';
20
+
21
+ /*------------------------------------------------------------------------*/
22
+ /* -------------------------------- Types ------------------------------- */
23
+ /*------------------------------------------------------------------------*/
24
+
25
+ // Props definition
26
+ type Props = {
27
+ // List of items in the dropdown menu
28
+ items: DropdownItem[],
29
+ // Information about the dropdown button
30
+ dropdownButton: {
31
+ // Aria label for accessibility
32
+ ariaLabel: string,
33
+ // Unique ID
34
+ id: string,
35
+ // Button content
36
+ content?: React.ReactNode,
37
+ // Variant
38
+ variant?: Variant,
39
+ },
40
+ // If true, no arrow shows in dropdown
41
+ // noArrow?: boolean,
42
+ // Size of dropdown
43
+ // size?: DropdownSize,
44
+ // Direction of dropdown
45
+ // direction?: DropdownDirection,
46
+ };
47
+
48
+ // Details about a dropdown item
49
+ type DropdownItem = (
50
+ | {
51
+ // Header element
52
+ type: DropdownItemType.Header,
53
+ // Header content
54
+ content: React.ReactNode,
55
+ }
56
+ | {
57
+ // Divider element
58
+ type: DropdownItemType.Divider,
59
+ }
60
+ | {
61
+ // Item from dropdown menu
62
+ type: DropdownItemType.Item,
63
+ // Item content
64
+ content: React.ReactNode,
65
+ // Item aria label
66
+ ariaLabel: string,
67
+ // Unique item ID
68
+ id: string,
69
+ // Click function for dropdown item
70
+ onClick: () => void,
71
+ }
72
+ );
73
+
74
+ // Dropdown menu sizes
75
+ // enum DropdownSize {
76
+ // // Small dropdown size
77
+ // Small = 'Small',
78
+ // // Medium dropdown size
79
+ // Medium = 'Medium',
80
+ // // Large dropdown size
81
+ // Large = 'Large',
82
+ // }
83
+
84
+ // Dropdown menu direction
85
+ // enum DropdownDirection {
86
+ // // Dropdown menu appears centered
87
+ // Centered = 'Centered',
88
+ // // Dropdown menu appears to right of button
89
+ // Right = 'Right',
90
+ // // Dropdown menu appears to left of button
91
+ // Left = 'Left',
92
+ // // Dropdown menu appears above the button
93
+ // Up = 'Up',
94
+ // // Dropdown menu appears below the button
95
+ // Down = 'Down',
96
+ // }
97
+
98
+ /*------------------------------------------------------------------------*/
99
+ /* -------------------------------- State ------------------------------- */
100
+ /*------------------------------------------------------------------------*/
101
+
102
+ /* -------- State Definition -------- */
103
+
104
+ type State = {
105
+ // If true, the dropdown menu is open
106
+ isDropdownOpen: boolean,
107
+ };
108
+
109
+ /* ------------- Actions ------------ */
110
+
111
+ // Types of actions
112
+ enum ActionType {
113
+ // Toggle opening the dropdown menu
114
+ ToggleDropdown = 'ToggleDropdown',
115
+ // Close the dropdown menu
116
+ CloseDropdown = 'CloseDropdown',
117
+ }
118
+
119
+ // Action definitions
120
+ type Action = (
121
+ | {
122
+ type: ActionType.ToggleDropdown,
123
+ }
124
+ | {
125
+ type: ActionType.CloseDropdown,
126
+ }
127
+ );
128
+
129
+ /**
130
+ * Reducer that executes actions
131
+ * @author Alessandra De Lucas
132
+ * @author Yuen Ler Chow
133
+ * @param state current state
134
+ * @param action action to execute
135
+ */
136
+ const reducer = (state: State, action: Action): State => {
137
+ switch (action.type) {
138
+ case ActionType.ToggleDropdown: {
139
+ return {
140
+ ...state,
141
+ isDropdownOpen: !state.isDropdownOpen,
142
+ };
143
+ }
144
+ case ActionType.CloseDropdown: {
145
+ return {
146
+ ...state,
147
+ isDropdownOpen: false,
148
+ };
149
+ }
150
+ default: {
151
+ return state;
152
+ }
153
+ }
154
+ };
155
+
156
+ /*------------------------------------------------------------------------*/
157
+ /* ------------------------------ Component ----------------------------- */
158
+ /*------------------------------------------------------------------------*/
159
+
160
+ const Dropdown: React.FC<Props> = (props) => {
161
+ /*------------------------------------------------------------------------*/
162
+ /* -------------------------------- Setup ------------------------------- */
163
+ /*------------------------------------------------------------------------*/
164
+
165
+ /* -------------- Props ------------- */
166
+
167
+ // Destructure all props
168
+ const {
169
+ dropdownButton,
170
+ items,
171
+ } = props;
172
+
173
+ /* -------------- State ------------- */
174
+
175
+ // Initial state
176
+ const initialState: State = {
177
+ isDropdownOpen: false,
178
+ };
179
+
180
+ // Initialize state
181
+ const [state, dispatch] = useReducer(reducer, initialState);
182
+
183
+ // Destructure common state
184
+ const {
185
+ isDropdownOpen,
186
+ } = state;
187
+
188
+ /* -------------- Refs -------------- */
189
+
190
+ // Initialize refs
191
+ const dropdownRef = useRef<HTMLDivElement>(null);
192
+
193
+ /*------------------------------------------------------------------------*/
194
+ /* ------------------------- Component Functions ------------------------ */
195
+ /*------------------------------------------------------------------------*/
196
+
197
+ /**
198
+ * Handle clicking outside of the dropdown menu to close it
199
+ * @author Yuen Ler Chow
200
+ * @param event the mouse event
201
+ */
202
+ const handleClickOutside = (event: MouseEvent) => {
203
+ if (
204
+ // Dropdown has been rendered
205
+ dropdownRef.current
206
+ // Click occurred outside the dropdown
207
+ && !dropdownRef.current.contains(event.target as Element)
208
+ ) {
209
+ dispatch({ type: ActionType.CloseDropdown });
210
+ }
211
+ };
212
+
213
+ /*------------------------------------------------------------------------*/
214
+ /* ------------------------- Lifecycle Functions ------------------------ */
215
+ /*------------------------------------------------------------------------*/
216
+
217
+ /**
218
+ * Mount
219
+ * @author Yuen Ler Chow
220
+ */
221
+ useEffect(() => {
222
+ // Add event listener to close dropdown when clicking outside
223
+ document.addEventListener('mousedown', handleClickOutside);
224
+
225
+ // Cleanup
226
+ return () => {
227
+ document.removeEventListener('mousedown', handleClickOutside);
228
+ };
229
+ }, []);
230
+
231
+ /*----------------------------------------*/
232
+ /* --------------- Main UI -------------- */
233
+ /*----------------------------------------*/
234
+
235
+ return (
236
+ <div
237
+ className="dropdown"
238
+ ref={dropdownRef}
239
+ data-bs-theme={isDarkModeOn() ? 'dark' : undefined}
240
+ >
241
+ <button
242
+ className={
243
+ combineClassNames([
244
+ 'btn dropdown-toggle border',
245
+ isDropdownOpen && 'show',
246
+ `btn-${dropdownButton.variant}`,
247
+ dropdownButton.variant === Variant.Light && 'text-dark',
248
+ ])
249
+ }
250
+ type="button"
251
+ id={dropdownButton.id}
252
+ aria-expanded={isDropdownOpen}
253
+ aria-label={dropdownButton.ariaLabel}
254
+ onClick={() => {
255
+ dispatch({
256
+ type: ActionType.ToggleDropdown,
257
+ });
258
+ }}
259
+ >
260
+ {
261
+ dropdownButton.content
262
+ }
263
+ </button>
264
+ <ul
265
+ className={
266
+ combineClassNames([
267
+ 'dropdown-menu',
268
+ isDarkModeOn() && 'dropdown-menu-dark',
269
+ isDropdownOpen && 'show',
270
+ ])
271
+ }
272
+ >
273
+ {Object.values(items).map((item) => {
274
+ if (item.type === DropdownItemType.Header) {
275
+ return (
276
+ // TODO: Implement header
277
+ <span />
278
+ );
279
+ }
280
+ if (item.type === DropdownItemType.Divider) {
281
+ return (
282
+ // TODO: Implement divider
283
+ <span />
284
+ );
285
+ }
286
+ return (
287
+ <li
288
+ key={item.id}
289
+ >
290
+ <button
291
+ type="button"
292
+ aria-label={item.ariaLabel}
293
+ className="dropdown-item"
294
+ onClick={(e) => {
295
+ e.preventDefault();
296
+ dispatch({
297
+ type: ActionType.CloseDropdown,
298
+ });
299
+ item.onClick();
300
+ }}
301
+ >
302
+ {item.content}
303
+ </button>
304
+ </li>
305
+ );
306
+ })}
307
+ </ul>
308
+ </div>
309
+ );
310
+ };
311
+
312
+ /*------------------------------------------------------------------------*/
313
+ /* ------------------------------- Wrap Up ------------------------------ */
314
+ /*------------------------------------------------------------------------*/
315
+
316
+ // Export component
317
+ export default Dropdown;
@@ -69,10 +69,6 @@ type ModalProps = {
69
69
  confirmVariant?: Variant,
70
70
  // True if modal should be on top of other modals
71
71
  onTopOfOtherModals?: boolean,
72
- // If true, the modal is loading
73
- isLoading?: boolean,
74
- // If true, the loading is cancelable
75
- isLoadingCancelable?: boolean,
76
72
  };
77
73
 
78
74
  export default ModalProps;
@@ -15,7 +15,6 @@ import ReactDOM from 'react-dom';
15
15
 
16
16
  // Import other components
17
17
  import waitMs from '../../helpers/waitMs';
18
- import LoadingSpinner from '../LoadingSpinner';
19
18
 
20
19
  // Import types
21
20
  import Variant from '../../types/Variant';
@@ -254,8 +253,6 @@ const Modal: React.FC<ModalProps> = (props) => {
254
253
  dontAllowBackdropExit,
255
254
  dontShowXButton,
256
255
  onTopOfOtherModals,
257
- isLoading,
258
- isLoadingCancelable,
259
256
  } = props;
260
257
 
261
258
  // Determine if no header either
@@ -266,7 +263,6 @@ const Modal: React.FC<ModalProps> = (props) => {
266
263
  // True if animation is in use
267
264
  const [animatingIn, setAnimatingIn] = useState(true);
268
265
  const [animatingPop, setAnimatingPop] = useState(false);
269
- const [showModal, setShowModal] = useState(false);
270
266
 
271
267
  /* -------------- Refs -------------- */
272
268
 
@@ -298,13 +294,6 @@ const Modal: React.FC<ModalProps> = (props) => {
298
294
  // Update to state after animated in
299
295
  if (mounted.current) {
300
296
  setAnimatingIn(false);
301
- if (isLoading) {
302
- // wait 1 second before showing modal
303
- await waitMs(1000);
304
- setShowModal(true);
305
- } else {
306
- setShowModal(true);
307
- }
308
297
  }
309
298
  })();
310
299
 
@@ -444,8 +433,6 @@ const Modal: React.FC<ModalProps> = (props) => {
444
433
  handleClose(ModalButtonType.Cancel);
445
434
  }}
446
435
  />
447
-
448
- { showModal && (
449
436
  <div
450
437
  className={`modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`}
451
438
  style={{
@@ -453,7 +440,7 @@ const Modal: React.FC<ModalProps> = (props) => {
453
440
  // Override sizes for even larger for XL
454
441
  width: (
455
442
  size === ModalSize.ExtraLarge
456
- ? 'calc(100vw - 3rem)'
443
+ ? 'calc(100vw - 1rem)'
457
444
  : undefined
458
445
  ),
459
446
  maxWidth: (
@@ -508,7 +495,7 @@ const Modal: React.FC<ModalProps> = (props) => {
508
495
  {title}
509
496
  </h5>
510
497
 
511
- {(onClose && !dontShowXButton && !(isLoading && !isLoadingCancelable)) && (
498
+ {(onClose && !dontShowXButton) && (
512
499
  <button
513
500
  type="button"
514
501
  className="Modal-x-button btn-close"
@@ -528,29 +515,7 @@ const Modal: React.FC<ModalProps> = (props) => {
528
515
  )}
529
516
  </div>
530
517
  )}
531
-
532
- {isLoading && (
533
- <div
534
- className="modal-body"
535
- style={{
536
- color: (
537
- isDarkModeOn()
538
- ? 'white'
539
- : undefined
540
- ),
541
- backgroundColor: (
542
- isDarkModeOn()
543
- ? '#444'
544
- : undefined
545
- ),
546
- }}
547
- >
548
- <LoadingSpinner />
549
- <span className="sr-only">Content loading</span>
550
- </div>
551
- )}
552
-
553
- {children && !isLoading && (
518
+ {children && (
554
519
  <div
555
520
  className="modal-body"
556
521
  style={{
@@ -595,7 +560,6 @@ const Modal: React.FC<ModalProps> = (props) => {
595
560
  )}
596
561
  </div>
597
562
  </div>
598
- )}
599
563
  </div>
600
564
  );
601
565
 
@@ -153,7 +153,7 @@ const MultiSwitch: React.FC<Props> = (props) => {
153
153
 
154
154
  // Constants
155
155
  const gutterRems = heightRem * 0.1;
156
- const itemWidthRems = heightRem * 1.3;
156
+ const itemWidthRems = heightRem * 2.2;
157
157
  const textFontSize = heightRem / 3.5;
158
158
  const iconFontSize = heightRem / 2;
159
159
  const borderWidthRems = 0.05;
@@ -0,0 +1,85 @@
1
+ // Importing the testing function
2
+ // import chicagoTitleCase from './chicagoTitleCase';
3
+
4
+ // // Import constants
5
+ // import { INVALID_STRING_ERRORS, INVALID_REGEX_ERROR } from './shared/constants/ERROR_MESSAGES';
6
+
7
+ // /*------------------------------------------------------------------------*/
8
+ // /* ---------------------------- Valid Tests --------------------------- */
9
+ // /*------------------------------------------------------------------------*/
10
+
11
+ // const validTests: { input: string, expected: string }[] = [
12
+ // {
13
+ // input: 'this is the american language',
14
+ // expected: 'This the American language',
15
+ // },
16
+ // {
17
+ // input: 'to kill a mockingbird',
18
+ // expected: 'To Kill a Mockingbird',
19
+ // },
20
+ // {
21
+ // input: 'the lord of the rings',
22
+ // expected: 'The Lord of the Rings',
23
+ // },
24
+ // {
25
+ // input: 'pride and prejudice',
26
+ // expected: 'Pride and Prejudice',
27
+ // },
28
+ // {
29
+ // input: 'the road not taken',
30
+ // expected: 'The Road Not Taken',
31
+ // },
32
+ // ];
33
+
34
+ // test(
35
+ // 'Converts strings to Chicago title case correctly.',
36
+ // async () => {
37
+ // validTests.forEach(({ input, expected }) => {
38
+ // expect(chicagoTitleCase(input)).toBe(expected);
39
+ // });
40
+ // },
41
+ // );
42
+
43
+ // /*------------------------------------------------------------------------*/
44
+ // /* ---------------------------- Edge Cases ---------------------------- */
45
+ // /*------------------------------------------------------------------------*/
46
+
47
+ // const edgeCases: { input: string, expected: string }[] = [
48
+ // {
49
+ // input: 'x',
50
+ // expected: 'X',
51
+ // },
52
+ // {
53
+ // input: ' ',
54
+ // expected: ' ',
55
+ // },
56
+ // {
57
+ // input: '',
58
+ // expected: '',
59
+ // },
60
+ // {
61
+ // input: 'the',
62
+ // expected: 'The',
63
+ // },
64
+ // {
65
+ // input: 'THE ROAD NOT TAKEN',
66
+ // expected: 'The Road Not Taken',
67
+ // },
68
+ // {
69
+ // input: 'tO kiLL A mOckingBirD',
70
+ // expected: 'To Kill a Mockingbird',
71
+ // },
72
+ // {
73
+ // input: 'pRiDe And pReduJiCe',
74
+ // expected: 'Pride and Prejudice',
75
+ // },
76
+ // ];
77
+
78
+ // test(
79
+ // 'Handles the edge cases for the function that converts a string to Chicago Title Case.',
80
+ // async () => {
81
+ // edgeCases.forEach(({ input, expected }) => {
82
+ // expect(chicagoTitleCase(input)).toBe(expected);
83
+ // });
84
+ // },
85
+ // );
@@ -0,0 +1,32 @@
1
+ /**
2
+ * This function converts a string to title case based on Chicago Title Case
3
+ * Style rules.
4
+ * @author Leisha Bhandari
5
+ * @param input: The input string that needs to be converted to Chicago title
6
+ * case.
7
+ * @returns Input string converted to title case
8
+ */
9
+
10
+
11
+ const chicagoTitleCase = (input: string): string => {
12
+ // List of lowercase words according to Chicago Manual of Style
13
+ const lowerCaseWords: string[] = [
14
+ 'in', 'nor', 'of', 'on', 'or', 'as', 'at', 'but', 'by',
15
+ 'for', 'if', 'so', 'the', 'a', 'an', 'and', 'to', 'up',
16
+ 'yet'
17
+ ];
18
+
19
+ return input
20
+ // Converts given input string to lowercase
21
+ .toLowerCase()
22
+ // Splits the given string into separate words
23
+ .split(' ')
24
+ .map((word, index, words) =>
25
+ // Capitalize the first word, last word, and the words that aren't
26
+ // in lowerCaseWords
27
+ index === 0 || !lowerCaseWords.includes(word) || index === words.length - 1
28
+ ? `${word.charAt(0).toUpperCase()}${word.slice(1)}` // Capitalize the first letter
29
+ : word // Nothing happens to the word
30
+ )
31
+ .join(' '); // Joins the words back
32
+ };
@@ -0,0 +1,83 @@
1
+ // Import the function to be tested
2
+ // import findURLs from './findURL';
3
+
4
+ // // Import constants
5
+ // import { INVALID_STRING_ERRORS, INVALID_REGEX_ERROR } from './shared/constants/ERROR_MESSAGES';
6
+
7
+ // /*------------------------------------------------------------------------*/
8
+ // /* ---------------------------- Valid Tests --------------------------- */
9
+ // /*------------------------------------------------------------------------*/
10
+
11
+ // const validTests: { block: string, expected: { start: number, end: number }[] }[] = [
12
+ // {
13
+ // block: 'Visit http://example.com for more information.',
14
+ // expected: [{ start: 6, end: 23 }],
15
+ // },
16
+ // {
17
+ // block: 'Multiple URLs: http://one.com and http://two.com.',
18
+ // expected: [
19
+ // { start: 15, end: 29 },
20
+ // { start: 34, end: 48 },
21
+ // ],
22
+ // },
23
+ // {
24
+ // block: 'Localhost URLs: http://localhost and http://127.0.0.1.',
25
+ // expected: [
26
+ // { start: 16, end: 31 },
27
+ // { start: 36, end: 52 },
28
+ // ],
29
+ // },
30
+ // {
31
+ // block: 'Embedded URL: text https://embedded-url.com?query=1&value=2.',
32
+ // expected: [{ start: 13, end: 51 }],
33
+ // },
34
+ // {
35
+ // block: 'Secure site: https://secure-site.com/path?query=string#fragment',
36
+ // expected: [{ start: 13, end: 58 }],
37
+ // },
38
+ // ];
39
+
40
+ // test(
41
+ // 'Returns correct start and end indices for valid URLs within a string.',
42
+ // async () => {
43
+ // validTests.forEach(({ block, expected }) => {
44
+ // expect(findURLs(block)).toStrictEqual(expected);
45
+ // });
46
+ // },
47
+ // );
48
+
49
+ // /*------------------------------------------------------------------------*/
50
+ // /* ---------------------------- Invalid Tests --------------------------- */
51
+ // /*------------------------------------------------------------------------*/
52
+
53
+ // const invalidTests: { block: string, expected: { start: number, end: number }[] }[] = [
54
+ // {
55
+ // block: 'No URL.',
56
+ // expected: [],
57
+ // },
58
+ // {
59
+ // block: 'URL format is wrong: http:/example.com and htt://example.com.',
60
+ // expected: [],
61
+ // },
62
+ // {
63
+ // block: 'Any punctuation after the URL http://example.com, should not count.',
64
+ // expected: [{ start: 20, end: 37 }],
65
+ // },
66
+ // {
67
+ // block: 'Starting punctuation ,http://example.com should not count.',
68
+ // expected: [{ start: 22, end: 39 }],
69
+ // },
70
+ // {
71
+ // block: 'Spaces in the middle http://exa mple.com is invalid.',
72
+ // expected: [],
73
+ // },
74
+ // ];
75
+
76
+ // test(
77
+ // 'Returns empty array for strings without valid URLs.',
78
+ // async () => {
79
+ // invalidTests.forEach(({ block, expected }) => {
80
+ // expect(findURLs(block)).toStrictEqual(expected);
81
+ // });
82
+ // },
83
+ // );
@@ -0,0 +1,43 @@
1
+ // /**
2
+ // * This function finds URLs within a given string and returns an array of
3
+ // * their locations.
4
+ // * @author Leisha Bhandari
5
+ // * @param block: The block of text to search for URLs
6
+ // * @returns Arrays where each of them contain the start and end index of the URL
7
+ // */
8
+ // function findURLs(block: string): { start: number, end: number }[] {
9
+ // // Expression representing the skeleton of a URL to help match and find the
10
+ // // URLs within the block of text (Also takes care of Unicode characters)
11
+ // const urlSkeleton = /(https?:\/\/[^\s\u0000-\u001F.,]+[^\s\u0000-\u001F.,]?(?:\?[^\s\u0000-\u001F]+)?(?:#[^\s\u0000-\u001F]+)?)/g;
12
+ // const found: { start: number, end: number }[] = [];
13
+ // let match: RegExpExecArray | null;
14
+
15
+ // // While loop to find the URLs in the given block of texts using the URL
16
+ // // skeleton
17
+ // while ((match === urlSkeleton.exec(block))
18
+ // && match !== null) {
19
+ // // To find the beginning index of the URL
20
+ // let start = match.index;
21
+ // // To find the last index of the URL
22
+ // let end = match.index + match[0].length;
23
+
24
+ // // If statements to check whether the URL is followed by some kind of
25
+ // // punctuation mark
26
+ // if (block.charAt(end) === '.' || /[,;:!?"]/.test(block.charAt(end))) {
27
+ // end--;
28
+ // }
29
+
30
+ // // While statement to make sure that the URL does not start with a
31
+ // // punctuation mark
32
+ // while (start > 0 && /[.,:;!?]/.test(block.charAt(start - 1))) {
33
+ // start--;
34
+ // }
35
+
36
+ // // The object with start and end index of the URL is found and pushed to
37
+ // // the found array
38
+ // found.push({ start, end });
39
+ // }
40
+
41
+ // // Returns the array of the found URLs location
42
+ // return found;
43
+ // }