Skip to content

Commit ea3e71f

Browse files
committed
feat: integrate TomSelect for member lookup
- Add TomSelect to admin members index page (loaded from CDN) - Search triggers after 3+ characters with 300ms debounce - Display member name and email in dropdown options - Update 'View Profile' link on member selection - Initialize TomSelect before Chosen to avoid conflicts - Exclude member_lookup_id from Chosen initialization
1 parent 5b835ae commit ea3e71f

3 files changed

Lines changed: 84 additions & 7 deletions

File tree

app/assets/javascripts/application.js

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,43 @@ $(function() {
4040
format: "HH:i",
4141
});
4242

43+
// TomSelect for admin member lookup
44+
if ($('#member_lookup_id').length) {
45+
new TomSelect('#member_lookup_id', {
46+
placeholder: 'Type to search members...',
47+
valueField: 'id',
48+
labelField: 'full_name',
49+
searchField: ['full_name', 'email'],
50+
create: false,
51+
loadThrottle: 300,
52+
shouldLoad: function(query) {
53+
return query.length >= 3;
54+
},
55+
load: function(query, callback) {
56+
fetch('/admin/members/search?q=' + encodeURIComponent(query))
57+
.then(response => response.json())
58+
.then(json => callback(json))
59+
.catch(() => callback());
60+
},
61+
render: {
62+
option: function(item, escape) {
63+
return '<div>' + escape(item.full_name) + ' <small class="text-muted">' + escape(item.email) + '</small></div>';
64+
}
65+
}
66+
});
67+
68+
$('#member_lookup_id').on('change', function() {
69+
$('#view_profile').attr('href', '/admin/members/' + $(this).val());
70+
});
71+
}
72+
73+
// Chosen for all other selects (exclude #member_lookup_id)
4374
// Chosen hides inputs and selects, which becomes problematic when they are
4475
// required: browser validation doesn't get shown to the user.
4576
// This fix places "the original input behind the Chosen input, matching the
4677
// height and width so that the warning appears in the correct position."
4778
// https://github.com/harvesthq/chosen/issues/515#issuecomment-474588057
48-
$('select').on('chosen:ready', function () {
79+
$('select').not('#member_lookup_id').on('chosen:ready', function () {
4980
var height = $(this).next('.chosen-container').height();
5081
var width = $(this).next('.chosen-container').width();
5182

@@ -57,14 +88,10 @@ $(function() {
5788
}).show();
5889
});
5990

60-
$('select').chosen({
91+
$('select').not('#member_lookup_id').chosen({
6192
allow_single_deselect: true,
6293
no_results_text: 'No results matched'
6394
});
6495

65-
$('#member_lookup_id').change(function(e) {
66-
$('#view_profile').attr('href', '/admin/members/' + $(this).val())
67-
});
68-
6996
$('[data-bs-toggle="tooltip"]').tooltip();
7097
});
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
- content_for :head do
2+
%link{ href: 'https://cdn.jsdelivr.net/npm/tom-select@2.4.3/dist/css/tom-select.bootstrap5.min.css', rel: 'stylesheet', type: 'text/css' }
3+
%script{ src: 'https://cdn.jsdelivr.net/npm/tom-select@2.4.3/dist/js/tom-select.complete.min.js' }
4+
15
.container.py-4.py-lg-5
26
.row.mb-4
37
.col
48
%h1 Members Directory
59
.row.mb-4
610
.col-12.col-md-6
7-
= select_tag 'member_lookup_id', options_for_select([['Select a member...', '']] + @members.collect{ |u| ["#{u.full_name} (#{u.email})", u.id] }), { class: 'chosen-select' }
11+
= select_tag 'member_lookup_id', nil, class: 'form-control'
812
.row
913
.col
1014
= link_to 'View Profile', '#', { class: 'btn btn-primary', id: 'view_profile' }
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
require 'rails_helper'
2+
3+
RSpec.describe 'Admin TomSelect Member Lookup', :js, type: :feature do
4+
let(:admin) { Fabricate(:member) }
5+
let!(:member_jane) { Fabricate(:member, name: 'Jane', surname: 'Doe', email: 'jane@example.com') }
6+
let!(:member_john) { Fabricate(:member, name: 'John', surname: 'Smith', email: 'john@test.com') }
7+
8+
before do
9+
admin.add_role(:admin)
10+
login_as_admin(admin)
11+
end
12+
13+
scenario 'searching for members with TomSelect' do
14+
visit admin_members_path
15+
16+
expect(page).to have_css('.ts-wrapper', wait: 5)
17+
18+
find('.ts-control').click
19+
find('.ts-control input').send_keys('Ja')
20+
sleep 0.5
21+
22+
find('.ts-control input').send_keys('ne')
23+
24+
expect(page).to have_css('.ts-dropdown .option', wait: 5)
25+
26+
expect(page).to have_content('Jane Doe')
27+
expect(page).to have_content('jane@example.com')
28+
29+
expect(page).not_to have_content('John Smith')
30+
end
31+
32+
scenario 'selecting a member updates view profile link' do
33+
visit admin_members_path
34+
35+
expect(page).to have_css('.ts-wrapper', wait: 5)
36+
37+
find('.ts-control').click
38+
find('.ts-control input').send_keys('Jane Doe')
39+
40+
expect(page).to have_css('.ts-dropdown .option', wait: 5)
41+
42+
find('.ts-dropdown .option', text: 'Jane Doe').click
43+
44+
expect(find('#view_profile')[:href]).to include(admin_member_path(member_jane))
45+
end
46+
end

0 commit comments

Comments
 (0)