From 7d95da6ae6156b3fc7f394ec112c3dea9e31ce85 Mon Sep 17 00:00:00 2001 From: Justin Littman Date: Mon, 25 Nov 2024 13:07:40 -0500 Subject: [PATCH] Adds update and display of file hidden attribute. closes #173 --- .../forms/checkbox_component.html.erb | 6 +++ .../elements/forms/checkbox_component.rb | 9 ++++ .../elements/forms/field_component.rb | 5 +- .../forms/file_field_component.html.erb | 2 +- .../forms/text_field_component.html.erb | 2 +- .../forms/textarea_field_component.html.erb | 2 +- app/controllers/content_files_controller.rb | 2 +- app/helpers/application_helper.rb | 3 ++ .../controllers/form_submit_controller.js | 8 +++ app/views/content_files/_show.html.erb | 44 +++++++++------- app/views/content_files/edit.html.erb | 2 +- app/views/contents/_show.html.erb | 4 +- app/views/contents/show_table.html.erb | 4 +- config/application.rb | 2 +- .../elements/forms/checkbox_component_spec.rb | 52 +++++++++++++++++++ .../forms/text_field_component_spec.rb | 7 +++ .../forms/textarea_field_component_spec.rb | 7 +++ spec/system/manage_files_spec.rb | 7 +++ spec/system/show_work_spec.rb | 4 +- 19 files changed, 139 insertions(+), 33 deletions(-) create mode 100644 app/components/elements/forms/checkbox_component.html.erb create mode 100644 app/components/elements/forms/checkbox_component.rb create mode 100644 app/javascript/controllers/form_submit_controller.js create mode 100644 spec/components/elements/forms/checkbox_component_spec.rb diff --git a/app/components/elements/forms/checkbox_component.html.erb b/app/components/elements/forms/checkbox_component.html.erb new file mode 100644 index 0000000..61e83b7 --- /dev/null +++ b/app/components/elements/forms/checkbox_component.html.erb @@ -0,0 +1,6 @@ +
+ <%= render Elements::Forms::LabelComponent.new(form:, field_name:, label_text: label, hidden_label:, default_label_class: 'form-check-label') %> + <%= render Elements::Forms::HelpTextComponent.new(id: help_text_id, help_text:) %> + <%= form.checkbox field_name, class: 'form-check-input', required:, aria: field_aria, data: %> + <%= render Elements::Forms::InvalidFeedbackComponent.new(form:, field_name:) %> +
diff --git a/app/components/elements/forms/checkbox_component.rb b/app/components/elements/forms/checkbox_component.rb new file mode 100644 index 0000000..3dcd8f5 --- /dev/null +++ b/app/components/elements/forms/checkbox_component.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Elements + module Forms + # Component for form checkbox field + class CheckboxComponent < Forms::FieldComponent + end + end +end diff --git a/app/components/elements/forms/field_component.rb b/app/components/elements/forms/field_component.rb index efdc2f6..22b6e22 100644 --- a/app/components/elements/forms/field_component.rb +++ b/app/components/elements/forms/field_component.rb @@ -4,17 +4,18 @@ module Elements module Forms # Base component for all form fields. class FieldComponent < ApplicationComponent - def initialize(form:, field_name:, required: false, hidden_label: false, label: nil, help_text: nil) # rubocop:disable Metrics/ParameterLists + def initialize(form:, field_name:, required: false, hidden_label: false, label: nil, help_text: nil, data: {}) # rubocop:disable Metrics/ParameterLists @form = form @field_name = field_name @required = required @hidden_label = hidden_label @label = label @help_text = help_text + @data = data super() end - attr_reader :form, :field_name, :required, :help_text, :hidden_label, :label + attr_reader :form, :field_name, :required, :help_text, :hidden_label, :label, :data def help_text_id @help_text_id ||= form.field_id(field_name, 'help') diff --git a/app/components/elements/forms/file_field_component.html.erb b/app/components/elements/forms/file_field_component.html.erb index d453b74..b8204ca 100644 --- a/app/components/elements/forms/file_field_component.html.erb +++ b/app/components/elements/forms/file_field_component.html.erb @@ -1,6 +1,6 @@
<%= render Elements::Forms::LabelComponent.new(form:, field_name:, label_text: label, hidden_label:) %> <%= render Elements::Forms::HelpTextComponent.new(id: help_text_id, help_text:) %> - <%= form.file_field field_name, class: 'form-control', required:, aria: field_aria, multiple: true %> + <%= form.file_field field_name, class: 'form-control', required:, aria: field_aria, multiple: true, data: %> <%= render Elements::Forms::InvalidFeedbackComponent.new(form:, field_name:) %>
diff --git a/app/components/elements/forms/text_field_component.html.erb b/app/components/elements/forms/text_field_component.html.erb index 4fc0971..6e356a3 100644 --- a/app/components/elements/forms/text_field_component.html.erb +++ b/app/components/elements/forms/text_field_component.html.erb @@ -1,6 +1,6 @@
<%= render Elements::Forms::LabelComponent.new(form:, field_name:, label_text: label, hidden_label:) %> <%= render Elements::Forms::HelpTextComponent.new(id: help_text_id, help_text:) %> - <%= form.text_field field_name, class: 'form-control', required:, aria: field_aria %> + <%= form.text_field field_name, class: 'form-control', required:, aria: field_aria, data: %> <%= render Elements::Forms::InvalidFeedbackComponent.new(form:, field_name:) %>
diff --git a/app/components/elements/forms/textarea_field_component.html.erb b/app/components/elements/forms/textarea_field_component.html.erb index f0b1c1c..116c469 100644 --- a/app/components/elements/forms/textarea_field_component.html.erb +++ b/app/components/elements/forms/textarea_field_component.html.erb @@ -1,6 +1,6 @@
<%= render Elements::Forms::LabelComponent.new(form:, field_name:, label_text: label, hidden_label:) %> <%= render Elements::Forms::HelpTextComponent.new(id: help_text_id, help_text:) %> - <%= form.textarea field_name, class: 'form-control', required:, aria: field_aria %> + <%= form.textarea field_name, class: 'form-control', required:, aria: field_aria, data: %> <%= render Elements::Forms::InvalidFeedbackComponent.new(form:, field_name:) %>
diff --git a/app/controllers/content_files_controller.rb b/app/controllers/content_files_controller.rb index 68807cc..f548acf 100644 --- a/app/controllers/content_files_controller.rb +++ b/app/controllers/content_files_controller.rb @@ -36,6 +36,6 @@ def set_content_file end def content_file_params - params.expect(content_file: [:label]) + params.expect(content_file: %i[label hide]) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 15b06f0..fd1a2be 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,4 +1,7 @@ # frozen_string_literal: true module ApplicationHelper + def human_boolean(boolean) + boolean ? 'Yes' : 'No' + end end diff --git a/app/javascript/controllers/form_submit_controller.js b/app/javascript/controllers/form_submit_controller.js new file mode 100644 index 0000000..69fb3f6 --- /dev/null +++ b/app/javascript/controllers/form_submit_controller.js @@ -0,0 +1,8 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + submit (event) { + event.preventDefault() + event.target.form.requestSubmit() + } +} \ No newline at end of file diff --git a/app/views/content_files/_show.html.erb b/app/views/content_files/_show.html.erb index 8c75bf4..14d78a5 100644 --- a/app/views/content_files/_show.html.erb +++ b/app/views/content_files/_show.html.erb @@ -1,22 +1,26 @@ - - <%= content_file.filename %> - - <%# turbo-frame and tables don't mix well: https://github.com/hotwired/turbo/issues/48 %> - <%= turbo_frame_tag content_file do %> -
-
<%= content_file.label %>
-
- <%= render Elements::ButtonLinkComponent.new(link: edit_content_file_path(content_file), variant: :'outline-secondary', size: :sm) do %> - <%= edit_icon %>Edit - <% end %> -
-
+ + <%= content_file.filename %> + + <%# turbo-frame and tables don't mix well: https://github.com/hotwired/turbo/issues/48 %> + <%= turbo_frame_tag content_file, 'description' do %> +
+ <%= content_file.label %> + <%= render Elements::ButtonLinkComponent.new(link: edit_content_file_path(content_file), variant: :'outline-secondary', size: :sm) do %> + <%= edit_icon %>Edit + <% end %> +
+ <% end %> + + + <%= turbo_frame_tag content_file, 'hide' do %> + <%= form_with model: content_file do |form| %> + <%= render Elements::Forms::CheckboxComponent.new(form:, field_name: :hide, hidden_label: true, data: { controller: 'form-submit', action: 'form-submit#submit' }) %> <% end %> - - <%= number_to_human_size(content_file.size) %> - - <%= render Elements::ButtonLinkComponent.new(link: content_file_path(content_file), variant: :'outline-secondary', size: :sm, data: { turbo_method: :delete, turbo_frame: dom_id(content_file.content, 'show') }) do %> - <%= delete_icon %>Remove - <% end %> - + <% end %> + + + <%= render Elements::ButtonLinkComponent.new(link: content_file_path(content_file), variant: :'outline-secondary', size: :sm, data: { turbo_method: :delete, turbo_frame: dom_id(content_file.content, 'show') }) do %> + <%= delete_icon %>Remove + <% end %> + diff --git a/app/views/content_files/edit.html.erb b/app/views/content_files/edit.html.erb index bb40835..8366da7 100644 --- a/app/views/content_files/edit.html.erb +++ b/app/views/content_files/edit.html.erb @@ -1,4 +1,4 @@ -<%= turbo_frame_tag @content_file do %> +<%= turbo_frame_tag @content_file, 'description' do %> <%= form_with model: @content_file do |form| %> <%= render Elements::Forms::TextFieldComponent.new(field_name: :label, form:, required: true, label: 'Description') %> <%= render Elements::Forms::SubmitComponent.new(form:, label: 'Update', classes: 'mt-2') %> diff --git a/app/views/contents/_show.html.erb b/app/views/contents/_show.html.erb index 0e423e4..80bbd8f 100644 --- a/app/views/contents/_show.html.erb +++ b/app/views/contents/_show.html.erb @@ -7,8 +7,8 @@ Filename Description - Size - + Hide + Actions diff --git a/app/views/contents/show_table.html.erb b/app/views/contents/show_table.html.erb index f63e8e5..3dcb96c 100644 --- a/app/views/contents/show_table.html.erb +++ b/app/views/contents/show_table.html.erb @@ -1,8 +1,8 @@ <%= turbo_frame_tag @content do %> <%= render Elements::Tables::TableComponent.new(id: 'files-table', classes: 'table-work', label: 'Files') do |component| %> - <% component.with_header(headers: %w[Filename Description], each_classes: ['col-6']) %> + <% component.with_header(headers: %w[Filename Description Hide], each_classes: ['col-6']) %> <% @content_files.each do |content_file| %> - <% component.with_row(values: [content_file.filename, content_file.label]) %> + <% component.with_row(values: [content_file.filename, content_file.label, human_boolean(content_file.hide)]) %> <% end %> <% end %>
diff --git a/config/application.rb b/config/application.rb index 5ed0747..b377198 100644 --- a/config/application.rb +++ b/config/application.rb @@ -53,7 +53,7 @@ class Application < Rails::Application # Bootstrap form error handling ActionView::Base.field_error_proc = proc do |html_tag, _instance| - html_tag.gsub('form-control', 'form-control is-invalid').html_safe # rubocop:disable Rails/OutputSafety + html_tag.gsub(/(form-control|form-check-input)/, '\1 is-invalid').html_safe # rubocop:disable Rails/OutputSafety end end end diff --git a/spec/components/elements/forms/checkbox_component_spec.rb b/spec/components/elements/forms/checkbox_component_spec.rb new file mode 100644 index 0000000..e2a615c --- /dev/null +++ b/spec/components/elements/forms/checkbox_component_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Elements::Forms::CheckboxComponent, type: :component do + let(:form) { ActionView::Helpers::FormBuilder.new(nil, work_form, vc_test_controller.view_context, {}) } + let(:work_form) { WorkForm.new } + let(:field_name) { 'title' } + + it 'creates field with label' do + render_inline(described_class.new(form: form, field_name:)) + expect(page).to have_css('label.form-check-label:not(.visually-hidden)', text: 'title') + expect(page).to have_css('input.form-check-input[type="checkbox"]:not(.is-invalid)') + expect(page).to have_no_css('small.form-text') + expect(page).to have_no_css('div.invalid-feedback.is-invalid') + end + + context 'when label is hidden' do + it 'creates field with hidden label' do + render_inline(described_class.new(form:, field_name:, hidden_label: true)) + expect(page).to have_css('label.form-check-label.visually-hidden', text: 'title') + end + end + + context 'when help text is provided' do + it 'creates field with help text' do + render_inline(described_class.new(form:, field_name:, help_text: 'Helpful text')) + expect(page).to have_css('input[aria-describedby="title_help"]') + expect(page).to have_css('small.form-text[id="title_help"]', text: 'Helpful text') + end + end + + context 'when field has an error' do + before do + work_form.errors.add(field_name, 'is required') + end + + it 'creates field with invalid feedback' do + render_inline(described_class.new(form:, field_name:)) + + expect(page).to have_css('input.form-check-input.is-invalid') # rubocop:disable Capybara/SpecificMatcher + expect(page).to have_css('div.invalid-feedback.is-invalid', text: 'is required') + end + end + + context 'when data is provided' do + it 'creates field with data' do + render_inline(described_class.new(form:, field_name:, data: { test: 'test_data' })) + expect(page).to have_css('input[data-test="test_data"]') + end + end +end diff --git a/spec/components/elements/forms/text_field_component_spec.rb b/spec/components/elements/forms/text_field_component_spec.rb index b83186a..e96d5cf 100644 --- a/spec/components/elements/forms/text_field_component_spec.rb +++ b/spec/components/elements/forms/text_field_component_spec.rb @@ -42,4 +42,11 @@ expect(page).to have_css('div.invalid-feedback.is-invalid', text: 'is required') end end + + context 'when data is provided' do + it 'creates field with data' do + render_inline(described_class.new(form:, field_name:, data: { test: 'test_data' })) + expect(page).to have_css('input[data-test="test_data"]') + end + end end diff --git a/spec/components/elements/forms/textarea_field_component_spec.rb b/spec/components/elements/forms/textarea_field_component_spec.rb index 2b9a1c9..9e31329 100644 --- a/spec/components/elements/forms/textarea_field_component_spec.rb +++ b/spec/components/elements/forms/textarea_field_component_spec.rb @@ -42,4 +42,11 @@ expect(page).to have_css('div.invalid-feedback.is-invalid', text: 'is required') end end + + context 'when data is provided' do + it 'creates field with data' do + render_inline(described_class.new(form:, field_name:, data: { test: 'test_data' })) + expect(page).to have_css('textarea[data-test="test_data"]') + end + end end diff --git a/spec/system/manage_files_spec.rb b/spec/system/manage_files_spec.rb index b505f29..f9b8ea8 100644 --- a/spec/system/manage_files_spec.rb +++ b/spec/system/manage_files_spec.rb @@ -67,5 +67,12 @@ # The description is updated. expect(page).to have_css('table#content-table td', text: 'hippo.svg') expect(page).to have_css('table#content-table td', text: 'This is a hippo.') + content_file = ContentFile.find_by(filename: 'hippo.svg') + expect(content_file.label).to eq('This is a hippo.') + + # Hide the file + all('input[type=checkbox][name="content_file[hide]"]').first.check + expect(page).to have_field('hide', checked: true) + expect(content_file.reload.hide).to be true end end diff --git a/spec/system/show_work_spec.rb b/spec/system/show_work_spec.rb index 2e4ab70..ed13284 100644 --- a/spec/system/show_work_spec.rb +++ b/spec/system/show_work_spec.rb @@ -36,7 +36,7 @@ ], access: { view: 'world', download: 'world', controlledDigitalLending: false }, - administrative: { publish: true, sdrPreserve: true, shelve: true } + administrative: { publish: false, sdrPreserve: true, shelve: false } } ] } @@ -133,6 +133,8 @@ expect(page).to have_css('td', text: 'My file1') expect(page).to have_css('td', text: 'my_file2.txt') expect(page).to have_no_css('td', text: 'my_file3.txt') + expect(page).to have_css('td', text: 'No') + expect(page).to have_css('td', text: 'Yes') end expect(page).to have_css('ul.pagination') click_link_or_button('Next')