Skip to content

Conversation

saudiwin
Copy link

@saudiwin saudiwin commented Jan 3, 2025

I was able to get the sortable drag/drop question to work without too much trouble. I ported sortable to a separate package (sortsurvey, see https://github.com/saudiwin/sortable_survey) and created a rank_list question as rank_list_survey. I then added this widget as a "mc_drag_drop" option in sd_question. The folder test_rank_question contains an example.

It seems to work fairly well. If sortable never addresses the issue, I could pare down sortsurvey so that it only contains the bare bones to run this particular question.

The only thing that would remain is to consider exposing more options--there are a whole bunch. Happy to keep working on this.

@saudiwin
Copy link
Author

saudiwin commented Jan 3, 2025

There are multiple commits as I attempted to just use sortable, thought it worked, then realized it actually didn't and went back to the ported package.

@jhelvy
Copy link
Collaborator

jhelvy commented Jan 3, 2025

Yeah that's more what I was suggesting on the discussion you started. I was thinking we could extract the relevant parts needed from {sortable} for this one specific question type and just bring those into {surveydown}. If the whole {sortable} package isn't really needed, then this might be sufficient since it's a pretty specific use case. And it would avoid the dependency and potential issues in the future if they change the package in ways that no longer work with {surveydown}.

What sorts of other features / options are available? Are there any that would make sense as a question type?

Also, I'm not sure if "mc_drag_drop" is the best name for this question type. When I look at the types of question widgets supported by SurveyJS, they call this a "ranking" type question, which I think is accurate because it's only for ranking things. MC is multiple choice, where you're making a choice, but this is just ranking the options.

Another type that looks attractive is "Select items to rank", which might be possible using sortable.

@saudiwin
Copy link
Author

saudiwin commented Jan 3, 2025

OK sounds like we are on the same page then. I made a new package (sortsurvey) because it seemed like a good idea to keep the code modular. That way if there was some issue with sortable down the line, and people weren't using the drag/drop question, then surveydown would remain viable.

They seem to have most of the sortable options implemented. See the github here:

https://github.com/rstudio/sortable

I'll work on adding the others as options. Seems like "mc_rank" would work? And "mc_rank_bucket"?

@jhelvy
Copy link
Collaborator

jhelvy commented Jan 3, 2025

Cool. Yeah I'm good with this approach. We could add your {sortsurvey} package as a dependency that only lives here on GitHub (I'm assuming you wouldn't want to publish it to CRAN).

In general I don't think any of these should have "mc" in the name of the question type because these questions are rank questions, not choice questions. So I think you can just leave the "mc" part off.

…rrectlly in shiny file (some inheritance issue)
@jhelvy
Copy link
Collaborator

jhelvy commented Jan 4, 2025

Okay I finally got a chance to look more at the docs for sortable, and for sure I think we should support both the rank list and the bucket list as question types. And I think the "type" names could be as simple as rank_list and bucket_list.

I also saw all the other super cool things out there like shinyjqui. There are lots of things that could be done with this. But for now I'll saw sortable seems like a solid target to work with for supporting more interactive question widgets.

@saudiwin
Copy link
Author

saudiwin commented Jan 7, 2025

OK I renamed the questions so they are now "rank_list" and "bucket_list". I also added a new function "bucket_list_survey" that does the same basic modification as "rank_list_survey" from sortable.

bucket_list_survey() works fine in a quarto doc but it doesn't produce a valid HTML widget from sd_question(). I think the problem is that the bucket_list object is a list with rank_list HTML widgets inside. Each of those widgets has its own input ID. At present the code is setting that input ID for the bucket_list object as a whole but the rank_list objects have separate IDs set internally.

These can also be set but I wanted to flag this as I'm not sure of the best way to do that. This will likely require some further modifications.

Alternatively, we could make our own bucket_list type function as their function is essentially a wrapper around rank_list.

Another more minor issue I encountered is that I could not get bucket_list to appear in horizontal model in a quarto doc (i.e. both buckets side by side). In a quarto doc, it was always in vertical mode. I tried using two-column layout in quarto but to no avail (it also doesn't matter what width you set on the widgets either). So not sure how to get around this one although it's not a dealbreaker in any case.

@saudiwin
Copy link
Author

saudiwin commented Jan 7, 2025

The other thing we don't have working out of the box yet is reactivity with sd_output. Apparently they are adding a helper function for updating ranked lists:

https://github.com/rstudio/sortable/blob/main/inst/shiny/update_rank_list_method/app.R

and there is a more involved way of doing it:

https://github.com/rstudio/sortable/blob/main/inst/shiny/update_rank_list_ui/app.R

@saudiwin
Copy link
Author

saudiwin commented Jan 7, 2025

Also finally it would be great to add numbers to the options so the user can see which is No. 1, 2, etc. Seems like that should be simple CSS adjustment (he says before looking at it).

@jhelvy
Copy link
Collaborator

jhelvy commented Jan 7, 2025

Can you help me understand what you're trying to do in terms of implementing the rank_list and bucket_list question types? There are two ways to generate a question:

  1. In the survey.qmd file, add a code chunk and use the sd_question() function.
  2. In the app.R file, use the sd_question() function inside the server() function, then in the survey.qmd file add a code chunk and use the sd_output(id) function where you want the question to display, matching the id to the id value used in sd_question() in the server.

The second option is the "reactive" way to do it, and for some situations this has to be done. For example, in this section of the docs describing how to display randomized question labels, the labels are randomly defined in the server along with the question, then sd_output() is used in the survey.qmd file to display the question.

We've also had to use this approach for certain types of shiny widgets as they just don't render properly in Quarto. For this purpose, we made a sd_question_custom() function that allows you to insert any html widget you want and then have the value(s) returned by that widget stored in the resulting survey data. In this case, this may be the approach you need to use if you're having trouble defining these sortable widgets inside the Quarto document. If that works, then you wouldn't need to modify the {sortable} package at all - just define the widgets in the app.R file then use sd_question_custom() to store the value in the data. If you give that approach a try and it works, then we have at least one way to do it.

Still, given how common this type of question is, it would be much nicer if a user could just use sd_question(type = "rank_list") directly in the survey.qmd file.

@saudiwin
Copy link
Author

Hi John -

So the issue with the latter. The rank_list and bucket_list work great (or mostly great) in the sd_question() function. It's only when using reactivity with the sd_output function that it doesn't work. Specifically, I tried setting the choices on the server side and it worked but then I could no longer drag and drop the choices in the survey.

I can send you a reprex for this if you need it, I would just need to clean the code a bit as it's from a current survey I'm testing. It's not a huge issue for what I'm working on at present but obviously we'd need to figure it out before moving it into the package proper.

@jhelvy
Copy link
Collaborator

jhelvy commented Jan 11, 2025

Hmm, my guess is that it requires some JS libraries to work. When you use it in the survey.qmd file with sd_question, Quarto probably adds the necessary dependencies when it renders, and those dependencies get captured and brought into the shiny app. When you instead define it in the app.R file, those dependencies may not be registered. At least that's my suspicion for what is going on.

One way to test that is to make a survey with two rank_list questions - one in the survey.qmd file, and one defined in the app.R file. Give them different options so you can distinguish them. I suspect that both will show up this way, because the required libraries will get loaded due to the one being defined in the survey.qmd file.

It would help if you could provide a simple reprex for each case.

@jhelvy
Copy link
Collaborator

jhelvy commented Jan 18, 2025

FYI, related conversation on Bluesky on the bucket list:

https://bsky.app/profile/willngiam.bsky.social/post/3lfzf4o4b6c22

@saudiwin
Copy link
Author

Hey man -

Just wanted to let you all know that I'm still interested in working on this, I just had to hit pause while I work on a big survey deployment with a lot of moving parts. Hopefully done by the end of the month with that (fingers crossed).

@jhelvy
Copy link
Collaborator

jhelvy commented Jan 28, 2025

No problem! We just pushed v0.8.0 to the main branch, so you'll probably need to pull in those changes to this PR. Definitely would love to see these features supported here. Still not sure the best approach re: modifying the {sortable} package to work with surveydown. If I ever get some time I can try to look at it too, but I have a lot of other priorities, including addressing actual broken things in surveydown.

@jhelvy
Copy link
Collaborator

jhelvy commented Mar 18, 2025

We've finally addressed most of the remaining open issues, so I'm sure this branch is rather outdated with the main branch, but in any case I'm still definitely interested in having this question type supported. If you have time to look back into it and want to give it a try, let me know, otherwise we may be able to look into it as well.

@saudiwin
Copy link
Author

saudiwin commented May 7, 2025

Hi John -

Sorry for the late response! I've been swamped with getting a survey up and running in this new framework and also the usual classes. However, I have a working version that also allows for reactivity with the ranking Qs. Essentially, the ranking Qs work fine within sd_question if reactivity isn't expected. However, if reactive values are passed to sd_question on the server side, sortable stops working and it produces instead just a static list.

However, if it's set up with sd_question_custom on the server side where the list order is stored as a reactive value, then it seems to work just fine.

So we would probably need to make a decision about how to incorporate it--should I keep the rank_list option in sd_question with the caveat that reactivity won't work, or should we just have it set up as a sd_question_custom function? Maybe a user-facing function that wraps sd_question_custom?

ON another note, I've spent a fair amount of time getting surveydown to work with shiny.18n, a pretty stellar translation tool. I've got it up and running my survey. I'll write an issue about it separately as it would be nice to have it work with the system translations etc.

@jhelvy
Copy link
Collaborator

jhelvy commented May 8, 2025

I haven't had time to look closely at this, but my preference would be to leave it as a type in sd_question() as for most users I expect they'll just want a ranking type question with static labels.

Have you tried just defining the sd_question() directly in the server then displaying it with sd_output() in the survey.qmd file? This example is what I mean. Usually, if you have some sort of dynamic label that changes based on some reactive element, I just define the whole question in the server, then use sd_output() to insert it into my survey. If that works, then that is the preferred workflow. If it doesn't we'll have to keep thinking how best to handle this.

@saudiwin
Copy link
Author

saudiwin commented May 8, 2025

Yes, that is what I initially tried (defining it on the server side and using sd_output, but it turns it into a static list. The current code works:

output$careerq <- renderUI({

      rank_list_survey(input_id = "career",text = "",
                       labels=translator()$t(c("Work for a small-to-medium-sized company", "Work for a large company",
                                               "Work for a company outside of the country", "Start my own business in the country",
                                               "Start my own business outside of the country", "Work in a government position",
                                               "Work for international organization", "Work for NGO/civil society organization", "Other")))


  })

  sorted_list_career <- reactiveVal(NULL)

observeEvent(input$career, {

        sorted_list_career(input$career)

  })

observe({

      sd_question_custom(
          id = "career_q_id",
          label = translator()$t("Please drag and drop to rank the following occupations from the most desirable (#1) to the least desirable (#9):"),
          output = uiOutput("careerq"),
          value = sorted_list_career
      )

)}

I'm fine leaving the option in sd_question and then perhaps we could stick this in a vignette?

@jhelvy
Copy link
Collaborator

jhelvy commented May 8, 2025

I see, so in this case is it the translation bit that is requiring you to define this in the server? I guess this works. I'm still not 100% happy with how we've set up sd_question_custom, but I haven't come up with something better. It works well enough for any kind of custom UI element like this. Maybe with good enough docs this will be easy enough to copy and manipulate to meet peoples' needs.

For now we describe this approach here, but we could probably come up with a library of custom widgets and how to configure them. So far I've seen the leaflet map and plotly plot examples from that page, and now this would be a third one.

Did you figure out the dependency issues? Do we have to list your sortsurvey package as the dependency, or the sortable package? Our dependencies are getting increasingly bloated, so I'd like to keep as clean a dependency history as possible going forward.

@saudiwin
Copy link
Author

saudiwin commented May 8, 2025

Let me give that another look. I only recently got reactivity working, so now that I understand that, I have a better idea what we actually need from the sortable package.

You can also define widgets using a lower-level interface that might make it easier to just use sortable as well.

@saudiwin
Copy link
Author

saudiwin commented May 8, 2025

I believe the reason that sd_question_custom is needed is because the sd_question function changes the reactive context, which screws with sortable.

But it's beyond me exactly how or why.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants