jupyterlab-tour 3.1.4 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,20 @@
1
1
  # jupyterlab-tour
2
2
 
3
- [![Extension status](https://img.shields.io/badge/status-ready-success "ready to be used")](https://jupyterlab-contrib.github.io/)
3
+ [![Extension status](https://img.shields.io/badge/status-ready-success 'ready to be used')](https://jupyterlab-contrib.github.io/)
4
4
  [![Github Actions Status](https://github.com/jupyterlab-contrib/jupyterlab-tour/workflows/Build/badge.svg)](https://github.com/jupyterlab-contrib/jupyterlab-tour/actions?query=workflow%3ABuild)
5
- [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyterlab-contrib/jupyterlab-tour.git/master?urlpath=lab)
5
+ [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyterlab-contrib/jupyterlab-tour/main?urlpath=lab)
6
6
  [![npm](https://img.shields.io/npm/v/jupyterlab-tour)](https://www.npmjs.com/package/jupyterlab-tour)
7
7
  [![PyPI](https://img.shields.io/pypi/v/jupyterlab-tour)](https://pypi.org/project/jupyterlab-tour)
8
8
  [![conda-forge](https://img.shields.io/conda/vn/conda-forge/jupyterlab-tour)](https://anaconda.org/conda-forge/jupyterlab-tour)
9
9
 
10
10
  A JupyterLab UI Tour based on [react-joyride](https://docs.react-joyride.com).
11
11
 
12
- ![demo](https://raw.githubusercontent.com/jupyterlab-contrib/jupyterlab-tour/master/doc/tourDemo.gif)
12
+ ![demo](https://raw.githubusercontent.com/jupyterlab-contrib/jupyterlab-tour/main/doc/tourDemo.gif)
13
13
 
14
14
  ## Install
15
15
 
16
+ To install the extension, execute:
17
+
16
18
  ```bash
17
19
  pip install jupyterlab-tour
18
20
  ```
@@ -31,31 +33,38 @@ This extension has the following features:
31
33
  - Welcome tour
32
34
  - Notebook tour
33
35
  - User-defined features in Settings
34
- - Toast proposing to start a tour - to experienced users the need to exit each time the tour.
35
- - If a tour has already be seen by the user, this is saved in the state database. So you can start tour on event only if the user have not seen it; e.g. the welcome tour is launched at JupyterLab start except if the user have seen it.
36
+ - Toast proposing to start a Tour
37
+ - If a Tour has already be seen by the user, this is saved in the state database. So you
38
+ can start Tour on event only if the user have not seen it; e.g. the _Welcome Tour_ is
39
+ launched at JupyterLab start except if the user have seen it.
36
40
 
37
41
  > The state is cleaned if this extension is updated
38
42
 
39
43
  - Tooltip are styled using JupyterLab theming system
40
44
  - Commands to _add_ and _launch_ tours
41
- - Through the tour manager (`ITourManager` extension token), add, modify or delete a tour
42
- - Connect to tour events through signals
43
- - Override the default style (and options) for the tour separately
45
+ - Through the _Tour Manager_ (`ITourManager` extension token), add, modify or delete a
46
+ Tour
47
+ - Connect to Tour events through signals
48
+ - Override the default style (and options) for the Tour separately
44
49
 
45
- This extension is inspired by [@cdat/jupyterlab-tutorial](https://github.com/CDAT/jupyterlab-tutorial) licensed under BSD 3-Clause License with Copyright (c) 2020, Lawrence Livermore National Security, LLC.
50
+ This extension is inspired by
51
+ [@cdat/jupyterlab-tutorial](https://github.com/CDAT/jupyterlab-tutorial) licensed under
52
+ BSD 3-Clause License with Copyright (c) 2020, Lawrence Livermore National Security, LLC.
46
53
 
47
54
  ## Requirements
48
55
 
49
- - JupyterLab >= 3.0
56
+ - JupyterLab >= 3.6
50
57
 
51
- For JupyterLab 2.x, have look [there](https://github.com/jupyterlab-contrib/jupyterlab-tour/tree/2.x).
58
+ For JupyterLab 2.x, have look
59
+ [there](https://github.com/jupyterlab-contrib/jupyterlab-tour/tree/2.x).
52
60
 
53
- > For developers, the API has changed between v3 (for JupyterLab 3) and v2 (for JupyterLab 2).
61
+ > For developers, the API has changed between v3 (for JupyterLab 3) and v2 (for
62
+ > JupyterLab 2).
54
63
 
55
- ## How to add a tour with Advanced Settings
64
+ ## How to add a Tour with Advanced Settings
56
65
 
57
- As a user of JupyterLab, after you've installed `jupyterlab-tour`, you can create
58
- your own _Tours_ as data.
66
+ As a user of JupyterLab, after you've installed `jupyterlab-tour`, you can create your
67
+ own _Tours_ as data.
59
68
 
60
69
  - Open the JupyterLab _Advanced Settings_ panel <kbd>Ctrl+,</kbd>
61
70
  - Select _Tours_ from list of settings groups
@@ -71,20 +80,18 @@ overlay when pressed:
71
80
  ```json5
72
81
  // json5 can have comments
73
82
  {
74
- "tours": [
83
+ tours: [
75
84
  {
76
- "id": "my-tour",
77
- "label": "My First Tour",
85
+ id: 'my-tour',
86
+ label: 'My First Tour',
78
87
  // steps are required, and have many, many options
79
- "steps": [
80
- {"target": "#jp-MainLogo", "content": "Look at this!"}
81
- ],
88
+ steps: [{ target: '#jp-MainLogo', content: 'Look at this!' }],
82
89
  // below here not required!
83
- "options": {
84
- "styles": {
85
- "options": {
90
+ options: {
91
+ styles: {
92
+ options: {
86
93
  // you can use jupyterlab theme variables
87
- "backgroundColor": "var(--jp-warn-color0)"
94
+ backgroundColor: 'var(--jp-warn-color0)'
88
95
  }
89
96
  }
90
97
  }
@@ -93,42 +100,65 @@ overlay when pressed:
93
100
  }
94
101
  ```
95
102
 
103
+ ## How to add a Tour to a Notebook
104
+
105
+ The same JSON used to create a Tour in _Advanced Settings_ can be added to a Notebook.
106
+
107
+ - Open the Notebook
108
+ - Open the _Property Inspector_ sidebar (the "gears" icon)
109
+ - Open _Advanced Tools_
110
+ - Create a key in _Notebook Metadata_ like:
111
+
112
+ ```json
113
+ {
114
+ "jupyterlab-tour": {
115
+ "tours": []
116
+ }
117
+ }
118
+ ```
119
+
120
+ Now, when the notebook is opened, a "pin" icon will be visible in the _Notebook Toolbar_
121
+ which will allow lauching one (or all) of the tours saved in the Notebook.
122
+
96
123
  ### Shipping a Tour to Binder
97
124
 
98
- On Binder, and elsewhere, you can store the above (_without_ comments) in
99
- an [overrides.json] file and put it in the _right place_,
100
- e.g. `{sys.prefix}/share/jupyter/lab/settings/overrides.json`. When JupyterLab is
101
- next opened, those overrides will become the defaults, and your tour will be available.
125
+ On Binder, and elsewhere, you can store the above (_without_ comments) in an
126
+ [overrides.json] file and put it in the _right place_, e.g.
127
+ `{sys.prefix}/share/jupyter/lab/settings/overrides.json`. When JupyterLab is next
128
+ opened, those overrides will become the defaults, and your Tour will be available.
102
129
 
103
130
  An example `overrides.json` might look like:
104
- ```json5
131
+
132
+ ```json
105
133
  {
106
134
  "jupyterlab-tour:user-tours": {
107
- "tours": [
108
- // that tour up there!
109
- ]
135
+ "tours": []
110
136
  }
111
137
  }
112
138
  ```
113
139
 
114
- [overrides.json]: https://jupyterlab.readthedocs.io/en/stable/user/directories.html#overrides-json
140
+ [overrides.json]:
141
+ https://jupyterlab.readthedocs.io/en/stable/user/directories.html#overrides-json
115
142
 
116
- ## How to add tour for my JupyterLab extension
143
+ ## How to add Tour for my JupyterLab extension
117
144
 
118
- As an extension developer, there are two methods to add a tour: the easiest is to use JupyterLab commands and the advanced version is to request this
119
- extension token `ITourManager`.
145
+ As an extension developer, there are two methods to add a tour: the easiest is to use
146
+ JupyterLab commands and the advanced version is to request this extension token
147
+ `ITourManager`.
120
148
 
121
- ### Add easily a tour
149
+ ### Add a Tour with TypeScript
122
150
 
123
151
  ```ts
124
152
  const { commands } = app;
125
- // Add a tour - returns the tour or null if something went wrong
153
+ // Add a Tour - returns the Tour or null if something went wrong
126
154
  const tour = (await app.commands.execute('jupyterlab-tour:add', {
127
- tour: { // Tour must be of type ITour - see src/tokens.ts
155
+ tour: {
156
+ // Tour must be of type ITour - see src/tokens.ts
128
157
  id: 'test-jupyterlab-tour:welcome',
129
158
  label: 'Welcome Tour',
130
159
  hasHelpEntry: true,
131
- steps: [ // Step must be of type IStep - see src/tokens.ts
160
+ steps: [
161
+ // Step must be of type IStep - see src/tokens.ts
132
162
  {
133
163
  content:
134
164
  'The following tutorial will point out some of the main UI components within JupyterLab.',
@@ -143,46 +173,48 @@ const tour = (await app.commands.execute('jupyterlab-tour:add', {
143
173
  target: '#jp-main-dock-panel',
144
174
  title: 'Main Content'
145
175
  }
146
- ],
176
+ ]
147
177
  // can also define `options`
148
178
  }
149
179
  })) as ITourHandler;
150
- if ( tour ) {
180
+ if (tour) {
151
181
  app.commands.execute('jupyterlab-tour:launch', {
152
182
  id: 'test-jupyterlab-tour:welcome',
153
- force: false // Optional, if false the tour will start only if the user have not seen or skipped it
154
- })
183
+ force: false // Optional, if false the Tour will start only if the user have not seen or skipped it
184
+ });
155
185
  }
156
186
  ```
157
187
 
158
- > One example is available on [Mamba navigator](https://github.com/mamba-org/gator/blob/master/packages/labextension/src/index.ts#L76).
188
+ > One example is available on
189
+ > [Mamba navigator](https://github.com/mamba-org/gator/blob/master/packages/labextension/src/index.ts#L76).
159
190
  > Test it on [binder](https://mybinder.org/v2/gh/mamba-org/gator/master?urlpath=lab).
160
191
 
161
- If you want to react to step changes to trigger elements of the UI (like opening sidebar), you can connect
162
- to the `stepChanged` signal. Building from the previous example, this snippet will open the filebrowser
163
- after the first step.
192
+ If you want to react to step changes to trigger elements of the UI (like opening
193
+ sidebar), you can connect to the `stepChanged` signal. Building from the previous
194
+ example, this snippet will open the filebrowser after the first step.
164
195
 
165
196
  ```ts
166
- tour.stepChanged.connect((_, data) => {
167
- switch (data.type) {
168
- case 'step:after':
169
- if (data.step.target === '#jp-main-dock-panel') {
170
- commands.execute('filebrowser:activate');
171
- }
172
- break;
173
- }
174
- });
197
+ tour.stepChanged.connect((_, data) => {
198
+ switch (data.type) {
199
+ case 'step:after':
200
+ if (data.step.target === '#jp-main-dock-panel') {
201
+ commands.execute('filebrowser:activate');
202
+ }
203
+ break;
204
+ }
205
+ });
175
206
  ```
176
207
 
177
- > `data` is an object of type [`CallbackProps`](https://docs.react-joyride.com/callback).
208
+ > `data` is an object of type
209
+ > [`CallbackProps`](https://docs.react-joyride.com/callback).
178
210
 
179
- ## Disabling the User and Default Tours
211
+ ## Disabling the User, Notebook, and Default Tours
180
212
 
181
- If you _only_ wish to see the default _Welcome_ and _Notebook_ tours, or ones
182
- defined by users, they can be disabled via the command line or a well-known file.
213
+ If you _only_ wish to see the default _Welcome_ and _Notebook_ tours, or ones defined by
214
+ users, they can be disabled via the command line or a well-known file.
183
215
 
184
- The examples below disable all tours not provided by third-party extensions.
185
- Adding `jupyterlab-tour:plugin` to either of these will disable tours altogether!
216
+ The examples below disable all tours not provided by third-party extensions. Adding
217
+ `jupyterlab-tour:plugin` to either of these will disable tours altogether!
186
218
 
187
219
  ### Disabling Tours from the Command Line
188
220
 
@@ -190,6 +222,7 @@ From the command line, run:
190
222
 
191
223
  ```bash
192
224
  jupyter labextension disable "jupyterlab-tour:user-tours"
225
+ jupyter labextension disable "jupyterlab-tour:notebook-tours"
193
226
  jupyter labextension disable "jupyterlab-tour:default-tours"
194
227
  ```
195
228
 
@@ -203,16 +236,19 @@ Create a [pageConfig.json] and put it in _the right place_, e.g.
203
236
  {
204
237
  "disabledExtensions": {
205
238
  "jupyterlab-tour:user-tours": true,
206
- "jupyterlab-tour:default-tours": true,
239
+ "jupyterlab-tour:notebook-tours": true,
240
+ "jupyterlab-tour:default-tours": true
207
241
  }
208
242
  }
209
243
  ```
210
244
 
211
- [pageConfig.json]: https://jupyterlab.readthedocs.io/en/stable/user/directories.html#labconfig-directories
212
-
245
+ [pageConfig.json]:
246
+ https://jupyterlab.readthedocs.io/en/stable/user/directories.html#labconfig-directories
213
247
 
214
248
  ## Uninstall
215
249
 
250
+ To remove the extension, execute:
251
+
216
252
  ```bash
217
253
  pip uninstall jupyterlab-tour
218
254
  ```
@@ -229,34 +265,76 @@ conda remove -c conda-forge jupyterlab-tour
229
265
 
230
266
  Note: You will need NodeJS to build the extension package.
231
267
 
232
- The `jlpm` command is JupyterLab's pinned version of
233
- [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use
234
- `yarn` or `npm` in lieu of `jlpm` below.
268
+ The `jlpm` command is JupyterLab's pinned version of [yarn](https://yarnpkg.com/) that
269
+ is installed with JupyterLab. You may use `yarn` or `npm` in lieu of `jlpm` below.
235
270
 
236
271
  ```bash
237
272
  # Clone the repo to your local environment
238
273
  # Change directory to the jupyterlab-tour directory
239
274
  # Install package in development mode
240
- pip install -e .
275
+ pip install -e "."
241
276
  # Link your development version of the extension with JupyterLab
242
277
  jupyter labextension develop . --overwrite
243
278
  # Rebuild extension Typescript source after making changes
244
- jlpm run build
279
+ jlpm build
245
280
  ```
246
281
 
247
- You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.
282
+ You can watch the source directory and run JupyterLab at the same time in different
283
+ terminals to watch for changes in the extension's source and automatically rebuild the
284
+ extension.
248
285
 
249
286
  ```bash
250
287
  # Watch the source directory in one terminal, automatically rebuilding when needed
251
- jlpm run watch
288
+ jlpm watch
252
289
  # Run JupyterLab in another terminal
253
290
  jupyter lab
254
291
  ```
255
292
 
256
- With the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt).
293
+ With the watch command running, every saved change will immediately be built locally and
294
+ available in your running JupyterLab. Refresh JupyterLab to load the change in your
295
+ browser (you may need to wait several seconds for the extension to be rebuilt).
257
296
 
258
- By default, the `jlpm run build` command generates the source maps for this extension to make it easier to debug using the browser dev tools. To also generate source maps for the JupyterLab core extensions, you can run the following command:
297
+ By default, the `jlpm build` command generates the source maps for this extension to
298
+ make it easier to debug using the browser dev tools. To also generate source maps for
299
+ the JupyterLab core extensions, you can run the following command:
259
300
 
260
301
  ```bash
261
302
  jupyter lab build --minimize=False
262
303
  ```
304
+
305
+ ### Development uninstall
306
+
307
+ ```bash
308
+ pip uninstall jupyterlab-tour
309
+ ```
310
+
311
+ In development mode, you will also need to remove the symlink created by
312
+ `jupyter labextension develop` command. To find its location, you can run
313
+ `jupyter labextension list` to figure out where the `labextensions` folder is located.
314
+ Then you can remove the symlink named `jupyterlab-tour` within that folder.
315
+
316
+ ### Testing the extension
317
+
318
+ #### Frontend tests
319
+
320
+ This extension is using [Jest](https://jestjs.io/) for JavaScript code testing.
321
+
322
+ To execute them, execute:
323
+
324
+ ```sh
325
+ jlpm
326
+ jlpm test
327
+ ```
328
+
329
+ #### Integration tests
330
+
331
+ This extension uses [Playwright](https://playwright.dev/docs/intro) for the integration
332
+ tests (aka user level tests). More precisely, the JupyterLab helper
333
+ [Galata](https://github.com/jupyterlab/jupyterlab/tree/master/galata) is used to handle
334
+ testing the extension in JupyterLab.
335
+
336
+ More information are provided within the [ui-tests](./ui-tests/README.md) README.
337
+
338
+ ### Packaging the extension
339
+
340
+ See [RELEASE](RELEASE.md)
@@ -0,0 +1 @@
1
+ import 'jest';
@@ -0,0 +1,151 @@
1
+ import { Notebook, NotebookModel, NotebookPanel } from '@jupyterlab/notebook';
2
+ import { CommandRegistry } from '@lumino/commands';
3
+ import { DocumentRegistry } from '@jupyterlab/docregistry';
4
+ import { CodeMirrorEditorFactory, CodeMirrorMimeTypeService, EditorLanguageRegistry } from '@jupyterlab/codemirror';
5
+ import { Signal } from '@lumino/signaling';
6
+ import 'jest';
7
+ import { CommandIDs } from '../constants';
8
+ import plugin from '../index';
9
+ import { NS } from '../tokens';
10
+ const DEFAULT_TOURS_SIZE = 2;
11
+ const [corePlugin, userPlugin, notebookPlugin, defaultsPlugin] = plugin;
12
+ function mockApp() {
13
+ return {
14
+ commands: new CommandRegistry(),
15
+ restored: Promise.resolve(),
16
+ docRegistry: new DocumentRegistry()
17
+ };
18
+ }
19
+ function aTour() {
20
+ return {
21
+ id: 'test-jupyterlab-tour:welcome',
22
+ label: 'Welcome Tour',
23
+ hasHelpEntry: true,
24
+ steps: [
25
+ {
26
+ content: 'Tours of Jupyter',
27
+ placement: 'auto',
28
+ target: '#jp-MainLogo',
29
+ title: 'Jupyter'
30
+ },
31
+ {
32
+ content: 'The following tour will point out some of the main UI components within JupyterLab.',
33
+ placement: 'right',
34
+ target: '#jp-main-dock-panel',
35
+ title: 'Welcome to Jupyter Lab!'
36
+ },
37
+ {
38
+ content: 'This is the main content area where notebooks and other content can be viewed and edited.',
39
+ placement: 'center',
40
+ target: '#jp-main-dock-panel',
41
+ title: 'Main Content'
42
+ }
43
+ ]
44
+ };
45
+ }
46
+ function mockSettingRegistry() {
47
+ const settings = {
48
+ composite: { tours: [aTour()] }
49
+ };
50
+ settings['changed'] = new Signal(settings);
51
+ return {
52
+ load: async () => settings
53
+ };
54
+ }
55
+ function mockNbTracker() {
56
+ const nbTracker = {};
57
+ nbTracker.widgetAdded = new Signal(nbTracker);
58
+ return nbTracker;
59
+ }
60
+ describe(corePlugin.id, () => {
61
+ describe('activation', () => {
62
+ it('should create add-tour command', () => {
63
+ var _a;
64
+ const app = mockApp();
65
+ corePlugin.activate(app);
66
+ expect((_a = app.commands) === null || _a === void 0 ? void 0 : _a.hasCommand(CommandIDs.addTour)).toEqual(true);
67
+ });
68
+ });
69
+ describe('commands', () => {
70
+ describe(`${CommandIDs.addTour}`, () => {
71
+ it('should add a tour command', async () => {
72
+ var _a;
73
+ const app = mockApp();
74
+ const manager = corePlugin.activate(app);
75
+ expect(manager.tours.size).toEqual(0);
76
+ const tour = await ((_a = app.commands) === null || _a === void 0 ? void 0 : _a.execute(CommandIDs.addTour, {
77
+ tour: aTour()
78
+ }));
79
+ expect(manager.tours.size).toEqual(1);
80
+ expect(tour).toBeTruthy();
81
+ expect(manager.tours.get(tour.id)).toBeTruthy();
82
+ });
83
+ });
84
+ });
85
+ });
86
+ describe(userPlugin.id, () => {
87
+ describe('activation', () => {
88
+ it('should have userTours', async () => {
89
+ const app = mockApp();
90
+ const manager = corePlugin.activate(app);
91
+ const settings = mockSettingRegistry();
92
+ const userManager = userPlugin.activate(app, settings, manager);
93
+ await userManager.ready;
94
+ expect(userManager.tourManager.tours.size).toBe(1);
95
+ });
96
+ });
97
+ describe('settings', () => {
98
+ it('should react to settings', async () => {
99
+ const app = mockApp();
100
+ const manager = corePlugin.activate(app);
101
+ const settingsRegistry = mockSettingRegistry();
102
+ const userManager = userPlugin.activate(app, settingsRegistry, manager);
103
+ await userManager.ready;
104
+ const settings = await settingsRegistry.load('whatever');
105
+ settings.composite = { tours: [] };
106
+ settings.changed.emit(void 0);
107
+ expect(userManager.tourManager.tours.size).toBe(0);
108
+ });
109
+ });
110
+ });
111
+ describe(notebookPlugin.id, () => {
112
+ describe('activation', () => {
113
+ it('should activate', async () => {
114
+ const app = mockApp();
115
+ const manager = corePlugin.activate(app);
116
+ const nbTracker = mockNbTracker();
117
+ const notebookTourManager = notebookPlugin.activate(app, nbTracker, manager);
118
+ const languages = new EditorLanguageRegistry();
119
+ const factoryService = new CodeMirrorEditorFactory({
120
+ languages
121
+ });
122
+ const mimeTypeService = new CodeMirrorMimeTypeService(languages);
123
+ const editorFactory = factoryService.newInlineEditor;
124
+ const notebook = new Notebook({
125
+ rendermime: null,
126
+ mimeTypeService,
127
+ contentFactory: new NotebookPanel.ContentFactory({ editorFactory })
128
+ });
129
+ const model = new NotebookModel();
130
+ notebook.model = model;
131
+ notebookTourManager.addNotebook(notebook);
132
+ expect(notebookTourManager.tourManager.tours.size).toBe(0);
133
+ model.setMetadata(NS, {
134
+ tours: [aTour()]
135
+ });
136
+ expect(notebookTourManager.tourManager.tours.size).toBe(1);
137
+ model.deleteMetadata(NS);
138
+ expect(notebookTourManager.tourManager.tours.size).toBe(0);
139
+ });
140
+ });
141
+ });
142
+ describe(defaultsPlugin.id, () => {
143
+ describe('activation', () => {
144
+ it('should activate', () => {
145
+ const app = mockApp();
146
+ const manager = corePlugin.activate(app);
147
+ defaultsPlugin.activate(app, manager);
148
+ expect(manager.tours.size).toEqual(DEFAULT_TOURS_SIZE);
149
+ });
150
+ });
151
+ });
package/lib/components.js CHANGED
@@ -37,7 +37,7 @@ class Tour extends React.Component {
37
37
  };
38
38
  }
39
39
  render() {
40
- return this.props.tours && this.props.tours[this.state.index] ? (React.createElement(ReactJoyride, Object.assign({ key: this.props.tours[this.state.index].id }, this.props.tours[this.state.index].options, { callback: this._handleJoyrideCallback, run: this.state.run, steps: this.props.tours[this.state.index].steps }))) : null;
40
+ return this.props.tours && this.props.tours[this.state.index] ? (React.createElement(ReactJoyride, { key: this.props.tours[this.state.index].id, ...this.props.tours[this.state.index].options, callback: this._handleJoyrideCallback, run: this.state.run, steps: this.props.tours[this.state.index].steps })) : null;
41
41
  }
42
42
  }
43
43
  /**