Skip to content

Commit 661b39e

Browse files
authored
docs: better JSDoc (#942)
## 📜 Description Better coverage of js-doc. ## 💡 Motivation and Context Current state os JSDoc was in semi-ready state. Some of components had JSDoc, some props declaration had JSDoc, but some of them wee missing completely. I decided to change that, so in this PR I am forcing the JSDoc for each hook/view/component/props. To assure JSDoc looks consistent across all files I added `eslint` plugin. Overall I think it's a great addition - documentation is a key to success and when you have it on your fingers tip it makes you more productive 😊 ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### JS - added eslint plugin to assure documentation is written for each function and is consistent; - added missing JSDoc for all public hooks/components/modules. ## 🤔 How Has This Been Tested? Tested in VSCode. ## 📸 Screenshots (if appropriate): <img width="933" alt="image" src="https://github.com/user-attachments/assets/f8e68021-b0d1-4163-a511-ecfd73d10552" /> ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent 17faba5 commit 661b39e

File tree

22 files changed

+844
-191
lines changed

22 files changed

+844
-191
lines changed

.eslintrc.cjs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
"eslint-comments",
1111
"prettier",
1212
"react-perf",
13+
"jsdoc",
1314
],
1415
extends: [
1516
"@react-native",
@@ -163,6 +164,63 @@ module.exports = {
163164
],
164165
},
165166
},
167+
{
168+
files: ["src/**"],
169+
excludedFiles: ["src/specs/**"],
170+
rules: {
171+
"jsdoc/check-access": "error",
172+
"jsdoc/check-alignment": "error",
173+
"jsdoc/check-indentation": 1,
174+
"jsdoc/check-line-alignment": 1,
175+
"jsdoc/check-param-names": "error",
176+
"jsdoc/check-template-names": 1,
177+
"jsdoc/check-property-names": "error",
178+
"jsdoc/check-syntax": 1,
179+
"jsdoc/check-tag-names": ["error", { definedTags: ["platform"] }],
180+
"jsdoc/check-types": "error",
181+
"jsdoc/check-values": "error",
182+
"jsdoc/empty-tags": "error",
183+
"jsdoc/implements-on-classes": "error",
184+
"jsdoc/informative-docs": 1,
185+
"jsdoc/match-description": 1,
186+
"jsdoc/multiline-blocks": "error",
187+
"jsdoc/no-bad-blocks": 1,
188+
"jsdoc/no-blank-block-descriptions": 1,
189+
"jsdoc/no-defaults": 1,
190+
"jsdoc/no-multi-asterisks": "error",
191+
"jsdoc/no-types": 1,
192+
"jsdoc/require-asterisk-prefix": 1,
193+
"jsdoc/require-description": 1,
194+
"jsdoc/require-description-complete-sentence": 1,
195+
"jsdoc/require-example": 1,
196+
"jsdoc/require-hyphen-before-param-description": 1,
197+
"jsdoc/require-jsdoc": "error",
198+
"jsdoc/require-param": "error",
199+
"jsdoc/require-param-description": "error",
200+
"jsdoc/require-param-name": "error",
201+
"jsdoc/require-property": "error",
202+
"jsdoc/require-property-description": "error",
203+
"jsdoc/require-property-name": "error",
204+
"jsdoc/require-returns": "error",
205+
"jsdoc/require-returns-check": "error",
206+
"jsdoc/require-returns-description": "error",
207+
"jsdoc/require-template": 1,
208+
"jsdoc/require-throws": 1,
209+
"jsdoc/require-yields": "error",
210+
"jsdoc/require-yields-check": "error",
211+
"jsdoc/sort-tags": 1,
212+
"jsdoc/tag-lines": ["error", "never", { startLines: 1 }],
213+
"jsdoc/valid-types": "error",
214+
"jsdoc/no-restricted-syntax": "off",
215+
"jsdoc/require-file-overview": "off",
216+
"jsdoc/no-missing-syntax": "off",
217+
"jsdoc/check-examples": "off",
218+
"jsdoc/no-undefined-types": "off",
219+
"jsdoc/require-param-type": "off",
220+
"jsdoc/require-property-type": "off",
221+
"jsdoc/require-returns-type": "off",
222+
},
223+
},
166224
],
167225
env: {
168226
"react-native/react-native": true,

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
"eslint-plugin-ft-flow": "^3.0.11",
102102
"eslint-plugin-import": "^2.28.1",
103103
"eslint-plugin-jest": "^26.5.3",
104+
"eslint-plugin-jsdoc": "^50.6.17",
104105
"eslint-plugin-prettier": "^4.2.1",
105106
"eslint-plugin-react-compiler": "0.0.0-experimental-9ed098e-20240725",
106107
"eslint-plugin-react-perf": "^3.3.2",

src/animated.tsx

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,29 @@ type KeyboardProviderProps = {
5656
* or `StatusBar` component from `react-native`, you can ignore it.
5757
* Defaults to `false`.
5858
*
59-
* @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/14
6059
* @platform android
60+
* @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/14
6161
*/
6262
statusBarTranslucent?: boolean;
6363
/**
6464
* Set the value to `true`, if you use translucent navigation bar on Android.
6565
* Defaults to `false`.
6666
*
67-
* @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/119
6867
* @platform android
68+
* @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/119
6969
*/
7070
navigationBarTranslucent?: boolean;
7171
/**
7272
* A boolean property indicating whether to keep edge-to-edge mode always enabled (even when you disable the module).
7373
* Defaults to `false`.
7474
*
75-
* @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/592
7675
* @platform android
76+
* @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/592
7777
*/
7878
preserveEdgeToEdge?: boolean;
7979
/**
80-
* A boolean prop indicating whether the module is enabled. It indicate only initial state,
81-
* i. e. if you try to change this prop after component mount it will not have any effect.
80+
* A boolean prop indicating whether the module is enabled. It indicate only initial state
81+
* (if you try to change this prop after component mount it will not have any effect).
8282
* To change the property in runtime use `useKeyboardController` hook and `setEnabled` method.
8383
* Defaults to `true`.
8484
*/
@@ -89,13 +89,28 @@ type KeyboardProviderProps = {
8989
// see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/393 and https://github.com/kirillzyusko/react-native-keyboard-controller/issues/294 for more details
9090
const OS = Platform.OS;
9191

92-
export const KeyboardProvider = ({
93-
children,
94-
statusBarTranslucent,
95-
navigationBarTranslucent,
96-
preserveEdgeToEdge,
97-
enabled: initiallyEnabled = true,
98-
}: KeyboardProviderProps) => {
92+
/**
93+
* A component that wrap your app. Under the hood it works with {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-controller-view|KeyboardControllerView} to receive events during keyboard movements,
94+
* maps these events to `Animated`/`Reanimated` values and store them in context.
95+
*
96+
* @param props - Provider props, such as `statusBarTranslucent`, `navigationBarTranslucent`, etc.
97+
* @returns A component that should be mounted in root of your App layout.
98+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-provider|Documentation} page for more details.
99+
* @example
100+
* ```tsx
101+
* <KeyboardProvider>
102+
* <NavigationContainer />
103+
* </KeyboardProvider>
104+
* ```
105+
*/
106+
export const KeyboardProvider = (props: KeyboardProviderProps) => {
107+
const {
108+
children,
109+
statusBarTranslucent,
110+
navigationBarTranslucent,
111+
preserveEdgeToEdge,
112+
enabled: initiallyEnabled = true,
113+
} = props;
99114
// ref
100115
const viewTagRef = useRef<React.Component<KeyboardControllerProps>>(null);
101116
// state

src/bindings.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ export const KeyboardControllerNative: KeyboardControllerNativeModule = {
2121
addListener: NOOP,
2222
removeListeners: NOOP,
2323
};
24+
/**
25+
* An event emitter that provides a way to subscribe to next keyboard events:
26+
* - `keyboardWillShow`;
27+
* - `keyboardDidShow`;
28+
* - `keyboardWillHide`;
29+
* - `keyboardDidHide`.
30+
*
31+
* Use `addListener` function to add your event listener for a specific keyboard event.
32+
*/
2433
export const KeyboardEvents: KeyboardEventsModule = {
2534
addListener: () => ({ remove: NOOP } as EmitterSubscription),
2635
};
@@ -34,8 +43,18 @@ export const FocusedInputEvents: FocusedInputEventsModule = {
3443
export const WindowDimensionsEvents: WindowDimensionsEventsModule = {
3544
addListener: () => ({ remove: NOOP } as EmitterSubscription),
3645
};
46+
/**
47+
* A view that sends events whenever keyboard or focused events are happening.
48+
*
49+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-controller-view|Documentation} page for more details.
50+
*/
3751
export const KeyboardControllerView =
3852
View as unknown as React.FC<KeyboardControllerProps>;
53+
/**
54+
* A view that defines a region on the screen, where gestures will control the keyboard position.
55+
*
56+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-gesture-area|Documentation} page for more details.
57+
*/
3958
export const KeyboardGestureArea =
4059
View as unknown as React.FC<KeyboardGestureAreaProps>;
4160
export const RCTOverKeyboardView =

src/components/KeyboardAvoidingView/index.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,17 @@ const defaultLayout: LayoutRectangle = {
6262
};
6363

6464
/**
65-
* View that moves out of the way when the keyboard appears by automatically
66-
* adjusting its height, position, or bottom padding.
65+
* A View component that automatically adjusts its height, position, or bottom padding
66+
* when the keyboard appears to ensure that the content remains visible.
67+
*
68+
* @returns A View component that adjusts to keyboard visibility.
69+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-avoiding-view|Documentation} page for more details.
70+
* @example
71+
* ```tsx
72+
* <KeyboardAvoidingView behavior="padding">
73+
* <TextInput />
74+
* </KeyboardAvoidingView>
75+
* ```
6776
*/
6877
const KeyboardAvoidingView = forwardRef<
6978
View,

src/components/KeyboardAwareScrollView/index.tsx

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,51 +35,66 @@ export type KeyboardAwareScrollViewProps = {
3535
bottomOffset?: number;
3636
/** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */
3737
disableScrollOnKeyboardHide?: boolean;
38-
/** Controls whether this `KeyboardAwareScrollView` instance should take effect. Default is `true` */
38+
/** Controls whether this `KeyboardAwareScrollView` instance should take effect. Default is `true`. */
3939
enabled?: boolean;
40-
/** Adjusting the bottom spacing of KeyboardAwareScrollView. Default is `0` */
40+
/** Adjusting the bottom spacing of KeyboardAwareScrollView. Default is `0`. */
4141
extraKeyboardSpace?: number;
42-
/** Custom component for `ScrollView`. Default is `ScrollView` */
42+
/** Custom component for `ScrollView`. Default is `ScrollView`. */
4343
ScrollViewComponent?: React.ComponentType<ScrollViewProps>;
4444
} & ScrollViewProps;
4545

46-
/*
47-
* Everything begins from `onStart` handler. This handler is called every time,
48-
* when keyboard changes its size or when focused `TextInput` was changed. In
49-
* this handler we are calculating/memoizing values which later will be used
50-
* during layout movement. For that we calculate:
51-
* - layout of focused field (`layout`) - to understand whether there will be overlap
52-
* - initial keyboard size (`initialKeyboardSize`) - used in scroll interpolation
53-
* - future keyboard height (`keyboardHeight`) - used in scroll interpolation
54-
* - current scroll position (`scrollPosition`) - used to scroll from this point
46+
// Everything begins from `onStart` handler. This handler is called every time,
47+
// when keyboard changes its size or when focused `TextInput` was changed. In
48+
// this handler we are calculating/memoizing values which later will be used
49+
// during layout movement. For that we calculate:
50+
// - layout of focused field (`layout`) - to understand whether there will be overlap
51+
// - initial keyboard size (`initialKeyboardSize`) - used in scroll interpolation
52+
// - future keyboard height (`keyboardHeight`) - used in scroll interpolation
53+
// - current scroll position (`scrollPosition`) - used to scroll from this point
54+
//
55+
// Once we've calculated all necessary variables - we can actually start to use them.
56+
// It happens in `onMove` handler - this function simply calls `maybeScroll` with
57+
// current keyboard frame height. This functions makes the smooth transition.
58+
//
59+
// When the transition has finished we go to `onEnd` handler. In this handler
60+
// we verify, that the current field is not overlapped within a keyboard frame.
61+
// For full `onStart`/`onMove`/`onEnd` flow it may look like a redundant thing,
62+
// however there could be some cases, when `onMove` is not called:
63+
// - on iOS when TextInput was changed - keyboard transition is instant
64+
// - on Android when TextInput was changed and keyboard size wasn't changed
65+
// So `onEnd` handler handle the case, when `onMove` wasn't triggered.
66+
//
67+
// ====================================================================================================================+
68+
// -----------------------------------------------------Flow chart-----------------------------------------------------+
69+
// ====================================================================================================================+
70+
//
71+
// +============================+ +============================+ +==================================+
72+
// + User Press on TextInput + => + Keyboard starts showing + => + As keyboard moves frame by frame + =>
73+
// + + + (run `onStart`) + + `onMove` is getting called +
74+
// +============================+ +============================+ +==================================+
75+
//
76+
// +============================+ +============================+ +=====================================+
77+
// + Keyboard is shown and we + => + User moved focus to + => + Only `onStart`/`onEnd` maybe called +
78+
// + call `onEnd` handler + + another `TextInput` + + (without involving `onMove`) +
79+
// +============================+ +============================+ +=====================================+
80+
//
81+
82+
/**
83+
* A ScrollView component that automatically handles keyboard appearance and disappearance
84+
* by adjusting its content position to ensure the focused input remains visible.
5585
*
56-
* Once we've calculated all necessary variables - we can actually start to use them.
57-
* It happens in `onMove` handler - this function simply calls `maybeScroll` with
58-
* current keyboard frame height. This functions makes the smooth transition.
59-
*
60-
* When the transition has finished we go to `onEnd` handler. In this handler
61-
* we verify, that the current field is not overlapped within a keyboard frame.
62-
* For full `onStart`/`onMove`/`onEnd` flow it may look like a redundant thing,
63-
* however there could be some cases, when `onMove` is not called:
64-
* - on iOS when TextInput was changed - keyboard transition is instant
65-
* - on Android when TextInput was changed and keyboard size wasn't changed
66-
* So `onEnd` handler handle the case, when `onMove` wasn't triggered.
67-
*
68-
* ====================================================================================================================+
69-
* -----------------------------------------------------Flow chart-----------------------------------------------------+
70-
* ====================================================================================================================+
71-
*
72-
* +============================+ +============================+ +==================================+
73-
* + User Press on TextInput + => + Keyboard starts showing + => + As keyboard moves frame by frame + =>
74-
* + + + (run `onStart`) + + `onMove` is getting called +
75-
* +============================+ +============================+ +==================================+
76-
*
77-
*
78-
* +============================+ +============================+ +=====================================+
79-
* + Keyboard is shown and we + => + User moved focus to + => + Only `onStart`/`onEnd` maybe called +
80-
* + call `onEnd` handler + + another `TextInput` + + (without involving `onMove`) +
81-
* +============================+ +============================+ +=====================================+
86+
* The component uses a sophisticated animation system to smoothly handle keyboard transitions
87+
* and maintain proper scroll position during keyboard interactions.
8288
*
89+
* @returns A ScrollView component that handles keyboard interactions.
90+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-aware-scroll-view|Documentation} page for more details.
91+
* @example
92+
* ```tsx
93+
* <KeyboardAwareScrollView bottomOffset={20}>
94+
* <TextInput placeholder="Enter text" />
95+
* <TextInput placeholder="Another input" />
96+
* </KeyboardAwareScrollView>
97+
* ```
8398
*/
8499
const KeyboardAwareScrollView = forwardRef<
85100
ScrollView,
@@ -133,7 +148,7 @@ const KeyboardAwareScrollView = forwardRef<
133148
);
134149

135150
/**
136-
* Function that will scroll a ScrollView as keyboard gets moving
151+
* Function that will scroll a ScrollView as keyboard gets moving.
137152
*/
138153
const maybeScroll = useCallback(
139154
(e: number, animated: boolean = false) => {

src/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,23 @@ const TELEGRAM_ANDROID_TIMING_CONFIG = {
2828

2929
/**
3030
* Hook that uses default transitions for iOS and Android > 11, and uses
31-
* custom interpolation on Android < 11 to achieve more smooth animation
31+
* custom interpolation on Android < 11 to achieve more smooth animation.
32+
*
33+
* @param handler - Object containing keyboard event handlers.
34+
* @param [deps] - Dependencies array for the effect.
35+
* @example
36+
* ```ts
37+
* useSmoothKeyboardHandler(
38+
* {
39+
* onStart: (e) => {
40+
* "worklet";
41+
*
42+
* // your handler for keyboard start
43+
* },
44+
* },
45+
* [],
46+
* );
47+
* ```
3248
*/
3349
export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
3450
handler,

src/components/KeyboardStickyView/index.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,23 @@ export type KeyboardStickyViewProps = {
2020
opened?: number;
2121
};
2222

23-
/** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true` */
23+
/** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true`. */
2424
enabled?: boolean;
2525
} & ViewProps;
2626

27+
/**
28+
* A View component that sticks to the keyboard and moves with it when it appears or disappears.
29+
* The view can be configured with custom offsets for both closed and open keyboard states.
30+
*
31+
* @returns An animated View component that sticks to the keyboard.
32+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-sticky-view|Documentation} page for more details.
33+
* @example
34+
* ```tsx
35+
* <KeyboardStickyView offset={{ closed: 0, opened: 20 }}>
36+
* <Button title="Submit" />
37+
* </KeyboardStickyView>
38+
* ```
39+
*/
2740
const KeyboardStickyView = forwardRef<
2841
View,
2942
React.PropsWithChildren<KeyboardStickyViewProps>

0 commit comments

Comments
 (0)