New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Robust solution for multi-page apps (such as navbar layout) #17

Closed
thercast opened this Issue Feb 2, 2017 · 6 comments

Comments

Projects
None yet
3 participants
@thercast

thercast commented Feb 2, 2017

I've been using rintrojs for more of my Shiny apps but came across a problem with my apps that use the navbarPage layout in shiny as seen in this gist. Basically the tool text appears on the screen but the focus is still on the first tabPanel and does not go to the second like it should. One of the listeners to my podcast also has experienced this issue and alerted me to a solution you posted as a workaround. I was just curious if there is a more robust solution we could use so that it would support going back towards the first tabPanel and not just for going forward. Thanks again for your great work, I highlighted as my package pick in episode 19 👍

@carlganz

This comment has been minimized.

Show comment
Hide comment
@carlganz

carlganz Feb 2, 2017

Owner

Hi Eric,

Thanks for the shoutout in the podcast!

Can you explain exactly what you mean by 'go back to the first tabPanel'? I find that when I hit next, then back, it does bring me back to the first tabPanel. Are you asking if it is possible to make it go back to the first tab upon completion and/or exiting?

Regards,
Carl

Owner

carlganz commented Feb 2, 2017

Hi Eric,

Thanks for the shoutout in the podcast!

Can you explain exactly what you mean by 'go back to the first tabPanel'? I find that when I hit next, then back, it does bring me back to the first tabPanel. Are you asking if it is possible to make it go back to the first tab upon completion and/or exiting?

Regards,
Carl

@thercast

This comment has been minimized.

Show comment
Hide comment
@thercast

thercast Feb 2, 2017

Hi Carl, thanks for your quick reply! I did a little more troubleshooting and figured out I hadn't adapted the code correctly. After changing the code to the following I can now at least step back to the first tabPanel after visiting the item in the second one:

  observeEvent(input$btn, {
    introjs(
      session,
      events = list(
        "onchange" = "if (this._currentStep==0) {
        $('a[data-value=\"Summaries\"]').removeClass('active');
        $('a[data-value=\"Plot\"]').addClass('active');
        $('a[data-value=\"Plot\"]').trigger('click');
        }
        if (this._currentStep==1) {
        $('a[data-value=\"Summaries\"]').removeClass('active');
        $('a[data-value=\"Plot\"]').addClass('active');
        $('a[data-value=\"Plot\"]').trigger('click');
        }
        if (this._currentStep==2) {
        $('a[data-value=\"Plot\"]').removeClass('active');
        $('a[data-value=\"Summaries\"]').addClass('active');
        $('a[data-value=\"Summaries\"]').trigger('click');
        }"
      ))
  })

But it would be nice if when the user hits Done the app would go back to the first tab, and yes the same for exiting. While this toy app only had two tabPanels and not many elements, I have a much larger app with 5 tabPanels and most of then have additional tabs within (approximate 30 inputs/outputs I want to highlight), so I'm looking for a way to implement the logic above in a more intuitive way at least from a shiny perspective.

thercast commented Feb 2, 2017

Hi Carl, thanks for your quick reply! I did a little more troubleshooting and figured out I hadn't adapted the code correctly. After changing the code to the following I can now at least step back to the first tabPanel after visiting the item in the second one:

  observeEvent(input$btn, {
    introjs(
      session,
      events = list(
        "onchange" = "if (this._currentStep==0) {
        $('a[data-value=\"Summaries\"]').removeClass('active');
        $('a[data-value=\"Plot\"]').addClass('active');
        $('a[data-value=\"Plot\"]').trigger('click');
        }
        if (this._currentStep==1) {
        $('a[data-value=\"Summaries\"]').removeClass('active');
        $('a[data-value=\"Plot\"]').addClass('active');
        $('a[data-value=\"Plot\"]').trigger('click');
        }
        if (this._currentStep==2) {
        $('a[data-value=\"Plot\"]').removeClass('active');
        $('a[data-value=\"Summaries\"]').addClass('active');
        $('a[data-value=\"Summaries\"]').trigger('click');
        }"
      ))
  })

But it would be nice if when the user hits Done the app would go back to the first tab, and yes the same for exiting. While this toy app only had two tabPanels and not many elements, I have a much larger app with 5 tabPanels and most of then have additional tabs within (approximate 30 inputs/outputs I want to highlight), so I'm looking for a way to implement the logic above in a more intuitive way at least from a shiny perspective.

@carlganz carlganz added the enhancement label Feb 2, 2017

@carlganz

This comment has been minimized.

Show comment
Hide comment
@carlganz

carlganz Feb 2, 2017

Owner

I get what you are saying. I agree there should be a nicer way to deal with tabs, and events. I will spend some time thinking about this.

Thanks for the feedback!

Owner

carlganz commented Feb 2, 2017

I get what you are saying. I agree there should be a nicer way to deal with tabs, and events. I will spend some time thinking about this.

Thanks for the feedback!

@crew102

This comment has been minimized.

Show comment
Hide comment
@crew102

crew102 Mar 28, 2018

Contributor

I was recently faced with a problem similar to the one @thercast described (i.e., creating an extensible solution for getting rintrojs to work nicely with multitab apps). The best I could come up with was providing a generic callback to introJs.onafterchange(). The callback would find the tabpanel element that was the ancestor of the next targetElement, then send a click event to the relevant link node (i.e., the link element corresponding to the ancestor node) so that the correct panel was displayed. This is basically the same solution that was provided above, except the step numbers aren't hard-coded into the logic. Perhaps rintrojs could provide an argument to introjs() that allows the user to choose a similar function as the default callback to onafterchange()? I could give a PR a shot if you think it's worth it.

Contributor

crew102 commented Mar 28, 2018

I was recently faced with a problem similar to the one @thercast described (i.e., creating an extensible solution for getting rintrojs to work nicely with multitab apps). The best I could come up with was providing a generic callback to introJs.onafterchange(). The callback would find the tabpanel element that was the ancestor of the next targetElement, then send a click event to the relevant link node (i.e., the link element corresponding to the ancestor node) so that the correct panel was displayed. This is basically the same solution that was provided above, except the step numbers aren't hard-coded into the logic. Perhaps rintrojs could provide an argument to introjs() that allows the user to choose a similar function as the default callback to onafterchange()? I could give a PR a shot if you think it's worth it.

@carlganz

This comment has been minimized.

Show comment
Hide comment
@carlganz

carlganz Mar 28, 2018

Owner

Hey @crew102,
I get what you are saying. Having smart defaults for the callback functions that tend to do-the-right-thing would be nice, and in this case would probably not be too hard to implement, but I have some concerns about changing how the package behaves by default, since it appears to be actively used and I don't want to break people's code.

I suppose adding some functions that auto-generate the necessary javascript would be pretty low maintenance, so something like:

introjs(session, events = list(
onbeforechange = NEW_FUNCTION(...)
))

Happy to look over a PR

Owner

carlganz commented Mar 28, 2018

Hey @crew102,
I get what you are saying. Having smart defaults for the callback functions that tend to do-the-right-thing would be nice, and in this case would probably not be too hard to implement, but I have some concerns about changing how the package behaves by default, since it appears to be actively used and I don't want to break people's code.

I suppose adding some functions that auto-generate the necessary javascript would be pretty low maintenance, so something like:

introjs(session, events = list(
onbeforechange = NEW_FUNCTION(...)
))

Happy to look over a PR

@crew102

This comment has been minimized.

Show comment
Hide comment
@crew102

crew102 Mar 29, 2018

Contributor

Yeah, I think the default should be to not change existing behavior...The user will have to choose to override the default to use the callback. I'll put together a PR sometime soon.

Contributor

crew102 commented Mar 29, 2018

Yeah, I think the default should be to not change existing behavior...The user will have to choose to override the default to use the callback. I'll put together a PR sometime soon.

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