Skip to content

Commit

Permalink
Merge pull request #1338 from SURFnet/feature/62_tablefilter
Browse files Browse the repository at this point in the history
Add the ability to filter the Entity IDP selection list
  • Loading branch information
johanib authored Dec 19, 2024
2 parents db6be3e + a06d835 commit 2beb040
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 2 deletions.
1 change: 1 addition & 0 deletions assets/js/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ import './components/tooltips.js';
import './components/modal.ts';
import './components/site_notice.ts'
import './components/a11y.ts';
import './components/table_filter.ts';
// Bootstrap the Stimulus components
import '../bootstrap';
33 changes: 33 additions & 0 deletions assets/js/components/table_filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { filterRow } from './table_filter_logic';

(function () {
"use strict";


function dquery(selector: string) {
return Array.prototype.slice.call(document.querySelectorAll(selector));
}

function onInputEvent(e: any) {
const input = e.target;
const search = input.value.toLocaleLowerCase();

const tableContainer = input.closest('.input-group').parentElement;
const table = tableContainer.querySelector('table');
const rows = table.querySelectorAll('tbody tr');

[].forEach.call(rows, function(row: any) {
filterRow(row, search);
});
}

function init() {
const inputs = dquery("input[data-table]");
[].forEach.call(inputs, function (input: HTMLInputElement) {
input.oninput = onInputEvent;
if (input.value !== "") input.oninput(({ target: input } as unknown) as Event);
});
}

init();
})();
101 changes: 101 additions & 0 deletions assets/js/components/table_filter_logic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* @jest-environment jsdom
*/

import { filterRow } from './table_filter_logic';

describe('filterRow', () => {
let row: any;

beforeEach(() => {
document.body.innerHTML = ''; // Clear the DOM
row = document.createElement('tr');
row.style.display = 'table-row';
document.body.appendChild(row);
});

afterEach(() => {
document.body.innerHTML = '';
});

describe('when row has checked inputs', () => {
beforeEach(() => {
const input = document.createElement('input');
input.type = 'checkbox';
input.checked = true;
row.appendChild(input);
});

it('should always display the row regardless of search term', () => {
filterRow(row, 'nonexistent');
expect(row.style.display).toBe('table-row');
});
});

describe('when row contains th elements', () => {
beforeEach(() => {
const th = document.createElement('th');
th.textContent = 'header content';
row.appendChild(th);
});

it('should always display the row regardless of search term', () => {
filterRow(row, 'nonexistent');
expect(row.style.display).toBe('table-row');
});
});

describe('when filtering regular content', () => {
beforeEach(() => {
row.textContent = 'Test Row Content';
});

it('should show row when search term matches content', () => {
filterRow(row, 'test');
expect(row.style.display).toBe('table-row');
});

it('should hide row when search term does not match content', () => {
filterRow(row, 'nonexistent');
expect(row.style.display).toBe('none');
});

it('should be case insensitive', () => {
filterRow(row, 'TEST');
expect(row.style.display).toBe('table-row');
});

it('should cache lowercase content after first call', () => {
filterRow(row, 'test');
const cachedContent = row.lowerTextContent;
filterRow(row, 'row');
expect(row.lowerTextContent).toBe(cachedContent);
});
});

describe('edge cases', () => {
beforeEach(() => {
row.textContent = 'Test Content';
});

it('should handle empty search string', () => {
filterRow(row, '');
expect(row.style.display).toBe('table-row');
});

it('should handle special characters in search', () => {
row.textContent = 'Test (Content)';
filterRow(row, '(content)');
expect(row.style.display).toBe('table-row');
});

it('should handle multiple consecutive calls with different search terms', () => {
filterRow(row, 'test');
expect(row.style.display).toBe('table-row');
filterRow(row, 'nonexistent');
expect(row.style.display).toBe('none');
filterRow(row, 'content');
expect(row.style.display).toBe('table-row');
});
});
});
17 changes: 17 additions & 0 deletions assets/js/components/table_filter_logic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function filterRow(row: any, search: string): void {
if (row.lowerTextContent === undefined) {
row.lowerTextContent = row.textContent.toLocaleLowerCase();
}

if (row.querySelectorAll('input:checked').length > 0) {
row.style.display = 'table-row';
return;
}

if (row.querySelectorAll('th').length > 0) {
row.style.display = 'table-row';
return;
}

row.style.display = row.lowerTextContent.indexOf(search.toLocaleLowerCase()) === -1 ? "none" : "table-row";
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

declare(strict_types = 1);
declare(strict_types=1);

/**
* Copyright 2024 SURFnet B.V.
Expand Down Expand Up @@ -33,7 +33,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('testEntities', TestIdpListType::class)
->add('institutionEntities', InstitutionIdpListType::class)
->add('institutionEntities', InstitutionIdpListType::class, ['attr' => ['show_filter' => true]])
->add('save', SubmitType::class, ['attr' => ['class' => 'button']]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ entity.idps.info.title: Which IdP's should be connected to your entity?
entity.idps.info.html: "
<p>In order to be able to login to your service you need to connect an IdP to it. You can either connect one of our test IdP's or you can connect one of the test Idp's of an institution.</p>
"
entity.idps.filter: Enter text to filter...

entity.idps.test-idps.title: Test IdP's
entity.idps.test-idps.html: "
Expand Down
5 changes: 5 additions & 0 deletions templates/form/fields.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@
{# Use a table to display the list of idp's for the entity acl's. #}
{% block acl_list_widget %}
{% apply spaceless %}
{% if form.vars.attr.show_filter is defined and form.vars.attr.show_filter == true %}
<div class="input-group mb-3">
<input type="text" class="form-control" data-table="table" placeholder="{{ 'entity.idps.filter'|trans }}">
</div>
{% endif %}
<table class="aclList" {{ block('widget_container_attributes') }}>
<tr>
<th class="coll1">&nbsp;</th>
Expand Down

0 comments on commit 2beb040

Please sign in to comment.