diff --git a/.github/workflows/validate-docs.yml b/.github/workflows/validate-docs.yml new file mode 100644 index 000000000..657ed8570 --- /dev/null +++ b/.github/workflows/validate-docs.yml @@ -0,0 +1,31 @@ +name: Validate Documentation + +on: + push: + branches: + - master + pull_request: + branches: + - master + merge_group: + types: + - checks_requested + +jobs: + validate-docs: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history for proper git info + + - name: Setup + uses: ./.github/actions/setup + + - name: Validate documentation + run: yarn docs --validation --treatWarningsAsErrors --json /dev/null diff --git a/CHANGELOG.md b/CHANGELOG.md index a89e153e0..410ed574d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.0.4 -## Updates +### Updates - Added API documentation via Netlify([1087275](https://github.com/Iterable/react-native-sdk/commit/1087275)) - Removed dependency on `react-native-vector-icons`, per issues [#513](https://github.com/Iterable/react-native-sdk/issues/513), diff --git a/docs-static/expand-nav.js b/docs-static/expand-nav.js new file mode 100644 index 000000000..bf504053b --- /dev/null +++ b/docs-static/expand-nav.js @@ -0,0 +1,91 @@ +// Expand all navigation folders on page load +(function () { + 'use strict'; + + function expandAllNavigation() { + // Find all details elements in various navigation contexts + const selectors = [ + '.tsd-navigation details', + '#tsd-nav-container details', + '.site-menu details', + '.tsd-accordion', + 'nav details', + ]; + + let foundAny = false; + selectors.forEach((selector) => { + const elements = document.querySelectorAll(selector); + if (elements.length > 0) { + foundAny = true; + elements.forEach((el) => { + if (el.tagName === 'DETAILS') { + el.open = true; + } + }); + } + }); + + return foundAny; + } + + // Strategy 1: Try immediately + expandAllNavigation(); + + // Strategy 2: Hook into TypeDoc's app if available + let checkCount = 0; + const maxChecks = 50; // Check for up to 5 seconds + + function checkAndExpand() { + checkCount++; + + if (expandAllNavigation()) { + // Keep expanding even if found, in case more navigation loads + if (checkCount < maxChecks) { + setTimeout(checkAndExpand, 100); + } + } else if (checkCount < maxChecks) { + setTimeout(checkAndExpand, 100); + } + } + + // Strategy 3: MutationObserver for dynamic content + // eslint-disable-next-line no-undef + const observer = new MutationObserver(() => { + expandAllNavigation(); + }); + + // Strategy 4: Multiple event listeners + window.addEventListener('load', () => { + expandAllNavigation(); + checkAndExpand(); + + // Start observing after page load + const navContainer = document.querySelector( + '#tsd-nav-container, .site-menu' + ); + if (navContainer) { + observer.observe(navContainer, { + childList: true, + subtree: true, + }); + } + }); + + // Strategy 5: Check for TypeDoc's app initialization + if (window.app) { + expandAllNavigation(); + } else { + Object.defineProperty(window, 'app', { + configurable: true, + set: function (value) { + delete window.app; + window.app = value; + setTimeout(expandAllNavigation, 100); + setTimeout(expandAllNavigation, 500); + }, + get: function () { + return window._app; + }, + }); + } +})(); diff --git a/package.json b/package.json index 7a49b12ac..7b7ec1907 100644 --- a/package.json +++ b/package.json @@ -99,8 +99,9 @@ "react-test-renderer": "19.0.0", "release-it": "^17.10.0", "turbo": "^1.10.7", - "typedoc": "^0.28.13", - "typedoc-plugin-coverage": "^3.3.0", + "typedoc": "latest", + "typedoc-github-theme": "^0.3.1", + "typedoc-plugin-coverage": "^4.0.2", "typedoc-plugin-mermaid": "^1.12.0", "typescript": "^5.2.2" }, diff --git a/src/core/classes/Iterable.ts b/src/core/classes/Iterable.ts index 1d070856b..c76c0afa7 100644 --- a/src/core/classes/Iterable.ts +++ b/src/core/classes/Iterable.ts @@ -57,8 +57,10 @@ export class Iterable { /** * Current configuration of the Iterable SDK + * + * @readonly */ - static savedConfig: IterableConfig = new IterableConfig(); + static savedConfig: Readonly = new IterableConfig(); /** * In-app message manager for the current user. @@ -488,7 +490,7 @@ export class Iterable { /** * Launch the application from the background in Android devices. * - * Android only. + * @group Android only. * * @example * ```typescript diff --git a/src/core/classes/IterableConfig.ts b/src/core/classes/IterableConfig.ts index 1c0550b0c..f78e39f51 100644 --- a/src/core/classes/IterableConfig.ts +++ b/src/core/classes/IterableConfig.ts @@ -291,6 +291,8 @@ export class IterableConfig { /** * Android only feature: This controls whether the SDK should enforce encryption for all PII stored on disk. * By default, the SDK will not enforce encryption and may fallback to unencrypted storage in case the encryption fails. + * + * @group Android only. */ encryptionEnforced = false; @@ -301,8 +303,11 @@ export class IterableConfig { */ toDict() { return { + /** The name of the Iterable push integration. */ pushIntegrationName: this.pushIntegrationName, + /** Whether automatic push registration is enabled. */ autoPushRegistration: this.autoPushRegistration, + /** The display interval between in-app messages (in seconds). */ inAppDisplayInterval: this.inAppDisplayInterval, /** * A boolean indicating if a URL handler is present. @@ -334,13 +339,22 @@ export class IterableConfig { authHandlerPresent: this.authHandler != undefined, /** The log level for the SDK. */ logLevel: this.logLevel, + /** The number of seconds before JWT expiration to refresh the token. */ expiringAuthTokenRefreshPeriod: this.expiringAuthTokenRefreshPeriod, + /** The array of allowed URL protocols that the SDK can handle. */ allowedProtocols: this.allowedProtocols, + /** + * @deprecated Whether to use in-memory storage for in-app messages on Android. + */ androidSdkUseInMemoryStorageForInApps: this.androidSdkUseInMemoryStorageForInApps, + /** Whether to use in-memory storage for in-app messages. */ useInMemoryStorageForInApps: this.useInMemoryStorageForInApps, + /** The data region determining the data center and endpoints. */ dataRegion: this.dataRegion, + /** The push platform to use for push notifications. */ pushPlatform: this.pushPlatform, + /** Whether encryption is enforced for PII stored on disk (Android only). */ encryptionEnforced: this.encryptionEnforced, }; } diff --git a/src/core/hooks/useAppStateListener.ts b/src/core/hooks/useAppStateListener.ts index cf10a5449..5e2c532ed 100644 --- a/src/core/hooks/useAppStateListener.ts +++ b/src/core/hooks/useAppStateListener.ts @@ -7,6 +7,9 @@ import { AppState } from 'react-native'; * * @returns The current app state. * + * @category Hooks + * @group Hooks + * * @example * ```typescript * const appState = useAppStateListener(); diff --git a/src/core/hooks/useDeviceOrientation.tsx b/src/core/hooks/useDeviceOrientation.tsx index 766e4becb..d8c5049cc 100644 --- a/src/core/hooks/useDeviceOrientation.tsx +++ b/src/core/hooks/useDeviceOrientation.tsx @@ -22,6 +22,9 @@ export interface IterableDeviceOrientation { * * @returns {IterableDeviceOrientation} An object containing the height, width, and a boolean `isPortrait` indicating if the device is in portrait mode. * + * @category Hooks + * @group Hooks + * * @example * const { height, width, isPortrait } = useDeviceOrientation(); * diff --git a/src/inbox/components/IterableInbox.tsx b/src/inbox/components/IterableInbox.tsx index 545403e03..54aa0844e 100644 --- a/src/inbox/components/IterableInbox.tsx +++ b/src/inbox/components/IterableInbox.tsx @@ -1,5 +1,10 @@ import { useIsFocused } from '@react-navigation/native'; -import { useEffect, useState } from 'react'; +import { + useEffect, + useState, + type PropsWithChildren, + type ReactElement, +} from 'react'; import { Animated, NativeEventEmitter, @@ -161,6 +166,9 @@ export interface IterableInboxProps * It handles fetching messages, displaying them in a list, and showing individual message details. * It also manages the state of the inbox, including loading state, selected message, and visible message impressions. * + * @category React Components + * @group React Components + * * @example * ```tsx * const [visible, setVisible] = useState(false); @@ -186,15 +194,18 @@ export interface IterableInboxProps * ) * ``` */ -export const IterableInbox = ({ - returnToInboxTrigger = true, - messageListItemLayout = () => null, - customizations = {} as IterableInboxCustomizations, - tabBarHeight = 80, - tabBarPadding = 20, - safeAreaMode = true, - showNavTitle = true, -}: IterableInboxProps) => { +export const IterableInbox = ( + params: PropsWithChildren +): ReactElement => { + const { + returnToInboxTrigger = true, + messageListItemLayout = () => null, + customizations = {} as IterableInboxCustomizations, + tabBarHeight = 80, + tabBarPadding = 20, + safeAreaMode = true, + showNavTitle = true, + } = params; const defaultInboxTitle = 'Inbox'; const inboxDataModel = new IterableInboxDataModel(); diff --git a/src/inbox/types/IterableInboxCustomizations.ts b/src/inbox/types/IterableInboxCustomizations.ts index d0d2f5e32..c409ecfe2 100644 --- a/src/inbox/types/IterableInboxCustomizations.ts +++ b/src/inbox/types/IterableInboxCustomizations.ts @@ -24,7 +24,7 @@ export interface IterableInboxCustomizations { noMessagesBody?: string; /** - * CThe container that holds the unread indicator. + * The container that holds the unread indicator. */ unreadIndicatorContainer?: { /** The flex direction of the container. */ diff --git a/src/index.tsx b/src/index.tsx index 885cd74bd..129e7d61e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -39,12 +39,11 @@ export { IterableInboxMetadata, type IterableHtmlInAppContentRaw, type IterableInAppContent, + type IterableInAppMessageRaw, } from './inApp'; export { IterableInbox, IterableInboxDataModel, - IterableInboxEmptyState, - IterableInboxMessageCell, type IterableInboxCustomizations, type IterableInboxEmptyStateProps, type IterableInboxImpressionRowInfo, diff --git a/tsdoc.json b/tsdoc.json index f4ac01a01..04fd4bc56 100644 --- a/tsdoc.json +++ b/tsdoc.json @@ -1,3 +1,5 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json" + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["typedoc/tsdoc.json", "typedoc-plugin-mermaid/tsdoc.json"], + "noStandardTags": false } diff --git a/typedoc.config.mjs b/typedoc.config.mjs index a24b4d0e8..ffa495883 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -1,38 +1,178 @@ /** @type {Partial} */ const config = { entryPoints: ['./src/index.tsx'], + projectDocuments: ['README.md', 'CHANGELOG.md'], out: './docs', tsconfig: './tsconfig.json', excludeInternal: true, + excludePrivate: true, + excludeExternals: false, + excludePrivateClassFields: true, + categorizeByGroup: true, + excludeProtected: true, validation: { + notExported: true, invalidLink: true, + rewrittenLink: true, notDocumented: true, - notExported: true, + unusedMergeModuleWith: true, }, + entryPointStrategy: 'expand', includeVersion: true, searchInComments: true, searchInDocuments: true, + treatValidationWarningsAsErrors: true, + useFirstParagraphOfCommentAsSummary: true, + disableSources: false, + cascadedModifierTags: ['@beta'], + theme: 'typedoc-github-theme', + router: 'structure', + customFooterHtml: '

Copyright Iterable 2025

', + customFooterHtmlDisableWrapper: true, + markdownLinkExternal: true, + hideGenerator: true, + // groupReferencesByType: true, + sortEntryPoints: true, + sidebarLinks: { + Iterable: 'https://app.iterable.com/', + Support: + 'https://support.iterable.com/hc/en-us/articles/360045714072-Overview-of-Iterable-s-React-Native-SDK', + Installation: + 'https://support.iterable.com/hc/en-us/articles/360045714132-Installing-Iterable-s-React-Native-SDK', + }, + headings: { + readme: true, + document: false, + }, + groupOrder: [ + 'Documents', + 'React Components', + 'Hooks', + 'Classes', + 'Enums', + 'Interfaces', + 'Types', + 'Functions', + 'Variables', + 'Constants', + '*', + ], + categoryOrder: ['React Components', 'Hooks', '*'], + // jsDocCompatibility: { + // inheritDocTag: true, + // }, + preservedTypeAnnotationTags: ['@fires', '@license'], + // transformTags: true, + requiredToBeDocumented: [ + // "Project", + // "Module", + // "Namespace", + 'Enum', + 'EnumMember', + 'Variable', + 'Function', + 'Class', + 'Interface', + 'Constructor', + 'Property', + 'Method', + // Implicitly set if function/method is set (this means you can't require docs on methods, but not functions) + // This exists because methods/functions can have multiple signatures due to overloads, and TypeDoc puts comment + // data on the signature. This might be improved someday, so you probably shouldn't set this directly. + 'CallSignature', + // Index signature { [k: string]: string } "properties" + 'IndexSignature', + // Equivalent to Constructor due to the same implementation detail as CallSignature + 'ConstructorSignature', + // "Parameter", // Commented out - React component props don't need individual parameter docs when the type is documented + // Used for object literal types. You probably should set TypeAlias instead, which refers to types created with `type X =`. + // This only really exists because of an implementation detail. + // "TypeLiteral", // Commented out to avoid warnings on inline function types like () => void + 'TypeParameter', + 'Accessor', // shorthand for GetSignature + SetSignature + 'GetSignature', + 'SetSignature', + 'TypeAlias', + // TypeDoc creates reference reflections if a symbol is exported from a package with multiple names. Most projects + // won't have any of these, and they just render as a link to the canonical name. + 'Reference', + ], navigation: { includeCategories: false, - includeGroups: false, + includeGroups: true, compactFolders: false, excludeReferences: false, - includeFolders: false, + includeFolders: true, + }, + alwaysCreateEntryPointModule: false, + navigationLinks: { + Github: 'https://github.com/Iterable/react-native-sdk', + Changelog: + 'https://github.com/Iterable/react-native-sdk/blob/master/CHANGELOG.md', + }, + searchCategoryBoosts: { + 'React Components': 1.5, + 'Hooks': 1.5, + }, + searchGroupBoosts: { + 'React Components': 1.5, + 'Hooks': 1.5, + 'Documents': 1.5, + 'Classes': 1.3, + }, + visibilityFilters: { + 'protected': false, + 'private': false, + 'inherited': true, + 'external': true, + '@alpha': true, + '@beta': true, }, + sort: [ + 'documents-first', + 'kind', + 'visibility', + 'required-first', + 'enum-value-ascending', + 'alphabetical-ignoring-documents', + 'external-last', + ], + highlightLanguages: [ + 'bash', + 'console', + 'css', + 'html', + 'javascript', + 'json', + 'jsonc', + 'json5', + 'tsx', + 'typescript', + 'ruby', + ], externalSymbolLinkMappings: { // used by `class Foo extends Component {}` '@types/react': { 'Component': 'https://react.dev/reference/react/Component', // used if no other names match + 'FunctionComponent': + 'https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/mdx/types.d.ts', '*': 'https://react.dev/', }, // used by {@link react!Component} 'react': { Component: 'https://react.dev/reference/react/Component', + FunctionComponent: + 'https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/mdx/types.d.ts', }, }, readme: './README.md', - plugin: ['typedoc-plugin-coverage', 'typedoc-plugin-mermaid'], + customJs: './docs-static/expand-nav.js', + plugin: [ + 'typedoc-plugin-coverage', + 'typedoc-plugin-mermaid', + 'typedoc-github-theme', + ], }; export default config; diff --git a/yarn.lock b/yarn.lock index ec4f21c4c..3e42f60d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3151,8 +3151,9 @@ __metadata: react-test-renderer: 19.0.0 release-it: ^17.10.0 turbo: ^1.10.7 - typedoc: ^0.28.13 - typedoc-plugin-coverage: ^3.3.0 + typedoc: latest + typedoc-github-theme: ^0.3.1 + typedoc-plugin-coverage: ^4.0.2 typedoc-plugin-mermaid: ^1.12.0 typescript: ^5.2.2 peerDependencies: @@ -14486,12 +14487,21 @@ __metadata: languageName: node linkType: hard -"typedoc-plugin-coverage@npm:^3.3.0": - version: 3.3.0 - resolution: "typedoc-plugin-coverage@npm:3.3.0" +"typedoc-github-theme@npm:^0.3.1": + version: 0.3.1 + resolution: "typedoc-github-theme@npm:0.3.1" + peerDependencies: + typedoc: ~0.28.0 + checksum: 0690481d64e7dec6264f1a3e10fd74b7ff54c92b1291d3ffcc81725d6ac7ce495db2bfbf37b1e69db542b653db837d5d84c32cfc21e02b41c01e2a2efa3c71e2 + languageName: node + linkType: hard + +"typedoc-plugin-coverage@npm:^4.0.2": + version: 4.0.2 + resolution: "typedoc-plugin-coverage@npm:4.0.2" peerDependencies: - typedoc: 0.25.x || 0.26.x - checksum: b1779429618ab4df38c6b066c4113376b455d19863f70a170fe2e3a06cdcb276b82c4205c67922ea05e926b2fb0c85d932d7e09d00a42d4c4d95689768243524 + typedoc: 0.28.x + checksum: afe01efa282061b0f38bcc53b003bf60d0fb111cca2d4e91a3bf601b92483992c4fc3a4490144e2e7130831d405395fdc3deac988431cd4a9c6cfe0a06735426 languageName: node linkType: hard @@ -14506,9 +14516,9 @@ __metadata: languageName: node linkType: hard -"typedoc@npm:^0.28.13": - version: 0.28.13 - resolution: "typedoc@npm:0.28.13" +"typedoc@npm:latest": + version: 0.28.14 + resolution: "typedoc@npm:0.28.14" dependencies: "@gerrit0/mini-shiki": ^3.12.0 lunr: ^2.3.9 @@ -14519,7 +14529,7 @@ __metadata: typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x bin: typedoc: bin/typedoc - checksum: 238b567661d4118eaf1bc61696ce2129dc0f0d4bd9b0928942bdd40ab9165df842143a04bc1fd8c7c1c2a8978ad2b48118f4bfcd753be322476568a8cc27e355 + checksum: d579280e58f50dfdb4c0017ab1ed5807a98c468ebd2aca4ae4d725194c3b575de2524543d142382c0fa7cd1b7a7b3732245f459677406365c7ba7151b3c87ebc languageName: node linkType: hard