Skip to content

Commit 78e650d

Browse files
committed
Added ScrollUtils as well as CollectionUtils methods
1 parent a389b78 commit 78e650d

6 files changed

Lines changed: 158 additions & 0 deletions

File tree

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export { KeyValuePair } from "./interfaces/key-value-pair";
3838
export { PagedResult } from "./interfaces/paged-result";
3939
export { Result } from "./interfaces/result";
4040
export { ResultError } from "./interfaces/result-error";
41+
export { ScrollOptions } from "./interfaces/scroll-options";
4142
export { ServiceResponse } from "./interfaces/service-response";
4243

4344
// #endregion Interfaces
@@ -60,6 +61,7 @@ export { EnvironmentUtils } from "./utilities/environment-utils";
6061
export { LocalizationUtils } from "./utilities/localization-utils";
6162
export { PromiseFactory } from "./utilities/promise-factory";
6263
export { RouteUtils } from "./utilities/route-utils";
64+
export { ScrollUtils } from "./utilities/scroll-utils";
6365
export { ServiceUtils } from "./utilities/service-utils";
6466
export { StringUtils } from "./utilities/string-utils";
6567

src/interfaces/scroll-options.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface ScrollOptions extends ScrollIntoViewOptions {
2+
initialDelay?: number;
3+
}

src/utilities/collection-utils.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,45 @@ describe("CollectionUtils", () => {
386386
});
387387
});
388388

389+
describe("#removeElementAt", () => {
390+
it("When index i < 0, returns source array", () => {
391+
// Arrange
392+
const arr = ["one", "two", "three"];
393+
const expected = [...arr];
394+
395+
// Act
396+
const result = CollectionUtils.removeElementAt(arr, -1);
397+
398+
// Assert
399+
expect(result).toStrictEqual(expected);
400+
});
401+
402+
it("When index > array.length, returns source array", () => {
403+
// Arrange
404+
const arr = ["one", "two", "three"];
405+
const expected = [...arr];
406+
407+
// Act
408+
const result = CollectionUtils.removeElementAt(arr, 50);
409+
410+
// Assert
411+
expect(result).toStrictEqual(expected);
412+
});
413+
414+
it("When index is in range, then removes element at index", () => {
415+
// Arrange
416+
const arr = ["one", "two", "three"];
417+
const expected = ["one", "three"];
418+
const indexToRemove = 1;
419+
420+
// Act
421+
const result = CollectionUtils.removeElementAt(arr, indexToRemove);
422+
423+
// Assert
424+
expect(result).toStrictEqual(expected);
425+
});
426+
});
427+
389428
describe("#replaceElementAt", () => {
390429
it("Replaces element at specified index and returns a new array", () => {
391430
// Arrange

src/utilities/collection-utils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,21 @@ const _length = (arr: Array<any> | List<any>): number => {
138138
return (arr as Array<any>).length;
139139
};
140140

141+
/**
142+
* Removes a supplied element by index
143+
* @param source original array
144+
* @param index array index to remove
145+
*/
146+
const _removeElementAt = <T>(source: Array<T>, index: number): Array<T> => {
147+
if (index < 0 || index > source.length) {
148+
return source;
149+
}
150+
151+
const newArr = [...source];
152+
newArr.splice(index, 1);
153+
return newArr;
154+
};
155+
141156
/**
142157
* Returns a NEW array with the element at the specified index
143158
* replaced with the specified value. Since it returns a new array,
@@ -181,6 +196,7 @@ export const CollectionUtils = {
181196
isEmpty: _isEmpty,
182197
isNotEmpty: _isNotEmpty,
183198
length: _length,
199+
removeElementAt: _removeElementAt,
184200
replaceElementAt: _replaceElementAt,
185201
sample: _.sample,
186202
sampleSize: _.sampleSize,

src/utilities/scroll-utils.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
describe("ScrollUtils", () => {
2+
test.skip("TOOD: Backfill tests for issue https://github.com/AndcultureCode/AndcultureCode.JavaScript.Core/issues/26", () => {});
3+
});

src/utilities/scroll-utils.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { EnvironmentUtils } from "./environment-utils";
2+
import { ScrollOptions } from "../interfaces/scroll-options";
3+
import { StringUtils } from "./string-utils";
4+
5+
// -----------------------------------------------------------------------------------------
6+
// #region Constants
7+
// -----------------------------------------------------------------------------------------
8+
9+
export const DefaultScrollOptions: ScrollOptions = {
10+
behavior: "auto",
11+
block: "start",
12+
inline: "nearest",
13+
};
14+
15+
// #endregion Constants
16+
17+
// -----------------------------------------------------------------------------------------
18+
// #region Functions
19+
// -----------------------------------------------------------------------------------------
20+
21+
/**
22+
* Attempts to scroll to the element specified by the given ID.
23+
* In the event of a slow page render, the element may not be immediately available.
24+
* This method will retry up to 50 times every 100ms to find the element before
25+
* giving up.
26+
*/
27+
const _scrollToElementById = (
28+
id: string,
29+
options: ScrollOptions = DefaultScrollOptions
30+
) => {
31+
let retryCount = 0;
32+
const tryToScroll = () => {
33+
retryCount += 1;
34+
35+
if (retryCount > 50) {
36+
EnvironmentUtils.runIfDevelopment(() =>
37+
console.warn(
38+
`Could not find element with ID ${id} in the page.`
39+
)
40+
);
41+
42+
// couldn't find element in 50 loops, give up.
43+
return;
44+
}
45+
46+
const el = document.getElementById(id);
47+
if (el != null) {
48+
el.scrollIntoView(options);
49+
return;
50+
}
51+
52+
setTimeout(tryToScroll, 100);
53+
};
54+
55+
if (options.initialDelay != null) {
56+
setTimeout(tryToScroll, options.initialDelay);
57+
return;
58+
}
59+
60+
tryToScroll();
61+
};
62+
63+
/**
64+
* Attempts to scroll to the element specified in the hash of the current path.
65+
* In the event of a slow page render, the element may not be immediately available.
66+
* This method will retry up to 50 times every 100ms to find the element before
67+
* giving up.
68+
*
69+
* Reference:
70+
* https://stackoverflow.com/a/54042987
71+
* https://stackoverflow.com/a/48195222
72+
*/
73+
const _scrollToHash = (
74+
location: any,
75+
options: ScrollOptions = DefaultScrollOptions
76+
) => {
77+
if (StringUtils.isEmpty(location.hash)) {
78+
return;
79+
}
80+
81+
const id = location.hash.replace("#", "");
82+
_scrollToElementById(id, options);
83+
};
84+
85+
// #endregion Functions
86+
87+
// -----------------------------------------------------------------------------------------
88+
// #region Exports
89+
// -----------------------------------------------------------------------------------------
90+
91+
export const ScrollUtils = {
92+
scrollToHash: _scrollToHash,
93+
};
94+
95+
// #endregion Exports

0 commit comments

Comments
 (0)