From baa32243355b8699e65ac732e55a05dca4af67f3 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Tue, 25 Aug 2026 23:32:52 +0200 Subject: [PATCH] refactor(feedback): replace jSimple Star Rating with progressive CSS stars Remove the abandoned jSimple Star Rating jQuery plugin and replace it with a small vanilla-JS initializer that progressively enhances radio buttons into CSS-generated Unicode stars. Why not Font Awesome: the project loads Font Awesome 5 with SVG+JS, which replaces elements at runtime. Toggling far/fas classes on those tags after replacement has no effect, so the widget is now rendered via CSS ::before content instead. Changes: - Add app/assets/javascripts/feedback-rating.js - Render radio buttons in feedback/show.html.haml instead of hidden input - Update _star-rating.scss to use CSS ::before star glyphs - Delete jsimple-star-rating.min.js and star-rating.gif - Update member_feedback feature spec to select a star and assert the persisted rating - Remove inline :javascript block Verification: - rubocop clean - haml-lint clean - rails runner boots - controller/model specs: 11 examples, 0 failures - feature spec: 9 examples, 0 failures - assets:precompile succeeds - browser snapshot: clicking star 4 fills stars 1-4 --- app/assets/images/star-rating.gif | Bin 3229 -> 0 bytes app/assets/javascripts/application.js | 2 +- app/assets/javascripts/feedback-rating.js | 62 ++++++ .../javascripts/jsimple-star-rating.min.js | 10 - .../stylesheets/partials/_star-rating.scss | 49 ++--- app/views/feedback/show.html.haml | 16 +- ...2246-refactor-feedback-star-rating-plan.md | 198 ++++++++++++++++++ spec/features/member_feedback_spec.rb | 6 +- 8 files changed, 295 insertions(+), 48 deletions(-) delete mode 100644 app/assets/images/star-rating.gif create mode 100644 app/assets/javascripts/feedback-rating.js delete mode 100644 app/assets/javascripts/jsimple-star-rating.min.js create mode 100644 docs/plans/2026-08-25-2246-refactor-feedback-star-rating-plan.md diff --git a/app/assets/images/star-rating.gif b/app/assets/images/star-rating.gif deleted file mode 100644 index 22edb43a48f932e7bc6959ffbf96f780517cab85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3229 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 F@o(8xJ9hv8 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index b12ee650b..5cc0d43c8 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -16,7 +16,7 @@ //= require rails-ujs //= require activestorage //= require chosen.jquery -//= require 'jsimple-star-rating.min.js' +//= require feedback-rating //= require pickadate/picker //= require pickadate/picker.date //= require pickadate/picker.time diff --git a/app/assets/javascripts/feedback-rating.js b/app/assets/javascripts/feedback-rating.js new file mode 100644 index 000000000..232381c46 --- /dev/null +++ b/app/assets/javascripts/feedback-rating.js @@ -0,0 +1,62 @@ +// Progressively enhance a .rating container of radio buttons into clickable stars. + +(function () { + function initRating(container) { + const radios = container.querySelectorAll('input[type="radio"]'); + const labels = container.querySelectorAll('label'); + + if (radios.length === 0 || labels.length === 0) return; + + function updateVisuals(checkedValue) { + labels.forEach(function (label, index) { + label.classList.toggle('selected', index < checkedValue); + }); + } + + function previewVisuals(previewValue) { + labels.forEach(function (label, index) { + label.classList.toggle('preview', index < previewValue); + }); + } + + function restoreVisuals() { + const checked = Array.from(radios).find(function (radio) { return radio.checked; }); + updateVisuals(checked ? parseInt(checked.value, 10) : 0); + previewVisuals(0); + } + + labels.forEach(function (label, index) { + const radio = radios[index]; + + label.addEventListener('click', function (event) { + if (radio.checked) { + event.preventDefault(); + radio.checked = false; + updateVisuals(0); + } + }); + + label.addEventListener('mouseenter', function () { + previewVisuals(index + 1); + }); + + radio.addEventListener('change', function () { + updateVisuals(index + 1); + }); + }); + + container.addEventListener('mouseleave', restoreVisuals); + + restoreVisuals(); + } + + function init() { + document.querySelectorAll('.rating').forEach(initRating); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/app/assets/javascripts/jsimple-star-rating.min.js b/app/assets/javascripts/jsimple-star-rating.min.js deleted file mode 100644 index 9328209ec..000000000 --- a/app/assets/javascripts/jsimple-star-rating.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * ---------------------------------------- * - * jSimple Star Rating.min * - * JavaScript * - * v2.0.0 * - * Matt O'Neill | www.matt-oneill.co.uk * - * ---------------------------------------- * - */ - -(function (e) { e.fn.starRating = function (t) { return this.each(function () { var n = e(this); n.append("
    "); var r = e(this).children("ul"); for (var i = 0; i < n.data("rating-max") ; i++) { r.append("
  • ") } $ratingFieldItem = r.children(); var o = 0; $ratingFieldItem.on({ click: function () { if (e(this).index() + 1 != o) { o = e(this).index() + 1; n.attr("data-val", o); e("li:lt(" + (e(this).index() + 1) + ")", r).addClass("active"); e("li:gt(" + e(this).index() + ")", r).removeClass("active") } else { e(this).parent().children("li").removeClass("active"); n.attr("data-val", null); o = 0 } }, mouseenter: function () { e("li:lt(" + (e(this).index() + 1) + ")", r).addClass("hover"); e("li:gt(" + e(this).index() + ")", r).removeClass("hover") }, mouseleave: function () { e(this).parent().children("li:gt(" + e(this).index() + ")").removeClass("hover") } }); r.on({ mouseleave: function () { $ratingFieldItem.removeClass("hover") } }); if (t.minus) { n.prepend(""); e("span.less", n).on("click", function () { e("li.active:last", r).removeClass("active") }) } }) } })(jQuery) \ No newline at end of file diff --git a/app/assets/stylesheets/partials/_star-rating.scss b/app/assets/stylesheets/partials/_star-rating.scss index 92c95c5af..75a8fed08 100644 --- a/app/assets/stylesheets/partials/_star-rating.scss +++ b/app/assets/stylesheets/partials/_star-rating.scss @@ -1,40 +1,37 @@ .rating { - float: left; + display: flex; + align-items: center; + gap: 0.25rem; - ul { - list-style: none; - float: left; + .form-check-inline { padding: 0; margin: 0; - li { - float: left; - margin-left: 2px; - background: image-url("star-rating.gif") no-repeat; - width: 25px; - height: 25px; + .form-check-label { cursor: pointer; - } + font-size: 1.5rem; + line-height: 1; + color: #ffc107; + user-select: none; - li:first-child { - margin-left: 0; - } + &::before { + content: '☆'; + } - li.hover { - background-position: -25px; + &.selected::before, + &.preview::before { + content: '★'; + } } - li.active { - background-position: -50px; + input:checked + .form-check-label::before { + content: '★'; } } - span.less { - cursor: pointer; - background: image-url("star-rating.gif") -75px no-repeat; - display: block; - float: left; - height: 25px; - width: 25px; + input:focus-visible + .form-check-label { + outline: 2px solid currentColor; + outline-offset: 2px; + border-radius: 2px; } -} \ No newline at end of file +} diff --git a/app/views/feedback/show.html.haml b/app/views/feedback/show.html.haml index e46e35322..9ab0c9afc 100644 --- a/app/views/feedback/show.html.haml +++ b/app/views/feedback/show.html.haml @@ -17,12 +17,15 @@ = simple_form_for @feedback, url: submit_feedback_path(params[:id]), html: { method: 'patch' } do |f| .row = f.hidden_field :token, value: params[:id] - = f.hidden_field :rating .mb-3 %label.required - %abbr(title='required') * + %abbr{ title: 'required' } * = t('feedback_form.rating') - .rating{:data => {:rating_max => 5 }} + .rating{ data: { rating_max: 5 } } + - (1..5).each do |value| + .form-check.form-check-inline + = f.radio_button :rating, value, id: "feedback_rating_#{value}", class: 'form-check-input visually-hidden' + %label.form-check-label{ for: "feedback_rating_#{value}" } .col-lg-6 = f.association :coach, collection: @coaches, label_method: :full_name, value_method: :id, label: t('feedback.coach') .col-lg-6 @@ -31,10 +34,3 @@ = f.input :suggestions, label: t('feedback_form.suggestions'), input_html: { rows: 3 } .text-right = f.button :button, 'Submit feedback', class: 'btn btn-primary' - -:javascript - $('.rating').starRating({ minus: false }); - - $('button[type="submit"]').on('click', function () { - $('#feedback_rating').val($('.rating').data('val')) - }); diff --git a/docs/plans/2026-08-25-2246-refactor-feedback-star-rating-plan.md b/docs/plans/2026-08-25-2246-refactor-feedback-star-rating-plan.md new file mode 100644 index 000000000..43a841a79 --- /dev/null +++ b/docs/plans/2026-08-25-2246-refactor-feedback-star-rating-plan.md @@ -0,0 +1,198 @@ +--- +title: Feedback star rating replacement - Plan +type: refactor +date: 2026-08-25 +topic: feedback-star-rating +artifact_contract: ce-unified-plan/v1 +artifact_readiness: implementation-ready +product_contract_source: ce-brainstorm +execution: code +--- + +# Feedback star rating replacement - Plan + +## Goal Capsule + +- **Objective:** Remove the abandoned jSimple Star Rating jQuery plugin and replace the member feedback form's rating control with a maintainable, progressively enhanced, dependency-free star-rating widget that preserves the existing click-to-rate and submit behaviour. +- **Means:** A vanilla-JavaScript initializer progressively enhances server-rendered radio buttons into CSS-generated stars. (KTD1, KTD2) +- **Product authority:** The maintainers of codebar planner. +- **Open blockers:** None. + +## Product Contract + +### Summary + +Replace the hand-vendored jSimple Star Rating plugin used on the member feedback form with a progressively enhanced, vanilla-JavaScript star-rating control. +The no-JS fallback will be a native form control (radio buttons), which JavaScript then enhances into clickable CSS-generated stars. +This removes the unmaintained dependency and the inline `:javascript` HAML block while keeping the current star-rating interaction unchanged. + +### Problem Frame + +The feedback form currently depends on `app/assets/javascripts/jsimple-star-rating.min.js`, an abandoned third-party jQuery plugin (v2.0.0, no active maintenance). +It is the only consumer of that plugin, yet it is loaded globally through the Sprockets manifest. +The view also embeds initialization and submit-handling logic inline via the `:javascript` HAML filter, which mixes behaviour with markup and is hard to test. + +### Key Decisions + +- K1. Use a vanilla JavaScript initializer file in `app/assets/javascripts/` rather than a Stimulus controller or inline script. Governs R1, R2. The project has not yet wired Stimulus/Turbo and the widget appears on a single page, so a small initializer keeps the change focused and avoids introducing a new framework dependency. +- K2. Preserve the existing visual interaction (up to five stars, click to set, click the same star to clear, hover preview) rather than redesigning the rating UI. Governs R3, R4. This is a maintenance refactor, not a visual redesign. +- K3. Drive the widget from the existing `data-rating-max` attribute. Governs R2, R3, R5. The number of stars remains data-driven. +- K4. Use progressive enhancement: server-render a native form control (radio buttons or a ``; Bootstrap 5 is already loaded. +- No new icon library, component library, or build tooling is introduced. +- `app/assets/stylesheets/partials/_star-rating.scss` is the only stylesheet that styles `.rating`; no other view uses that class. + +### Acceptance Examples + +- AE1. Given a feedback page with no rating selected, when the member clicks the third star, then the rating radio with value `3` is checked and the first three stars display as selected. +- AE2. Given a feedback page with the third star selected, when the member clicks the third star again, then no rating radio is checked and no stars display as selected. +- AE3. Given a feedback page with the third star selected, when the member hovers over the fifth star and then moves the mouse away, then the display returns to three selected stars and the rating radio with value `3` remains checked. + +--- + +## Planning Contract + +### Key Technical Decisions + +- KTD1. Use radio buttons as the no-JS fallback and enhance them into stars with vanilla JavaScript. (session-settled: user-approved — chosen over hidden-input-only approach: progressive enhancement improves accessibility and resilience, and the existing controller already accepts `feedback[rating]`). Governs R2, R5, R6. +- KTD2. Use CSS `::before` content with Unicode star glyphs for the visual layer. (session-settled: user-approved — chosen over keeping the `star-rating.gif` sprite: removes an image asset and avoids coupling to Font Awesome's SVG-with-JS runtime replacement). Governs R2, R3, U3. +- KTD3. Update the existing feature spec to click the rendered star label or radio input rather than the legacy `
  • ` elements. Governs R7, U4. + +### Assumptions + +- The JS-enabled feature spec runs with a browser driver that executes the initializer before interactions. +- The existing `_star-rating.scss` can be safely reworked because `star-rating.gif` is only used by this component. +- No other form or page reuses the `.rating` class or the `jsimple-star-rating.min.js` plugin. + +### Sequencing + +1. U1 and U2 can be drafted in either order, but both must be present before U3 styling is finalised. +2. U3 depends on U1 and U2. +3. U4 depends on U1, U2, and U3. + +--- + +## Implementation Units + +### U1. Add vanilla-JavaScript star-rating initializer + +- **Goal:** Replace the jSimple plugin behaviour with a small native-JS initializer that progressively enhances a `.rating` container. +- **Requirements:** R1, R2, R3, R4 +- **Dependencies:** None +- **Files:** + - `app/assets/javascripts/feedback-rating.js` (create) + - `app/assets/javascripts/application.js` (modify) +- **Approach:** + 1. Create `app/assets/javascripts/feedback-rating.js`. + 2. On `DOMContentLoaded`, find `.rating` elements. + 3. Read `data-rating-max` to know how many stars to render. + 4. For each star, inject a clickable `
  • ` sprites. + 2. Style selected stars with `fas fa-star` and unselected stars with `far fa-star`. + 3. Keep hover preview distinct from selected state using CSS classes driven by the initializer. + 4. Visually hide the native radio inputs while keeping them keyboard-focusable. + 5. Delete `app/assets/images/star-rating.gif`. +- **Patterns to follow:** Keep the existing partial structure; import path in `application.scss` already references `partials/star-rating`. +- **Test scenarios:** + - Visual state: selected rating shows the correct number of filled stars. + - Hover state: mouseover highlights stars up to the hovered position without changing the submitted value. + - No asset 404: precompile succeeds and the deleted sprite is no longer referenced. +- **Verification:** `RAILS_ENV=test bundle exec rails assets:precompile` succeeds and `bundle exec rspec spec/features/member_feedback_spec.rb` passes. + +### U4. Update feature test and remove old plugin file + +- **Goal:** Remove the abandoned plugin asset and update the test suite to exercise the new widget. +- **Requirements:** R1, R7 +- **Dependencies:** U1, U2, U3 +- **Files:** + - `app/assets/javascripts/jsimple-star-rating.min.js` (delete) + - `spec/features/member_feedback_spec.rb` (modify) +- **Approach:** + 1. Delete `app/assets/javascripts/jsimple-star-rating.min.js`. + 2. In `spec/features/member_feedback_spec.rb`, replace `within('.rating') { all('li').at(3).click }` with a selector that targets the new star label or radio input (e.g., `within('.rating') { choose('feedback_rating_4') }` or clicking the label for the fourth star). + 3. Keep the existing Chosen dropdown waits and success/error assertions unchanged. +- **Patterns to follow:** Use Capybara's label-based or radio-based helpers rather than brittle CSS selectors. +- **Test scenarios:** + - Covers AE1: selecting a rating and submitting shows the success message. + - Covers the validation path in the existing spec: omitting the rating still shows "Rating can't be blank". +- **Verification:** `bundle exec rspec spec/features/member_feedback_spec.rb` passes. + +--- + +## Verification Contract + +| Gate | Command | Expected outcome | +|---|---|---| +| Unit/controller tests | `bundle exec rspec spec/controllers/feedback_controller_spec.rb spec/models/feedback_spec.rb` | All pass | +| Feature tests | `bundle exec rspec spec/features/member_feedback_spec.rb` | Passes, including the JS-enabled success scenario | +| Style/lint | `bundle exec rubocop app/assets/javascripts/feedback-rating.js app/views/feedback/show.html.haml app/assets/stylesheets/partials/_star-rating.scss spec/features/member_feedback_spec.rb` | No new offenses | +| Asset precompile | `RAILS_ENV=test bundle exec rails assets:precompile` | Succeeds without `star-rating.gif` or the jSimple plugin | + +## Definition of Done + +- jSimple Star Rating file and its Sprockets require are removed. +- The feedback view contains no inline `:javascript` block. +- The rating widget works without JavaScript (radio fallback) and is enhanced to CSS-generated stars when JavaScript runs. +- All tests in the Verification Contract pass. +- No new runtime dependencies are introduced. +- No `.rating` or `star-rating.gif` references remain outside the updated component. diff --git a/spec/features/member_feedback_spec.rb b/spec/features/member_feedback_spec.rb index 6034e563c..a381b820b 100644 --- a/spec/features/member_feedback_spec.rb +++ b/spec/features/member_feedback_spec.rb @@ -109,7 +109,7 @@ expect(page).to have_css('#feedback_coach_id_chosen') expect(page).to have_css('#feedback_tutorial_id_chosen') - within('.rating') { all('li').at(3).click } + within('.rating') { find("label[for='feedback_rating_4']").click } select_from_chosen(coach.full_name, from: 'feedback_coach_id') select_from_chosen(tutorial.title, from: 'feedback_tutorial_id') click_button('Submit feedback') @@ -117,6 +117,10 @@ expect(page).to have_current_path(root_path) expect(page).to have_text(feedback_submited_message) + + feedback = Feedback.find_by(workshop: feedback_request.workshop, coach: coach, tutorial: tutorial) + expect(feedback).to be_present + expect(feedback.rating).to eq(4) end scenario 'renders an error message when not all mandatory fields have been completed' do