learnr icon indicating copy to clipboard operation
learnr copied to clipboard

Horizontal layout option for `question_radio()`

Open dtkaplan opened this issue 4 years ago • 4 comments

Motivation:

Pedagogically, it can sometimes be useful to provide a long list of distractors. It's also helpful to be able to ask many short questions. Regrettably, the question options in {learnr} lay things out in a vertical format. The result is visually confusing.

Proposal:

I propose a layout option like this:

image

Actually, I'd prefer if the submit/try-again buttons were inline with the question prompt, to save further vertical space.

I'm implemented the above via an inline field passed via the general-purpose options= argument. Then a very small change in radio_question.R will provide the option while preserving the default behavior ...

question_ui_initialize.learnr_radio <- function(question, value, ...) {
  choice_names <- answer_labels(question)
  choice_values <- answer_values(question)

  radioButtons(
    question$ids$answer,
    label = question$question,
    inline = ifelse("inline" %in% names(question$options),
                 question$options$inline,
                 FALSE),
    choiceNames = choice_names,
    choiceValues = choice_values,
    selected = value %||% FALSE # setting to NULL, selects the first item
  )
}

dtkaplan avatar May 25 '20 15:05 dtkaplan

To get the question type, try defining this in one of your setup chunks...

question_ui_initialize.learnr_radio_inline <- function(question, value, ...) {
  choice_names <- answer_labels(question)
  choice_values <- answer_values(question)

  radioButtons(
    question$ids$answer,
    label = question$question,
    inline = TRUE,
    choiceNames = choice_names,
    choiceValues = choice_values,
    selected = value %||% FALSE # setting to NULL, selects the first item
  )
}

Then in your exercise chunk... (See Custom Question Types)

question(...., type = c("learnr_radio_inline", "learnr_radio"))

This will not solve the inline try again/submit button, but it should get you half way.


I don't know of a best solution for having the submit/try again button being inline. Don't know if it should be a question() option or an S3 method for the layout.

Ex:

  • question(inline = FALSE, question_inline = inline, answer_inline = inline)
  • question_ui_layout.default <- ....

schloerke avatar May 26 '20 01:05 schloerke

This is a big help. Thanks, Barret @schloerke. For others trying to use this technique, please note that ...

  1. answer_labels() is not exported by {learnr}, so be sure to qualify it as learnr:::answer_labels
  2. the %||% expression used in the selected = argument is also an unexported helper from {learnr}. The definition is "%||%" <- function(x, y) if (is.null(x)) y else x
  3. you will also need to implement the required method question_is_correct, which you can do with

question_is_correct.learnr_radio_inline <- learnr:::question_is_correct.learnr_radio

dtkaplan avatar May 28 '20 16:05 dtkaplan

I have tried this and it worked - however, the answer is still printed vertically. Can this be changed?

tvedebrink avatar Mar 24 '21 09:03 tvedebrink

With the help of @dtkaplan and @schloerke I managed to get the wanted behaviour:

answer_labels <- learnr:::answer_labels
answer_values <- learnr:::answer_values
"%||%" <- function(x, y) if (is.null(x)) y else x
question_is_correct.learnr_radio_inline <- learnr:::question_is_correct.learnr_radio

question_ui_initialize.learnr_radio_inline <- function(question, value, ...) {
  choice_names <- answer_labels(question)
  choice_values <- answer_values(question)

  radioButtons(
    question$ids$answer,
    label = question$question,
    inline = TRUE,
    choiceNames = choice_names,
    choiceValues = choice_values,
    selected = value %||% FALSE # setting to NULL, selects the first item
  )
}

question_ui_completed.learnr_radio_inline <- function (question, value, ...) {
    choice_values <- learnr:::answer_values(question)
    choice_names_final <- lapply(question$answers, function(ans) {
        if (ans$correct) {
            tag <- " &#10003; "
            tagClass <- "correct"
        }
        else {
            tag <- " &#10007; "
            tagClass <- "incorrect"
        }
        tags$span(ans$label, HTML(tag), class = tagClass)
    })
    learnr:::disable_all_tags(radioButtons(question$ids$answer, label = question$question,
        choiceValues = choice_values, choiceNames = choice_names_final,
        selected = value, inline = TRUE))
}

Below I have made a radio_q function just to avoid repeating the input in the questions. If you just need to insert an inline question just follow @schloerke's question(...., type = c("learnr_radio_inline", "learnr_radio")) advice

radio_q <- function(inline = FALSE){
  question(type = c("learnr_radio_inline", "learnr_radio")[c(inline, TRUE)],
    text = "Which package contains functions for installing other R packages?",
    answer("base [N]"),
    answer("tools [N]"),
    answer("utils [Y]", correct = TRUE),
    answer("codetools [N]")
  )
}

quiz(radio_q(), radio_q(TRUE))

tvedebrink avatar Apr 16 '21 08:04 tvedebrink