├── altdoc ├── .nojekyll ├── freeze.rds ├── docsify.md └── docsify.html ├── .github ├── .gitignore ├── dependabot.yml └── workflows │ ├── R-CMD-check.yaml │ ├── packer-check.yml │ └── altdoc.yaml ├── srcjs ├── config │ ├── misc.json │ ├── output_path.json │ ├── externals.json │ ├── entry_points.json │ └── loaders.json ├── index.js └── exts │ ├── custom.css │ └── conductor.js ├── tests ├── testthat.R └── testthat │ ├── setup.R │ ├── test-apps │ └── basic │ │ └── app.R │ ├── test-basic.R │ └── _snaps │ └── basic.md ├── LICENSE ├── inst ├── packer │ ├── conductor.js.LICENSE.txt │ └── conductor.js └── doc │ └── assets │ ├── demo.gif │ ├── arrow.png │ ├── buttons.png │ ├── cancel-icon.png │ └── title_and_content.png ├── vignettes ├── assets │ ├── arrow.png │ ├── demo.gif │ ├── buttons.png │ ├── cancel-icon.png │ └── title_and_content.png ├── html-formatting.Rmd ├── use-modules.Rmd ├── first-tour.Rmd ├── javascript.Rmd ├── navbar-tabs.Rmd ├── buttons.Rmd ├── customize.Rmd ├── basic-options.Rmd ├── demo.Rmd └── methods.Rmd ├── R ├── zzz.R └── conductor.R ├── .gitignore ├── webpack.prod.js ├── webpack.dev.js ├── NAMESPACE ├── .Rbuildignore ├── cran-comments.md ├── NEWS.md ├── conductor.Rproj ├── man ├── use_conductor.Rd └── Conductor.Rd ├── package.json ├── DESCRIPTION ├── LICENSE.md ├── webpack.common.js ├── README.md └── CODE_OF_CONDUCT.md /altdoc/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /srcjs/config/misc.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | shinytest2::test_app() 2 | -------------------------------------------------------------------------------- /srcjs/index.js: -------------------------------------------------------------------------------- 1 | import './exts/conductor.js'; 2 | -------------------------------------------------------------------------------- /srcjs/config/output_path.json: -------------------------------------------------------------------------------- 1 | "./inst/packer" 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2022 2 | COPYRIGHT HOLDER: conductor authors 3 | -------------------------------------------------------------------------------- /inst/packer/conductor.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! shepherd.js 10.0.1 */ 2 | -------------------------------------------------------------------------------- /srcjs/config/externals.json: -------------------------------------------------------------------------------- 1 | { 2 | "shiny": "Shiny", 3 | "jquery": "jQuery" 4 | } 5 | -------------------------------------------------------------------------------- /altdoc/freeze.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/altdoc/freeze.rds -------------------------------------------------------------------------------- /srcjs/config/entry_points.json: -------------------------------------------------------------------------------- 1 | { 2 | "conductor": "./srcjs/exts/conductor.js" 3 | } 4 | -------------------------------------------------------------------------------- /inst/doc/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/inst/doc/assets/demo.gif -------------------------------------------------------------------------------- /inst/doc/assets/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/inst/doc/assets/arrow.png -------------------------------------------------------------------------------- /inst/doc/assets/buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/inst/doc/assets/buttons.png -------------------------------------------------------------------------------- /vignettes/assets/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/vignettes/assets/arrow.png -------------------------------------------------------------------------------- /vignettes/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/vignettes/assets/demo.gif -------------------------------------------------------------------------------- /tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | # Load application support files into testing environment 2 | shinytest2::load_app_env() 3 | -------------------------------------------------------------------------------- /vignettes/assets/buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/vignettes/assets/buttons.png -------------------------------------------------------------------------------- /inst/doc/assets/cancel-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/inst/doc/assets/cancel-icon.png -------------------------------------------------------------------------------- /vignettes/assets/cancel-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/vignettes/assets/cancel-icon.png -------------------------------------------------------------------------------- /srcjs/config/loaders.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test": ".css$", 4 | "use": ["style-loader", "css-loader"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /inst/doc/assets/title_and_content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/inst/doc/assets/title_and_content.png -------------------------------------------------------------------------------- /vignettes/assets/title_and_content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennebacher/conductor/HEAD/vignettes/assets/title_and_content.png -------------------------------------------------------------------------------- /srcjs/exts/custom.css: -------------------------------------------------------------------------------- 1 | .shepherd-title { 2 | font-size: 2rem; 3 | width: 100%; 4 | } 5 | 6 | .shepherd-text { 7 | font-size: 1.5rem; 8 | } 9 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | .onLoad <- function(libname, pkgname){ 2 | path <- system.file("packer", package = "conductor") 3 | shiny::addResourcePath('conductor-assets', path) 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | node_modules 6 | # {shinytest2}: Ignore new debug snapshots for `$expect_values()` 7 | *_.new.png 8 | docs 9 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'production', 6 | }); 7 | -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'development', 6 | devtool: 'inline-source-map' 7 | }); 8 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(Conductor) 4 | export(useConductor) 5 | export(use_conductor) 6 | importFrom(R6,R6Class) 7 | importFrom(htmltools,htmlDependency) 8 | importFrom(shiny,singleton) 9 | importFrom(shiny,tags) 10 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^srcjs$ 4 | ^node_modules$ 5 | ^package\.json$ 6 | ^package-lock\.json$ 7 | ^webpack\.dev\.js$ 8 | ^webpack\.prod\.js$ 9 | ^webpack\.common\.js$ 10 | ^LICENSE\.md$ 11 | ^docs$ 12 | ^\.github$ 13 | test.R 14 | hex-conductor.png 15 | _\.new\.png$ 16 | ^cran-comments\.md$ 17 | ^CRAN-SUBMISSION$ 18 | ^CODE_OF_CONDUCT\.md$ 19 | ^altdoc$ 20 | ^vignettes$ 21 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## R CMD check results 2 | 3 | 0 errors | 0 warnings | 1 note 4 | 5 | * This is a new release. 6 | 7 | * devtools::check_rhub() returns a NOTE for Windows Server 2022 (R-devel, 64 bit): 8 | ❯ checking for detritus in the temp directory ... NOTE Found the following files/directories: 'lastMiKTeXException' 9 | 10 | It turns out that this NOTE is due to a bug in Miktex and can be ignored (r-hub/rhub#503). 11 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # conductor 0.1.1.9000 2 | 3 | * Methods `isActive()`, `getCurrentStep()` and `getHighlightedElement()` now work 4 | correctly (#13). 5 | 6 | # conductor 0.1.1 7 | 8 | * `conductor` now scrolls smoothly between elements (#19). 9 | * Update `shepherd.js` to v.10.0.1 10 | * Method `isCentered()` is no longer supported because it was removed in 11 | `shepherd.js` v.10.0.0. 12 | 13 | # conductor 0.1.0 14 | 15 | * Initial version 16 | -------------------------------------------------------------------------------- /conductor.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /vignettes/html-formatting.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: HTML formatting 3 | --- 4 | 5 | You can use HTML tags to format the `title` and `text` of `$step()`: 6 | 7 | ```r 8 | library(shiny) 9 | library(conductor) 10 | 11 | conductor <- Conductor$ 12 | new()$ 13 | step( 14 | "Format with HTML tags", 15 | "This is in bold.This is in italics.
This is in red.
" 16 | ) 17 | 18 | ui <- fluidPage( 19 | useConductor() 20 | ) 21 | 22 | server <- function(input, output, session){ 23 | conductor$init()$start() 24 | } 25 | 26 | shinyApp(ui, server) 27 | ``` 28 | -------------------------------------------------------------------------------- /man/use_conductor.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conductor.R 3 | \name{useConductor} 4 | \alias{useConductor} 5 | \alias{use_conductor} 6 | \title{Dependencies} 7 | \usage{ 8 | useConductor() 9 | 10 | use_conductor() 11 | } 12 | \description{ 13 | Include dependencies, place anywhere in the shiny UI. 14 | } 15 | \examples{ 16 | library(shiny) 17 | library(conductor) 18 | 19 | ui <- fluidPage( 20 | useConductor() 21 | # also works: 22 | # use_conductor() 23 | ) 24 | 25 | server <- function(input, output){} 26 | 27 | if(interactive()) shinyApp(ui, server) 28 | } 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | # Copied from: https://github.com/shipshapecode/shepherd/blob/master/.github/dependabot.yml 7 | 8 | version: 2 9 | updates: 10 | - package-ecosystem: npm 11 | directory: "/" 12 | schedule: 13 | interval: weekly 14 | time: "10:00" 15 | open-pull-requests-limit: 10 16 | labels: 17 | - dependencies 18 | versioning-strategy: increase 19 | -------------------------------------------------------------------------------- /altdoc/docsify.md: -------------------------------------------------------------------------------- 1 | * [Home](/) 2 | 3 | * Get started 4 | * [Your first tour](vignettes/first-tour.md) 5 | * [Basic options](vignettes/basic-options.md) 6 | * [Customize the tour](vignettes/customize.md) 7 | 8 | * Advanced 9 | * [Navbar & tabs](vignettes/navbar-tabs.md) 10 | * [Use in modules](vignettes/use-modules.md) 11 | * [JavaScript functions](vignettes/javascript.md) 12 | * [Methods](vignettes/methods.md) 13 | 14 | * Misc 15 | * [Buttons](vignettes/buttons.md) 16 | * [HTML formatting](vignettes/html-formatting.md) 17 | 18 | * Reference: $ALTDOC_MAN_BLOCK 19 | 20 | * [Changelog]($ALTDOC_NEWS) 21 | * [Code of Conduct]($ALTDOC_CODE_OF_CONDUCT) 22 | * [License]($ALTDOC_LICENSE) 23 | -------------------------------------------------------------------------------- /tests/testthat/test-apps/basic/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(conductor) 3 | 4 | guide <- Conductor$new()$ 5 | step( 6 | el = "#foo", 7 | "hello welcome on this tour", 8 | "This is the content of the modal", 9 | position = "right", 10 | id = "step1" 11 | )$ 12 | step("hi", id = "step2")$ 13 | step( 14 | el = "#foo2", 15 | "This is the content of the modal", 16 | position = "right", 17 | id = "step3" 18 | ) 19 | 20 | ui <- fluidPage( 21 | useConductor(), 22 | textInput("foo", "foo"), 23 | textInput("foo2", "foo2"), 24 | textOutput("test") 25 | ) 26 | 27 | server <- function(input, output, session){ 28 | guide$init()$start() 29 | } 30 | 31 | shinyApp(ui, server) 32 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | R_KEEP_PKG_SOURCE: yes 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: rcmdcheck 27 | 28 | - uses: r-lib/actions/check-r-package@v2 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "conductor", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "man": "man" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "none": "webpack --config webpack.dev.js --mode=none", 12 | "development": "webpack --config webpack.dev.js", 13 | "production": "webpack --config webpack.prod.js", 14 | "watch": "webpack --config webpack.dev.js -d --watch" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "ISC", 19 | "devDependencies": { 20 | "style-loader": "^4.0.0", 21 | "css-loader": "^7.1.2", 22 | "webpack": "^5.104.1", 23 | "webpack-cli": "^6.0.1", 24 | "webpack-merge": "^6.0.1" 25 | }, 26 | "dependencies": { 27 | "shepherd.js": "^14.5.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/testthat/test-basic.R: -------------------------------------------------------------------------------- 1 | skip_on_cran() 2 | 3 | library(shinytest2) 4 | 5 | test_that("foo", { 6 | app <- AppDriver$new(app = testthat::test_path("test-apps/basic")) 7 | # step 1 8 | app$expect_html(selector = "div.shepherd-has-title.shepherd-element.shepherd-enabled") 9 | # step 2 10 | app$click(selector = "button.shepherd-button:nth-child(2)") 11 | app$expect_html(selector = "div.shepherd-has-title.shepherd-element.shepherd-centered.shepherd-enabled") 12 | # step 3 13 | app$click(selector = "div.shepherd-content:nth-child(1) > footer:nth-child(2) > button:nth-child(2)") 14 | app$expect_html(selector = "div.shepherd-has-title:nth-child(5)") 15 | # body after tour 16 | app$click(selector = "div.shepherd-has-title:nth-child(5) > div:nth-child(2) > footer:nth-child(2) > button:nth-child(2)") 17 | app$expect_html(selector = "body") 18 | }) 19 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: conductor 2 | Type: Package 3 | Title: Create Tours in 'Shiny' Apps Using 'Shepherd.js' 4 | Version: 0.1.1 5 | Authors@R: c( 6 | person(given = "Etienne", 7 | family = "Bacher", 8 | role = c("aut", "cre"), 9 | email = "etienne.bacher@protonmail.com") 10 | ) 11 | Description: Enable the use of 'Shepherd.js' to create tours in 'Shiny' 12 | applications. 13 | License: MIT + file LICENSE 14 | Encoding: UTF-8 15 | LazyData: true 16 | Imports: 17 | htmltools, 18 | R6, 19 | shiny 20 | Suggests: 21 | altdoc, 22 | shinytest2, 23 | testthat 24 | Remotes: 25 | etiennebacher/altdoc 26 | RoxygenNote: 7.2.1 27 | URL: https://github.com/etiennebacher/conductor 28 | BugReports: https://github.com/etiennebacher/conductor/issues 29 | Roxygen: list(markdown = TRUE) 30 | Config/testthat/edition: 3 31 | -------------------------------------------------------------------------------- /.github/workflows/packer-check.yml: -------------------------------------------------------------------------------- 1 | name: packer 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | 9 | jobs: 10 | check: 11 | 12 | env: 13 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | 22 | - name: Install R dependencies 23 | uses: r-lib/actions/setup-r-dependencies@v2 24 | with: 25 | extra-packages: | 26 | any::packer 27 | any::remotes 28 | needs: check 29 | 30 | - name: Install system dependencies 31 | if: runner.os == 'Linux' 32 | run: | 33 | while read -r cmd 34 | do 35 | eval sudo $cmd 36 | done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') 37 | 38 | - name: Install NPM Dependencies 39 | run: Rscript -e "packer:::engine_install()" 40 | 41 | - name: Check minification 42 | run: Rscript -e "packer:::are_minified_r('inst/packer')" 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2022 conductor authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/altdoc.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: altdoc 13 | 14 | jobs: 15 | altdoc: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: altdoc-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | permissions: 23 | contents: write 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - uses: quarto-dev/quarto-actions/setup@v2 28 | 29 | - name: Get Script 30 | run: curl -OLs https://eddelbuettel.github.io/r-ci/run.sh && chmod 0755 run.sh 31 | 32 | - name: Bootstrap 33 | run: ./run.sh bootstrap 34 | 35 | - name: Dependencies 36 | run: ./run.sh install_all 37 | 38 | - name: Build site 39 | run: | 40 | install.packages(".", repos = NULL, type = "source") 41 | install.packages("pkgload") 42 | pkgload::load_all() 43 | altdoc::render_docs(freeze = FALSE) 44 | shell: Rscript {0} 45 | 46 | - name: Deploy to GitHub pages 🚀 47 | if: github.event_name != 'pull_request' 48 | uses: JamesIves/github-pages-deploy-action@v4.4.1 49 | with: 50 | clean: false 51 | branch: gh-pages 52 | folder: docs 53 | -------------------------------------------------------------------------------- /vignettes/use-modules.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Use `conductor` in modules 3 | --- 4 | 5 | Using [Shiny modules](https://shiny.rstudio.com/articles/modules.html) is common in large Shiny apps. In modules, input names must be wrapped in `ns()` to avoid namespace collision. 6 | 7 | To use `conductor` with inputs defined in modules, you can first define your `Conductor` outside of the app and then use `$step()` in the server part of the module, while still wrapping the `el` argument in `ns()`: 8 | 9 | ```r 10 | library(shiny) 11 | library(conductor) 12 | 13 | guide <- Conductor$new()$ 14 | step( 15 | title = "hello there" 16 | ) 17 | 18 | # First module 19 | 20 | mod_num_ui <- function(id){ 21 | ns <- NS(id) 22 | numericInput(ns("num"), label = "Enter a number", value = 5, min = 0, max = 10) 23 | } 24 | 25 | mod_num_server <- function(input, output, session){ 26 | ns <- session$ns 27 | guide$ 28 | step( 29 | el = ns("num"), # use namespace 30 | title = "Step 1", 31 | text = "This is a numericInput." 32 | ) 33 | } 34 | 35 | # Second module 36 | 37 | mod_text_ui <- function(id){ 38 | ns <- NS(id) 39 | textInput(ns("text"), label = "Type some text") 40 | } 41 | 42 | mod_text_server <- function(input, output, session){ 43 | ns <- session$ns 44 | guide$ 45 | step( 46 | el = ns("text"), 47 | title = "Step 2", 48 | text = "This is a textInput." 49 | ) 50 | } 51 | 52 | # Main app 53 | 54 | ui <- fluidPage( 55 | useConductor(), 56 | mod_num_ui("num1"), 57 | mod_text_ui("text1") 58 | ) 59 | 60 | server <- function(input, output, session){ 61 | 62 | callModule(mod_num_server, "num1") 63 | callModule(mod_text_server, "text1") 64 | 65 | # launch after module called 66 | guide$init()$start() 67 | } 68 | 69 | shinyApp(ui = ui, server = server) 70 | ``` 71 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | // defaults 5 | var outputPath = [], 6 | entryPoints = [], 7 | externals = [], 8 | misc = [], 9 | loaders = []; 10 | 11 | var outputPathFile = './srcjs/config/output_path.json', 12 | entryPointsFile = './srcjs/config/entry_points.json', 13 | externalsFile = './srcjs/config/externals.json', 14 | miscFile = './srcjs/config/misc.json', 15 | loadersFile = './srcjs/config/loaders.json'; 16 | 17 | // Read config files 18 | if(fs.existsSync(outputPathFile)){ 19 | outputPath = fs.readFileSync(outputPathFile, 'utf8'); 20 | } 21 | 22 | if(fs.existsSync(entryPointsFile)){ 23 | entryPoints = fs.readFileSync(entryPointsFile, 'utf8'); 24 | } 25 | 26 | if(fs.existsSync(externalsFile)){ 27 | externals = fs.readFileSync(externalsFile, 'utf8'); 28 | } 29 | 30 | if(fs.existsSync(miscFile)){ 31 | misc = fs.readFileSync(miscFile, 'utf8'); 32 | } 33 | 34 | if(fs.existsSync(loadersFile)){ 35 | loaders = fs.readFileSync(loadersFile, 'utf8'); 36 | } 37 | 38 | if(fs.existsSync(loadersFile)){ 39 | loaders = fs.readFileSync(loadersFile, 'utf8'); 40 | } 41 | 42 | // parse 43 | loaders = JSON.parse(loaders); 44 | misc = JSON.parse(misc); 45 | externals = JSON.parse(externals); 46 | entryPoints = JSON.parse(entryPoints); 47 | 48 | // parse regex 49 | loaders.forEach((loader) => { 50 | loader.test = RegExp(loader.test); 51 | return(loader); 52 | }) 53 | 54 | // placeholder for plugins 55 | var plugins = [ 56 | ]; 57 | 58 | // define options 59 | var options = { 60 | entry: entryPoints, 61 | output: { 62 | filename: '[name].js', 63 | path: path.resolve(__dirname, JSON.parse(outputPath)), 64 | }, 65 | externals: externals, 66 | module: { 67 | rules: loaders 68 | }, 69 | resolve: { 70 | extensions: ['.tsx', '.ts', '.js'], 71 | }, 72 | plugins: plugins 73 | }; 74 | 75 | // add misc 76 | if(misc.resolve) 77 | options.resolve = misc.resolve; 78 | 79 | // export 80 | module.exports = options; 81 | -------------------------------------------------------------------------------- /vignettes/first-tour.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Your first tour 3 | --- 4 | 5 | ## A simple Shiny app 6 | 7 | First, let's create a simple Shiny app that will serve as a demo: 8 | ```r 9 | library(shiny) 10 | 11 | ui <- fluidPage( 12 | actionButton("mybutton", "Test"), 13 | br(), 14 | div(class = "to-highlight", p("Hello, I'm the first p tag")), 15 | div(class = "to-highlight", p("Hi, I'm the second p tag")) 16 | ) 17 | 18 | server <- function(input, output, session){} 19 | 20 | shinyApp(ui, server) 21 | ``` 22 | 23 | ## Create a conductor 24 | 25 | Then, create a `Conductor` with: 26 | ```r 27 | library(conductor) 28 | 29 | conductor <- Conductor$new() 30 | ``` 31 | This can be done anywhere, not necessarily in the `ui` or `server` parts of the app. To add steps in the tour, use `$step()`. In each `$step()`, you can specify a `title` and some `text`, along with other options that will be detailed in other articles. 32 | 33 | In each step, you can also define the argument `el`. This argument tells `Conductor` which element should be highlighted. By default, `el = NULL`, which means that steps will be displayed in the center of the screen. The argument `el` can take several types of value: 34 | 35 | * an element id, e.g `#my-element` 36 | * an element class, e.g `.my-element` 37 | * a specific HTML tag, e.g `button` 38 | 39 | If you use a class or a tag that is shared by several elements, only the first one will be used. 40 | 41 | ```r 42 | conductor <- Conductor$ 43 | new()$ 44 | step( 45 | title = "Hello there", 46 | text = "This popover is displayed in the center of the screen." 47 | )$ 48 | step( 49 | "This is a button", 50 | "This button has no purpose. Its only goal is to serve as support for demo.", 51 | "#mybutton" 52 | )$ 53 | step( 54 | ".to-highlight", 55 | title = "This is some text", 56 | text = "Only the first p tag is highlighted." 57 | ) 58 | ``` 59 | 60 | !> If the arguments in `$step()` are unnamed, they should follow the order "title", "text", "el". 61 | 62 | ## Call the conductor 63 | 64 | Finally, call `useConductor()` in the `ui` and call `conductor$init()$start()` anywhere in the `server`. 65 | 66 | ```r 67 | library(shiny) 68 | 69 | ui <- fluidPage( 70 | useConductor(), 71 | actionButton("mybutton", "Test"), 72 | br(), 73 | div(class = "to-highlight", p("Hello, I'm the first p tag")), 74 | div(class = "to-highlight", p("Hi, I'm the second p tag")) 75 | ) 76 | 77 | server <- function(input, output, session){ 78 | conductor$init()$start() 79 | } 80 | 81 | shinyApp(ui, server) 82 | ``` 83 | -------------------------------------------------------------------------------- /vignettes/javascript.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Run custom Javascript during the tour 3 | --- 4 | 5 | There are several events occurring during a tour: the tour starts, is cancelled, is complete, etc. It is possible to run some JavaScript code during each of these events. Just like options, there are **tour events** and **step events**. 6 | 7 | ## Tour events 8 | 9 | There are 7 tour events available: start, show, hide, cancel, complete, active, inactive. You can pass JS code at each of these events with the arguments starting with `on` in `$new()`. For example, let's say I want the page body to be red during the tour but white when the tour is not active. Then I can use `onStart` and `onShow` to make it red, and `onHide`, `onCancel` and `onComplete` to make it back to white: 10 | ```r 11 | library(shiny) 12 | library(conductor) 13 | 14 | bg_to_red = "document.body.style.background = 'red';" 15 | bg_to_white = "document.body.style.background = 'white';" 16 | 17 | conductor <- Conductor$ 18 | new( 19 | onStart = bg_to_red, 20 | onShow = bg_to_red, 21 | onHide = bg_to_white, 22 | onCancel = bg_to_white, 23 | onComplete = bg_to_white 24 | )$ 25 | step( 26 | title = "Hello there", 27 | text = "This popover is displayed in the center of the screen." 28 | )$ 29 | step( 30 | "This is a button", 31 | "This button has no purpose. Its only goal is to serve as support for demo.", 32 | "#mybutton" 33 | )$ 34 | step( 35 | ".to-highlight", 36 | title = "This is some text", 37 | text = "Only the first p tag is highlighted." 38 | ) 39 | 40 | ui <- fluidPage( 41 | useConductor(), 42 | actionButton("mybutton", "Test"), 43 | br(), 44 | div(class = "to-highlight", p("Hello, I'm the first p tag")), 45 | div(class = "to-highlight", p("Hi, I'm the second p tag")) 46 | ) 47 | 48 | server <- function(input, output, session){ 49 | conductor$init()$start() 50 | } 51 | 52 | shinyApp(ui, server) 53 | ``` 54 | 55 | ## Step events 56 | 57 | There are 4 step events available: `onShow`, `onHide`, `onCancel`, `onComplete`. Just like tour events, they also only accept JavaScript code that is supposed to run when the step gets the event. For example, if you want to change the background of the page when a specific step is show, you can do: 58 | ```r 59 | library(shiny) 60 | library(conductor) 61 | 62 | guide <- Conductor$new()$ 63 | step("hello")$ 64 | step( 65 | el = "#foo", 66 | "hello welcome on this tour", 67 | onShow = "document.body.style.background = 'red';", 68 | onHide = "document.body.style.background = 'white';" 69 | )$ 70 | step("hi") 71 | 72 | ui <- fluidPage( 73 | useConductor(), 74 | textInput("foo", "foo") 75 | ) 76 | 77 | server <- function(input, output, session){ 78 | guide$init()$start() 79 | } 80 | 81 | shinyApp(ui, server) 82 | ``` 83 | -------------------------------------------------------------------------------- /vignettes/navbar-tabs.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tabs & navbar 3 | --- 4 | 5 | Sometimes, you want the tour to navigate across tabs of a `tabsetPanel()` or a `navbarPage()`. This can be done with the arguments `tabId` and `tab`: 6 | 7 | * `tabId` is the id of the `tabsetPanel()` or of the `navbarPage()`; 8 | * `tab` is the id of the specific tab you want to navigate to. 9 | 10 | ?> It is best to specify `tabId` and `tab` on the steps just before and after switching tabs. For example, if you want step 1 to be on tab A and step 2 to be on tab B, you should specify `tabId` and `tab` on steps 1 and 2. 11 | 12 | 13 | 14 | #### **Tabset** 15 | 16 | ```r 17 | library(shiny) 18 | library(conductor) 19 | 20 | guide <- Conductor$ 21 | new()$ 22 | step( 23 | el = "#text", 24 | title = "This is a textInput.", 25 | text = "There is no need to specify tab and tabId on this step." 26 | )$ 27 | step( 28 | el = "#text", 29 | title = "Text Input", 30 | text = "But this step is just before switching tabs so I should specify tab and tabId.", 31 | tab = "text_tab", 32 | tabId = "my_tabset" 33 | )$ 34 | step( 35 | el = "#numeric", 36 | "This is a numericInput", 37 | "This step is on another tab.", 38 | tab = "numeric_tab", 39 | tabId = "my_tabset" 40 | ) 41 | 42 | ui <- fluidPage( 43 | useConductor(), # include dependencies 44 | h1("tabs"), 45 | tabsetPanel( 46 | id = "my_tabset", 47 | tabPanel("text_tab", textInput("text", "Text")), 48 | tabPanel("numeric_tab", numericInput("numeric", "Numeric", 0)) 49 | ) 50 | ) 51 | 52 | server <- function(input, output){ 53 | guide$init()$start() 54 | } 55 | 56 | shinyApp(ui, server) 57 | ``` 58 | 59 | #### **Navbar** 60 | 61 | ```r 62 | library(shiny) 63 | library(conductor) 64 | 65 | home_guide <- Conductor$ 66 | new()$ 67 | step( 68 | el = "[data-value='home']", 69 | "You can select the tab itself", 70 | "Use the data-value attribute for this" 71 | )$ 72 | step( 73 | el = ".p-home", 74 | text = "This is an element on tab 'Home'", 75 | tab = "home", 76 | tabId = "nav" 77 | )$ 78 | step( 79 | el = ".p-about", 80 | text = "This is an element on tab 'About'", 81 | tab = "about", 82 | tabId = "nav" 83 | ) 84 | 85 | ui <- navbarPage( 86 | "conductor", 87 | header = list(useConductor()), 88 | id = "nav", 89 | tabPanel( 90 | "home", 91 | p(class = "p-home", "This is the tab 'Home'.") 92 | ), 93 | tabPanel( 94 | "about", 95 | p(class = "p-about", "This is the tab 'About'.") 96 | ) 97 | ) 98 | 99 | server <- function(input, output, session){ 100 | home_guide$init()$start() 101 | } 102 | 103 | shinyApp(ui, server) 104 | ``` 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /vignettes/buttons.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Buttons 3 | --- 4 | 5 | It is possible to specify the buttons on the popover. This can be done for each step one by one, but it is also possible to define "default buttons" that will be applied to all steps. 6 | 7 | Let's start with buttons for a specific step. By default, buttons "Previous" and "Next" are put on every popover, but maybe you only want the button "Next" on the first step and maybe you want to rename "Next" as "Finish" on the last step. 8 | 9 | ```r 10 | library(shiny) 11 | library(conductor) 12 | 13 | guide <- Conductor$new()$ 14 | step( 15 | el = "#foo", 16 | "hello welcome on this tour", 17 | buttons = list( 18 | list( 19 | action = "next", 20 | text = "Next" 21 | ) 22 | ) 23 | )$ 24 | step("Second step")$ 25 | step( 26 | "End of the tour", 27 | buttons = list( 28 | list( 29 | action = "back", 30 | secondary = TRUE, 31 | text = "Previous" 32 | ), 33 | list( 34 | action = "next", 35 | text = "Finish" 36 | ) 37 | ) 38 | ) 39 | 40 | ui <- fluidPage( 41 | useConductor(), 42 | textInput("foo", "foo") 43 | ) 44 | 45 | server <- function(input, output, session){ 46 | guide$init()$start() 47 | } 48 | 49 | shinyApp(ui, server) 50 | ``` 51 | 52 | Note that the argument `buttons` is a nested list: there is one big list that contains several "sublists", one for each button. In each "sublist", there are six possible arguments: 53 | * action: either "back" or "next"; 54 | * text: the name of the button; 55 | * secondary: a boolean indicating whether the class "secondary" should be applied; 56 | * disabled: a boolean indicating whether the button should be disabled; 57 | * label: aria-label for the button; 58 | * classes: extra classes to give to the button for more CSS customization. 59 | 60 | You can also modify the default buttons (e.g buttons visible on step 2 on the example above). This can be done by specifying `buttons` in `defaultStepOptions` in `$new()`. For example: 61 | 62 | ```r 63 | guide <- Conductor$new( 64 | defaultStepOptions = list( 65 | buttons = list( 66 | list( 67 | action = "back", 68 | secondary = TRUE, 69 | text = "PREVIOUS" 70 | ), 71 | list( 72 | action = "next", 73 | text = "NEXT" 74 | ) 75 | ) 76 | ) 77 | )$ 78 | step( 79 | el = "#foo", 80 | "hello welcome on this tour", 81 | buttons = list( 82 | list( 83 | action = "next", 84 | text = "NEXT" 85 | ) 86 | ) 87 | )$ 88 | step("Second step")$ 89 | step( 90 | "End of the tour", 91 | buttons = list( 92 | list( 93 | action = "back", 94 | secondary = TRUE, 95 | text = "PREVIOUS" 96 | ), 97 | list( 98 | action = "next", 99 | text = "FINISH" 100 | ) 101 | ) 102 | ) 103 | ``` 104 | -------------------------------------------------------------------------------- /altdoc/docsify.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 35 | 36 | 37 | 38 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/basic.md: -------------------------------------------------------------------------------- 1 | # foo 2 | 3 | "