Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-joyride

Package Overview
Dependencies
Maintainers
1
Versions
137
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-joyride - npm Package Compare versions

Comparing version 2.0.1 to 2.0.2-0

src/modules/propTypes.js

22

package.json
{
"name": "react-joyride",
"version": "2.0.1",
"version": "2.0.2-0",
"description": "Create guided tours for your apps",

@@ -33,4 +33,4 @@ "author": "Gil Barbara <gilbarbara@gmail.com>",

"peerDependencies": {
"react": "^0.14.0 || ^15.0.0 || ^16.0.0",
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0",
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0",
"prop-types": "^15.0.0"

@@ -45,3 +45,3 @@ },

"react-floater": "^0.6.2",
"react-proptype-conditional-require": "^1.0.4",
"react-is": "^16.7.0",
"scroll": "^2.0.3",

@@ -89,3 +89,3 @@ "scroll-doc": "^0.2.1",

"enzyme-adapter-react-16": "^1.7.1",
"eslint": "^5.11.0",
"eslint": "^5.12.0",
"eslint-config-airbnb": "^17.1.0",

@@ -96,5 +96,5 @@ "eslint-plugin-babel": "^5.3.0",

"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-react": "^7.11.1",
"eslint-plugin-react": "^7.12.3",
"flow-bin": "^0.89.0",
"husky": "^1.2.1",
"husky": "^1.3.1",
"jest": "^23.6.0",

@@ -109,8 +109,8 @@ "jest-chain": "^1.0.5",

"react-dom": "^16.7.0",
"rimraf": "^2.6.2",
"rimraf": "^2.6.3",
"rollup": "^0.68.2",
"rollup-plugin-babel": "^4.1.0",
"rollup-plugin-babel": "^4.2.0",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-filesize": "^5.0.1",
"rollup-plugin-node-resolve": "^4.0.0",
"typescript": "^3.2.2",
"yargs": "^12.0.5"

@@ -120,3 +120,3 @@ },

"build": "cross-env NODE_ENV=production npm run clean && rollup -c",
"watch": "cross-env NODE_ENV=production rollup -cw",
"watch": "cross-env NODE_ENV=development rollup -cw",
"clean": "rimraf es && rimraf lib",

@@ -123,0 +123,0 @@ "lint": "eslint --ext .js --ext .jsx src test",

@@ -5,3 +5,3 @@ # React Joyride

[![Joyride example image](http://gilbarbara.github.io/react-joyride/media/example.png)](http://gilbarbara.github.io/react-joyride/)
[![Joyride example image](http://gilbarbara.com/files/react-joyride.png)](https://react-joyride.com/)

@@ -15,5 +15,5 @@ #### Create awesome tours for your app!

**View the demo [here](https://2zpjporp4p.codesandbox.io/)**
**View the demo [here](https://react-joyride.com/)**
**Read the [docs](https://gilbarbara.gitbook.io/react-joyride/)**
**Read the [docs](https://docs.react-joyride.com/)**

@@ -20,0 +20,0 @@ ## Setup

import React from 'react';
import PropTypes from 'prop-types';
import is from 'is-lite';
import { componentTypeWithRefs } from '../modules/propTypes';

@@ -48,6 +50,3 @@ export default class JoyrideBeacon extends React.Component {

static propTypes = {
beaconComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.element,
]),
beaconComponent: componentTypeWithRefs,
locale: PropTypes.object.isRequired,

@@ -58,2 +57,16 @@ onClickOrHover: PropTypes.func.isRequired,

componentDidMount() {
if (process.env.NODE_ENV !== 'production') {
if (!is.domElement(this.beacon)) {
console.warn('beacon is not a valid DOM element'); //eslint-disable-line no-console
}
}
if (is.domElement(this.beacon)) {
setTimeout(() => {
this.beacon.focus();
}, 0);
}
}
componentWillUnmount() {

@@ -67,2 +80,6 @@ const style = document.getElementById('joyride-beacon-animation');

setBeaconRef = (c) => {
this.beacon = c;
};
render() {

@@ -74,2 +91,3 @@ const { beaconComponent, locale, onClickOrHover, styles } = this.props;

onMouseEnter: onClickOrHover,
ref: this.setBeaconRef,
title: locale.open,

@@ -76,0 +94,0 @@ };

@@ -16,2 +16,3 @@ import React from 'react';

import { canUseDOM, isEqual, log } from '../modules/helpers';
import { componentTypeWithRefs } from '../modules/propTypes';
import { getMergedStep, validateSteps } from '../modules/step';

@@ -31,6 +32,3 @@

static propTypes = {
beaconComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.element,
]),
beaconComponent: componentTypeWithRefs,
callback: PropTypes.func,

@@ -60,6 +58,3 @@ continuous: PropTypes.bool,

styles: PropTypes.object,
tooltipComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.element,
]),
tooltipComponent: componentTypeWithRefs,
};

@@ -296,4 +291,3 @@

const target = getElement(step.target);
const shouldScroll = step
&& !disableScrolling
const shouldScroll = !disableScrolling
&& step.placement !== 'center'

@@ -342,3 +336,3 @@ && (!step.isFixed || !isFixed(target)) // fixed steps don't need to scroll

/* istanbul ignore else */
if (status === STATUS.RUNNING && shouldScroll) {
if (status === STATUS.RUNNING) {
scrollTo(scrollY, scrollParent);

@@ -345,0 +339,0 @@ }

@@ -195,3 +195,3 @@ import React from 'react';

const stylesOverlay = {
cursor: disableOverlay ? 'default' : 'pointer',
cursor: 'pointer',
height: getDocumentHeight(),

@@ -198,0 +198,0 @@ pointerEvents: mouseOverSpotlight ? 'none' : 'auto',

import React from 'react';
import PropTypes from 'prop-types';
import isRequiredIf from 'react-proptype-conditional-require';
import Floater from 'react-floater';

@@ -12,3 +11,4 @@ import treeChanges from 'tree-changes';

import { log, hideBeacon } from '../modules/helpers';
import { setScope, removeScope } from '../modules/scope';
import { componentTypeWithRefs } from '../modules/propTypes';
import Scope from '../modules/scope';
import { validateStep } from '../modules/step';

@@ -22,2 +22,4 @@

export default class JoyrideStep extends React.Component {
scope = { removeScope: () => {} };
static propTypes = {

@@ -36,7 +38,4 @@ action: PropTypes.string.isRequired,

step: PropTypes.shape({
beaconComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.element,
]),
content: isRequiredIf(PropTypes.node, props => !props.tooltipComponent && !props.title),
beaconComponent: componentTypeWithRefs,
content: PropTypes.node.isRequired,
disableBeacon: PropTypes.bool,

@@ -72,6 +71,3 @@ disableOverlay: PropTypes.bool,

title: PropTypes.node,
tooltipComponent: isRequiredIf(PropTypes.oneOfType([
PropTypes.func,
PropTypes.element,
]), props => !props.content && !props.title),
tooltipComponent: componentTypeWithRefs,
}).isRequired,

@@ -82,6 +78,6 @@ update: PropTypes.func.isRequired,

componentDidMount() {
const { debug, lifecycle } = this.props;
const { debug, index } = this.props;
log({
title: `step:${lifecycle}`,
title: `step:${index}`,
data: [

@@ -177,7 +173,8 @@ { key: 'props', value: this.props },

setScope(this.tooltip);
this.scope = new Scope(this.tooltip, { selector: '[data-action=primary]' });
this.scope.setFocus();
}
if (changedFrom('lifecycle', [LIFECYCLE.TOOLTIP, LIFECYCLE.INIT], LIFECYCLE.INIT)) {
removeScope();
this.scope.removeScope();
delete this.beaconPopper;

@@ -189,3 +186,3 @@ delete this.tooltipPopper;

componentWillUnmount() {
removeScope();
this.scope.removeScope();
}

@@ -192,0 +189,0 @@

@@ -6,119 +6,118 @@ import React from 'react';

const JoyrideTooltipContainer = ({
continuous,
backProps,
closeProps,
primaryProps,
skipProps,
index,
isLastStep,
setTooltipRef,
size,
step,
}) => {
const {
content,
hideBackButton,
hideCloseButton,
hideFooter,
locale,
showProgress,
showSkipButton,
title,
styles,
} = step;
const { back, close, last, next, skip } = locale;
const output = {
primary: close,
export default class JoyrideTooltipContainer extends React.Component {
static propTypes = {
backProps: PropTypes.object.isRequired,
closeProps: PropTypes.object.isRequired,
continuous: PropTypes.bool.isRequired,
index: PropTypes.number.isRequired,
isLastStep: PropTypes.bool.isRequired,
primaryProps: PropTypes.object.isRequired,
size: PropTypes.number.isRequired,
skipProps: PropTypes.object.isRequired,
step: PropTypes.object.isRequired,
tooltipProps: PropTypes.object.isRequired,
};
if (continuous) {
output.primary = isLastStep ? last : next;
render() {
const {
backProps,
closeProps,
continuous,
index,
isLastStep,
primaryProps,
size,
skipProps,
step,
tooltipProps,
} = this.props;
const {
content,
hideBackButton,
hideCloseButton,
hideFooter,
showProgress,
showSkipButton,
title,
styles,
} = step;
const { back, close, last, next, skip } = step.locale;
const output = {
primary: close,
};
if (showProgress) {
output.primary = <span>{output.primary} ({index + 1}/{size})</span>;
if (continuous) {
output.primary = isLastStep ? last : next;
if (showProgress) {
output.primary = <span>{output.primary} ({index + 1}/{size})</span>;
}
}
}
if (showSkipButton && !isLastStep) {
output.skip = (
<button
style={styles.buttonSkip}
type="button"
data-test-id="button-skip"
{...skipProps}
>
{skip}
</button>
if (showSkipButton && !isLastStep) {
output.skip = (
<button
style={styles.buttonSkip}
type="button"
data-test-id="button-skip"
aria-live="off"
{...skipProps}
>
{skip}
</button>
);
}
if (!hideBackButton && index > 0) {
output.back = (
<button
style={styles.buttonBack}
type="button"
data-test-id="button-back"
{...backProps}
>
{back}
</button>
);
}
output.close = !hideCloseButton && (
<CloseBtn
styles={styles.buttonClose}
data-test-id="button-close"
{...closeProps}
/>
);
}
if (!hideBackButton && index > 0) {
output.back = (
<button
style={styles.buttonBack}
type="button"
data-test-id="button-back"
{...backProps}
return (
<div
key="JoyrideTooltip"
className="react-joyride__tooltip"
style={styles.tooltip}
{...tooltipProps}
>
{back}
</button>
);
}
output.close = !hideCloseButton && (
<CloseBtn
styles={styles.buttonClose}
data-test-id="button-close"
{...closeProps}
/>
);
return (
<div
key="JoyrideTooltip"
className="react-joyride__tooltip"
ref={setTooltipRef}
style={styles.tooltip}
>
<div style={styles.tooltipContainer}>
{output.close}
{title && (<h4 style={styles.tooltipTitle}>{title}</h4>)}
{!!content && (
<div style={styles.tooltipContainer}>
{title && (<h4 style={styles.tooltipTitle} aria-label={title}>{title}</h4>)}
<div style={styles.tooltipContent}>
{content}
</div>
</div>
{!hideFooter && (
<div style={styles.tooltipFooter} aria-relevant="additions">
{output.skip}
{output.back}
<button
style={styles.buttonNext}
type="button"
data-test-id="button-primary"
{...primaryProps}
>
{output.primary}
</button>
</div>
)}
{output.close}
</div>
{!hideFooter && (
<div style={styles.tooltipFooter}>
{output.skip}
{output.back}
<button
style={styles.buttonNext}
type="button"
data-test-id="button-primary"
{...primaryProps}
>
{output.primary}
</button>
</div>
)}
</div>
);
};
JoyrideTooltipContainer.propTypes = {
backProps: PropTypes.object.isRequired,
closeProps: PropTypes.object.isRequired,
continuous: PropTypes.bool.isRequired,
index: PropTypes.number.isRequired,
isLastStep: PropTypes.bool.isRequired,
primaryProps: PropTypes.object.isRequired,
setTooltipRef: PropTypes.func.isRequired,
size: PropTypes.number.isRequired,
skipProps: PropTypes.object.isRequired,
step: PropTypes.object.isRequired,
};
export default JoyrideTooltipContainer;
);
}
}
import React from 'react';
import PropTypes from 'prop-types';
import { getText } from '../../modules/helpers';
import Container from './Container';

@@ -50,27 +50,60 @@

render() {
const { continuous, index, isLastStep, setTooltipRef, size, step } = this.props;
const { beaconComponent, tooltipComponent, ...cleanStep } = step;
getElementsProps = () => {
const { continuous, isLastStep, setTooltipRef, step } = this.props;
const { back, close, last, next, skip } = step.locale;
let primaryText = continuous ? next : close;
let primaryText = getText(continuous ? next : close);
if (isLastStep) {
primaryText = last;
primaryText = getText(last);
}
let component;
const buttonProps = {
backProps: { 'aria-label': back, onClick: this.handleClickBack, role: 'button', title: back },
closeProps: { 'aria-label': close, onClick: this.handleClickClose, role: 'button', title: close },
primaryProps: { 'aria-label': primaryText, onClick: this.handleClickPrimary, role: 'button', title: primaryText },
skipProps: { 'aria-label': skip, onClick: this.handleClickSkip, role: 'button', title: skip },
return {
backProps: {
'aria-label': getText(back),
'data-action': 'back',
onClick: this.handleClickBack,
role: 'button',
title: back,
},
closeProps: {
'aria-label': getText(close),
'data-action': 'close',
onClick: this.handleClickClose,
role: 'button',
title: close,
},
primaryProps: {
'aria-label': getText(primaryText),
'data-action': 'primary',
onClick: this.handleClickPrimary,
role: 'button',
title: primaryText,
},
skipProps: {
'aria-label': getText(skip),
'data-action': 'skip',
onClick: this.handleClickSkip,
role: 'button',
title: skip,
},
tooltipProps: {
role: 'alertdialog',
'aria-modal': true,
ref: setTooltipRef,
},
};
};
render() {
const { continuous, index, isLastStep, size, step } = this.props;
const { beaconComponent, tooltipComponent, ...cleanStep } = step;
let component;
if (tooltipComponent) {
const renderProps = {
...buttonProps,
...this.getElementsProps(),
continuous,
index,
isLastStep,
setTooltipRef,
size,

@@ -86,9 +119,8 @@ step: cleanStep,

<Container
{...this.getElementsProps()}
continuous={continuous}
index={index}
isLastStep={isLastStep}
setTooltipRef={setTooltipRef}
size={size}
step={step}
{...buttonProps}
/>

@@ -95,0 +127,0 @@ );

@@ -18,3 +18,3 @@ export default {

next: 'Next',
open: 'Open',
open: 'Open the dialog',
skip: 'Skip',

@@ -21,0 +21,0 @@ },

@@ -70,2 +70,29 @@ // @flow

export function getText(rootChild: any): string {
let res = '';
const recurse = (child) => {
if (typeof child === 'string' || typeof child === 'number') {
res += child;
}
else if (Array.isArray(child)) {
child.forEach(c => recurse(c));
}
else if (child && child.props) {
const { children } = child.props;
if (Array.isArray(children)) {
children.forEach(c => recurse(c));
}
else {
recurse(children);
}
}
};
recurse(rootChild);
return res;
}
export function hasKey(value: Object, key: string): boolean {

@@ -72,0 +99,0 @@ return Object.prototype.hasOwnProperty.call(value, key);

@@ -1,41 +0,55 @@

const validTabNodes = /input|select|textarea|button|object/;
const TAB_KEY = 9;
// @flow
class Scope {
constructor(tooltip) {
this.tooltip = tooltip;
export default class Scope {
element: HTMLElement;
options: Object;
constructor(element: HTMLElement, options: ?Object = {}) {
if (!(element instanceof HTMLElement)) {
throw new TypeError('Invalid parameter: element must be an HTMLElement');
}
this.element = element;
this.options = options;
window.addEventListener('keydown', this.handleKeyDown, false);
this.setFocus();
}
canBeTabbed = (element) => {
let tabIndex = element.getAttribute('tabindex');
if (tabIndex === null) tabIndex = undefined;
canBeTabbed = (element: HTMLElement): boolean => {
let { tabIndex } = element;
if (tabIndex === null || tabIndex < 0) tabIndex = undefined;
const isTabIndexNaN = isNaN(tabIndex);
return (isTabIndexNaN || tabIndex >= 0) && this.canHaveFocus(element, !isTabIndexNaN);
return (!isTabIndexNaN) && this.canHaveFocus(element, true);
};
canHaveFocus = (element, isTabIndexNotNaN) => {
canHaveFocus = (element: HTMLElement, isTabIndexNotNaN: boolean): boolean => {
const validTabNodes = /input|select|textarea|button|object/;
const nodeName = element.nodeName.toLowerCase();
const res = (validTabNodes.test(nodeName) && !element.disabled)
|| (nodeName === 'a' ? element.href || isTabIndexNotNaN : isTabIndexNotNaN);
const res = (validTabNodes.test(nodeName) && !element.getAttribute('disabled'))
|| (nodeName === 'a' ? element.getAttribute('href') || isTabIndexNotNaN : isTabIndexNotNaN);
return res && this.isVisible(element);
};
findValidTabElements = (element) => [].slice.call(element.querySelectorAll('*'), 0).filter(this.canBeTabbed);
findValidTabElements = () => [].slice.call(this.element.querySelectorAll('*'), 0).filter(this.canBeTabbed);
handleKeyDown = (e) => {
if (!this.tooltip) {
return;
}
handleKeyDown = (e: KeyboardEvent) => {
const { keyCode = 9 } = this.options;
if (e.keyCode === TAB_KEY) {
this.interceptTab(this.tooltip, e);
/* istanbul ignore else */
if (e.keyCode === keyCode) {
this.interceptTab(e);
}
};
interceptTab = (node, event) => {
const elements = this.findValidTabElements(node);
interceptTab = (event: KeyboardEvent) => {
event.preventDefault();
const elements = this.findValidTabElements();
const { shiftKey } = event;
if (!elements.length) {
event.preventDefault();
return;

@@ -49,2 +63,5 @@ }

}
else if (shiftKey && x === 0) {
x = elements.length - 1;
}
else {

@@ -54,8 +71,6 @@ x += shiftKey ? -1 : 1;

event.preventDefault();
elements[x].focus();
};
isHidden = (element) => {
isHidden = (element: HTMLElement) => {
const noSize = element.offsetWidth <= 0 && element.offsetHeight <= 0;

@@ -69,23 +84,33 @@ const style = window.getComputedStyle(element);

isVisible = (element) => {
isVisible = (element: HTMLElement) => {
let parentElement = element;
while (parentElement) {
if (parentElement === document.body) break;
if (this.isHidden(parentElement)) return false;
parentElement = parentElement.parentNode;
/* istanbul ignore else */
if (parentElement instanceof HTMLElement) {
if (parentElement === document.body) break;
/* istanbul ignore else */
if (this.isHidden(parentElement)) return false;
parentElement = parentElement.parentNode;
}
}
return true;
};
}
let scope;
removeScope = () => {
window.removeEventListener('keydown', this.handleKeyDown);
};
export function setScope(element) {
scope = new Scope(element);
setFocus = () => {
const { selector } = this.options;
if (!selector) return;
window.addEventListener('keydown', scope.handleKeyDown, false);
}
const target = this.element.querySelector(selector);
export function removeScope() {
window.removeEventListener('keydown', scope.handleKeyDown);
/* istanbul ignore else */
if (target) {
target.focus();
}
};
}

@@ -32,3 +32,3 @@ import deepmerge from 'deepmerge';

export default function getStyles(stepStyles) {
export default function getStyles(stepStyles = {}) {
const options = deepmerge(defaultOptions, stepStyles.options || {});

@@ -40,5 +40,2 @@ let width = 290;

}
else if (window.innerWidth > 768) {
width = 490;
}

@@ -126,3 +123,3 @@ if (options.width) {

display: 'flex',
justifyContent: 'space-between',
justifyContent: 'flex-end',
marginTop: 15,

@@ -156,2 +153,3 @@ },

fontSize: 14,
marginRight: 'auto',
},

@@ -189,3 +187,3 @@ overlay: {

return deepmerge(defaultStyles, stepStyles || {});
return deepmerge(defaultStyles, stepStyles);
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc