Skip to content

React Joyride tour does not display on initial load in my event creation form#1101

Description

@PierrealFOY

馃悰 Bug Report

Hi all ! I'm experiencing an issue where my tour, built with React Joyride, fails to display on the initial load of my event creation form. The tour initializes, but no tooltip or guidance step appears on the screen, even though there are no visible errors in the console. Here鈥檚 a summary of the setup and troubleshooting steps taken:

Target Element: I've specified a target element using target: "[data-tour='eventForm-organism']" , and the element is present in the DOM when the tour starts. However, the tooltip does not appear.
I've created a steps.json file, here is one piece of the structure :

{
      "id": "event-creation",
      "title": "Creating a New Event",
      "steps": [
        {
          "content": "organism",
          "target": "[data-tour='eventForm-organism']",
          "placement": "top",
          "title": "Organism selection"
        },
        {
          "content": "Here is the form to create a new event. This is where you add all the settings for your event. Let's follow the steps to well understand the form and not forget anything",
          "target": "[data-tour='eventForm-start']",
          "placement": "top",
          "title": "Event Details"
        },
        {
          "content": "Start by filling in the details of the event, such as title, description, and date",
          "target": "[data-tour='eventForm-details']",
          "placement": "top",
          "title": "Event Details"
        },
        {
          "content": "Here you can select the video content type for the event",
          "target": "[data-tour='eventForm-videoType']",
          "placement": "top",
          "title": "Video Content Type"
        },
        {
          "content": "Here you can set up livestream or external feeds",
          "target": "[data-tour='eventForm-streamOptions']",
          "placement": "top",
          "title": "Livestream or External Feed Options"
        }
      ]
    },

and here is how I add the target in the jsx :

<Input
  data-tour="itemSet-search"
  icon="search"
  loading={loading}
  placeholder={intl.formatMessage({
    id: 'users.search',
  })}
  onChange={handleOnSearchChange}
/>

Controlled Mode: The tour is set in controlled mode, and callbacks like onStepChange and onStatusChange are used to monitor its status.

Troubleshooting Steps:
Verified the visibility and rendering of the target element.
Confirmed there are no CSS conflicts or opacity issues in the styles.
Ensured that the state is synchronized with the component lifecycle, avoiding conditional rendering issues.

In another component of my project, the tour i've implemented works perfectly. And my component DocumentTour is implemented the same way in both components :

/* eslint-disable react/no-unescaped-entities */
import React, { useState } from 'react'

import { Button, Icon } from 'semantic-ui-react'

import DocumentTour from '~/Docs/DocumentTour'

import SportItemSetsViewBase from './SportItemSetsViewBase'

const SportEventsView = () => {
  const [runTour, setRunTour] = useState(false) // state to manage the runTour

// handler activated to start the tour onClick 
  const handleStartTour = () => {
    setRunTour(true)
  }

  return (
    <>
      <div style={{ zIndex: 1000 }}>
        <DocumentTour segmentId="event-list-overview" run={runTour} setRun={setRunTour} />
        <Button size="medium" color="blue" onClick={handleStartTour}>
          Besoin d'aide ?
          <Icon className="pl-4" name="lightbulb" color="green" outline={false} />
        </Button>
      </div>
      <SportItemSetsViewBase
        type={SportItemSetType.SportEvent}
        sportItemUrlFormat={applicationUris.sportEventItems('{{id}}')}
        sportItemSetUrlFormat={applicationUris.sportEvent('{{id}}')}
      />
    </>
  )
}

export default SportEventsView

Here is the custom component I've created to manage react-joyride tour:

import React, { useEffect, useState } from 'react'
import Joyride, { CallBackProps, Step as JoyrideStep, Placement, STATUS, ACTIONS, EVENTS } from 'react-joyride'

import stepsJson from './steps/steps.json'

interface TourProps {
  run: boolean
  setRun: (run: boolean) => void
  segmentId: string
}

type Step = {
  content: string
  target: string
  placement: Placement
  title: string
}

type Segment = {
  id: string
  title: string
  steps: Step[]
}

type TourData = {
  segments: Segment[]
}

const DocumentTour = ({ run, setRun, segmentId }: TourProps) => {
  const [steps, setSteps] = useState<JoyrideStep[]>([])
  const [stepIndex, setStepIndex] = useState(0)

  const handleJoyrideCallback = (data: CallBackProps) => {
    const { action, index, status, type } = data
    console.log(`Joyride status: ${status}, Step: ${index}`)

    // Update the step index based on action
    if (type === EVENTS.STEP_AFTER || type === EVENTS.TARGET_NOT_FOUND) {
      // Update state to advance the tour
      setStepIndex(index + (action === ACTIONS.PREV ? -1 : 1))
    } else if (status === STATUS.FINISHED || status === STATUS.SKIPPED) {
      // You need to set our running state to false, so we can restart if we click start again.
      setRun(false)
    }

    console.groupCollapsed(type)
    console.log(data)
    console.groupEnd()
  }

  const handleClickStart = () => {
    setRun(true)
  }

  useEffect(() => {
    // Load the JSON data dynamically
    const data: TourData = stepsJson as TourData

    // Find the corresponding segment and transform the steps
    const segment = data?.segments?.find(seg => seg.id === segmentId)

    if (segment) {
      // Flatten the steps from the segment
      const joyrideSteps: JoyrideStep[] = segment.steps.map(step => ({
        target: step.target,
        placement: step.placement,
        title: step.title,
        content: (
          <div>
            <p>{step.content}</p>
          </div>
        ),
      }))
      console.log('Steps found for segment:', joyrideSteps)
      setSteps(joyrideSteps)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [segmentId]) // Reload data if `segmentId` changes

  // Check if the steps are ready before launching Joyride
  if (steps.length === 0) {
    return <p>Pas d'茅tapes disponibles pour ce segment.</p>
  }

  return (
    <>
      <Joyride
        callback={handleJoyrideCallback}
        continuous={true}
        run={run}
        scrollToFirstStep
        showProgress
        showSkipButton
        steps={steps}
        stepIndex={stepIndex}
        styles={{
          options: {
            zIndex: 10000,
          },
        }}
      />
      <button onClick={handleClickStart}>Start</button>
    </>
  )
}

export default DocumentTour

The logs are well in the components where the tour works but in the one where it does not i just have Joyride status: running, Step: 0 an nothing appears...

The component where the joyride tour fails is too big to be displayed here in the issue. But if needed I can provide more informations.

If you have any insights into potential causes for this behavior or best practices for initializing Joyride in dynamically rendered forms, I鈥檇 appreciate your guidance. Thank you!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions