diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/_star_rating.scss b/playbook/app/pb_kits/playbook/pb_star_rating/_star_rating.scss index f40cd68086..0965960c72 100644 --- a/playbook/app/pb_kits/playbook/pb_star_rating/_star_rating.scss +++ b/playbook/app/pb_kits/playbook/pb_star_rating/_star_rating.scss @@ -48,8 +48,8 @@ $star-styles: ( - yellow_star: (color: #F9BB00), - primary_star: (color: #0056CF), + yellow_star: (color: $yellow), + primary_star: (color: $royal), suble_star_light: (color: $text_lt_default), suble_star_dark: (color: $text_dk_default), empty_star_dark: (color: $border_dark), @@ -111,4 +111,13 @@ } } } + .yellow-star-selected { + color: $yellow; + } + .primary-star-selected { + color: $royal + } + .suble-star-selected { + color: $text_lt_default; + } } diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/docs/_star_rating_interactive.html.erb b/playbook/app/pb_kits/playbook/pb_star_rating/docs/_star_rating_interactive.html.erb new file mode 100644 index 0000000000..41b1f249b1 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_star_rating/docs/_star_rating_interactive.html.erb @@ -0,0 +1 @@ +<%= pb_rails("star_rating", props: { padding_bottom: "xs", variant: "interactive" }) %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/docs/example.yml b/playbook/app/pb_kits/playbook/pb_star_rating/docs/example.yml index d367d24b39..9253d7b9c7 100644 --- a/playbook/app/pb_kits/playbook/pb_star_rating/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_star_rating/docs/example.yml @@ -13,4 +13,4 @@ examples: - star_rating_background_options: Background Options - star_rating_hide: Layout Options - star_rating_number_config: Number Config - - star_rating_size_options: Size Options + - star_rating_size_options: Size Options \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/index.js b/playbook/app/pb_kits/playbook/pb_star_rating/index.js new file mode 100644 index 0000000000..34b8eb3859 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_star_rating/index.js @@ -0,0 +1,50 @@ +import PbEnhancedElement from "../pb_enhanced_element"; + +const STAR_RATING_SELECTOR = "[data-pb-star-rating]"; +const STAR_RATING_INPUT_ID = "star-rating-input"; + +export default class PbStarRating extends PbEnhancedElement { + static get selector() { + return STAR_RATING_SELECTOR; + } + + connect() { + this.element.addEventListener("click", (event) => { + const clickedStarId = event.currentTarget.id; + this.updateStarColors(clickedStarId); + this.updateHiddenInputValue(clickedStarId); + }); + } + + updateStarColors(clickedStarId) { + const allStars = document.querySelectorAll(STAR_RATING_SELECTOR); + + allStars.forEach(star => { + const starId = star.id; + const icon = star.querySelector(".interactive-star-icon"); + + if (icon) { + if (starId <= clickedStarId) { + if (star.classList.contains("yellow_star")) { + icon.classList.add("yellow-star-selected"); + } else if (star.classList.contains("primary_star")) { + icon.classList.add("primary-star-selected"); + } else if (star.classList.contains("suble_star_light")) { + icon.classList.add("suble-star-selected"); + } else { + icon.classList.add("yellow-star-selected"); + } + } else { + icon.classList.remove("yellow-star-selected", "primary-star-selected", "suble-star-selected"); + } + } + }); + } + + updateHiddenInputValue(value) { + const hiddenInput = document.getElementById(STAR_RATING_INPUT_ID); + if (hiddenInput) { + hiddenInput.value = value; + } + } +} diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb b/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb index 21703f56ba..25814366d3 100644 --- a/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb +++ b/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb @@ -28,13 +28,33 @@ <% end %> <% end %> <%= pb_rails("flex", props: { }) do %> - <% object.star_count.times do %> - <%= pb_rails("icon", props: { classname: "#{star_color} pb_star_#{size}" , custom_icon: Playbook::Engine.root.join(star_svg_path) } ) %> - <% end %> - <% object.empty_stars.times do %> - <%= pb_rails("icon", props: { classname: "#{background_star_color} pb_star_#{size}", custom_icon: Playbook::Engine.root.join(background_star_path) } ) %> + + <% if object.variant == "display" %> + + <% object.star_count.times do %> + <%= pb_rails("icon", props: { classname: "#{star_color} pb_star_#{size}" , custom_icon: Playbook::Engine.root.join(star_svg_path) } ) %> + <% end %> + <% object.empty_stars.times do %> + <%= pb_rails("icon", props: { classname: "#{background_star_color} pb_star_#{size}", custom_icon: Playbook::Engine.root.join(background_star_path) } ) %> + <% end %> + + <% else %> + <%= pb_rails("flex", props: { orientation: "column" }) do %> + <% if object.label.present? %> + <%= pb_rails("caption", props: {text: object.label, margin_bottom:"xs"}) %> + <% end %> + + <%= pb_rails("flex", props: { orientation: "row" }) do %> + <% object.denominator.times do |index| %> +
+ <%= pb_rails("icon", props: { classname: "#{background_star_color} pb_star_#{size} interactive-star-icon", custom_icon: Playbook::Engine.root.join(background_star_path)} ) %> +
+ <% end %> + <% end %> + <% end %> <% end %> <% end %> + <% if layout_option == "onestar" %> <%= content_tag(:div, class: "pb_star_rating_number_#{size}") do %> <% case object.size %> diff --git a/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.rb b/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.rb index a6320b5400..d1c8536a5f 100644 --- a/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.rb +++ b/playbook/app/pb_kits/playbook/pb_star_rating/star_rating.rb @@ -25,6 +25,12 @@ class StarRating < Playbook::KitBase values: %w[fill outline], default: "fill" + prop :variant, type: Playbook::Props::Enum, + values: %w[display interactive], + default: "display" + prop :label, type: Playbook::Props::String + prop :name, type: Playbook::Props::String + def one_decimal_rating rating.to_f.round(1) end diff --git a/playbook/app/pb_kits/playbook/playbook-rails.js b/playbook/app/pb_kits/playbook/playbook-rails.js index a8edc7b2b6..6a58f1c263 100644 --- a/playbook/app/pb_kits/playbook/playbook-rails.js +++ b/playbook/app/pb_kits/playbook/playbook-rails.js @@ -39,6 +39,9 @@ PbDropdown.start() import PbAdvancedTable from './pb_advanced_table' PbAdvancedTable.start() +import PbStarRating from './pb_star_rating' +PbStarRating.start() + import 'flatpickr' // React-Rendered Rails Kits ===== diff --git a/playbook/lib/playbook/forms/builder.rb b/playbook/lib/playbook/forms/builder.rb index 0fe0f9b3df..171d4ca34c 100644 --- a/playbook/lib/playbook/forms/builder.rb +++ b/playbook/lib/playbook/forms/builder.rb @@ -14,6 +14,7 @@ class Builder < ::ActionView::Helpers::FormBuilder require_relative "builder/multi_level_select_field" require_relative "builder/phone_number_field" require_relative "builder/dropdown_field" + require_relative "builder/star_rating_field" prepend(FormFieldBuilder.new(:email_field, kit_name: "text_input")) prepend(FormFieldBuilder.new(:number_field, kit_name: "text_input")) diff --git a/playbook/lib/playbook/forms/builder/star_rating_field.rb b/playbook/lib/playbook/forms/builder/star_rating_field.rb new file mode 100644 index 0000000000..3c3c266cbe --- /dev/null +++ b/playbook/lib/playbook/forms/builder/star_rating_field.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Playbook + module Forms + class Builder + def star_rating_field(name, props: {}) + props[:name] = name + props[:margin_bottom] = "sm" + props[:label] = @template.label(@object_name, name) if props[:label] == true + @template.pb_rails("star_rating", props: props) + end + end + end +end diff --git a/playbook/spec/pb_kits/playbook/kits/star_rating_spec.rb b/playbook/spec/pb_kits/playbook/kits/star_rating_spec.rb index e8f292c1ab..8cc8d43625 100644 --- a/playbook/spec/pb_kits/playbook/kits/star_rating_spec.rb +++ b/playbook/spec/pb_kits/playbook/kits/star_rating_spec.rb @@ -39,6 +39,12 @@ .with_values("xs", "sm", "md", "lg") } + it { + is_expected.to define_enum_prop(:variant) + .with_default("display") + .with_values("display", "interactive") + } + describe "#classname" do it "returns namespaced class name", :aggregate_failures do expect(subject.new({}).classname).to eq "pb_star_rating_kit"