Browse Source

Fixes #4255 - Tree select and multi tree select fields are not set to read-only mode

Bola Ahmed Buari 2 years ago
parent
commit
16d713323d

+ 9 - 1
app/assets/javascripts/app/lib/app_post/searchable_select.coffee

@@ -17,7 +17,8 @@ class App.SearchableSelect extends Spine.Controller
     'shown.bs.dropdown':                              'onDropdownShown'
     'hidden.bs.dropdown':                             'onDropdownHidden'
     'keyup .js-input':                                'onKeyUp'
-    'click .js-remove':                               'removeThisToken'
+    'click .js-remove:not(.is-disabled)':             'removeThisToken'
+    'show.bs.dropdown':                               'onDropdownShow'
 
   elements:
     '.js-dropdown':               'dropdown'
@@ -51,6 +52,7 @@ class App.SearchableSelect extends Spine.Controller
 
       # create tokens and attribute values
       values = []
+      disabled = @attribute.disabled
       if relation
         for dataId in @attribute.value
           if App[relation] && App[relation].exists dataId
@@ -61,6 +63,7 @@ class App.SearchableSelect extends Spine.Controller
               name: name
               value: value
               object: relation
+              disabled: disabled
             )
 
       else
@@ -69,6 +72,7 @@ class App.SearchableSelect extends Spine.Controller
           tokens += App.view('generic/token')(
             name: value
             value: value
+            disabled: disabled
           )
 
       @attribute.value = values
@@ -159,6 +163,10 @@ class App.SearchableSelect extends Spine.Controller
           html += @renderAllOptions("#{parentName} — #{option.name}", option.children, level+1)
     html
 
+  onDropdownShow: (event)  =>
+    if @attribute.disabled
+      event.preventDefault()
+
   onDropdownShown: =>
     @input.on('click', @stopPropagation)
     @highlightFirst()

+ 2 - 2
app/assets/javascripts/app/views/generic/searchable_select.jst.eco

@@ -1,6 +1,6 @@
-<div class="dropdown-toggle<%=" tokenfield form-control" if @attribute.multiple %>" data-toggle="dropdown">
+<div class="dropdown-toggle<%=" tokenfield form-control" if @attribute.multiple %> <% if @attribute.disabled: %> is-disabled<% end %>" data-toggle="dropdown">
   <% if @attribute.multiple: %>
-    <select multiple class="js-shadow hide" name="<%- @attribute.name %>" tabindex="-1">
+    <select multiple class="js-shadow hide" name="<%- @attribute.name %>" tabindex="-1" <% if @attribute.disabled: %> disabled<% end %>>
       <% if @attribute.value: %>
         <% for option in @attribute.value: %>
           <option value="<%= option.value %>" selected><%= option.name %></option>

+ 1 - 1
app/assets/javascripts/app/views/generic/token.jst.eco

@@ -1,4 +1,4 @@
 <div class="token" data-value="<%= @value %>"<% if !@object: %> title="<%= @name || '-' %>"<% end %>>
   <span class="token-label"><%= @name.split('::').join(' › ') || '-' %></span>
-  <span class="token-close js-remove">×</span>
+  <span class="token-close js-remove<% if @disabled: %> is-disabled<% end %>">×</span>
 </div>

+ 141 - 0
spec/system/ticket/zoom/readonly_spec.rb

@@ -18,6 +18,24 @@ RSpec.describe 'Ticket Access Zoom', type: :system, authenticated_as: :user do
     end
   end
 
+  let(:name)       { attribute.name }
+  let(:data_type)  { attribute.data_type }
+  let(:display)    { attribute.display }
+  let(:value)      { data_option['options'].values.first }
+
+  let(:data_option) do
+    {
+      'default'  => '',
+      'options'  => { 'value_1' => 'value_1', 'value_2' => 'value_2', 'value_3' => 'value_3' },
+      'relation' => '', 'nulloption' => true, 'multiple' => false,
+      'null'     => true, 'translate' => true, 'maxlength' => 255
+    }
+  end
+
+  let(:multi_data_option) do
+    data_option.merge({ 'multiple' => true })
+  end
+
   before do
     visit "ticket/zoom/#{ticket.id}"
   end
@@ -41,6 +59,62 @@ RSpec.describe 'Ticket Access Zoom', type: :system, authenticated_as: :user do
       expect(page).to have_selector('.links .icon-diagonal-cross')
       expect(page).to have_content('+ Add Link')
     end
+
+    context 'with select, treeselect, multiselect and multi-treeselect fields', db_strategy: :reset, authenticated_as: :authenticated do
+      def authenticated
+        attribute
+        ObjectManager::Attribute.migration_execute
+        user
+      end
+
+      shared_examples 'allow agents to select another field' do
+        it 'allows agents to select another field' do
+          within attribute_selector do
+            expect(page).to have_content(%r{#{display}}i)
+            find(".controls select[name=#{name}]", visible: :all).select value
+            expect(page).to have_select(name, selected: value, visible: :all)
+          end
+        end
+      end
+
+      shared_examples 'allows agents to select a treeselect/multi-treeselect field' do
+        it 'allows agents to select another field' do
+          within attribute_selector { expect(page).to have_content(%r{#{display}}i) }
+
+          dropdown_toggle
+          within attribute_selector do
+            find(".js-optionsList > .js-option[data-value=#{value}]", visible: :all).click
+            expect(page).to have_element
+          end
+        end
+      end
+
+      context 'with a select field' do
+        let(:attribute) { create(:object_manager_attribute_select, screens: attributes_for(:required_screen), data_option: data_option) }
+
+        include_examples 'allow agents to select another field'
+      end
+
+      context 'with a multiselect field' do
+        let(:attribute) { create(:object_manager_attribute_multiselect, screens: attributes_for(:required_screen), data_option: multi_data_option) }
+
+        include_examples 'allow agents to select another field'
+      end
+
+      context 'with a tree select field' do
+        let(:attribute)    { create(:object_manager_attribute_tree_select, screens: attributes_for(:required_screen), data_option: data_option) }
+        let(:have_element) { have_field(name, with: value, visible: :all) }
+
+        include_examples 'allows agents to select a treeselect/multi-treeselect field'
+      end
+
+      context 'with a multi tree select field' do
+        let(:attribute) { create(:object_manager_attribute_multi_tree_select, screens: attributes_for(:required_screen), data_option: multi_data_option) }
+        let(:have_element) { have_select(name, selected: value, visible: :all) }
+
+        include_examples 'allows agents to select a treeselect/multi-treeselect field'
+      end
+    end
   end
 
   context 'with read access' do
@@ -54,5 +128,72 @@ RSpec.describe 'Ticket Access Zoom', type: :system, authenticated_as: :user do
       expect(page).to have_no_selector('.links .icon-diagonal-cross')
       expect(page).to have_no_content('+ Add Link')
     end
+
+    context 'with select, treeselect, multiselect and multi-treeselect fields', db_strategy: :reset, authenticated_as: :authenticated do
+      def authenticated
+        attribute
+        ObjectManager::Attribute.migration_execute
+        user
+      end
+
+      shared_examples 'does not allow agents to select another field' do
+        it 'does not allow agents to select another field' do
+          within attribute_selector do
+            expect(page).to have_content(%r{#{display}}i)
+            find(".controls select[name=#{name}]", visible: :all).select value, disabled: true
+            expect(page).to have_no_select(name, selected: value, visible: :all, disabled: :all)
+          end
+        end
+      end
+
+      shared_examples 'does not allow agents to select another treeselect/multi-treeselect field' do
+        it 'does not allow agents to select another field' do
+          within attribute_selector do
+            expect(page).to have_content(%r{#{display}}i)
+            find('.controls .dropdown .dropdown-toggle').click
+            expect(page).to have_no_css(".js-optionsList > .js-option[data-value=#{value}]", wait: 15)
+          end
+        end
+      end
+
+      context 'with a select field' do
+        let(:attribute) { create(:object_manager_attribute_select, screens: attributes_for(:required_screen), data_option: data_option) }
+
+        include_examples 'does not allow agents to select another field'
+      end
+
+      context 'with a multiselect field' do
+        let(:attribute) { create(:object_manager_attribute_multiselect, screens: attributes_for(:required_screen), data_option: multi_data_option) }
+
+        include_examples 'does not allow agents to select another field'
+      end
+
+      context 'with a tree select field' do
+        let(:attribute) { create(:object_manager_attribute_tree_select, screens: attributes_for(:required_screen), data_option: data_option) }
+
+        include_examples 'does not allow agents to select another treeselect/multi-treeselect field'
+      end
+
+      context 'with a multi tree select field' do
+        let(:attribute) { create(:object_manager_attribute_multi_tree_select, screens: attributes_for(:required_screen), data_option: multi_data_option) }
+
+        include_examples 'does not allow agents to select another treeselect/multi-treeselect field'
+      end
+    end
+  end
+
+  def dropdown_toggle
+    loop do
+      find('.controls .dropdown .dropdown-toggle').click
+
+      break if find_all(".js-optionsList > .js-option[data-value=#{value}]", allow_reload: true, wait: 0).any?
+
+      # If we could not find the dropdown options, we sleep and try again.
+      sleep 0.1
+    end
+  end
+
+  def attribute_selector
+    ".sidebar-content .#{data_type}[data-attribute-name=#{name}]"
   end
 end