Skip to contents

Overview

modUpload uses {shinyvalidate} for comprehensive input validation across the multi-step wizard. This guide explains the validation architecture and how to work with validation rules.

Validation Architecture

The wizard uses two validators, one for each step:

  1. Step 1 Validator - Required fields (artwork name, collection, files)
  2. Step 2 Validator - Optional fields (additional variants, story)

Step 1: Required Fields

validator_step_1 <- function() {
  iv <- shinyvalidate::InputValidator$new()
  
  # Artwork name validation
  iv$add_rule("art_name", shinyvalidate::sv_required())
  iv$add_rule("art_name", shinyvalidate::sv_regex(
    "[a-zA-Z0-9\_\-\ ]+", 
    "No special characters allowed"
  ))
  
  # Collection selection
  iv$add_rule("art_collection", shinyvalidate::sv_required())
  
  # File validation using ckfile()
  iv$add_rule("stats_file", shinyvalidate::sv_required(
    "Expecting a single file ending in .png or .jpeg",
    function(x) nrow(x) == 1 && (ckfile(x, "png") || ckfile(x, "jpeg"))
  ))
  
  iv$add_rule("image_file", shinyvalidate::sv_required(
    "Expecting a single file ending in .png",
    function(x) nrow(x) == 1 && ckfile(x, "png")
  ))
  
  iv$add_rule("raw_file", shinyvalidate::sv_required(
    "Expecting a single file ending in .procreate",
    function(x) nrow(x) == 1 && ckfile(x, "procreate")
  ))
  
  iv$add_rule("video_file", shinyvalidate::sv_required(
    "Expecting a single file ending in .mp4",
    function(x) nrow(x) == 1 && ckfile(x, "mp4")
  ))
  
  iv
}

Step 2: Optional Fields

validator_step_2 <- function() {
  iv <- shinyvalidate::InputValidator$new()
  
  # Optional zip file for additional frames
  iv$add_rule("frames_zip", shinyvalidate::sv_optional())
  iv$add_rule("frames_zip", ~ if (!ckfile(., "zip")) "Zip folder required")
  
  iv
}

The ckfile() Helper

The ckfile() function validates uploaded files:

ckfile <- function(x, ext) {
  # Extract file extension
  fileExt <- function(filename) {
    stringr::str_split_i(filename, "\\\\|/", -1) |>
      stringr::str_extract("(?<=\.).+(?=$)")
  }
  
  # Check file structure and extension
  if (is.null(x)) return(FALSE)
  
  all(names(x) %in% c("name", "size", "type", "datapath")) &&
    x$size > 500 &&  # Minimum 500 bytes
    fileExt(stringr::str_to_lower(x$name)) == ext
}

Validation Checks

ckfile() validates:

  1. Structure - File input has expected columns (name, size, type, datapath)
  2. Size - File is larger than 500 bytes (not empty)
  3. Extension - File extension matches expected type

Using Validators in Server

The validators are initialized and used in the server:

modUploadServer <- function(id, r) {
  shiny::moduleServer(id, function(input, output, session) {
    
    # Initialize validators
    iv_1 <- validator_step_1()
    iv_2 <- validator_step_2()
    
    # On submit, validate Step 1
    shiny::observeEvent(input$btn_submit, {
      iv_1$enable()
      
      if (iv_1$is_valid()) {
        iv_1$disable()
        shiny::removeModal()
        # Proceed with upload
      } else {
        # Validation errors displayed automatically
      }
    })
    
    # On cancel, disable validators
    shiny::observeEvent(input$btn_cancel, {
      iv_1$disable()
      iv_2$disable()
      shiny::removeModal()
    })
  })
}

Validation Flow

User opens wizard modal
        |
Step 1: Fill required fields
        |
Click Submit
        |
iv_1$enable() -> Display validation errors
        |
iv_1$is_valid()?
    |--- No: Show errors, stay on form
    |--- Yes: iv_1$disable(), proceed to processing

Best Practices

  1. Enable validators on submit, not on input change
  2. Disable validators when modal is closed or cancelled
  3. Use sv_required() with custom predicates for file validation
  4. Provide clear error messages that guide users to fix issues

Supported File Types

File Type Extension Validation
Artwork Image .png Required, single file
Canvas Stats .png, .jpeg Required, single file
Canvas File .procreate Required, single file
Timelapse Video .mp4 Required, single file
Frames Archive .zip Optional
Variants .png Optional, multiple files