diff --git a/.nvmrc b/.nvmrc index 5007551..17719ce 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -10.16.0 +18.20.4 diff --git a/README.md b/README.md index 2da48c5..0ade302 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ export default [ |---|----|-----|----| |content|function/Element| YES | This is the view displayed in the tooltip popover bubble | |id|string|YES|id string that matches the corresponding WalkthroughElement| +|listenForOutcomesWhileDisplayed|bool|NO | Listens for possible outcomes while the tooltip is displayed |placement|string|NO | Determines placement of tooltip in relation to the element it is wrapping |possibleOutcomes|array|NO|An array of objects with keys (`event`, `action`) that creates event listeners for multiple events to provide the ability to have an outcome tree that responds to a user's actions (listens to events dispatched via `dispatchWalkthroughEvent`| |tooltipProps|object|NO|additional props to customize the tooltip functionality and style diff --git a/package.json b/package.json index 36d3ded..fd930d1 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ }, "homepage": "https://github.com/jasongaare/react-native-walkthrough#readme", "dependencies": { - "events": "3.0.0", - "react-native-walkthrough-tooltip": "1.1.7" + "events": "^3.3.0", + "react-native-walkthrough-tooltip": "^1.3.1" }, "peerDependencies": { "react": "^16.4.1", @@ -34,7 +34,7 @@ "@types/events": "^3.0.0", "@types/jest": "^24.9.0", "@types/react": "^16.9.17", - "@types/react-native": "^0.60.30", + "@types/react-native": "0.65", "@types/react-test-renderer": "^16.9.1", "babel-core": "6.26.3", "babel-eslint": "8.2.3", diff --git a/src/ContextWrapper.tsx b/src/ContextWrapper.tsx index 3691008..7efc581 100644 --- a/src/ContextWrapper.tsx +++ b/src/ContextWrapper.tsx @@ -15,6 +15,7 @@ export type ElementType = { tooltipProps?: TooltipProps; onClose?: () => void; possibleOutcomes?: OutcomeType[]; + listenForOutcomesWhileDisplayed?: boolean; }; export type GuideType = ElementType[]; @@ -37,6 +38,7 @@ export const WalkthroughContext = React.createContext({ }); interface Props { + debug?: boolean; eventEmitter: EventEmitter; } type State = { @@ -65,6 +67,11 @@ class ContextWrapper extends Component { clearCurrentPossibleOutcomes = () => { const { eventEmitter } = this.props; + if (this.props.debug) { + console.debug( + `[react-native-walkthrough] clearing ${this.state.currentPossibleOutcomes.length} possible outcomes` + ); + } this.state.currentPossibleOutcomes.forEach(({ event, action }) => { eventEmitter.removeListener(event, action); @@ -84,6 +91,11 @@ class ContextWrapper extends Component { if (outcomeListenerStartTimestamp === undefined) { console.warn('[react-native-walkthrough] outcomeListenerStartTimestamp not initialized'); } else if (Date.now() - outcomeListenerStartTimestamp >= WAIT_NO_MORE_TIMEOUT) { + if (this.props.debug) { + console.debug( + `[react-native-walkthrough] clearing guide because of walkthrough timeout of ${WAIT_NO_MORE_TIMEOUT}ms` + ); + } this.clearGuide(); } else { originalAction(); @@ -131,6 +143,13 @@ class ContextWrapper extends Component { setGuide = (guide: GuideType, callback?: () => void) => { this.setElementNull(); + if (__DEV__) { + const duplicateElements = guide.filter((element, index) => guide.indexOf(element) !== index); + if (duplicateElements.length > 0) { + const duplicateElementIds = duplicateElements.map(element => element.id).join(', '); + console.warn(`[react-native-walkthrough] guide uses duplicated element IDs: ${duplicateElementIds}`); + } + } this.setState(safeSetGuide(guide), callback); }; @@ -145,6 +164,9 @@ class ContextWrapper extends Component { eventEmitter.once(triggerEvent, () => { const waitEnd = Date.now(); const currentGuide = JSON.stringify(this.state.currentGuide); + if (this.props.debug) { + console.debug(`[react-native-walkthrough] triggering for ${element.id} from ${String(triggerEvent)}`); + } if (waitEnd - waitStart >= WAIT_NO_MORE_TIMEOUT) { this.clearGuide(); @@ -167,7 +189,12 @@ class ContextWrapper extends Component { const elementWithId = this.state.currentGuide.find(element => element.id === id); if (elementWithId) { + if (this.props.debug) { + console.debug(`[react-native-walkthrough] moving to element with ID ${id}`); + } this.goToElement(elementWithId); + } else if (this.props.debug) { + console.debug(`[react-native-walkthrough] could not find element with ID ${id}`); } }; @@ -176,11 +203,32 @@ class ContextWrapper extends Component { const nextIndex = this.getCurrentElementIndex() + 1; if (currentElement.possibleOutcomes) { - this.listenForPossibleOutcomes(currentElement); + if (this.props.debug) { + console.debug(`[react-native-walkthrough] current element has possible outcomes, listening...`); + } + if (!currentElement.listenForOutcomesWhileDisplayed) { + // Only listen if we are not already listening... + this.listenForPossibleOutcomes(currentElement); + } this.setElementNull(); } else if (nextIndex < this.state.currentGuide.length) { - this.goToElement(this.state.currentGuide[nextIndex]); + if (this.props.debug) { + console.debug(`[react-native-walkthrough] moving to next element at index ${nextIndex}`); + } + const nextElement = this.state.currentGuide[nextIndex]; + this.goToElement(nextElement); + if (nextElement.listenForOutcomesWhileDisplayed && nextElement.possibleOutcomes) { + if (this.props.debug) { + console.debug( + `[react-native-walkthrough] next element has ${nextElement.possibleOutcomes.length} possible outcomes, listening...` + ); + } + this.listenForPossibleOutcomes(nextElement); + } } else { + if (this.props.debug) { + console.debug(`[react-native-walkthrough] no more elements, exiting walkthrough`); + } this.setElementNull(); this.clearGuide(); } diff --git a/src/WalkthroughElement.tsx b/src/WalkthroughElement.tsx index b4edeed..f74dd0c 100644 --- a/src/WalkthroughElement.tsx +++ b/src/WalkthroughElement.tsx @@ -55,10 +55,4 @@ const WalkthroughElement: FunctionComponent = props => { ); }; -WalkthroughElement.defaultProps = { - content: undefined, - tooltipProps: undefined, - useTooltipChildContext: false, -}; - export default WalkthroughElement; diff --git a/src/WalkthroughProvider.tsx b/src/WalkthroughProvider.tsx index 097829b..010d350 100644 --- a/src/WalkthroughProvider.tsx +++ b/src/WalkthroughProvider.tsx @@ -1,4 +1,4 @@ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, PropsWithChildren } from 'react'; import PropTypes from 'prop-types'; import { EventEmitter } from 'events'; @@ -7,8 +7,8 @@ import ContextWrapper, { ElementType, GuideType, nullElement } from './ContextWr const wrapperRef = React.createRef(); const ee = new EventEmitter(); -const WalkthroughProvider: FunctionComponent = ({ children }) => ( - +const WalkthroughProvider: FunctionComponent> = ({ debug, children }) => ( + {children} ); diff --git a/yarn.lock b/yarn.lock index 3f285d0..8863c70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1245,12 +1245,11 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/react-native@^0.60.30": - version "0.60.30" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.60.30.tgz#33ab525194142a5e3b428e60f0e77e0d1dbd253d" - integrity sha512-Ho41o+6NBlv1K5q2Z+NREST3UsGahXn4V1to2D2U4bcn1hO5MGjzOdkruzsnN10WiP8hW33jvQE6eERClgwvJg== +"@types/react-native@0.65": + version "0.65.21" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.65.21.tgz#f731b172765f17e4866473de41e1d3a4890ae536" + integrity sha512-6TmhHLEBH7xMOBG+MIExOILOEI+nq/VHmlAJZ7SynJ+/ezG318EFrrxDPge46WPqWT25ZbnhSR6uxzBn7TDRbQ== dependencies: - "@types/prop-types" "*" "@types/react" "*" "@types/react-test-renderer@^16.9.1": @@ -2005,9 +2004,9 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== caniuse-lite@^1.0.30001038: - version "1.0.30001039" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001039.tgz#b3814a1c38ffeb23567f8323500c09526a577bbe" - integrity sha512-SezbWCTT34eyFoWHgx8UWso7YtvtM7oosmFoXbCkdC6qJzRfBTeTgE9REtKtiuKXuMwWTZEvdnFNGAyVMorv8Q== + version "1.0.30001322" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz" + integrity sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew== capture-exit@^2.0.0: version "2.0.0" @@ -2878,10 +2877,10 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -events@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" - integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== exec-sh@^0.3.2: version "0.3.4" @@ -5149,10 +5148,10 @@ react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-native-walkthrough-tooltip@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/react-native-walkthrough-tooltip/-/react-native-walkthrough-tooltip-1.1.7.tgz#972ce320bee617cfe959690cec6d7239f5bbb52a" - integrity sha512-kgN6mokMUtAVTq1qJJjf4SAEujK+A4bZbpkMykoURpzRQuUU7UlHJ/7k8LA7DRTI3sg1Pn0qaDWxDZHgil6I3w== +react-native-walkthrough-tooltip@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/react-native-walkthrough-tooltip/-/react-native-walkthrough-tooltip-1.3.1.tgz#039f644636dbdff1208d1174c26c6f1e43af7e4d" + integrity sha512-YDsmfMZJDwCjWTcqb7P2RNExh7C/hGMm2WIy4txKZDPrU/Hx1INcujG+JkRDvbhBq9KgO4mc3ig65sdjb6IbPQ== dependencies: prop-types "^15.6.1" react-fast-compare "^2.0.4"