Skip to content

Commit

Permalink
Merge pull request #13 from shahronak47/9-variable-stars
Browse files Browse the repository at this point in the history
Include variable stars and add default number of stars selected
  • Loading branch information
shahronak47 authored May 7, 2023
2 parents eb1a0f1 + 2164ffc commit 0248b17
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 38 deletions.
7 changes: 4 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
Package: shinyRatings
Title: An intuitive way of providing star rating on a shiny app
Version: 0.0.1
Version: 0.0.2
Authors@R: person("Ronak", "Shah", , "[email protected]", role = c("aut", "cre"))
Description: An intuitive way of providing star rating on a shiny app.
License: MIT + file LICENSE
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.1
RoxygenNote: 7.2.3
Suggests:
covr,
testthat (>= 3.0.0)
Config/testthat/edition: 3
Imports:
shiny,
htmltools
htmltools,
jsonlite
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# shinyRatings 0.0.2

* Add a disabled argument in `shinyRatings` function.
* [Allow possibility of variable number of stars](https://github.com/shahronak47/shinyRatings/issues/9)
* [Add default value of starts selected](https://github.com/shahronak47/shinyRatings/issues/9)

# shinyRatings 0.0.1

* [Fix center alignment of all the elements](https://github.com/shahronak47/shinyRatings/issues/11)
Expand Down
71 changes: 42 additions & 29 deletions R/shinyRatings.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#' Include ratings in shiny UI
#'
#' @param inputId The input slot that will be used to access the value of number of stars.
#'
#' @param no_of_stars Number of stars that you want to display on the UI.
#' @param default Number of stars selected by default.
#' @param disabled Keep number of stars as fixed? Disable the option of changing values
#' @return Ratings to be added to UI definition
#' @examples
#' if(interactive()){
Expand All @@ -22,44 +24,55 @@
#'
#' @export
#'
shinyRatings <- function(inputId) {
shinyRatings <- function(inputId, no_of_stars = 5, default = no_of_stars, disabled = FALSE) {
do_checks(no_of_stars, default, disabled)
# Since in HTML 2 check box constitute of 1 star
calculate_def <- default * 2
htmltools::tags$html(
htmltools::tags$head(
htmltools::tags$link(type = "text/css", href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css", rel = "stylesheet"),
htmltools::tags$link(type = "text/css", href = "files/style.css", rel = "stylesheet"),
htmltools::tags$script(src="files/shinyRatingsBinding.js"),
htmltools::tags$script(src="files/shinyRatingsBinding.js")
),
htmltools::tags$body(
shiny::div(id = inputId, class = "shinyRatings",
htmltools::HTML(ratings_html())
htmltools::HTML(ratings_html(no_of_stars, disabled)), data = jsonlite::toJSON(list(n = calculate_def))
)
)
)
}

#' @noRd
ratings_html <- function() {
'
<div class="rating-group">
<label aria-label="0.5 stars" class="rating__label rating__label--half" for="rating2-05"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input class="rating__input" name="rating2" id="rating2-05" value="0.5" type="radio">
<label aria-label="1 star" class="rating__label" for="rating2-10"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input class="rating__input" name="rating2" id="rating2-10" value="1" type="radio">
<label aria-label="1.5 stars" class="rating__label rating__label--half" for="rating2-15"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input class="rating__input" name="rating2" id="rating2-15" value="1.5" type="radio">
<label aria-label="2 stars" class="rating__label" for="rating2-20"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input class="rating__input" name="rating2" id="rating2-20" value="2" type="radio">
<label aria-label="2.5 stars" class="rating__label rating__label--half" for="rating2-25"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input class="rating__input" name="rating2" id="rating2-25" value="2.5" type="radio">
<label aria-label="3 stars" class="rating__label" for="rating2-30"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input class="rating__input" name="rating2" id="rating2-30" value="3" type="radio">
<label aria-label="3.5 stars" class="rating__label rating__label--half" for="rating2-35"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input class="rating__input" name="rating2" id="rating2-35" value="3.5" type="radio">
<label aria-label="4 stars" class="rating__label" for="rating2-40"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input class="rating__input" name="rating2" id="rating2-40" value="4" type="radio">
<label aria-label="4.5 stars" class="rating__label rating__label--half" for="rating2-45"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input class="rating__input" name="rating2" id="rating2-45" value="4.5" type="radio">
<label aria-label="5 stars" class="rating__label" for="rating2-50"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input class="rating__input" name="rating2" id="rating2-50" value="5" type="radio">
</div>'
}
ratings_html <- function(n, disabled) {
half_num <- seq(0.5, n, 1)
full_num <- seq(1, n, 1)
half_num_chr <- sub('.', '', half_num, fixed = TRUE)
full_num_chr <- paste0(full_num, '0')

dynamic_html <- paste0(
sprintf(
'<label aria-label="%s stars" class="rating__label rating__label--half" for="rating2-%s"><i class="rating__icon--star fa fa-star-half"></i></label>
<input class="rating__input" name="rating2" id="rating2-%s" value="%s" type="radio">
<label aria-label="%s star" class="rating__label" for="rating2-%s"><i class="rating__icon--star fa fa-star"></i></label>
<input class="rating__input" name="rating2" id="rating2-%s" value="%s" type="radio">',
half_num, half_num_chr, half_num_chr, half_num, full_num, full_num_chr, full_num_chr, full_num
), collapse = '')

if(isTRUE(disabled)) {
# Add disabled attribute to input elements
dynamic_html <- gsub('<input', '<input disabled', dynamic_html, fixed = TRUE)
}
sprintf('<div class="rating-group">%s</div>', dynamic_html)
}

#' @noRd
do_checks <- function(no_of_stars, default, disabled) {
# no_of_stars should be a whole number or should end with .5
stopifnot("no_of_stars can be a whole number or a number ending with .5" = no_of_stars %% 1 %in% c(0, .5))
# default value should be a whole number or should end with .5
stopifnot("default value can be a whole number or a number ending with .5" = default %% 1 %in% c(0, .5))
# default value should not be greater than no_of_stars
stopifnot("default value can be greater than 0 and less than `no_of_stars`" = default > 0 && default <= no_of_stars)
# disabled can handle only logical values
stopifnot("disabled can take only logical values (TRUE/FALSE)" = is.logical(disabled))
}
19 changes: 15 additions & 4 deletions inst/exdata/shinyRatingsBinding.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,34 @@ $.extend(shinyRatingsInputBinding, {
return $(scope).find(".shinyRatings");
},

initialize: function(el){
/* Get default value */
var data = JSON.parse(el.getAttribute('data'));
var n = data.n;
/* Manually set the check property to TRUE for default number of stars */
var count = 0
$(el).find("input").each(function(inputitem){
count = count + 1
if(count <= n) {
$(this).prop("checked", "true");
}
});
},

getValue: function(el) {
var rate_value = 0;
$(el).find("input").each(function(inputitem){
if ($(this).prop("checked")) {
rate_value = parseFloat($(this).val());
}
});
if (rate_value == 0) {
rate_value = 5;
}
return(rate_value)
},

setValue: function(el, value) {
el.value = value;
},

subscribe: function(el, callback) {
$(el).on('change.shinyRatings', function(event) {
callback(false);
Expand Down
8 changes: 7 additions & 1 deletion man/shinyRatings.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion tests/testthat/test-shinyRatings.R
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
test_that("shinyRatings works", {
expect_s3_class(shinyRatings('star'), 'shiny.tag')
expect_s3_class(shinyRatings("star", no_of_stars = 5, default = 4), 'shiny.tag')
expect_s3_class(shinyRatings("star", no_of_stars = 5, disabled = TRUE), 'shiny.tag')
})

test_that("ratings_html works", {
res <- ratings_html()
res <- ratings_html(5, disabled = FALSE)
expect_type(res, 'character')
expect_s3_class(htmltools::HTML(res), 'html')
})


test_that("shinyRatings returns error", {
expect_error(shinyRatings("star", default = 10), "default value can be greater than 0 and less than `no_of_stars`")
expect_error(shinyRatings("star", no_of_stars = 5.86), "no_of_stars can be a whole number or a number ending with .5")
expect_error(shinyRatings("star", default = 5.86), "default value can be a whole number or a number ending with .5")
expect_error(shinyRatings("star", disabled = 5.86), "disabled can take only logical values")
})

0 comments on commit 0248b17

Please sign in to comment.