Skip to content

Commit 16a26ce

Browse files
committed
main 🧊 add test for observers
1 parent 9f70949 commit 16a26ce

File tree

16 files changed

+1152
-197
lines changed

16 files changed

+1152
-197
lines changed

‎packages/core/src/bundle/hooks/useBattery/useBattery.js‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export const useBattery = () => {
4545
});
4646
return () => {
4747
if (!battery) return;
48-
console.log('unmount', battery);
4948
battery.removeEventListener('levelchange', onChange);
5049
battery.removeEventListener('chargingchange', onChange);
5150
battery.removeEventListener('chargingtimechange', onChange);

‎packages/core/src/bundle/hooks/useFocus/useFocus.js‎

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ import { useRefState } from '../useRefState/useRefState';
99
*
1010
* @overload
1111
* @param {HookTarget} target The target element to focus
12+
* @param {(event: FocusEvent) => void} [callback] The callback function to be invoked on focus
13+
* @returns {UseFocusReturn} An object with focus state and methods
14+
*
15+
* @example
16+
* const { focus, blur, focused } = useFocus(ref, () => console.log('focused'));
17+
*
18+
* @overload
19+
* @param {HookTarget} target The target element to focus
20+
* @param {boolean} [options.enabled=true] The enabled state of the focus hook
1221
* @param {boolean} [options.initialValue=false] The initial focus state of the target
1322
* @param {(event: FocusEvent) => void} [options.onFocus] The callback function to be invoked on focus
1423
* @param {(event: FocusEvent) => void} [options.onBlur] The callback function to be invoked on blur
@@ -19,6 +28,15 @@ import { useRefState } from '../useRefState/useRefState';
1928
*
2029
* @overload
2130
* @template Target The target element
31+
* @param {(event: FocusEvent) => void} [callback] The callback function to be invoked on focus
32+
* @returns {UseFocusReturn & { ref: StateRef<Target> }} An object with focus state, methods and ref
33+
*
34+
* @example
35+
* const { ref, focus, blur, focused } = useFocus(() => console.log('focused'));
36+
*
37+
* @overload
38+
* @template Target The target element
39+
* @param {boolean} [options.enabled=true] The enabled state of the focus hook
2240
* @param {boolean} [options.initialValue=false] The initial focus state of the target
2341
* @param {(event: FocusEvent) => void} [options.onFocus] The callback function to be invoked on focus
2442
* @param {(event: FocusEvent) => void} [options.onBlur] The callback function to be invoked on blur
@@ -29,17 +47,32 @@ import { useRefState } from '../useRefState/useRefState';
2947
*/
3048
export const useFocus = (...params) => {
3149
const target = isTarget(params[0]) ? params[0] : undefined;
32-
const options = (target ? params[1] : params[0]) ?? {};
33-
const initialValue = options.initialValue ?? false;
50+
const options = target
51+
? typeof params[1] === 'object'
52+
? params[1]
53+
: { onFocus: params[1] }
54+
: typeof params[0] === 'object'
55+
? params[0]
56+
: { onFocus: params[0] };
57+
const enabled = options?.enabled ?? true;
58+
const initialValue = options?.initialValue ?? false;
3459
const [focused, setFocused] = useState(initialValue);
3560
const internalRef = useRefState();
3661
const internalOptionsRef = useRef(options);
3762
internalOptionsRef.current = options;
3863
const elementRef = useRef(null);
39-
const focus = () => elementRef.current?.focus();
40-
const blur = () => elementRef.current?.blur();
64+
const focus = () => {
65+
if (!elementRef.current) return;
66+
elementRef.current.focus();
67+
setFocused(true);
68+
};
69+
const blur = () => {
70+
if (!elementRef.current) return;
71+
elementRef.current.blur();
72+
setFocused(false);
73+
};
4174
useEffect(() => {
42-
if (!target && !internalRef.state) return;
75+
if (!enabled || (!target && !internalRef.state)) return;
4376
const element = target ? getElement(target) : internalRef.current;
4477
if (!element) return;
4578
elementRef.current = element;
@@ -58,7 +91,7 @@ export const useFocus = (...params) => {
5891
element.removeEventListener('focus', onFocus);
5992
element.removeEventListener('blur', onBlur);
6093
};
61-
}, [target, internalRef.state]);
94+
}, [target, internalRef.state, enabled]);
6295
if (target) return { focus, blur, focused };
6396
return {
6497
ref: internalRef,

‎packages/core/src/bundle/hooks/useIntersectionObserver/useIntersectionObserver.js‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { useRefState } from '../useRefState/useRefState';
1717
* @returns {UseIntersectionObserverReturn} An object containing the state
1818
*
1919
* @example
20-
* const { ref, entry, inView } = useIntersectionObserver();
20+
* const { ref, entry, inView, observer } = useIntersectionObserver();
2121
*
2222
* @overload
2323
* @template Target The target element
@@ -27,23 +27,23 @@ import { useRefState } from '../useRefState/useRefState';
2727
* @returns {UseIntersectionObserverReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
2828
*
2929
* @example
30-
* const { entry, inView } = useIntersectionObserver(ref);
30+
* const { entry, inView, observer } = useIntersectionObserver(ref);
3131
*
3232
* @overload
3333
* @template Target The target element
3434
* @param {UseIntersectionObserverCallback} callback The callback to execute when intersection is detected
3535
* @returns {UseIntersectionObserverReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
3636
*
3737
* @example
38-
* const { ref, entry, inView } = useIntersectionObserver(() => console.log('callback'));
38+
* const { ref, entry, inView, observer } = useIntersectionObserver(() => console.log('callback'));
3939
*
4040
* @overload
4141
* @param {UseIntersectionObserverCallback} callback The callback to execute when intersection is detected
4242
* @param {HookTarget} target The target element to detect intersection
4343
* @returns {UseIntersectionObserverReturn} An object containing the state
4444
*
4545
* @example
46-
* const { entry, inView } = useIntersectionObserver(() => console.log('callback'), ref);
46+
* const { entry, inView, observer } = useIntersectionObserver(() => console.log('callback'), ref);
4747
*/
4848
export const useIntersectionObserver = (...params) => {
4949
const target = isTarget(params[0]) ? params[0] : undefined;
@@ -56,6 +56,7 @@ export const useIntersectionObserver = (...params) => {
5656
: { onChange: params[0] };
5757
const callback = options?.onChange;
5858
const enabled = options?.enabled ?? true;
59+
const [observer, setObserver] = useState();
5960
const [entry, setEntry] = useState();
6061
const internalRef = useRefState();
6162
const internalCallbackRef = useRef(callback);
@@ -65,22 +66,24 @@ export const useIntersectionObserver = (...params) => {
6566
const element = target ? getElement(target) : internalRef.current;
6667
if (!element) return;
6768
const observer = new IntersectionObserver(
68-
([entry]) => {
69+
([entry], observer) => {
6970
setEntry(entry);
70-
internalCallbackRef.current?.(entry);
71+
internalCallbackRef.current?.(entry, observer);
7172
},
7273
{
7374
...options,
7475
root: options?.root ? getElement(options.root) : document
7576
}
7677
);
78+
setObserver(observer);
7779
observer.observe(element);
7880
return () => {
7981
observer.disconnect();
8082
};
8183
}, [target, internalRef.state, options?.rootMargin, options?.threshold, options?.root, enabled]);
82-
if (target) return { entry, inView: !!entry?.isIntersecting };
84+
if (target) return { observer, entry, inView: !!entry?.isIntersecting };
8385
return {
86+
observer,
8487
ref: internalRef,
8588
entry,
8689
inView: !!entry?.isIntersecting

‎packages/core/src/bundle/hooks/useMutationObserver/useMutationObserver.js‎

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,65 +7,92 @@ import { useRefState } from '../useRefState/useRefState';
77
* @category Sensors
88
* @usage low
99
*
10+
* @browserapi MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
11+
*
1012
* @overload
11-
* @template Target The target element
12-
* @param {MutationCallback} callback The callback to execute when mutation is detected
13+
* @param {HookTarget} target The target element to observe
1314
* @param {boolean} [options.enabled=true] The enabled state of the mutation observer
15+
* @param {UseMutationObserverCallback} [options.onChange] The callback to execute when mutation is detected
1416
* @param {boolean} [options.attributes] Set to true if mutations to target's attributes are to be observed
1517
* @param {boolean} [options.characterData] Set to true if mutations to target's data are to be observed
1618
* @param {boolean} [options.childList] Set to true if mutations to target's children are to be observed
17-
* @param {boolean} [options.subtree] Set to true if mutations to not just target, but also target's descendants are to be observed
18-
* @param {boolean} [options.characterDataOldValue] Set to true if characterData is set to true or omitted and target's data before the mutation needs to be recorded
19-
* @param {boolean} [options.attributeOldValue] Set to a list of attribute local names (without namespace) if not all attribute mutations need to be observed and attributes is true or omitted
20-
* @param {string[]} [options.attributeFilter] Set to a list of attribute local names (without namespace) if not all attribute mutations need to be observed and attributes is true or omitted
21-
* @returns {UseMutationObserverReturn & { ref: StateRef<Target> }} An object containing the mutation observer state
19+
* @param {boolean} [options.subtree] Set to true if mutations to not just target, but also target's descendants are to be observed
20+
* @returns {UseMutationObserverReturn} An object containing the mutation observer state
2221
*
2322
* @example
24-
* const { ref, observer, stop } = useMutationObserver(() => console.log('callback'))
23+
* const { observer, stop } = useMutationObserver(ref, { childList: true });
2524
*
2625
* @overload
27-
* @param {HookTarget} target The target element to observe
28-
* @param {MutationCallback} callback The callback to execute when mutation is detected
26+
* @template Target The target element
2927
* @param {boolean} [options.enabled=true] The enabled state of the mutation observer
28+
* @param {UseMutationObserverCallback} [options.onChange] The callback to execute when mutation is detected
3029
* @param {boolean} [options.attributes] Set to true if mutations to target's attributes are to be observed
3130
* @param {boolean} [options.characterData] Set to true if mutations to target's data are to be observed
3231
* @param {boolean} [options.childList] Set to true if mutations to target's children are to be observed
33-
* @param {boolean} [options.subtree] Set to true if mutations to not just target, but also target's descendants are to be observed
34-
* @param {boolean} [options.characterDataOldValue] Set to true if characterData is set to true or omitted and target's data before the mutation needs to be recorded
35-
* @param {boolean} [options.attributeOldValue] Set to a list of attribute local names (without namespace) if not all attribute mutations need to be observed and attributes is true or omitted
36-
* @param {string[]} [options.attributeFilter] Set to a list of attribute local names (without namespace) if not all attribute mutations need to be observed and attributes is true or omitted
32+
* @param {boolean} [options.subtree] Set to true if mutations to not just target, but also target's descendants are to be observed
33+
* @returns {UseMutationObserverReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
34+
*
35+
* @example
36+
* const { ref, observer, stop } = useMutationObserver({ childList: true });
37+
*
38+
* @overload
39+
* @template Target The target element
40+
* @param {UseMutationObserverCallback} callback The callback to execute when mutation is detected
41+
* @returns {UseMutationObserverReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
42+
*
43+
* @example
44+
* const { ref, observer, stop } = useMutationObserver((mutations) => console.log(mutations));
45+
*
46+
* @overload
47+
* @param {UseMutationObserverCallback} callback The callback to execute when mutation is detected
48+
* @param {HookTarget} target The target element to observe
3749
* @returns {UseMutationObserverReturn} An object containing the mutation observer state
3850
*
3951
* @example
40-
* const { observer, stop } = useMutationObserver(ref, () => console.log('callback'))
52+
* const { observer, stop } = useMutationObserver((mutations) => console.log(mutations), ref);
4153
*/
4254
export const useMutationObserver = (...params) => {
4355
const target = isTarget(params[0]) ? params[0] : undefined;
44-
const callback = target ? params[1] : params[0];
45-
const options = target ? params[2] : params[1];
46-
const [observer, setObserver] = useState();
56+
const options = target
57+
? typeof params[1] === 'object'
58+
? params[1]
59+
: { onChange: params[1] }
60+
: typeof params[0] === 'object'
61+
? params[0]
62+
: { onChange: params[0] };
63+
const callback = options?.onChange;
4764
const enabled = options?.enabled ?? true;
48-
const internalRef = useRefState(window.document.documentElement);
65+
const [observer, setObserver] = useState();
66+
const internalRef = useRefState();
4967
const internalCallbackRef = useRef(callback);
5068
internalCallbackRef.current = callback;
51-
const internalOptionsRef = useRef(options);
52-
internalOptionsRef.current = options;
5369
useEffect(() => {
5470
if (!enabled || (!target && !internalRef.state)) return;
5571
const element = target ? getElement(target) : internalRef.current;
5672
if (!element) return;
57-
const observer = new MutationObserver(internalCallbackRef.current);
73+
const observer = new MutationObserver((mutations, observer) => {
74+
internalCallbackRef.current?.(mutations, observer);
75+
});
5876
setObserver(observer);
59-
observer.observe(element, internalOptionsRef.current);
77+
observer.observe(element, options);
6078
return () => {
6179
observer.disconnect();
6280
};
63-
}, [target, internalRef.state]);
64-
const stop = () => observer?.disconnect();
65-
if (target) return { stop, observer };
81+
}, [
82+
target,
83+
internalRef.state,
84+
options?.childList,
85+
options?.attributes,
86+
options?.characterData,
87+
options?.subtree,
88+
options?.attributeOldValue,
89+
options?.characterDataOldValue,
90+
options?.attributeFilter,
91+
enabled
92+
]);
93+
if (target) return { observer };
6694
return {
6795
ref: internalRef,
68-
stop,
6996
observer
7097
};
7198
};

‎packages/core/src/bundle/hooks/useResizeObserver/useResizeObserver.js‎

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,83 @@ import { useEffect, useRef, useState } from 'react';
22
import { getElement, isTarget } from '@/utils/helpers';
33
import { useRefState } from '../useRefState/useRefState';
44
/**
5-
* @name useResizeObserver
6-
* @description - Hook that gives you resize observer state
7-
* @category Sensors
8-
* @usage low
9-
*
10-
* @browserapi ResizeObserver https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
11-
*
12-
* @overload
13-
* @template Target The target element
14-
* @param {boolean} [options.enabled=true] The IntersectionObserver options
15-
* @param {boolean} [options.box] The IntersectionObserver options
16-
* @param {(entries: ResizeObserverEntry[], observer: ResizeObserver) => void} [options.onChange] The callback to execute when resize is detected
17-
* @returns {UseResizeObserverReturn & { ref: StateRef<Target> }} An object containing the resize observer state
18-
*
19-
* @example
20-
* const { ref, entries } = useResizeObserver();
21-
*
22-
* @overload
23-
* @template Target The target element
24-
* @param {HookTarget} target The target element to observe
25-
* @param {boolean} [options.enabled=true] The IntersectionObserver options
26-
* @param {boolean} [options.box] The IntersectionObserver options
27-
* @param {(entries: ResizeObserverEntry[], observer: ResizeObserver) => void} [options.onChange] The callback to execute when resize is detected
28-
* @returns {UseResizeObserverReturn} An object containing the resize observer state
29-
*
30-
* @example
31-
* const { entries } = useResizeObserver(ref);
5+
* @name useResizeObserver
6+
* @description - Hook that gives you resize observer state
7+
* @category Sensors
8+
* @usage low
9+
*
10+
* @browserapi ResizeObserver https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
11+
*
12+
* @overload
13+
* @param {HookTarget} target The target element to observe
14+
* @param {boolean} [options.enabled=true] The enabled state of the resize observer
15+
* @param {ResizeObserverBoxOptions} [options.box] The box model to observe
16+
* @param {UseResizeObserverCallback} [options.onChange] The callback to execute when resize is detected
17+
* @returns {UseResizeObserverReturn} An object containing the resize observer state
18+
*
19+
* @example
20+
* const { entries, observer } = useResizeObserver(ref);
21+
*
22+
* @overload
23+
* @template Target The target element
24+
* @param {boolean} [options.enabled=true] The enabled state of the resize observer
25+
* @param {ResizeObserverBoxOptions} [options.box] The box model to observe
26+
* @param {UseResizeObserverCallback} [options.onChange] The callback to execute when resize is detected
27+
* @returns {UseResizeObserverReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
28+
*
29+
* @example
30+
* const { ref, entry, observer } = useResizeObserver();
31+
*
32+
* @overload
33+
* @template Target The target element
34+
* @param {UseResizeObserverCallback} callback The callback to execute when resize is detected
35+
* @returns {UseResizeObserverReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
36+
*
37+
* @example
38+
* const { ref, entry, observer } = useResizeObserver((entry) => console.log(entry));
39+
*
40+
* @overload
41+
* @param {HookTarget} target The target element to observe
42+
* @param {UseResizeObserverCallback} callback The callback to execute when resize is detected
43+
* @returns {UseResizeObserverReturn} An object containing the resize observer state
44+
*
45+
* @example
46+
* const { entry, observer } = useResizeObserver(ref, (entry) => console.log(entry));
3247
*/
3348
export const useResizeObserver = (...params) => {
3449
const target = isTarget(params[0]) ? params[0] : undefined;
35-
const options = target ? params[1] : params[0];
50+
const options = target
51+
? typeof params[1] === 'object'
52+
? params[1]
53+
: { onChange: params[1] }
54+
: typeof params[0] === 'object'
55+
? params[0]
56+
: { onChange: params[0] };
57+
const callback = options?.onChange;
3658
const enabled = options?.enabled ?? true;
37-
const [entries, setEntries] = useState([]);
59+
const [entry, setEntry] = useState();
60+
const [observer, setObserver] = useState();
3861
const internalRef = useRefState();
39-
const internalOnChangeRef = useRef(options?.onChange);
40-
internalOnChangeRef.current = options?.onChange;
62+
const internalCallbackRef = useRef(callback);
63+
internalCallbackRef.current = callback;
4164
useEffect(() => {
4265
if (!enabled || (!target && !internalRef.state)) return;
4366
const element = target ? getElement(target) : internalRef.current;
4467
if (!element) return;
45-
const observer = new ResizeObserver((entries) => {
46-
setEntries(entries);
47-
internalOnChangeRef.current?.(entries, observer);
68+
const observer = new ResizeObserver(([entry], observer) => {
69+
setEntry(entry);
70+
internalCallbackRef.current?.(entry, observer);
4871
});
72+
setObserver(observer);
4973
observer.observe(element, options);
5074
return () => {
5175
observer.disconnect();
5276
};
5377
}, [target, internalRef.state, options?.box, enabled]);
54-
if (target) return { entries };
78+
if (target) return { entry, observer };
5579
return {
5680
ref: internalRef,
57-
entries
81+
entry,
82+
observer
5883
};
5984
};

0 commit comments

Comments
 (0)