From f84c4dcdcc540f0731e90a5b4b701eef1ee512ca Mon Sep 17 00:00:00 2001 From: murielle Delmotte Date: Thu, 6 Jul 2023 14:41:26 +0200 Subject: [PATCH 1/9] WIP dateRangeInput --- dev/flat_composants/flat_dateRangeInput.Rmd | 466 ++++++++++++++++++++ inst/external_deps/radiogroupbuttons.js | 6 + inst/v1.9.3/composant/dateRangeInput.html | 18 + 3 files changed, 490 insertions(+) create mode 100644 dev/flat_composants/flat_dateRangeInput.Rmd create mode 100644 inst/v1.9.3/composant/dateRangeInput.html diff --git a/dev/flat_composants/flat_dateRangeInput.Rmd b/dev/flat_composants/flat_dateRangeInput.Rmd new file mode 100644 index 00000000..1ec08c65 --- /dev/null +++ b/dev/flat_composants/flat_dateRangeInput.Rmd @@ -0,0 +1,466 @@ +--- +title: "flat_new_one.Rmd empty" +output: html_document +editor_options: + chunk_output_type: console +--- + + +```{r development, include=FALSE} +library(testthat) +``` + +```{r development-load} +# Load already included functions if relevant +pkgload::load_all(export_all = TRUE) +``` + + + +# dateRangeInput_dsfr_template + +```{r function-dateRangeInput_dsfr_template} +#' dateRangeInput_dsfr_template +#' +#' @param inputId inputId +#' @param label label +#' @param options options pour les futurs choices +#' @param selected selected +#' @param multiple FALSE par defaut +#' +#' @importFrom htmltools htmlTemplate +#' @importFrom purrr pmap +#' @return html +#' @noRd +dateRangeInput_dsfr_template <- function( + inputId, + label, + start, + end + ) { + + + htmltools::htmlTemplate( + filename = system.file( + get_dsfr_version(with_v = TRUE), + "composant", + "dateRangeInput.html", + package = "shinygouv" + ), + inputId = inputId, + label = label, + start = start, + end = end + ) + + } +``` + + +```{r tests-dateRangeInput_dsfr_template} +test_that("dateRangeInput_dsfr_template works", { + expect_true(inherits(dateRangeInput_dsfr_template, "function")) + + htmlfile <- readLines( + system.file( + get_dsfr_version(with_v = TRUE), + "composant", + "dateRangeInput.html", + package = "shinygouv" + ) + ) + + #' @description Comparer les parametres par rapport a ceux de la version precedente + + + purrr::walk( + c( + "inputId", + "label" + ), + function(param) { + with_moustache <- paste0("\\{\\{", param, "\\}\\}") + expect_true( + any(grepl(pattern = with_moustache, htmlfile)), + label = paste0("sans moustache '", param, "'") + ) + } + ) + + + test_html <- dateRangeInput_dsfr_template( + inputId = "toto", + label = "titi" + ) + + + #' @description tester si tous les params sont remplaces + expect_false(grepl(pattern = "\\{\\{", test_html)) + + + #' @description Verifie que les parametres ont bien ete remplace par leurs valeurs + + purrr::walk( + c( + inputId = "toto", + label = "titi" + ), + function(param) { + expect_true( + any(grepl(pattern = param, test_html)), + label = paste0("remplacement de '", param, "'") + ) + } + ) + + ## lecture snapshot + snapshot_html <- readRDS( + file = file.path( + "snapshot", # pour passer les tests en production (apres le inflate), + # "tests/testthat/snapshot", # pour passer les tests en developpement (avant le inflate), + "dateRangeInput_dsfr_template.Rda" + ) + ) + + #' @description Verifie le HTML créé + # Retire tous les espaces et saut de ligne pour la comparaison + # Pour eviter les problèmes inter-OS + expect_equal( + gsub("\\s|\\n", "", test_html), + gsub("\\s|\\n", "", snapshot_html) + ) + + + # Si erreur au précedent test deux cas possibles : + # + # - nouveau composant: Lancer le saveRDS, relancer le test et recommenter le saveRDS + # + # - composant a mettre a jour: si le test ne passe plus avant de changer le snapshot, + # assurez vous d'avoir bien pris en compte la nouvelle personnalisation + # dans la fonction radioButtons_unique_dsfr_template puis lancer le saveRDS, relancer le test et recommenter le saveRDS + # + # saveRDS(test_html, + # file = file.path("tests/testthat/snapshot", + # "dateRangeInput_dsfr_template.Rda" + # ) + # ) + # +}) +``` + + +# dateRangeInput_dsfr + +```{r function-dateRangeInput_dsfr} +#' dateRangeInput_dsfr +#' +#' @param inputId id de l'input +#' @param label label du bouton +#' @param choices Liste des valeurs à sélectionner (si les éléments de la liste portent un nom, c'est ce nom qui est affiché à l'utilisateur et non la valeur) +#' @param selected Element selectionné +#' @importFrom assertthat assert_that +#' @importFrom purrr map +#' @return html +#' +#' @export +#' +#' @examples +dateRangeInput_dsfr <- function( + inputId, + label, + start = NULL, + end = NULL + ) { + # check les params + assertthat::assert_that(is.character(inputId)) + assertthat::assert_that(is.character(label)) + + dateRangeInput_dsfr_template( + inputId = inputId, + label = label, + start = start, + end = end + ) %>% + parse_html() + + +} +``` + + + + + +```{r examples-dateRangeInput_dsfr} +## Only run examples in interactive R sessions +if (interactive()) { + library(shiny) + library(shinygouv) + + ui <- fluidPage_dsfr( + header = header_dsfr( + intitule = "Intitule", + officiel = "Officiel", + nom_site_service = "Nom du site / service", + baseline = "baseline - precisions sur l organisation", + class = "fr-m-1w" + ), + title = "Exemple", + fluidRow_dsfr( + # sans vecteur nommé + dateRangeInput_dsfr(inputId = "daterange1", + label = "Date range:", start = "2001-01-01", end = "2002-01-01") + ) + ) + server <- function(input, output, session) { + + observeEvent(input$daterange1, { + print(input$daterange1) + }) + + } + + + shinyApp(ui, server) +} +``` + +```{r tests-dateRangeInput_dsfr} +test_that("dateRangeInput_dsfr works", { + expect_true(inherits(dateRangeInput_dsfr, "function")) + + test_html <- dateRangeInput_dsfr( + inputId = "variable", + label = "Variable:", + choices = c( + "Cylinders" = "cyl", + "Transmission" = "am", + "Gears" = "gear" + ) + ) + + #' @description tester si shiny.tag + expect_s3_class(test_html, "shiny.tag") + + + expect_error( + dateRangeInput_dsfr( + inputId = 1234, + label = "Variable:", + choices = c( + "Cylinders" = "cyl", + "Transmission" = "am", + "Gears" = "gear" + ) + ) + ) + + expect_error( + dateRangeInput_dsfr( + inputId = "variable", + label = 1234, + choices = c( + "Cylinders" = "cyl", + "Transmission" = "am", + "Gears" = "gear" + ) + ) + ) + + expect_error( + dateRangeInput_dsfr( + inputId = "variable", + label = "Variable:", + choices = c(1234, 5678) + ) + ) + + expect_error( + dateRangeInput_dsfr( + inputId = "variable", + label = "Variable:", + choices = c( + "Cylinders" = "cyl", + "Transmission" = "am", + "Gears" = "gear" + ), + selected = c("gear", "am") + ) + ) + + expect_error( + dateRangeInput_dsfr( + inputId = "variable", + label = "Variable:", + choices = list( + "Cylinders" = "cyl", + "Transmission" = "am", + "Gears" = "gear" + ) + ), + regexp = NA + ) + + snapshot_html <- readRDS( + file = file.path( + "snapshot", # pour passer les tests en production (apres le inflate), + # "tests/testthat/snapshot", # pour passer les tests en developpement (avant le inflate), + "dateRangeInput_dsfr.Rda" + ) + ) + #' @description Verifie le parametre selected dans le HTML + expect_equal( + gsub("\\s|\\n", "", test_html), + gsub("\\s|\\n", "", snapshot_html) + ) + + + # Si erreur au précedent test deux cas possible : + # + # - nouveau composant: Lancer le saveRDS, relancer le test et recommenter le saveRDS + # + # - composant a mettre a jour: si le test ne passe plus avant de changer le snapshot, + # assurez vous d'avoir bien pris en compte la nouvelle personnalisation + # dans la fonction radioButtons_dsfr_template puis lancer le saveRDS, relancer le test et recommenter le saveRDS + + # saveRDS(test_html, + # file = file.path("tests/testthat/snapshot", + # "dateRangeInput_dsfr.Rda" + # ) + # ) + # +}) +``` + +# updatedateRangeInput_dsfr + +```{r function-updatedateRangeInput_dsfr} +#' updatedateRangeInput_dsfr +#' +#' @param inputId id de l'input +#' @param label label du bouton +#' @param choices Liste des valeurs à sélectionner (si les éléments de la liste portent un nom, c'est ce nom qui est affiché à l'utilisateur et non la valeur) +#' @param selected Element selectionné +#' @param session la session, la valeur par défaut est getDefaultReactiveDomain(). +#' +#' @importFrom shiny updatedateRangeInput +#' @return html +#' +#' @export +updatedateRangeInput_dsfr <- function( + inputId, + label = NULL, + choices = NULL, + selected = NULL, + session = shiny::getDefaultReactiveDomain() + ) { + ns <- session$ns + + updatedateRangeInput( + session = session, + inputId = inputId, + label = label, + choices = choices, + selected = selected + ) +} +``` + +```{r example-updatedateRangeInput_dsfr} +## Only run examples in interactive R sessions +if (interactive()) { + library(shiny) + library(shinygouv) + + ui <- fluidPage_dsfr( + header = header_dsfr( + intitule = "Intitule", + officiel = "Officiel", + nom_site_service = "Nom du site / service", + baseline = "baseline - precisions sur l organisation", + class = "fr-m-1w" + ), + title = "Exemple", + fluidRow_dsfr( + dateRangeInput_dsfr( + inputId = "variable", + label = "Variable:", + choices = c( + "Cylinders" = "cyl", + "Transmission" = "am", + "Gears" = "gear" + ) + ) + ), + fluidRow_dsfr( + actionButton_dsfr(inputId = "update", label = "update label") + ), + fluidRow_dsfr( + actionButton_dsfr(inputId = "update2", label = "update choices") + ), + fluidRow_dsfr( + actionButton_dsfr(inputId = "update3", label = "update selected") + ) + ) + server <- function(input, output, session) { + observeEvent(input$update, { + updatedateRangeInput_dsfr(inputId = "variable", label = "Nouveau label") + }) + + observeEvent(input$update2, { + updatedateRangeInput_dsfr(inputId = "variable", choices = LETTERS) + }) + + observeEvent(input$update3, { + updatedateRangeInput_dsfr(inputId = "variable", selected = LETTERS[12]) + }) + } + + shinyApp(ui, server) +} +``` + +```{r tests-updatedateRangeInput_dsfr} +test_that("updatedateRangeInput_dsfr works", { + expect_true(inherits(updatedateRangeInput_dsfr, "function")) + + sessA <- createModuleSession("modA") + + updatedateRangeInput_dsfr( + session = sessA, + inputId = "variable", + label = "Nouveau label", + choices = LETTERS[1:5], + selected = LETTERS[4] + ) + + resultA <- sessA$lastInputMessage + # inputId + expect_equal("variable", resultA$id) + # label + expect_equal("Nouveau label", resultA$message$label) + # HTML content + expect_equal( + resultA$message$options, + structure( + "\n\n\n\n", + html = TRUE, + class = c( + "html", + "character" + ) + ) + ) +}) +``` + + +```{r development-inflate, eval=FALSE} +# Run but keep eval=FALSE to avoid infinite loop +# Execute in the console directly +fusen::inflate( + flat_file = "dev/flat_composants/flat_dateRangeInput.Rmd", + vignette_name = NA, + check = FALSE +) +``` diff --git a/inst/external_deps/radiogroupbuttons.js b/inst/external_deps/radiogroupbuttons.js index a07887b0..2095191d 100644 --- a/inst/external_deps/radiogroupbuttons.js +++ b/inst/external_deps/radiogroupbuttons.js @@ -11,3 +11,9 @@ $(current_name).parent().parent().parent().find("button").not(current_button).ad }; +const updatedate = function(inputId){ + var start = $("#"+inputId+"-start").val(); + var end = $("#"+inputId+"-end").val(); + + Shiny.setInputValue(inputId, {start: start, end: end}) +} diff --git a/inst/v1.9.3/composant/dateRangeInput.html b/inst/v1.9.3/composant/dateRangeInput.html new file mode 100644 index 00000000..2d4b0a49 --- /dev/null +++ b/inst/v1.9.3/composant/dateRangeInput.html @@ -0,0 +1,18 @@ +
+ +
+ + + to + + +
+
+
+ +
From efdb50c42ce5b71ac9609107a5b9da78e11f04e3 Mon Sep 17 00:00:00 2001 From: cervan Date: Thu, 6 Jul 2023 14:55:36 +0200 Subject: [PATCH 2/9] js pour danterange --- R/shinygouv-dependencies.R | 3 ++- inst/external_deps/daterange.js | 6 ++++++ inst/external_deps/radiogroupbuttons.js | 7 ------- inst/v1.9.3/composant/dateRangeInput.html | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 inst/external_deps/daterange.js diff --git a/R/shinygouv-dependencies.R b/R/shinygouv-dependencies.R index 2a516114..bea2ba4c 100644 --- a/R/shinygouv-dependencies.R +++ b/R/shinygouv-dependencies.R @@ -38,7 +38,8 @@ add_dsfr_deps <- function(tag, version = get_dsfr_version()) { ), script = list( list(type = "text/javascript", src = "shiny-compat.js"), - list(type = "text/javascript", src = "navbarPage.js") + list(type = "text/javascript", src = "navbarPage.js"), + list(type = "text/javascript", src = "daterange.js") ), all_files = TRUE ), diff --git a/inst/external_deps/daterange.js b/inst/external_deps/daterange.js new file mode 100644 index 00000000..fddbe259 --- /dev/null +++ b/inst/external_deps/daterange.js @@ -0,0 +1,6 @@ +const updatedate = function(inputId){ + var start = $("#"+inputId+"-start").val(); + var end = $("#"+inputId+"-end").val(); + + Shiny.setInputValue(inputId, {start: start, end: end}) + } \ No newline at end of file diff --git a/inst/external_deps/radiogroupbuttons.js b/inst/external_deps/radiogroupbuttons.js index 2095191d..55ce3579 100644 --- a/inst/external_deps/radiogroupbuttons.js +++ b/inst/external_deps/radiogroupbuttons.js @@ -10,10 +10,3 @@ $(current_name).parent().removeClass('fr-btn--secondary'); $(current_name).parent().parent().parent().find("button").not(current_button).addClass("fr-btn--secondary"); }; - -const updatedate = function(inputId){ - var start = $("#"+inputId+"-start").val(); - var end = $("#"+inputId+"-end").val(); - - Shiny.setInputValue(inputId, {start: start, end: end}) -} diff --git a/inst/v1.9.3/composant/dateRangeInput.html b/inst/v1.9.3/composant/dateRangeInput.html index 2d4b0a49..9bfdb599 100644 --- a/inst/v1.9.3/composant/dateRangeInput.html +++ b/inst/v1.9.3/composant/dateRangeInput.html @@ -1,15 +1,15 @@ -
+
- to -
From e4a891eb560d411844646ef46067dac5a4db5538 Mon Sep 17 00:00:00 2001 From: murielle Delmotte Date: Thu, 6 Jul 2023 16:24:38 +0200 Subject: [PATCH 3/9] feat: Implementation dateRangeInput_dsfr WIP tags: doc, test pourquoi: - avoir une dateRangeInput semblable a shiny quoi: - implementation du dateRangeInput_dsfr dans un flat dedie - ajout de js pour la concatenation de la date start et end Issue #128 --- DESCRIPTION | 1 - NAMESPACE | 1 + R/daterangeinput_dsfr.R | 83 ++++++ R/daterangeinput_dsfr_template.R | 35 +++ R/shinygouv-dependencies.R | 3 +- dev/flat_composants/flat_dateRangeInput.Rmd | 252 +++++------------- inst/external_deps/daterange.js | 17 +- inst/external_deps/daterange2.js | 12 + inst/v1.9.3/composant/dateRangeInput.html | 6 +- man/dateRangeInput_dsfr.Rd | 56 ++++ man/shinygouv-package.Rd | 1 + man/updateTabsetPanel_dsfr.Rd | 2 +- .../testthat/snapshot/dateRangeInput_dsfr.Rda | Bin 0 -> 487 bytes .../snapshot/dateRangeInput_dsfr_template.Rda | Bin 0 -> 610 bytes tests/testthat/test-daterangeinput_dsfr.R | 86 ++++++ .../test-daterangeinput_dsfr_template.R | 96 +++++++ 16 files changed, 452 insertions(+), 199 deletions(-) create mode 100644 R/daterangeinput_dsfr.R create mode 100644 R/daterangeinput_dsfr_template.R create mode 100644 inst/external_deps/daterange2.js create mode 100644 man/dateRangeInput_dsfr.Rd create mode 100644 tests/testthat/snapshot/dateRangeInput_dsfr.Rda create mode 100644 tests/testthat/snapshot/dateRangeInput_dsfr_template.Rda create mode 100644 tests/testthat/test-daterangeinput_dsfr.R create mode 100644 tests/testthat/test-daterangeinput_dsfr_template.R diff --git a/DESCRIPTION b/DESCRIPTION index 114bba07..08d7c2d6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -39,7 +39,6 @@ Suggests: knitr (>= 1.39), readxl (>= 1.4.0), rmarkdown (>= 2.14), - rstudioapi (>= 0.13), testthat (>= 3.0.0), tibble (>= 3.1.7), withr diff --git a/NAMESPACE b/NAMESPACE index 656e2259..0adef35c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -7,6 +7,7 @@ export(checkboxGroupInput_dsfr) export(checkboxInput_dsfr) export(column_dsfr) export(convert_to_dsfr) +export(dateRangeInput_dsfr) export(fluidPage_dsfr) export(fluidRow_dsfr) export(get_dsfr_version) diff --git a/R/daterangeinput_dsfr.R b/R/daterangeinput_dsfr.R new file mode 100644 index 00000000..8a4ca8fa --- /dev/null +++ b/R/daterangeinput_dsfr.R @@ -0,0 +1,83 @@ +# WARNING - Generated by {fusen} from /dev/flat_composants/flat_dateRangeInput.Rmd: do not edit by hand + +#' dateRangeInput_dsfr +#' +#' @param inputId inputId +#' @param start character La date de début au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour. +#' @param end character La date de fin au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour. +#' @param label label +#' +#' @importFrom assertthat assert_that +#' @importFrom purrr map +#' @return html +#' +#' @export +#' +#' @examples +#' ## Only run examples in interactive R sessions +#' if (interactive()) { +#' library(shiny) +#' library(shinygouv) +#' +#' ui <- fluidPage_dsfr( +#' header = header_dsfr( +#' intitule = "Intitule", +#' officiel = "Officiel", +#' nom_site_service = "Nom du site / service", +#' baseline = "baseline - precisions sur l organisation", +#' class = "fr-m-1w" +#' ), +#' title = "Exemple", +#' fluidRow_dsfr( +#' # sans vecteur nommé +#' dateRangeInput_dsfr(inputId = "daterange1", +#' label = "Date range:", start = "2001-01-01") +#' ) +#' ) +#' server <- function(input, output, session) { +#' +#' observeEvent(input$daterange1, ignoreNULL = FALSE , { +#' print(input$daterange1) +#' }) +#' +#' } +#' +#' +#' shinyApp(ui, server) +#' } +dateRangeInput_dsfr <- function( + inputId, + label, + start = NULL, + end = NULL + ) { + # check les params + assertthat::assert_that(is.character(inputId)) + assertthat::assert_that(is.character(label)) + + + if (isTRUE(is.null(start))) { + start <- Sys.Date() + } else { + assertthat::assert_that(is.character(start)) + } + + if (isTRUE(is.null(end))) { + end <- Sys.Date() + } else { + assertthat::assert_that(is.character(end)) + } + + dateRangeInput_dsfr_template( + inputId = inputId, + label = label, + start = start, + end = end + ) %>% + parse_html() + + + # session$sendCustomMessage("dateRangeInput_dsfr_js", + # message = list(id = inputId)) + +} diff --git a/R/daterangeinput_dsfr_template.R b/R/daterangeinput_dsfr_template.R new file mode 100644 index 00000000..d429f458 --- /dev/null +++ b/R/daterangeinput_dsfr_template.R @@ -0,0 +1,35 @@ +# WARNING - Generated by {fusen} from /dev/flat_composants/flat_dateRangeInput.Rmd: do not edit by hand + +#' dateRangeInput_dsfr_template +#' +#' @param inputId inputId +#' @param start character La date de début au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour. +#' @param end character La date de fin au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour. +#' @param label label +#' +#' @importFrom htmltools htmlTemplate +#' @importFrom purrr pmap +#' @return html +#' @noRd +dateRangeInput_dsfr_template <- function( + inputId, + label, + start, + end + ) { + + + htmltools::htmlTemplate( + filename = system.file( + get_dsfr_version(with_v = TRUE), + "composant", + "dateRangeInput.html", + package = "shinygouv" + ), + inputId = inputId, + label = label, + start = start, + end = end + ) + + } diff --git a/R/shinygouv-dependencies.R b/R/shinygouv-dependencies.R index bea2ba4c..b9eea5e8 100644 --- a/R/shinygouv-dependencies.R +++ b/R/shinygouv-dependencies.R @@ -39,7 +39,8 @@ add_dsfr_deps <- function(tag, version = get_dsfr_version()) { script = list( list(type = "text/javascript", src = "shiny-compat.js"), list(type = "text/javascript", src = "navbarPage.js"), - list(type = "text/javascript", src = "daterange.js") + list(type = "text/javascript", src = "daterange.js"), + list(type = "text/javascript", src = "daterange2.js") ), all_files = TRUE ), diff --git a/dev/flat_composants/flat_dateRangeInput.Rmd b/dev/flat_composants/flat_dateRangeInput.Rmd index 1ec08c65..83a46112 100644 --- a/dev/flat_composants/flat_dateRangeInput.Rmd +++ b/dev/flat_composants/flat_dateRangeInput.Rmd @@ -23,10 +23,9 @@ pkgload::load_all(export_all = TRUE) #' dateRangeInput_dsfr_template #' #' @param inputId inputId +#' @param start character La date de début au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour. +#' @param end character La date de fin au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour. #' @param label label -#' @param options options pour les futurs choices -#' @param selected selected -#' @param multiple FALSE par defaut #' #' @importFrom htmltools htmlTemplate #' @importFrom purrr pmap @@ -76,7 +75,9 @@ test_that("dateRangeInput_dsfr_template works", { purrr::walk( c( "inputId", - "label" + "label", + "start", + "end" ), function(param) { with_moustache <- paste0("\\{\\{", param, "\\}\\}") @@ -90,7 +91,9 @@ test_that("dateRangeInput_dsfr_template works", { test_html <- dateRangeInput_dsfr_template( inputId = "toto", - label = "titi" + label = "titi", + start = "2001-01-01", + end = "2010-12-31" ) @@ -102,8 +105,10 @@ test_that("dateRangeInput_dsfr_template works", { purrr::walk( c( - inputId = "toto", - label = "titi" + inputId = "toto", + label = "titi", + start = "2001-01-01", + end = "2010-12-31" ), function(param) { expect_true( @@ -154,10 +159,11 @@ test_that("dateRangeInput_dsfr_template works", { ```{r function-dateRangeInput_dsfr} #' dateRangeInput_dsfr #' -#' @param inputId id de l'input -#' @param label label du bouton -#' @param choices Liste des valeurs à sélectionner (si les éléments de la liste portent un nom, c'est ce nom qui est affiché à l'utilisateur et non la valeur) -#' @param selected Element selectionné +#' @param inputId inputId +#' @param start character La date de début au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour. +#' @param end character La date de fin au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour. +#' @param label label +#' #' @importFrom assertthat assert_that #' @importFrom purrr map #' @return html @@ -175,6 +181,19 @@ dateRangeInput_dsfr <- function( assertthat::assert_that(is.character(inputId)) assertthat::assert_that(is.character(label)) + + if (isTRUE(is.null(start))) { + start <- Sys.Date() + } else { + assertthat::assert_that(is.character(start)) + } + + if (isTRUE(is.null(end))) { + end <- Sys.Date() + } else { + assertthat::assert_that(is.character(end)) + } + dateRangeInput_dsfr_template( inputId = inputId, label = label, @@ -182,13 +201,13 @@ dateRangeInput_dsfr <- function( end = end ) %>% parse_html() - - -} -``` + # session$sendCustomMessage("dateRangeInput_dsfr_js", + # message = list(id = inputId)) +} +``` ```{r examples-dateRangeInput_dsfr} @@ -209,12 +228,12 @@ if (interactive()) { fluidRow_dsfr( # sans vecteur nommé dateRangeInput_dsfr(inputId = "daterange1", - label = "Date range:", start = "2001-01-01", end = "2002-01-01") + label = "Date range:", start = "2001-01-01") ) ) server <- function(input, output, session) { - observeEvent(input$daterange1, { + observeEvent(input$daterange1, ignoreNULL = FALSE , { print(input$daterange1) }) @@ -229,15 +248,9 @@ if (interactive()) { test_that("dateRangeInput_dsfr works", { expect_true(inherits(dateRangeInput_dsfr, "function")) - test_html <- dateRangeInput_dsfr( - inputId = "variable", - label = "Variable:", - choices = c( - "Cylinders" = "cyl", - "Transmission" = "am", - "Gears" = "gear" - ) - ) + test_html <- dateRangeInput_dsfr(inputId = "daterange1", + label = "Date range:", start = "2001-01-01", + end = "2003-01-01") #' @description tester si shiny.tag expect_s3_class(test_html, "shiny.tag") @@ -245,58 +258,44 @@ test_that("dateRangeInput_dsfr works", { expect_error( dateRangeInput_dsfr( - inputId = 1234, - label = "Variable:", - choices = c( - "Cylinders" = "cyl", - "Transmission" = "am", - "Gears" = "gear" - ) + inputId = 123, + label = "Date range:", + start = "2001-01-01", + end = "2003-01-01" ) ) expect_error( dateRangeInput_dsfr( - inputId = "variable", - label = 1234, - choices = c( - "Cylinders" = "cyl", - "Transmission" = "am", - "Gears" = "gear" - ) + inputId = "daterange1", + label = 123, + start = "2001-01-01", + end = "2003-01-01" ) ) - + expect_error( dateRangeInput_dsfr( - inputId = "variable", - label = "Variable:", - choices = c(1234, 5678) + inputId = "daterange1", + label = "Date range:", + start = 123, + end = "2003-01-01" ) ) expect_error( dateRangeInput_dsfr( - inputId = "variable", - label = "Variable:", - choices = c( - "Cylinders" = "cyl", - "Transmission" = "am", - "Gears" = "gear" - ), - selected = c("gear", "am") + inputId = "daterange1", + label = "Date range:", + end = 123, + start = "2003-01-01" ) ) expect_error( - dateRangeInput_dsfr( - inputId = "variable", - label = "Variable:", - choices = list( - "Cylinders" = "cyl", - "Transmission" = "am", - "Gears" = "gear" - ) + dateRangeInput_dsfr( + inputId = "daterange1", + label = "Date range:" ), regexp = NA ) @@ -323,136 +322,15 @@ test_that("dateRangeInput_dsfr works", { # assurez vous d'avoir bien pris en compte la nouvelle personnalisation # dans la fonction radioButtons_dsfr_template puis lancer le saveRDS, relancer le test et recommenter le saveRDS - # saveRDS(test_html, - # file = file.path("tests/testthat/snapshot", - # "dateRangeInput_dsfr.Rda" - # ) - # ) - # -}) -``` - -# updatedateRangeInput_dsfr - -```{r function-updatedateRangeInput_dsfr} -#' updatedateRangeInput_dsfr -#' -#' @param inputId id de l'input -#' @param label label du bouton -#' @param choices Liste des valeurs à sélectionner (si les éléments de la liste portent un nom, c'est ce nom qui est affiché à l'utilisateur et non la valeur) -#' @param selected Element selectionné -#' @param session la session, la valeur par défaut est getDefaultReactiveDomain(). -#' -#' @importFrom shiny updatedateRangeInput -#' @return html -#' -#' @export -updatedateRangeInput_dsfr <- function( - inputId, - label = NULL, - choices = NULL, - selected = NULL, - session = shiny::getDefaultReactiveDomain() - ) { - ns <- session$ns - - updatedateRangeInput( - session = session, - inputId = inputId, - label = label, - choices = choices, - selected = selected - ) -} -``` - -```{r example-updatedateRangeInput_dsfr} -## Only run examples in interactive R sessions -if (interactive()) { - library(shiny) - library(shinygouv) - - ui <- fluidPage_dsfr( - header = header_dsfr( - intitule = "Intitule", - officiel = "Officiel", - nom_site_service = "Nom du site / service", - baseline = "baseline - precisions sur l organisation", - class = "fr-m-1w" - ), - title = "Exemple", - fluidRow_dsfr( - dateRangeInput_dsfr( - inputId = "variable", - label = "Variable:", - choices = c( - "Cylinders" = "cyl", - "Transmission" = "am", - "Gears" = "gear" - ) - ) - ), - fluidRow_dsfr( - actionButton_dsfr(inputId = "update", label = "update label") - ), - fluidRow_dsfr( - actionButton_dsfr(inputId = "update2", label = "update choices") - ), - fluidRow_dsfr( - actionButton_dsfr(inputId = "update3", label = "update selected") - ) - ) - server <- function(input, output, session) { - observeEvent(input$update, { - updatedateRangeInput_dsfr(inputId = "variable", label = "Nouveau label") - }) - - observeEvent(input$update2, { - updatedateRangeInput_dsfr(inputId = "variable", choices = LETTERS) - }) - - observeEvent(input$update3, { - updatedateRangeInput_dsfr(inputId = "variable", selected = LETTERS[12]) - }) - } - - shinyApp(ui, server) -} -``` - -```{r tests-updatedateRangeInput_dsfr} -test_that("updatedateRangeInput_dsfr works", { - expect_true(inherits(updatedateRangeInput_dsfr, "function")) - - sessA <- createModuleSession("modA") - - updatedateRangeInput_dsfr( - session = sessA, - inputId = "variable", - label = "Nouveau label", - choices = LETTERS[1:5], - selected = LETTERS[4] - ) + # saveRDS(test_html, + # file = file.path("tests/testthat/snapshot", + # "dateRangeInput_dsfr.Rda" + # ) + # ) - resultA <- sessA$lastInputMessage - # inputId - expect_equal("variable", resultA$id) - # label - expect_equal("Nouveau label", resultA$message$label) - # HTML content - expect_equal( - resultA$message$options, - structure( - "\n\n\n\n", - html = TRUE, - class = c( - "html", - "character" - ) - ) - ) }) ``` + ```{r development-inflate, eval=FALSE} diff --git a/inst/external_deps/daterange.js b/inst/external_deps/daterange.js index fddbe259..719cb0c9 100644 --- a/inst/external_deps/daterange.js +++ b/inst/external_deps/daterange.js @@ -1,6 +1,11 @@ -const updatedate = function(inputId){ - var start = $("#"+inputId+"-start").val(); - var end = $("#"+inputId+"-end").val(); - - Shiny.setInputValue(inputId, {start: start, end: end}) - } \ No newline at end of file +$(document).ready(function() { + + Shiny.addCustomMessageHandler('dateRangeInput_dsfr_js', function(message) { +console.log("coucou") + + var start = $("#"+message.id+"-start").val(); + var end = $("#"+message.id+"-end").val(); + + Shiny.setInputValue(message.id, {start: start, end: end}) +}); + diff --git a/inst/external_deps/daterange2.js b/inst/external_deps/daterange2.js new file mode 100644 index 00000000..9515ab75 --- /dev/null +++ b/inst/external_deps/daterange2.js @@ -0,0 +1,12 @@ +const inputdate = function (inputId) { + + + var start = $("#"+inputId+"-start").val(); + var end = $("#"+inputId+"-end").val(); + + +console.log(start) +console.log(end) + + Shiny.setInputValue(inputId, {start: start, end: end}) + } diff --git a/inst/v1.9.3/composant/dateRangeInput.html b/inst/v1.9.3/composant/dateRangeInput.html index 9bfdb599..c2853e04 100644 --- a/inst/v1.9.3/composant/dateRangeInput.html +++ b/inst/v1.9.3/composant/dateRangeInput.html @@ -1,15 +1,15 @@
-
diff --git a/inst/v1.9.3/table_correspondance_shiny_dsfr.csv b/inst/v1.9.3/table_correspondance_shiny_dsfr.csv index 7b98fead..7831f06c 100644 --- a/inst/v1.9.3/table_correspondance_shiny_dsfr.csv +++ b/inst/v1.9.3/table_correspondance_shiny_dsfr.csv @@ -29,3 +29,5 @@ 28;modalDialog;modalDialog_dsfr;modalDialog.html;oui 29;showModal;showModal_dsfr;;oui 30;removeModal;removeModal_dsfr;;oui +31;dateRangeInput;dateRangeInput_dsfr;dateRangeInput.html;oui +32;updateDateRangeInput;updateDateRangeInput_dsfr;;oui diff --git a/man/dateRangeInput_dsfr.Rd b/man/dateRangeInput_dsfr.Rd index e2b8911a..339c275f 100644 --- a/man/dateRangeInput_dsfr.Rd +++ b/man/dateRangeInput_dsfr.Rd @@ -4,7 +4,7 @@ \alias{dateRangeInput_dsfr} \title{dateRangeInput_dsfr} \usage{ -dateRangeInput_dsfr(inputId, label, start = NULL, end = NULL) +dateRangeInput_dsfr(inputId, label, start = NULL, end = NULL, separator = "to") } \arguments{ \item{inputId}{inputId} @@ -14,6 +14,8 @@ dateRangeInput_dsfr(inputId, label, start = NULL, end = NULL) \item{start}{character La date de début au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour.} \item{end}{character La date de fin au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour.} + +\item{separator}{character Chaîne à afficher entre les zones de saisie de début et de fin de dates.} } \value{ html @@ -39,12 +41,12 @@ if (interactive()) { fluidRow_dsfr( # sans vecteur nommé dateRangeInput_dsfr(inputId = "daterange1", - label = "Date range:", start = "2001-01-01") + label = "Date range:", start = "2001-01-01",separator = "à") ) ) server <- function(input, output, session) { - observeEvent(input$daterange1, ignoreNULL = FALSE , { + observeEvent(input$daterange1, { print(input$daterange1) }) diff --git a/man/updateDateRangeInput_dsfr.Rd b/man/updateDateRangeInput_dsfr.Rd new file mode 100644 index 00000000..053055f3 --- /dev/null +++ b/man/updateDateRangeInput_dsfr.Rd @@ -0,0 +1,82 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/updatedaterangeinput_dsfr.R +\name{updateDateRangeInput_dsfr} +\alias{updateDateRangeInput_dsfr} +\title{updateDateRangeInput_dsfr} +\usage{ +updateDateRangeInput_dsfr( + inputId, + label = NULL, + start = NULL, + end = NULL, + session = shiny::getDefaultReactiveDomain() +) +} +\arguments{ +\item{inputId}{inputId} + +\item{label}{label} + +\item{start}{character La date de début au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour.} + +\item{end}{character La date de fin au format aaaa-mm-jj. Si NULL (valeur par défaut), la date utilisée est la date du jour.} + +\item{session}{la session, la valeur par défaut est getDefaultReactiveDomain().} +} +\value{ +html +} +\description{ +updateDateRangeInput_dsfr +} +\examples{ +## Only run examples in interactive R sessions +if (interactive()) { + + library(shiny) + + ui <- fluidPage_dsfr( + dateRangeInput_dsfr(inputId = "daterange1", + label = "Date range:", start = "2001-01-01",separator = "à"), + actionButton_dsfr("go", "Change label"), + actionButton_dsfr("go2", "Change start"), + actionButton_dsfr("go3", "Change end") + ) + + server <- function(input, output, session) { + observeEvent(input$daterange1, { + print(input$daterange1) + }) + + + observeEvent(input$go, { + updateDateRangeInput_dsfr( + session = session, + inputId = "daterange1", + label = "new label" + ) + + }) + + + observeEvent(input$go2, { + updateDateRangeInput_dsfr( + session = session, + inputId = "daterange1", + start = "2000-01-01" + ) + + }) + + observeEvent(input$go3, { + updateDateRangeInput_dsfr( + session = session, + inputId = "daterange1", + end = "2020-01-01" + ) + + }) + } + shinyApp(ui, server) +} +} diff --git a/tests/testthat/snapshot/dateRangeInput_dsfr.Rda b/tests/testthat/snapshot/dateRangeInput_dsfr.Rda index 95d373fb15c01dda849b1dff40a9ef73818a5139..bb4ac206735361f2c9486af9f986493d057f4099 100644 GIT binary patch literal 774 zcmV+h1NruohGH|P8&^Gl>BJ=>D9=!!nNTWI|p!%X|tT!!%rwMEh zI;Fy;N2oxZjO>YtaStr$@$zxwz}z^+KuAMC5uV4u9Ku851yn`$fChtk$-L;KUMzVo z=Lzj&rHxU{z$AQP4o(fWc1i_)d&k9wB{P*(`wxNc)!ts$(|-kY=V(b`RZJv=<>MeF zIthZ9<*1)%A46Z)&pg<9lHPUaE`+I5V(w{jx^(Yn`P3 z?YQ1oK3JmKZ2kY(zHMypTVvbHa5|IWfGZO!1Q}70#$yRDD8Hx-OxPm4SxiaP}t0rKp zv6V96oerc53#^tGtjpZT1--mLYsCxMx#lK*qOX9qB%^i|iY2>hS_W&s0MRvHXH63T E0E5(cq5uE@ literal 487 zcmVlhG}0 zA@b+dwzSe+K}Dm+m@v}y+?}4@bGqH{AcU$YuIm4q9-`Xa?M>3xcP$Gb)YMl6#i(KK z37a7l87KnK4#A2F%ty2bkn^EP7%!$O8Hs#aw1>HvxS<>~K6hZ6bA=hF0--nx9#0l1@J>JZIMBfIFUn3k08lP<6~0==8i45 z0ZuwglFlj2%tg1`OY9GHV{2Wy-S)6xI3ZM~+G|P%bFW3s3!j8~&eO43*AvWeHl;yh z(U;>CLGwP8pjZ}|uZd7U%m^kV=cUvXf)6?dfFE~fms?Cq$L7Cz^4&vp%JBY!n976lr6#cE0ctqtQ_@JN_7~%_|2Hhqq d6r0h_K8Ow5)672ilRf&|{LePHi&DP{001a~?Na~% diff --git a/tests/testthat/snapshot/dateRangeInput_dsfr_template.Rda b/tests/testthat/snapshot/dateRangeInput_dsfr_template.Rda index dfbcc14c3e8b4ef29fd689a5ea7e1bff37a9564d..eff7898e5c0ef53664fb91d6a574c3ccf3667a98 100644 GIT binary patch literal 761 zcmV`bPO_SQd?hp{2ia~T>QD^d0x}=oAA31U2pxv`TOt? zMlWHo=6Ma%Zo_!Xj(u+v+EIT*Nqo{msIMZ)QhkD6w__1a#+2)zBVlpYzMSw#Gr`*d zn!`?n6xHW!OvOYmuOW0cVtm$tJ*PaK=#M0s&^GowzmYrFAL(%dZ7aiOzQuJeKAl0E z$GQRMDHhYx*7YbMO1Zs_g8Zz%8I6b}k){#`-%CP$Ewm_I$_u=U=1LQ(kKi`&c6Q*} z^)lYZ^_>`-*AEHnRcJBcpfe>&JJ>~($F%}@6au0JysO~&Y8Ul_V+1|JudX9ltKZEy z*XqY?idc*W2-ie|FBn-i4e+H5nW1kulwy*iOCiUus|>x}VkEdGj8loN3nk=&COHXq zj6uagT@-w_zyu1_2GoYVJ%pYF4%4gz=c{F_y{8P9<*Bo3X7`wYIKqb%m3WfthidHf z0tt4$PDD6#!KBRhjOm8gQUFbeWF(BKaor0VU(9NQznkG-&{@w?I>5#(7zqx-z&6fU zX0p9g6WCij0d}02n#4GG0$i>Ay}e#&KP7Z?v>>dh2U5a|tj}mo;#lwk(NjrNi2Q{) zlA0;}8}c;$rt!&BIDccwJa0^z0-QP6g>n7-_^r*k|Ed=s81xlB?5_L2C;!rudwXGT zKRhUVa+qUqE_(q###AXXq$=~iS`{bnZooOpCTt3}2jo<0=>ANnUC_zp=B(?Q$5EvC z{NMyoyMJ##`m@R@WBubazU={1x$BPiMCJZJDeo%<{>5s$^1M>QkG+%yDclc4#k9XTt!pxTrK1Z`Mpx}Rnu*yu$8jky$sTX16Ioj rmB&0pfL;VpzZ{UgTO#pu8v;6-3_A%^3wHUo57K@B*$?j{_!0mB%W!)O literal 610 zcmV-o0-gOIiwFP!000002JKcoZ`&{sWo#KiRI~`tF$eW6)bF;`{jLYrz*ZeNzrz;ri=Jw^% zXr8gw(m|S2t^L|w!+t3rq;slhYM27$N6Tnrq>(M$UlQzWPLgXyxG0u}%#>Uf_*$xk z7xkQrwf~q(VJH_&C6_VA7df4>yk^1a3B+OIqp-Kx_Pn?Gt+)NneLDEO!UPU=2GqWb z9>$LXha294^VOzu=;Bo3a%A>dE%TmHFh|9V;TBJ7GSLg~7g%ufT_VCc(S|Cs#rKlw zme)!GO{n6OWK5@uPgyoycLx8k#9z_btP3_tY+4d51sH?e_+;71SH~TJeR%vj@tj!E ze90z{fGa*7jmE_Nwa|UX6=8w0x}c)T`jXZ(%cN)!y-=)RB5Tai!cHZBVb8E{Cb`5$ zLebeZwJi<3do}ciSQnpa1)W*+f%@^`{$E9ZtfKLVj8Dl~yNXDS!L^qI__1JG(;3qx z^E=fTyuAVEDVy^Z_JHOXKE8B(dqvec0wO1_Y;;xTM(AZ9Q{GB4- whXZ!I_kzdV%QaM86H)uxLBF_fUbtdEF?4p4b6xQ$axz%^38u8!yaW#b00Uz;9{>OV diff --git a/tests/testthat/test-daterangeinput_dsfr.R b/tests/testthat/test-daterangeinput_dsfr.R index b3ce8790..eec88312 100644 --- a/tests/testthat/test-daterangeinput_dsfr.R +++ b/tests/testthat/test-daterangeinput_dsfr.R @@ -5,10 +5,7 @@ test_that("dateRangeInput_dsfr works", { test_html <- dateRangeInput_dsfr(inputId = "daterange1", label = "Date range:", start = "2001-01-01", - end = "2003-01-01") - - #' @description tester si shiny.tag - expect_s3_class(test_html, "shiny.tag") + end = "2003-01-01", separator = "à") expect_error( @@ -47,6 +44,16 @@ test_that("dateRangeInput_dsfr works", { ) ) + expect_error( + dateRangeInput_dsfr( + inputId = "daterange1", + label = "Date range:", + end = "2003-02-01", + start = "2003-01-01", + separator = 456 + ) + ) + expect_error( dateRangeInput_dsfr( inputId = "daterange1", diff --git a/tests/testthat/test-daterangeinput_dsfr_template.R b/tests/testthat/test-daterangeinput_dsfr_template.R index dbb05158..c1765afb 100644 --- a/tests/testthat/test-daterangeinput_dsfr_template.R +++ b/tests/testthat/test-daterangeinput_dsfr_template.R @@ -20,7 +20,8 @@ test_that("dateRangeInput_dsfr_template works", { "inputId", "label", "start", - "end" + "end", + "separator" ), function(param) { with_moustache <- paste0("\\{\\{", param, "\\}\\}") @@ -36,7 +37,8 @@ test_that("dateRangeInput_dsfr_template works", { inputId = "toto", label = "titi", start = "2001-01-01", - end = "2010-12-31" + end = "2010-12-31", + separator = "to" ) @@ -51,7 +53,8 @@ test_that("dateRangeInput_dsfr_template works", { inputId = "toto", label = "titi", start = "2001-01-01", - end = "2010-12-31" + end = "2010-12-31", + separator = "to" ), function(param) { expect_true( From 7d7e238eaa6ab8984cf57b36177c81e1ecb34452 Mon Sep 17 00:00:00 2001 From: murielle Delmotte Date: Mon, 17 Jul 2023 11:39:15 +0200 Subject: [PATCH 8/9] fix: update news dateRangeInput_dsfr --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index ebb5b047..61ab2099 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,7 @@ ## feat +* Ajout de `dateRangeInput_dsfr()` et `updateDateRangeInput_dsfr()` * Ajout de `numericInput_dsfr()` et `updateNumericInput_dsfr()` * Ajout de `navbarPage_dsfr()` et `navbarPanel_dsfr()` * Ajout de `radioButtons_dsfr()` et `updateRadioButtons_dsfr()` From 2f435e48fc3dee8a1c4dd1beeedde211ea5cf57f Mon Sep 17 00:00:00 2001 From: murielle Delmotte Date: Mon, 17 Jul 2023 14:22:21 +0200 Subject: [PATCH 9/9] fix: Correction class fluidPage pourquoi: - typo dans la classe de fluidPage quoi: - correction - incrementation de la version - Maj News Issue #136 --- DESCRIPTION | 2 +- NEWS.md | 6 ++++++ inst/v1.9.3/composant/fluidpage.html | 2 +- tests/testthat/snapshot/fluidPage_dsfr.Rda | Bin 1491 -> 1532 bytes .../snapshot/fluidPage_dsfr_template.Rda | Bin 455 -> 455 bytes 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 80f31634..044003f2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: shinygouv Title: Implement the DSFR for your shiny applications -Version: 1.0.1 +Version: 1.0.2 Authors@R: c( person("Juliette", "ENGELARE-LEFEBVRE", , "juliette.engelaere-lefebvre@developpement-durable.gouv.fr", role = c("aut", "cre")), person("Sébastien", "Rochette", , "sebastien@thinkr.fr", role = "aut", diff --git a/NEWS.md b/NEWS.md index 61ab2099..c701b5bd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,9 @@ +# shinygouv 1.0.2 + +## fix + +* Correction de la classe du `fluidPage_dsfr()` + # shinygouv 1.0.0 ## chore diff --git a/inst/v1.9.3/composant/fluidpage.html b/inst/v1.9.3/composant/fluidpage.html index 01b72a87..5f5be1bb 100644 --- a/inst/v1.9.3/composant/fluidpage.html +++ b/inst/v1.9.3/composant/fluidpage.html @@ -5,7 +5,7 @@ {{header}}
-
+
{{body}}
diff --git a/tests/testthat/snapshot/fluidPage_dsfr.Rda b/tests/testthat/snapshot/fluidPage_dsfr.Rda index ead2137764f7f804ba41acaebad00b82b1361ebc..54a25bf43aaac02d653d60c97893c7508949d763 100644 GIT binary patch literal 1532 zcmVyqxst}BkyP}fappV#fCx_7R1M>3Aw2+?8B8*tAJ zkwpH0`*MV1yu01%jxY-0>cXchiHz{B<1`zFh^CSN*H)%ReXISH{c#trrrHgbNiA%zsCt2dTdRofAe%$wWzS{S+Lo@9wFY3N) zyl@WlSIyvgTvWiJB6nTct43;YH0<6!qEyQk66?9V?ykf#y}`x0^wbViLK&`zwpx5WCT{ z$Nyjo?C|u7`$bGB5XjSiIlkfw9Pp=4B;o`~H+=T^Z`F`j+#xU*<`k%x$}eYy*NEdm zF$>K+^UNZd;jRsE*Ad-R8F29qSc9dS?GGFSWm0U3S}pehpx{C)l9H3Y$eU+^5s5;M zsl{(4<&=b+S@NdKKPsj#7?`GL+()m>P3!$sN=9>i>rS1X`jYxS;4pQwhOKFUB7v=k zt(b)<^1s{p`WvgKsU%s?wlW*k@NezmxApMK9M`LJ%(v>|yuuPpahN~dR)M979JV4+ zc{oBMVM&_g4;v&rB$Qri(;JAr*A3ej4Qva{$0V=`A_5k>dkX8~JLP4Eg>bN=aA@TWa>YtORb3%*wH3I?f>n28q>TEETDx z?t?`6=+HUw{lM(}G{;>BoDAU7KZGwQOcw$fLVMnr;QfSgX$;;cp&a!>tPi`oam6K+NCMfk2oS{9PW$QGZqr!l^VT%ZA7d^EqvjIiqm)4fyhOlBQUpay$Vr672;(wL6oC}I zu#2EzWQ5j6f`UhASg71YQM9M9EoN*i=d$?3ko$+21{3Gt%wWgenl4QoCGfWKU%)67 zX$3Ya7YV&{5+rsWqA|*yWOeJ6fyGo{I+oabiNfuylynCizEIeeRUZ+-R>$*LdOl^u zy9&Auk&r3>pd3H!s>_in)oWVaN{XJ-m9ke#)e$j=;}s?}N$p5^t$5;WY3eda;Ki!%}-SSMaoW+KeA1kue z&7NJVZX0G-E!51;^9ICnwA6|-u~|)sLg8~lhpO!0_FSW_o!9dq^-Cf&`JOTg$(VX| iH!CH){8sPVKr-#qNJTsdU)`CIA5PK?n-~ literal 1491 zcmV;^1uXg>iwFP!000001MOMeZre5#R%|C}oMvg-4qJy^2pTYSKx8-Dx~+q4pewdv z7wrPH#d;Ue5^b}kNR6bN`7V#Ky}|k%x!hayL7I`&PbA7tY$q|=CI(W8G7 zd}xR+dl+z@$W{vaY~7&i?tH!PsA$HfmyXJT zs~lM;1*_!kH1SE1P|&-gyj7AK7Zr1~3kcDA^1W+v7U^fon_9^mMlm#(Q+i^Z> z!^919F*h@VdwVLZhS?Hw&S2`gH04nu03xO7ou{2!#*|z}_bLE(?d8#eX`h}otV>`*b}Tis4o#`n?+-mMf(YBL{e%X#4{iMB z;2%gNH5|US?*@oS_Bi~P!EKvEj~%`ifFU4k|INYQsv)-RzN9V4h-6(NzM7O?0|tBP zEHv#*GmCISxK?GjW(-q7;O4IM1`0LXpBR*UNuZ&snr#A-e^aYK2!^^mXT2Y zgWodB$=EpdGY^VLr9>X^lqrc(nKz|B3|em z03OmX3X{$X@=r*mQ+;|5vG=ZF`(_T?9I_$uOahsCcQ~1{^;zq{QojZ8>!;=Exrx&0 zCTY6)p+I6{#6BArG0>OCl*<&eEBzma{*+bL6G;2K$EGoPz6I(NS#GuhS=HF8HfuZT z)zRgt+9c&+(@DyE$vt%N0&7 zabwQAsiaIuE_yD8Me=Yx6%dI-JxWF9(75HAnn_b|Z>TT#eCkE2>Yg@FG$Sl0q)|6B zP_w+jJUfjCjc+DU*Sh+^9VQ~3vgKYQ*D>0FlN^ zKIt4RMqvt~Dra79-ln~4gpRd%=h$K@F^|oH$X0F^Qk9pLf}iVwNFa>FD1Bx!MaTYY z1yBH6Wg&;6?daR`6uo+AAVlcGKO;13J1^Yl58X#J2!TLd3}wp1aBn-m{oICr9~Qn; zT-kPp2=0fJiJb60^2MO-Lw%yvjcpqdB#EvQmB;CHg12<_ai+OzWIxw=-iS|98oj zzA%;LLhX>8tJCF7+Se0qk5_j>KII2*aDx*2Q84cr%VgjoVpI9-)=?y}T)q!v{0<6RO82#tTvfhtF3J8)o%$L=>Bx2$KXzP6X%!}z7(8J z104oc`-vzNEmY9zL)cq~jjqC0D#I}43r5;)`{Gs^&o=OE=&|wQz$hJedp4a>;GCAm z3v0XRi~J7J(L&cqPTRS?wk068B(YMG&B`V{N;1ZYq=iAch4spe70a>0^M+`*c+qGP z0a3W{O1wx*_HV9Ag-{!=I@j}{ucsaiz=B{u-Sy<~cKIXvntM)np7;I?{@v_#t@$I% x^kv*C-^TT}av?*fb_z!N)fXa?MGj<|hbL=)k2x#br%n~a5uZ;VF*#}m008Uc-Od02 literal 455 zcmV;&0XY62iwFP!000001I1L!PQx$|beaT61PH{5Ta+i`f1a|=qa-d5sfbdk@~$828P9AUeT0yW9NR*+3n6mv@2;aOm^*{SLTI2z5Au^L zcXW)#ai($}a9qqM;Y@}B!3sw+8L5osH1Xq%Vv_g){9?rvrwKyNpi{un=VA2gOZ30L zUWkWV7`vD5i-jr~p~hA><+v=Hy@Ou;I`+~GOPneyA$c>3x&Tj7gWhF4s!{v#sAdvR z1z84+OhP4|!=SoSGmr9VG`@HZHP;%Kwc4aLSba;yUTuqhPovW-J@_{OCeAS{0x3A1 zggOi>@Tn*iEmYV@L)c4)ie7}BR7NwtV5Hf!D_)h~Y@?nH2pbDp#?oOIv1y3{=d?5q ztbL*%@w1AK*0`?Zw8-tXAE9DP5-TOyv~1FYG-I4dS{S6;vR;|7VmVZJ-Vp6Bp4UQ* zfM?=s*+yEd|8mnz2({s+Q)mMHGF3A`rNrM2w4M&$_8*>)KIimz{oU`mzv;EE6@Roy xU$(F1+IY@Z9%Sg!+WknMd?F%U