indexAsync Boolean, default: false
diff --git a/src/fuzzy-search.js b/src/fuzzy-search.js
index 1852526a..77f9f4f4 100755
--- a/src/fuzzy-search.js
+++ b/src/fuzzy-search.js
@@ -59,7 +59,7 @@ module.exports = function(list, options) {
events.bind(getByClass(list.listContainer, options.searchClass), 'keyup', list.utils.events.debounce(function(e) {
var target = e.target || e.srcElement; // IE have srcElement
list.search(target.value, fuzzySearch.search);
- }, 750));
+ }, list.searchDelay));
return function(str, columns) {
list.search(str, columns, fuzzySearch.search);
diff --git a/src/index.js b/src/index.js
old mode 100644
new mode 100755
index 887019ff..5004e912
--- a/src/index.js
+++ b/src/index.js
@@ -29,6 +29,7 @@ module.exports = function(id, options, values) {
self.searched = false;
self.filtered = false;
self.searchColumns = undefined;
+ self.searchDelay = 0;
self.handlers = { 'updated': [] };
self.valueNames = [];
self.utils = {
diff --git a/src/search.js b/src/search.js
index f4c355da..f3d98250 100755
--- a/src/search.js
+++ b/src/search.js
@@ -118,7 +118,7 @@ module.exports = function(list) {
if (!alreadyCleared) { // If oninput already have resetted the list, do nothing
searchMethod(target.value);
}
- }, 750));
+ }, list.searchDelay));
// Used to detect click on HTML5 clear button
list.utils.events.bind(list.utils.getByClass(list.listContainer, list.searchClass), 'input', function(e) {
diff --git a/src/utils/events.js b/src/utils/events.js
index 63a14e92..85903198 100755
--- a/src/utils/events.js
+++ b/src/utils/events.js
@@ -51,7 +51,7 @@ exports.unbind = function(el, type, fn, capture){
exports.debounce = function(fn, wait, immediate){
var timeout;
- return function() {
+ return wait ? function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
@@ -61,5 +61,5 @@ exports.debounce = function(fn, wait, immediate){
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) fn.apply(context, args);
- };
+ } : fn;
};
From 0c90d04d454281f4407cbf1e6c00ac35620cb3d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Sun, 22 Nov 2020 14:15:26 +0100
Subject: [PATCH 32/42] Move all existing tests to /integration
---
__test__/create.test.js | 303 -----------------
.../{ => integration}/add-get-remove.test.js | 0
__test__/{ => integration}/buttons.test.js | 2 +-
__test__/integration/create.test.js | 315 ++++++++++++++++++
__test__/{ => integration}/defaults.test.js | 0
__test__/{ => integration}/filter.test.js | 0
.../{ => integration}/fixtures-fuzzysearch.js | 0
.../{ => integration}/fixtures-pagination.js | 0
__test__/{ => integration}/fixtures.js | 2 +-
.../{ => integration}/fuzzysearch.test.js | 2 +-
__test__/{ => integration}/item.test.js | 0
__test__/{ => integration}/off.test.js | 0
__test__/{ => integration}/on.test.js | 0
__test__/{ => integration}/pagination.test.js | 2 +-
__test__/{ => integration}/parse.test.js | 2 +-
__test__/{ => integration}/re-index.test.js | 0
.../{ => integration}/search-filter.test.js | 0
__test__/{ => integration}/search.test.js | 0
__test__/{ => integration}/show.test.js | 0
__test__/{ => integration}/sort.test.js | 0
__test__/{ => integration}/trigger.test.js | 0
.../{ => integration}/utils.classes.test.js | 2 +-
.../utils.get-by-class.test.js | 2 +-
23 files changed, 322 insertions(+), 310 deletions(-)
delete mode 100644 __test__/create.test.js
rename __test__/{ => integration}/add-get-remove.test.js (100%)
rename __test__/{ => integration}/buttons.test.js (99%)
create mode 100644 __test__/integration/create.test.js
rename __test__/{ => integration}/defaults.test.js (100%)
rename __test__/{ => integration}/filter.test.js (100%)
rename __test__/{ => integration}/fixtures-fuzzysearch.js (100%)
rename __test__/{ => integration}/fixtures-pagination.js (100%)
rename __test__/{ => integration}/fixtures.js (96%)
rename __test__/{ => integration}/fuzzysearch.test.js (98%)
rename __test__/{ => integration}/item.test.js (100%)
rename __test__/{ => integration}/off.test.js (100%)
rename __test__/{ => integration}/on.test.js (100%)
rename __test__/{ => integration}/pagination.test.js (99%)
rename __test__/{ => integration}/parse.test.js (99%)
rename __test__/{ => integration}/re-index.test.js (100%)
rename __test__/{ => integration}/search-filter.test.js (100%)
rename __test__/{ => integration}/search.test.js (100%)
rename __test__/{ => integration}/show.test.js (100%)
rename __test__/{ => integration}/sort.test.js (100%)
rename __test__/{ => integration}/trigger.test.js (100%)
rename __test__/{ => integration}/utils.classes.test.js (96%)
rename __test__/{ => integration}/utils.get-by-class.test.js (92%)
diff --git a/__test__/create.test.js b/__test__/create.test.js
deleted file mode 100644
index bae4c927..00000000
--- a/__test__/create.test.js
+++ /dev/null
@@ -1,303 +0,0 @@
-const $ = require('jquery'),
- List = require('../src/index')
-
-describe('Create', function () {
- describe('With HTML items', function () {
- var listEl = $(
- ''
- )
-
- $(document.body).append(listEl)
-
- var list = new List('list', { valueNames: ['name'] })
-
- it('should contain one item', function () {
- expect(list.items.length).toEqual(1)
- expect(listEl.find('li').length).toEqual(1)
- })
-
- it('should contain two items', function () {
- list.add({ name: 'Jonas' })
- expect(list.items.length).toEqual(2)
- expect(listEl.find('li').length).toEqual(2)
- })
-
- listEl.remove()
- })
-
- describe('With and element instead of id', function () {
- var listEl = $(
- ''
- )
-
- $(document.body).append(listEl)
- var el = document.getElementById('list')
-
- var list = new List(el, { valueNames: ['name'] })
-
- it('should contain one item', function () {
- expect(list.items.length).toEqual(1)
- expect(listEl.find('li').length).toEqual(1)
- })
-
- listEl.remove()
- })
-
- describe('Without items and with string template', function () {
- var listEl = $('')
-
- $(document.body).append(listEl)
-
- var list = new List(
- 'list',
- {
- valueNames: ['name'],
- item: ' ',
- },
- [{ name: 'Jonny' }]
- )
-
- it('should contain one item', function () {
- expect(list.items.length).toEqual(1)
- expect(listEl.find('li').length).toEqual(1)
- })
-
- it('should contain two items', function () {
- list.add({ name: 'Jonas' })
- expect(list.items.length).toEqual(2)
- expect(listEl.find('li').length).toEqual(2)
- })
-
- listEl.remove()
- })
-
- describe('Without items and with string template for table', function () {
- var listEl = $('')
-
- $(document.body).append(listEl)
-
- var list = new List(
- 'list',
- {
- valueNames: ['name'],
- item: ' ',
- },
- [{ name: 'Jonny' }]
- )
-
- it('should contain one item', function () {
- expect(list.items.length).toEqual(1)
- expect(listEl.find('tr').length).toEqual(1)
- })
-
- it('should contain two items', function () {
- list.add({ name: 'Jonas' })
- expect(list.items.length).toEqual(2)
- expect(listEl.find('tr').length).toEqual(2)
- })
-
- listEl.remove()
- })
-
- describe('Without items and with template function', function () {
- var listEl = $('')
-
- $(document.body).append(listEl)
-
- var list = new List(
- 'list',
- {
- valueNames: ['name'],
- item: function (values) {
- return ` `
- },
- },
- [{ name: 'Jonny' }]
- )
-
- it('should contain one item', function () {
- expect(list.items.length).toEqual(1)
- expect(listEl.find('li').length).toEqual(1)
- })
-
- it('should contain two items', function () {
- list.add({ name: 'Jonas' })
- expect(list.items.length).toEqual(2)
- expect(listEl.find('li').length).toEqual(2)
- })
-
- it('should get values from items', function () {
- list.add({ name: 'Egon' })
- expect(listEl.find('li[data-template-fn-egon]').length).toEqual(1)
- })
-
- listEl.remove()
- })
-
- describe('without items and or template', function () {
- it('should throw error on init', function () {
- var listEl = $('')
- $(document.body).append(listEl)
-
- expect(function () {
- var list = new List('list', {
- valueNames: ['name'],
- })
- }).toThrow()
- listEl.remove()
- })
- })
-
- describe('Without items and with HTML template', function () {
- var listEl = $('')
-
- var templateEl = $(' ')
-
- $(document.body).append(listEl)
- $(document.body).append(templateEl)
-
- var list = new List(
- 'list',
- {
- valueNames: ['name'],
- item: 'template-item',
- },
- [{ name: 'Jonny' }]
- )
-
- it('should contain one item', function () {
- expect(list.items.length).toEqual(1)
- expect(listEl.find('li').length).toEqual(1)
- })
-
- it('should contain two items', function () {
- list.add({ name: 'Jonas' })
- expect(list.items.length).toEqual(2)
- expect(listEl.find('li').length).toEqual(2)
- })
-
- listEl.remove()
- templateEl.remove()
- })
-
- describe('Asyn index with existing list', function () {
- var listEl = $(
- ''
- )
-
- it('should contain one item', function (done) {
- $(document.body).append(listEl)
- var list = new List('list', {
- valueNames: ['name'],
- indexAsync: true,
- parseComplete: function (list) {
- expect(listEl.find('li').length).toEqual(162)
- listEl.remove()
- done()
- },
- })
- })
- })
-})
diff --git a/__test__/add-get-remove.test.js b/__test__/integration/add-get-remove.test.js
similarity index 100%
rename from __test__/add-get-remove.test.js
rename to __test__/integration/add-get-remove.test.js
diff --git a/__test__/buttons.test.js b/__test__/integration/buttons.test.js
similarity index 99%
rename from __test__/buttons.test.js
rename to __test__/integration/buttons.test.js
index 2165579e..ca6103fa 100644
--- a/__test__/buttons.test.js
+++ b/__test__/integration/buttons.test.js
@@ -1,5 +1,5 @@
const $ = require('jquery'),
- List = require('../src/index')
+ List = require('../../src/index')
function fireKeyup(el) {
if (document.createEvent) {
diff --git a/__test__/integration/create.test.js b/__test__/integration/create.test.js
new file mode 100644
index 00000000..8a353fbb
--- /dev/null
+++ b/__test__/integration/create.test.js
@@ -0,0 +1,315 @@
+const $ = require('jquery')
+const List = require('../../src/index')
+
+describe('Create', () => {
+ describe('With HTML items', () => {
+ let list, listEl
+ beforeEach(function () {
+ listEl = $(`
+
+ `)
+ $(document.body).append(listEl)
+ list = new List('list', { valueNames: ['name'] })
+ })
+ afterEach(() => {
+ listEl.remove()
+ })
+
+ it('should contain one item', () => {
+ expect(list.items.length).toEqual(1)
+ expect(listEl.find('li').length).toEqual(1)
+ })
+
+ it('should contain two items', () => {
+ list.add({ name: 'Jonas' })
+ expect(list.items.length).toEqual(2)
+ expect(listEl.find('li').length).toEqual(2)
+ })
+ })
+
+ describe('With and element instead of id', () => {
+ var listEl, list
+ beforeEach(() => {
+ listEl = $(`
+
+ `)
+ $(document.body).append(listEl)
+ var el = document.getElementById('list')
+ list = new List(el, { valueNames: ['name'] })
+ })
+ afterEach(() => {
+ listEl.remove()
+ })
+
+ it('should contain one item', () => {
+ expect(list.items.length).toEqual(1)
+ expect(listEl.find('li').length).toEqual(1)
+ })
+ })
+
+ describe('Without items and with string template', () => {
+ var listEl, list
+ beforeEach(() => {
+ listEl = $('')
+ $(document.body).append(listEl)
+ list = new List(
+ 'list',
+ {
+ valueNames: ['name'],
+ item: ' ',
+ },
+ [{ name: 'Jonny' }]
+ )
+ })
+ afterEach(() => {
+ listEl.remove()
+ })
+
+ it('should contain one item', () => {
+ expect(list.items.length).toEqual(1)
+ expect(listEl.find('li').length).toEqual(1)
+ })
+
+ it('should contain two items', () => {
+ list.add({ name: 'Jonas' })
+ expect(list.items.length).toEqual(2)
+ expect(listEl.find('li').length).toEqual(2)
+ })
+ })
+
+ describe('Without items and with string template for table', () => {
+ var listEl, list
+
+ beforeEach(() => {
+ listEl = $(`
+
+ `)
+ $(document.body).append(listEl)
+ list = new List(
+ 'list',
+ {
+ valueNames: ['name'],
+ item: ' ',
+ },
+ [{ name: 'Jonny' }]
+ )
+ })
+
+ afterEach(() => {
+ listEl.remove()
+ })
+
+ it('should contain one item', () => {
+ expect(list.items.length).toEqual(1)
+ expect(listEl.find('tr').length).toEqual(1)
+ })
+
+ it('should contain two items', () => {
+ list.add({ name: 'Jonas' })
+ expect(list.items.length).toEqual(2)
+ expect(listEl.find('tr').length).toEqual(2)
+ })
+ })
+
+ describe('Without items and with template function', () => {
+ var listEl, list
+ beforeEach(() => {
+ listEl = $('')
+ $(document.body).append(listEl)
+ list = new List(
+ 'list',
+ {
+ valueNames: ['name'],
+ item: function (values) {
+ return ` `
+ },
+ },
+ [{ name: 'Jonny' }]
+ )
+ })
+ afterEach(() => {
+ listEl.remove()
+ })
+
+ it('should contain one item', () => {
+ expect(list.items.length).toEqual(1)
+ expect(listEl.find('li').length).toEqual(1)
+ })
+
+ it('should contain two items', () => {
+ list.add({ name: 'Jonas' })
+ expect(list.items.length).toEqual(2)
+ expect(listEl.find('li').length).toEqual(2)
+ })
+
+ it('should get values from items', () => {
+ list.add({ name: 'Egon' })
+ expect(listEl.find('li[data-template-fn-egon]').length).toEqual(1)
+ })
+ })
+
+ describe('without items and or template', () => {
+ it('should throw error on init', () => {
+ var listEl = $('')
+ $(document.body).append(listEl)
+
+ expect(() => {
+ var list = new List('list', {
+ valueNames: ['name'],
+ })
+ }).toThrow()
+
+ listEl.remove()
+ })
+ })
+
+ describe('Without items and with HTML template', () => {
+ var listEl, list, templateEl
+ beforeEach(() => {
+ listEl = $('')
+
+ templateEl = $(' ')
+
+ $(document.body).append(listEl)
+ $(document.body).append(templateEl)
+
+ list = new List(
+ 'list',
+ {
+ valueNames: ['name'],
+ item: 'template-item',
+ },
+ [{ name: 'Jonny' }]
+ )
+ })
+ afterEach(() => {
+ listEl.remove()
+ templateEl.remove()
+ })
+
+ it('should contain one item', () => {
+ expect(list.items.length).toEqual(1)
+ expect(listEl.find('li').length).toEqual(1)
+ })
+
+ it('should contain two items', () => {
+ list.add({ name: 'Jonas' })
+ expect(list.items.length).toEqual(2)
+ expect(listEl.find('li').length).toEqual(2)
+ })
+ })
+
+ describe('Asyn index with existing list', () => {
+ it('should contain 162 items', (done) => {
+ var listEl = $(`
+
+ `)
+ $(document.body).append(listEl)
+ var list = new List('list', {
+ valueNames: ['name'],
+ indexAsync: true,
+ parseComplete: function (list) {
+ expect(listEl.find('li').length).toEqual(162)
+ listEl.remove()
+ done()
+ },
+ })
+ })
+ })
+})
diff --git a/__test__/defaults.test.js b/__test__/integration/defaults.test.js
similarity index 100%
rename from __test__/defaults.test.js
rename to __test__/integration/defaults.test.js
diff --git a/__test__/filter.test.js b/__test__/integration/filter.test.js
similarity index 100%
rename from __test__/filter.test.js
rename to __test__/integration/filter.test.js
diff --git a/__test__/fixtures-fuzzysearch.js b/__test__/integration/fixtures-fuzzysearch.js
similarity index 100%
rename from __test__/fixtures-fuzzysearch.js
rename to __test__/integration/fixtures-fuzzysearch.js
diff --git a/__test__/fixtures-pagination.js b/__test__/integration/fixtures-pagination.js
similarity index 100%
rename from __test__/fixtures-pagination.js
rename to __test__/integration/fixtures-pagination.js
diff --git a/__test__/fixtures.js b/__test__/integration/fixtures.js
similarity index 96%
rename from __test__/fixtures.js
rename to __test__/integration/fixtures.js
index 083dae56..32245c99 100644
--- a/__test__/fixtures.js
+++ b/__test__/integration/fixtures.js
@@ -1,5 +1,5 @@
const $ = require('jquery'),
- List = require('../src/index')
+ List = require('../../src/index')
var fixture = {
list: function (valueNames, items) {
diff --git a/__test__/fuzzysearch.test.js b/__test__/integration/fuzzysearch.test.js
similarity index 98%
rename from __test__/fuzzysearch.test.js
rename to __test__/integration/fuzzysearch.test.js
index eea89f5d..f0ba9afc 100644
--- a/__test__/fuzzysearch.test.js
+++ b/__test__/integration/fuzzysearch.test.js
@@ -1,6 +1,6 @@
const $ = require('jquery'),
fixtureFuzzysearch = require('./fixtures-fuzzysearch'),
- List = require('../src/index')
+ List = require('../../src/index')
function fireKeyup(el) {
if (document.createEvent) {
diff --git a/__test__/item.test.js b/__test__/integration/item.test.js
similarity index 100%
rename from __test__/item.test.js
rename to __test__/integration/item.test.js
diff --git a/__test__/off.test.js b/__test__/integration/off.test.js
similarity index 100%
rename from __test__/off.test.js
rename to __test__/integration/off.test.js
diff --git a/__test__/on.test.js b/__test__/integration/on.test.js
similarity index 100%
rename from __test__/on.test.js
rename to __test__/integration/on.test.js
diff --git a/__test__/pagination.test.js b/__test__/integration/pagination.test.js
similarity index 99%
rename from __test__/pagination.test.js
rename to __test__/integration/pagination.test.js
index 0e113b1e..3dc1ff3e 100644
--- a/__test__/pagination.test.js
+++ b/__test__/integration/pagination.test.js
@@ -1,6 +1,6 @@
const $ = require('jquery'),
fixturePagination = require('./fixtures-pagination'),
- List = require('../src/index')
+ List = require('../../src/index')
describe('Pagination', function () {
describe('Default settings, innerWindow: 2, outerWindow: 0, left: 0, right: 0', function () {
diff --git a/__test__/parse.test.js b/__test__/integration/parse.test.js
similarity index 99%
rename from __test__/parse.test.js
rename to __test__/integration/parse.test.js
index 4055050f..64c7e290 100644
--- a/__test__/parse.test.js
+++ b/__test__/integration/parse.test.js
@@ -1,5 +1,5 @@
const $ = require('jquery'),
- List = require('../src/index')
+ List = require('../../src/index')
describe('Parse', function () {
describe('Parse class', function () {
diff --git a/__test__/re-index.test.js b/__test__/integration/re-index.test.js
similarity index 100%
rename from __test__/re-index.test.js
rename to __test__/integration/re-index.test.js
diff --git a/__test__/search-filter.test.js b/__test__/integration/search-filter.test.js
similarity index 100%
rename from __test__/search-filter.test.js
rename to __test__/integration/search-filter.test.js
diff --git a/__test__/search.test.js b/__test__/integration/search.test.js
similarity index 100%
rename from __test__/search.test.js
rename to __test__/integration/search.test.js
diff --git a/__test__/show.test.js b/__test__/integration/show.test.js
similarity index 100%
rename from __test__/show.test.js
rename to __test__/integration/show.test.js
diff --git a/__test__/sort.test.js b/__test__/integration/sort.test.js
similarity index 100%
rename from __test__/sort.test.js
rename to __test__/integration/sort.test.js
diff --git a/__test__/trigger.test.js b/__test__/integration/trigger.test.js
similarity index 100%
rename from __test__/trigger.test.js
rename to __test__/integration/trigger.test.js
diff --git a/__test__/utils.classes.test.js b/__test__/integration/utils.classes.test.js
similarity index 96%
rename from __test__/utils.classes.test.js
rename to __test__/integration/utils.classes.test.js
index e2ad006f..71038e7d 100644
--- a/__test__/utils.classes.test.js
+++ b/__test__/integration/utils.classes.test.js
@@ -1,4 +1,4 @@
-const classes = require('../src/utils/classes')
+const classes = require('../../src/utils/classes')
describe('Classes', function () {
var el
diff --git a/__test__/utils.get-by-class.test.js b/__test__/integration/utils.get-by-class.test.js
similarity index 92%
rename from __test__/utils.get-by-class.test.js
rename to __test__/integration/utils.get-by-class.test.js
index b524b544..b3f08d95 100644
--- a/__test__/utils.get-by-class.test.js
+++ b/__test__/integration/utils.get-by-class.test.js
@@ -1,4 +1,4 @@
-const getByClass = require('../src/utils/get-by-class')
+const getByClass = require('../../src/utils/get-by-class')
describe('GetByClass', function () {
var el
From fa8173dc1ad2f11f2ba8e95baa273b3c6d388c3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Mon, 23 Nov 2020 11:53:54 +0100
Subject: [PATCH 33/42] Add unit tests for templates and star decouple
Templater api from Item
---
__test__/unit/templater.test.js | 165 ++++++++++++++++++++++++++++++++
src/index.js | 2 +-
src/item.js | 13 ++-
src/templater.js | 66 ++++++-------
4 files changed, 203 insertions(+), 43 deletions(-)
create mode 100644 __test__/unit/templater.test.js
diff --git a/__test__/unit/templater.test.js b/__test__/unit/templater.test.js
new file mode 100644
index 00000000..e19ac7d3
--- /dev/null
+++ b/__test__/unit/templater.test.js
@@ -0,0 +1,165 @@
+const $ = require('jquery')
+const Templater = require('../../src/templater')
+
+describe('Templater', () => {
+ describe('init', () => {
+ it('should init with item string of a div', () => {
+ const item = `Foo
`
+ const valueNames = ['name']
+ const templater = Templater({ item, valueNames })
+ const itemEl = templater.createItem()
+ expect(itemEl.outerHTML).toEqual('
')
+ })
+ it('should init with item string of a tr', () => {
+ const item = `Foo `
+ const valueNames = ['name']
+ const templater = Templater({ item, valueNames })
+ const itemEl = templater.createItem()
+ expect(itemEl.outerHTML).toEqual(' ')
+ })
+ it('should init with item function', () => {
+ const item = () => {
+ return `
`
+ }
+ const valueNames = ['name']
+ const templater = Templater({ item, valueNames })
+ const itemEl = templater.createItem()
+ expect(itemEl.outerHTML).toEqual('
')
+ })
+ it('should init without item', () => {
+ const listEl = $(`
+
+ `)
+ $(document.body).append(listEl)
+ const valueNames = ['name']
+ const templater = Templater({ valueNames, list: document.querySelector('.list') })
+ const itemEl = templater.createItem()
+ expect(itemEl.outerHTML).toEqual(' ')
+ })
+ })
+ describe('create', () => {
+ it('should create item with values', () => {
+ const item =
+ '' +
+ '
Jonny ' +
+ '
1986 ' +
+ '
' +
+ '
' +
+ '
'
+
+ const valueNames = [
+ 'name',
+ 'born',
+ { data: ['id'] },
+ { attr: 'src', name: 'image' },
+ { attr: 'href', name: 'link' },
+ { attr: 'value', name: 'foo' },
+ { attr: 'data-timestamp', name: 'timestamp' },
+ ]
+ const templater = Templater({ item, valueNames })
+ const itemEl = templater.create({
+ name: 'Sven',
+ born: 1950,
+ id: 4,
+ image: 'usage/rey.jpeg',
+ link: 'localhost',
+ timestamp: '1337',
+ foo: 'hej',
+ })
+ expect(itemEl.outerHTML).toEqual(
+ '' +
+ '
Sven ' +
+ '
1950 ' +
+ '
' +
+ '
' +
+ '
'
+ )
+ })
+ })
+ describe('remove', () => {
+ it('should remove element from list', () => {
+ const listEl = $(`
+
+ `)[0]
+ document.body.appendChild(listEl)
+ const item = listEl.querySelector('div')
+ const valueNames = ['name']
+ const templater = Templater({ list: listEl, valueNames })
+ expect(listEl.querySelector('div')).not.toEqual(null)
+ templater.remove(item)
+ expect(listEl.querySelector('div')).toEqual(null)
+ })
+ })
+ describe('show', () => {
+ it('should add element to list', () => {
+ const listEl = $(`
`)[0]
+ document.body.appendChild(listEl)
+ const item = $('Foo
')[0]
+ const valueNames = ['name']
+ const templater = Templater({ list: listEl, valueNames, item: '
' })
+ expect(listEl.querySelector('div')).toEqual(null)
+ templater.show(item)
+ expect(listEl.querySelector('div')).not.toEqual(null)
+ })
+ })
+ describe('clear', () => {
+ it('should clear the entire list of children', () => {
+ const listEl = $(`
+
+ `)[0]
+ document.body.appendChild(listEl)
+ const valueNames = ['name']
+ const templater = Templater({ list: listEl, valueNames })
+ expect(listEl.querySelectorAll('div').length).toEqual(2)
+ templater.clear()
+ expect(listEl.querySelectorAll('div').length).toEqual(0)
+ })
+ })
+ describe('set', () => {
+ it('should set values to element', () => {
+ const itemEl = $(
+ '' +
+ '
Jonny ' +
+ '
1986 ' +
+ '
' +
+ '
' +
+ '
'
+ )[0]
+ const valueNames = [
+ 'name',
+ 'born',
+ { data: ['id'] },
+ { attr: 'src', name: 'image' },
+ { attr: 'href', name: 'link' },
+ { attr: 'value', name: 'foo' },
+ { attr: 'data-timestamp', name: 'timestamp' },
+ ]
+ const templater = Templater({ item: '
', valueNames })
+ templater.set(itemEl, {
+ name: 'Sven',
+ born: 1950,
+ id: 4,
+ image: 'usage/rey.jpeg',
+ link: 'localhost',
+ timestamp: '1337',
+ foo: 'hej',
+ })
+ expect(itemEl.outerHTML).toEqual(
+ '' +
+ '
Sven ' +
+ '
1950 ' +
+ '
' +
+ '
' +
+ '
'
+ )
+ })
+ })
+ describe('get', () => {})
+})
diff --git a/src/index.js b/src/index.js
index 39ffcbac..ca6bb6d0 100644
--- a/src/index.js
+++ b/src/index.js
@@ -153,7 +153,7 @@ module.exports = function (id, options, values) {
var found = 0
for (var i = 0, il = self.items.length; i < il; i++) {
if (self.items[i].values()[valueName] == value) {
- self.templater.remove(self.items[i], options)
+ self.templater.remove(self.items[i].elm)
self.items.splice(i, 1)
il--
i--
diff --git a/src/item.js b/src/item.js
index 41410625..942d7768 100644
--- a/src/item.js
+++ b/src/item.js
@@ -27,7 +27,11 @@ module.exports = function (list) {
item._values[name] = newValues[name]
}
if (notCreate !== true) {
- list.templater.set(item, item.values())
+ if (item.elm) {
+ list.templater.set(item.elm, item.values())
+ } else {
+ list.templater.create(item.values())
+ }
}
} else {
return item._values
@@ -35,11 +39,14 @@ module.exports = function (list) {
}
this.show = function () {
- list.templater.show(item)
+ if (!item.elm) {
+ item.elm = list.templater.create(item.values())
+ }
+ list.templater.show(item.elm)
}
this.hide = function () {
- list.templater.hide(item)
+ list.templater.remove(item.elm)
}
this.matching = function () {
diff --git a/src/templater.js b/src/templater.js
index 5d09973d..ebe2e654 100644
--- a/src/templater.js
+++ b/src/templater.js
@@ -1,4 +1,6 @@
-var Templater = function (list) {
+var getByClass = require('./utils/get-by-class')
+
+module.exports = function (list) {
var createItem,
templater = this
@@ -6,7 +8,7 @@ var Templater = function (list) {
var itemSource
if (typeof list.item === 'function') {
- createItem = function (values) {
+ templater.createItem = function (values) {
var item = list.item(values)
return getItemSource(item)
}
@@ -31,7 +33,7 @@ var Templater = function (list) {
itemSource = createCleanTemplateItem(itemSource, list.valueNames)
- createItem = function () {
+ templater.createItem = function () {
return itemSource.cloneNode(true)
}
}
@@ -48,12 +50,12 @@ var Templater = function (list) {
el.setAttribute('data-' + valueName.data[j], '')
}
} else if (valueName.attr && valueName.name) {
- elm = list.utils.getByClass(el, valueName.name, true)
+ elm = getByClass(el, valueName.name, true)
if (elm) {
elm.setAttribute(valueName.attr, '')
}
} else {
- elm = list.utils.getByClass(el, valueName, true)
+ elm = getByClass(el, valueName, true)
if (elm) {
elm.innerHTML = ''
}
@@ -106,19 +108,19 @@ var Templater = function (list) {
}
}
- var setValue = function (item, name, value) {
+ var setValue = function (el, name, value) {
var elm = undefined,
valueName = getValueName(name)
if (!valueName) return
if (valueName.data) {
- item.elm.setAttribute('data-' + valueName.data, value)
+ el.setAttribute('data-' + valueName.data, value)
} else if (valueName.attr && valueName.name) {
- elm = list.utils.getByClass(item.elm, valueName.name, true)
+ elm = getByClass(el, valueName.name, true)
if (elm) {
elm.setAttribute(valueName.attr, value)
}
} else {
- elm = list.utils.getByClass(item.elm, valueName, true)
+ elm = getByClass(el, valueName, true)
if (elm) {
elm.innerHTML = value
}
@@ -136,47 +138,36 @@ var Templater = function (list) {
values[valueName.data[j]] = list.utils.getAttribute(item.elm, 'data-' + valueName.data[j])
}
} else if (valueName.attr && valueName.name) {
- elm = list.utils.getByClass(item.elm, valueName.name, true)
+ elm = getByClass(item.elm, valueName.name, true)
values[valueName.name] = elm ? list.utils.getAttribute(elm, valueName.attr) : ''
} else {
- elm = list.utils.getByClass(item.elm, valueName, true)
+ elm = getByClass(item.elm, valueName, true)
values[valueName] = elm ? elm.innerHTML : ''
}
}
return values
}
- this.set = function (item, values) {
- if (!templater.create(item)) {
- for (var v in values) {
- if (values.hasOwnProperty(v)) {
- setValue(item, v, values[v])
- }
+ this.set = function (el, values) {
+ for (var v in values) {
+ if (values.hasOwnProperty(v)) {
+ setValue(el, v, values[v])
}
}
}
- this.create = function (item) {
- if (item.elm !== undefined) {
- return false
- }
- item.elm = createItem(item.values())
- templater.set(item, item.values())
- return true
+ this.create = function (values) {
+ var elm = templater.createItem(values)
+ templater.set(elm, values)
+ return elm
}
- this.remove = function (item) {
- if (item.elm.parentNode === list.list) {
- list.list.removeChild(item.elm)
+ this.remove = function (el) {
+ if (el !== undefined && el.parentNode === list.list) {
+ list.list.removeChild(el)
}
}
- this.show = function (item) {
- templater.create(item)
- list.list.appendChild(item.elm)
- }
- this.hide = function (item) {
- if (item.elm !== undefined && item.elm.parentNode === list.list) {
- list.list.removeChild(item.elm)
- }
+ this.show = function (el) {
+ list.list.appendChild(el)
}
this.clear = function () {
/* .innerHTML = ''; fucks up IE */
@@ -188,8 +179,5 @@ var Templater = function (list) {
}
init()
-}
-
-module.exports = function (list) {
- return new Templater(list)
+ return templater
}
From 3b2792d808bccb3a502b1f93e2f9bb43113d1c52 Mon Sep 17 00:00:00 2001
From: sheffieldnick
Date: Sat, 4 Apr 2020 20:23:29 +0000
Subject: [PATCH 34/42] Added new documentation page "Automagical Searching +
Sorting" with links from left sidebar and "Getting started" page
---
docs/_includes/menu.html | 1 +
docs/docs/index.html | 5 +--
docs/docs/search-sort.html | 91 ++++++++++++++++++++++++++++++++++++++
3 files changed, 94 insertions(+), 3 deletions(-)
mode change 100644 => 100755 docs/_includes/menu.html
mode change 100644 => 100755 docs/docs/index.html
create mode 100755 docs/docs/search-sort.html
diff --git a/docs/_includes/menu.html b/docs/_includes/menu.html
old mode 100644
new mode 100755
index f09173ac..79a81266
--- a/docs/_includes/menu.html
+++ b/docs/_includes/menu.html
@@ -14,6 +14,7 @@ Documentation
Getting started
List API
Item API
+ Searching + Sorting
FAQ
diff --git a/docs/docs/index.html b/docs/docs/index.html
old mode 100644
new mode 100755
index b2f8d1ec..45632742
--- a/docs/docs/index.html
+++ b/docs/docs/index.html
@@ -4,7 +4,7 @@
---
Really simple examples
-You can use List.js on either exising HTML or create new with super simple templating.
+You can use List.js on either existing HTML or create new with super simple templating.
Example 1: Using existing list
@@ -127,8 +127,7 @@ JavaScript (nothing special)
var hackerList = new List('hacker-list', options);
-
-
+Read more here
Example 6: Using data attributes and other custom attributes (introduced in v1.2.0)
diff --git a/docs/docs/search-sort.html b/docs/docs/search-sort.html
new file mode 100755
index 00000000..7dbac087
--- /dev/null
+++ b/docs/docs/search-sort.html
@@ -0,0 +1,91 @@
+---
+layout: default
+title: Automagical Searching + Sorting
+---
+
+
+ Automagical Searching + Sorting
+
+It is easy to add search input and sort buttons with just a few classes and attributes in your HTML. ‘Automagical’ because List.js registers the event handlers, searches/sorts and updates the list for you:
+
+
+ Searching
+
+
+
+
+ class String. *required
+ The default class search
is how List.js finds your writable search field. If you change it also set options.searchClass .
+
+ Alternatively, using fuzzy-search
here will switch to the Fuzzy Search function.
+
+
+
+
+ type String. *required
+ The default input type search
is similar to using text
, but web browsers may render it slightly differently: see https://developer.mozilla.org/.../input/search . Either type will work with List.js.
+
+
+
+
+
+<input type="search" class="search" placeholder="normal search"> or
+<input type="search" class="fuzzy-search" placeholder="fuzzy search!">
+
+
+ Sorting
+
+
+
+ class String. *required
+ The default class sort
is how List.js finds clickable sort buttons. If you change it also set options.sortClass .
+
+
+
+
+ data-sort String. *required
+ This attribute on a clickable sort button should match the column name passed to List.js in options.valueNames .
+
+
+
+
+ data-order String
+ Set to asc
or desc
to enforce that sorting order for a column. The user won't be able to change the order, and any data-default-order
attribute is ignored.
+
+
+
+
+ data-default-order String, default: "asc"
+ Set to desc
to change the initial sorting order for a column. Subsequent clicks will toggle the sorting order between ascending/descending, as usual.
+
+
+
+
+ data-insensitive Boolean, default: true
+ Set to false
for case-sensitive sorting of that column.
+
+
+
+
+
+Sort by:
+<span class='sort' data-sort='name'>Name</span> or
+<span class='sort' data-sort='born' data-default-order='desc'>Born in Year</span> or
+<span class='sort' data-sort='city'>City</span>
+
+
+The CSS classes asc
and desc
are added when a sort button is clicked on, so List.js can show which column is currently sorted. For example, using this CSS sets a yellow background with ⬆ or ⬇ added after the button text:
+
+
+.sort.asc, .sort.desc {
+ background-color: yellow;
+ }
+.sort.asc::after {
+ content: "\002B06";
+ padding-left: 3px;
+ }
+.sort.desc::after {
+ content: "\002B07";
+ padding-left: 3px
+ }
+
From 7e3539f408dcb1ccca7ebd13a37c34c11dc0b30f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Mon, 23 Nov 2020 12:47:23 +0100
Subject: [PATCH 35/42] Update changelog
---
CHANGELOG.md | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41dd9649..07fd2eee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+### 2.2.0
+
+- **[Feature]** #682 Multiple word search
+- **[Feature]** #683 Debounced keyup handler in search
+- **[Website]** #684 Add more documentation for automagical search and sort elements
+
### 2.1.0 - 2020-11-21
- **[Feature]** #634 Add item template function
@@ -12,8 +18,6 @@
### 2.0.0 - 2020-11-21 - Winter cleanup 🧹
- **[Breaking]** Drop support for IE6-8
-- **[Website]** Update Jekyll to remove security warnings
-- **[Website]** Fix all examples (sorry that they we're broken)
- **[Misc]** Update dev dependencies to latest version: Webpack 3.12.0 -> 5.6.0, jest 23.3 -> 26.6.3, jquery 3.3.1 -> 3.5.1, Removed: jshint jshint-loader
- **[Misc]** Replace uglify-js with terser
- **[Misc]** Update Node for dev from 6.15 to 14.15.1
@@ -22,14 +26,12 @@
- **[Misc]** Rename History.md to CHANGELOG.md
- **[Misc]** Use `babal-loader` with `@babel/preset-env` for supporting IE9-11
- **[Misc]** Add source-maps to `/dist`
-
-### 1.5.1
-
- **[Misc]** Added WISHLIST.md for feature requests to allow cleanup of issue list.
- **[Misc]** Update CircleCI from 1.0 to 2.0
+- **[Website]** Update Jekyll to remove security warnings
+- **[Website]** Fix all examples (sorry that they we're broken)
- **[Website]** Use https instead of http for listjs.com
- **[Website]** Update Contribute guidelines [See commit →](https://github.com/javve/list.js/commit/6242496de2ac5c07903fb1590a5cb5129f0887a7)
-- **[Website]** Update Jekyll & jQuery versions to remove security warnings.
- **[Bugfix]** Use one event listener per pagination and select page via data attributes
[See commit →](https://github.com/javve/list.js/commit/7610c59039f3b39f52175cd1a200e935664869e8)
- **[Bugfix]** Don't break pagination if page=0
From 5d522e727bb58f58cd0b43410350575895ff45bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Wed, 25 Nov 2020 22:06:28 +0100
Subject: [PATCH 36/42] Refactor templater.get and add unit test
---
__test__/unit/templater.test.js | 33 ++++++++++++++++++++++++++++++++-
src/item.js | 2 +-
src/templater.js | 16 ++++++++--------
3 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/__test__/unit/templater.test.js b/__test__/unit/templater.test.js
index e19ac7d3..3047e9bb 100644
--- a/__test__/unit/templater.test.js
+++ b/__test__/unit/templater.test.js
@@ -161,5 +161,36 @@ describe('Templater', () => {
)
})
})
- describe('get', () => {})
+ describe('get', () => {
+ it('should get all values from element', () => {
+ const itemEl = $(
+ '' +
+ '
Jonny ' +
+ '
1986 ' +
+ '
' +
+ '
' +
+ '
'
+ )[0]
+ const valueNames = [
+ 'name',
+ 'born',
+ { data: ['id'] },
+ { attr: 'src', name: 'image' },
+ { attr: 'href', name: 'link' },
+ { attr: 'value', name: 'foo' },
+ { attr: 'data-timestamp', name: 'timestamp' },
+ ]
+ const templater = Templater({ item: '
', valueNames })
+ const values = templater.get(itemEl)
+ expect(values).toEqual({
+ name: 'Jonny',
+ born: '1986',
+ id: '1',
+ image: 'usage/boba.jpeg',
+ link: 'http://lol.com',
+ foo: 'Bar',
+ timestamp: '54321',
+ })
+ })
+ })
})
diff --git a/src/item.js b/src/item.js
index 942d7768..c62a3d06 100644
--- a/src/item.js
+++ b/src/item.js
@@ -16,7 +16,7 @@ module.exports = function (list) {
}
} else {
item.elm = element
- var values = list.templater.get(item, initValues)
+ var values = list.templater.get(item.elm)
item.values(values)
}
}
diff --git a/src/templater.js b/src/templater.js
index ebe2e654..b5856e9f 100644
--- a/src/templater.js
+++ b/src/templater.js
@@ -1,4 +1,5 @@
var getByClass = require('./utils/get-by-class')
+var getAttribute = require('./utils/get-attribute')
module.exports = function (list) {
var createItem,
@@ -127,21 +128,20 @@ module.exports = function (list) {
}
}
- this.get = function (item, valueNames) {
- templater.create(item)
+ this.get = function (el) {
+ var valueNames = list.valueNames
var values = {}
for (var i = 0, il = valueNames.length; i < il; i++) {
- var elm = undefined,
- valueName = valueNames[i]
+ var valueName = valueNames[i]
if (valueName.data) {
for (var j = 0, jl = valueName.data.length; j < jl; j++) {
- values[valueName.data[j]] = list.utils.getAttribute(item.elm, 'data-' + valueName.data[j])
+ values[valueName.data[j]] = getAttribute(el, 'data-' + valueName.data[j])
}
} else if (valueName.attr && valueName.name) {
- elm = getByClass(item.elm, valueName.name, true)
- values[valueName.name] = elm ? list.utils.getAttribute(elm, valueName.attr) : ''
+ var elm = getByClass(el, valueName.name, true)
+ values[valueName.name] = elm ? getAttribute(elm, valueName.attr) : ''
} else {
- elm = getByClass(item.elm, valueName, true)
+ var elm = getByClass(el, valueName, true)
values[valueName] = elm ? elm.innerHTML : ''
}
}
From b27b1f63355f656c5bfd114d9233b994c13818f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Sat, 28 Nov 2020 20:18:16 +0100
Subject: [PATCH 37/42] Complete decoupling of templater from list
---
__test__/unit/templater.test.js | 83 ++++----
__test__/unit/utils.value-names.test.js | 65 +++++++
src/index.js | 17 +-
src/item.js | 17 +-
src/parse.js | 3 +-
src/search.js | 2 +-
src/templater.js | 239 +++++++++---------------
src/utils/value-names.js | 39 ++++
8 files changed, 258 insertions(+), 207 deletions(-)
create mode 100644 __test__/unit/utils.value-names.test.js
create mode 100644 src/utils/value-names.js
diff --git a/__test__/unit/templater.test.js b/__test__/unit/templater.test.js
index 3047e9bb..d56fd4db 100644
--- a/__test__/unit/templater.test.js
+++ b/__test__/unit/templater.test.js
@@ -1,20 +1,20 @@
const $ = require('jquery')
-const Templater = require('../../src/templater')
+const templater = require('../../src/templater')
describe('Templater', () => {
- describe('init', () => {
- it('should init with item string of a div', () => {
+ describe('getTemplate function', () => {
+ it('should get from string with div', () => {
const item = `Foo
`
const valueNames = ['name']
- const templater = Templater({ item, valueNames })
- const itemEl = templater.createItem()
+ const template = templater.getTemplate({ template: item, valueNames })
+ const itemEl = template()
expect(itemEl.outerHTML).toEqual('
')
})
it('should init with item string of a tr', () => {
const item = `Foo `
const valueNames = ['name']
- const templater = Templater({ item, valueNames })
- const itemEl = templater.createItem()
+ const template = templater.getTemplate({ template: item, valueNames })
+ const itemEl = template()
expect(itemEl.outerHTML).toEqual(' ')
})
it('should init with item function', () => {
@@ -22,8 +22,8 @@ describe('Templater', () => {
return `
`
}
const valueNames = ['name']
- const templater = Templater({ item, valueNames })
- const itemEl = templater.createItem()
+ const template = templater.getTemplate({ template: item, valueNames })
+ const itemEl = template()
expect(itemEl.outerHTML).toEqual('
')
})
it('should init without item', () => {
@@ -34,8 +34,8 @@ describe('Templater', () => {
`)
$(document.body).append(listEl)
const valueNames = ['name']
- const templater = Templater({ valueNames, list: document.querySelector('.list') })
- const itemEl = templater.createItem()
+ const template = templater.getTemplate({ parentEl: document.querySelector('.list'), valueNames })
+ const itemEl = template()
expect(itemEl.outerHTML).toEqual(' ')
})
})
@@ -58,16 +58,20 @@ describe('Templater', () => {
{ attr: 'value', name: 'foo' },
{ attr: 'data-timestamp', name: 'timestamp' },
]
- const templater = Templater({ item, valueNames })
- const itemEl = templater.create({
- name: 'Sven',
- born: 1950,
- id: 4,
- image: 'usage/rey.jpeg',
- link: 'localhost',
- timestamp: '1337',
- foo: 'hej',
- })
+ const template = templater.getTemplate({ template: item, valueNames })
+ const itemEl = templater.create(
+ {
+ name: 'Sven',
+ born: 1950,
+ id: 4,
+ image: 'usage/rey.jpeg',
+ link: 'localhost',
+ timestamp: '1337',
+ foo: 'hej',
+ },
+ valueNames,
+ template
+ )
expect(itemEl.outerHTML).toEqual(
'' +
'
Sven ' +
@@ -88,9 +92,8 @@ describe('Templater', () => {
document.body.appendChild(listEl)
const item = listEl.querySelector('div')
const valueNames = ['name']
- const templater = Templater({ list: listEl, valueNames })
expect(listEl.querySelector('div')).not.toEqual(null)
- templater.remove(item)
+ templater.remove(item, listEl)
expect(listEl.querySelector('div')).toEqual(null)
})
})
@@ -99,10 +102,8 @@ describe('Templater', () => {
const listEl = $(`
`)[0]
document.body.appendChild(listEl)
const item = $('
Foo
')[0]
- const valueNames = ['name']
- const templater = Templater({ list: listEl, valueNames, item: '
' })
expect(listEl.querySelector('div')).toEqual(null)
- templater.show(item)
+ templater.show(item, listEl)
expect(listEl.querySelector('div')).not.toEqual(null)
})
})
@@ -115,10 +116,8 @@ describe('Templater', () => {
`)[0]
document.body.appendChild(listEl)
- const valueNames = ['name']
- const templater = Templater({ list: listEl, valueNames })
expect(listEl.querySelectorAll('div').length).toEqual(2)
- templater.clear()
+ templater.clear(listEl)
expect(listEl.querySelectorAll('div').length).toEqual(0)
})
})
@@ -141,16 +140,19 @@ describe('Templater', () => {
{ attr: 'value', name: 'foo' },
{ attr: 'data-timestamp', name: 'timestamp' },
]
- const templater = Templater({ item: '
', valueNames })
- templater.set(itemEl, {
- name: 'Sven',
- born: 1950,
- id: 4,
- image: 'usage/rey.jpeg',
- link: 'localhost',
- timestamp: '1337',
- foo: 'hej',
- })
+ templater.set(
+ itemEl,
+ {
+ name: 'Sven',
+ born: 1950,
+ id: 4,
+ image: 'usage/rey.jpeg',
+ link: 'localhost',
+ timestamp: '1337',
+ foo: 'hej',
+ },
+ valueNames
+ )
expect(itemEl.outerHTML).toEqual(
'' +
'
Sven ' +
@@ -180,8 +182,7 @@ describe('Templater', () => {
{ attr: 'value', name: 'foo' },
{ attr: 'data-timestamp', name: 'timestamp' },
]
- const templater = Templater({ item: '
', valueNames })
- const values = templater.get(itemEl)
+ const values = templater.get(itemEl, valueNames)
expect(values).toEqual({
name: 'Jonny',
born: '1986',
diff --git a/__test__/unit/utils.value-names.test.js b/__test__/unit/utils.value-names.test.js
new file mode 100644
index 00000000..b48c2b65
--- /dev/null
+++ b/__test__/unit/utils.value-names.test.js
@@ -0,0 +1,65 @@
+const $ = require('jquery')
+const valueNamesUtils = require('../../src/utils/value-names')
+
+describe('Utils / Value Names', () => {
+ let valueNames, itemEl
+
+ beforeEach(() => {
+ valueNames = [
+ 'name',
+ 'born',
+ { data: ['id'] },
+ { attr: 'src', name: 'image' },
+ { attr: 'href', name: 'link' },
+ { attr: 'value', name: 'foo' },
+ { attr: 'data-timestamp', name: 'timestamp' },
+ ]
+ itemEl = $(
+ '
' +
+ '
Jonny ' +
+ '
1986 ' +
+ '
' +
+ '
' +
+ '
'
+ )[0]
+ })
+ describe('getDefinitionFromName', () => {
+ it('should get regular class name', () => {
+ expect(valueNamesUtils.getDefinitionFromName('born', valueNames)).toEqual('born')
+ })
+ it('should get root data attribute', () => {
+ expect(valueNamesUtils.getDefinitionFromName('id', valueNames)).toEqual({ data: 'id' })
+ })
+ it('should get attribute', () => {
+ expect(valueNamesUtils.getDefinitionFromName('link', valueNames)).toEqual({ attr: 'href', name: 'link' })
+ })
+ it('should get data attribute', () => {
+ expect(valueNamesUtils.getDefinitionFromName('timestamp', valueNames)).toEqual({
+ attr: 'data-timestamp',
+ name: 'timestamp',
+ })
+ })
+ })
+ describe('set', () => {
+ it('should set class', () => {
+ valueNamesUtils.set(itemEl, 'born', '1337', valueNames)
+ const born = itemEl.querySelector('.born').innerHTML
+ expect(born).toEqual('1337')
+ })
+ it('should set root data attribute', () => {
+ valueNamesUtils.set(itemEl, 'id', '555', valueNames)
+ const id = itemEl.getAttribute('data-id')
+ expect(id).toEqual('555')
+ })
+ it('should set attribute', () => {
+ valueNamesUtils.set(itemEl, 'link', 'http://foo.se/', valueNames)
+ const link = itemEl.querySelector('a').href
+ expect(link).toEqual('http://foo.se/')
+ })
+ it('should set data attribut', () => {
+ valueNamesUtils.set(itemEl, 'timestamp', 'bar-bar', valueNames)
+ const timestamp = itemEl.querySelector('span').getAttribute('data-timestamp')
+ expect(timestamp).toEqual('bar-bar')
+ })
+ })
+})
diff --git a/src/index.js b/src/index.js
index 9171c458..78754210 100755
--- a/src/index.js
+++ b/src/index.js
@@ -52,7 +52,12 @@ module.exports = function (id, options, values) {
self.list = getByClass(self.listContainer, self.listClass, true)
self.parse = require('./parse')(self)
- self.templater = require('./templater')(self)
+ self.templater = require('./templater')
+ self.template = self.templater.getTemplate({
+ parentEl: self.list,
+ valueNames: self.valueNames,
+ template: self.item,
+ })
self.search = require('./search')(self)
self.filter = require('./filter')(self)
self.sort = require('./sort')(self)
@@ -129,9 +134,7 @@ module.exports = function (id, options, values) {
values = [values]
}
for (var i = 0, il = values.length; i < il; i++) {
- var item = null
- notCreate = self.items.length > self.page ? true : false
- item = new Item(values[i], undefined, notCreate)
+ var item = new Item(values[i], undefined, notCreate)
self.items.push(item)
added.push(item)
}
@@ -154,7 +157,7 @@ module.exports = function (id, options, values) {
var found = 0
for (var i = 0, il = self.items.length; i < il; i++) {
if (self.items[i].values()[valueName] == value) {
- self.templater.remove(self.items[i].elm)
+ self.templater.remove(self.items[i].elm, self.list)
self.items.splice(i, 1)
il--
i--
@@ -190,7 +193,7 @@ module.exports = function (id, options, values) {
* Removes all items from the list
*/
this.clear = function () {
- self.templater.clear()
+ self.templater.clear(self.list)
self.items = []
return self
}
@@ -242,7 +245,7 @@ module.exports = function (id, options, values) {
self.visibleItems = []
self.matchingItems = []
- self.templater.clear()
+ self.templater.clear(self.list)
for (var i = 0; i < il; i++) {
if (is[i].matching() && self.matchingItems.length + 1 >= self.i && self.visibleItems.length < self.page) {
is[i].show()
diff --git a/src/item.js b/src/item.js
index c62a3d06..0d5a264d 100644
--- a/src/item.js
+++ b/src/item.js
@@ -16,8 +16,7 @@ module.exports = function (list) {
}
} else {
item.elm = element
- var values = list.templater.get(item.elm)
- item.values(values)
+ item.values(initValues)
}
}
@@ -26,12 +25,8 @@ module.exports = function (list) {
for (var name in newValues) {
item._values[name] = newValues[name]
}
- if (notCreate !== true) {
- if (item.elm) {
- list.templater.set(item.elm, item.values())
- } else {
- list.templater.create(item.values())
- }
+ if (item.elm) {
+ list.templater.set(item.elm, item.values(), list.valueNames)
}
} else {
return item._values
@@ -40,13 +35,13 @@ module.exports = function (list) {
this.show = function () {
if (!item.elm) {
- item.elm = list.templater.create(item.values())
+ item.elm = list.templater.create(item.values(), list.valueNames, list.template)
}
- list.templater.show(item.elm)
+ list.templater.show(item.elm, list.list)
}
this.hide = function () {
- list.templater.remove(item.elm)
+ list.templater.remove(item.elm, list.list)
}
this.matching = function () {
diff --git a/src/parse.js b/src/parse.js
index 0abe5900..7d7fe561 100644
--- a/src/parse.js
+++ b/src/parse.js
@@ -15,7 +15,8 @@ module.exports = function (list) {
var parse = function (itemElements, valueNames) {
for (var i = 0, il = itemElements.length; i < il; i++) {
- list.items.push(new Item(valueNames, itemElements[i]))
+ var values = list.templater.get(itemElements[i], list.valueNames)
+ list.items.push(new Item(values, itemElements[i]))
}
}
var parseAsync = function (itemElements, valueNames) {
diff --git a/src/search.js b/src/search.js
index 06c2d4b1..f64ae4cc 100755
--- a/src/search.js
+++ b/src/search.js
@@ -4,7 +4,7 @@ module.exports = function (list) {
var prepare = {
resetList: function () {
list.i = 1
- list.templater.clear()
+ list.templater.clear(list.list)
customSearch = undefined
},
setOptions: function (args) {
diff --git a/src/templater.js b/src/templater.js
index b5856e9f..19af827d 100644
--- a/src/templater.js
+++ b/src/templater.js
@@ -1,183 +1,130 @@
var getByClass = require('./utils/get-by-class')
var getAttribute = require('./utils/get-attribute')
+var valueNamesUtils = require('./utils/value-names')
-module.exports = function (list) {
- var createItem,
- templater = this
-
- var init = function () {
- var itemSource
-
- if (typeof list.item === 'function') {
- templater.createItem = function (values) {
- var item = list.item(values)
- return getItemSource(item)
+var createCleanTemplateItem = function (templateNode, valueNames) {
+ var el = templateNode.cloneNode(true)
+ el.removeAttribute('id')
+ for (var i = 0, il = valueNames.length; i < il; i++) {
+ var elm = undefined,
+ valueName = valueNames[i]
+ if (valueName.data) {
+ for (var j = 0, jl = valueName.data.length; j < jl; j++) {
+ el.setAttribute('data-' + valueName.data[j], '')
}
- return
- }
-
- if (typeof list.item === 'string') {
- if (list.item.indexOf('<') === -1) {
- itemSource = document.getElementById(list.item)
- } else {
- itemSource = getItemSource(list.item)
+ } else if (valueName.attr && valueName.name) {
+ elm = getByClass(el, valueName.name, true)
+ if (elm) {
+ elm.setAttribute(valueName.attr, '')
}
} else {
- /* If item source does not exists, use the first item in list as
- source for new items */
- itemSource = getFirstListItem()
- }
-
- if (!itemSource) {
- throw new Error("The list needs to have at least one item on init otherwise you'll have to add a template.")
+ elm = getByClass(el, valueName, true)
+ if (elm) {
+ elm.innerHTML = ''
+ }
}
+ }
+ return el
+}
- itemSource = createCleanTemplateItem(itemSource, list.valueNames)
-
- templater.createItem = function () {
- return itemSource.cloneNode(true)
- }
+var stringToDOMElement = function (itemHTML) {
+ if (typeof itemHTML !== 'string') return undefined
+ if (/
]/g.exec(itemHTML)) {
+ var tbody = document.createElement('tbody')
+ tbody.innerHTML = itemHTML
+ return tbody.firstElementChild
+ } else if (itemHTML.indexOf('<') !== -1) {
+ var div = document.createElement('div')
+ div.innerHTML = itemHTML
+ return div.firstElementChild
}
+ return undefined
+}
- var createCleanTemplateItem = function (templateNode, valueNames) {
- var el = templateNode.cloneNode(true)
- el.removeAttribute('id')
+var templater = {}
- for (var i = 0, il = valueNames.length; i < il; i++) {
- var elm = undefined,
- valueName = valueNames[i]
- if (valueName.data) {
- for (var j = 0, jl = valueName.data.length; j < jl; j++) {
- el.setAttribute('data-' + valueName.data[j], '')
- }
- } else if (valueName.attr && valueName.name) {
- elm = getByClass(el, valueName.name, true)
- if (elm) {
- elm.setAttribute(valueName.attr, '')
- }
- } else {
- elm = getByClass(el, valueName, true)
- if (elm) {
- elm.innerHTML = ''
- }
- }
+templater.getTemplate = function ({ valueNames, parentEl, template }) {
+ if (typeof template === 'function') {
+ return function (values) {
+ var item = template(values)
+ return stringToDOMElement(item)
}
- return el
}
- var getFirstListItem = function () {
- var nodes = list.list.childNodes
-
+ var itemSource
+ if (typeof template === 'string') {
+ if (template.indexOf('<') === -1) {
+ itemSource = document.getElementById(template)
+ } else {
+ itemSource = stringToDOMElement(template)
+ }
+ } else {
+ var nodes = parentEl.childNodes
for (var i = 0, il = nodes.length; i < il; i++) {
// Only textnodes have a data attribute
if (nodes[i].data === undefined) {
- return nodes[i].cloneNode(true)
+ itemSource = nodes[i].cloneNode(true)
+ break
}
}
- return undefined
}
+ if (!itemSource)
+ throw new Error("The list needs to have at least one item on init otherwise you'll have to add a template.")
- var getItemSource = function (itemHTML) {
- if (typeof itemHTML !== 'string') return undefined
- if (/ ]/g.exec(itemHTML)) {
- var tbody = document.createElement('tbody')
- tbody.innerHTML = itemHTML
- return tbody.firstElementChild
- } else if (itemHTML.indexOf('<') !== -1) {
- var div = document.createElement('div')
- div.innerHTML = itemHTML
- return div.firstElementChild
- }
- return undefined
- }
+ itemSource = createCleanTemplateItem(itemSource, valueNames)
- var getValueName = function (name) {
- for (var i = 0, il = list.valueNames.length; i < il; i++) {
- var valueName = list.valueNames[i]
- if (valueName.data) {
- var data = valueName.data
- for (var j = 0, jl = data.length; j < jl; j++) {
- if (data[j] === name) {
- return { data: name }
- }
- }
- } else if (valueName.attr && valueName.name && valueName.name == name) {
- return valueName
- } else if (valueName === name) {
- return name
- }
- }
+ return function () {
+ return itemSource.cloneNode(true)
}
+}
- var setValue = function (el, name, value) {
- var elm = undefined,
- valueName = getValueName(name)
- if (!valueName) return
+templater.get = function (el, valueNames) {
+ var values = {}
+ for (var i = 0, il = valueNames.length; i < il; i++) {
+ var valueName = valueNames[i]
if (valueName.data) {
- el.setAttribute('data-' + valueName.data, value)
- } else if (valueName.attr && valueName.name) {
- elm = getByClass(el, valueName.name, true)
- if (elm) {
- elm.setAttribute(valueName.attr, value)
+ for (var j = 0, jl = valueName.data.length; j < jl; j++) {
+ values[valueName.data[j]] = getAttribute(el, 'data-' + valueName.data[j])
}
+ } else if (valueName.attr && valueName.name) {
+ var elm = getByClass(el, valueName.name, true)
+ values[valueName.name] = elm ? getAttribute(elm, valueName.attr) : ''
} else {
- elm = getByClass(el, valueName, true)
- if (elm) {
- elm.innerHTML = value
- }
- }
- }
-
- this.get = function (el) {
- var valueNames = list.valueNames
- var values = {}
- for (var i = 0, il = valueNames.length; i < il; i++) {
- var valueName = valueNames[i]
- if (valueName.data) {
- for (var j = 0, jl = valueName.data.length; j < jl; j++) {
- values[valueName.data[j]] = getAttribute(el, 'data-' + valueName.data[j])
- }
- } else if (valueName.attr && valueName.name) {
- var elm = getByClass(el, valueName.name, true)
- values[valueName.name] = elm ? getAttribute(elm, valueName.attr) : ''
- } else {
- var elm = getByClass(el, valueName, true)
- values[valueName] = elm ? elm.innerHTML : ''
- }
+ var elm = getByClass(el, valueName, true)
+ values[valueName] = elm ? elm.innerHTML : ''
}
- return values
}
+ return values
+}
- this.set = function (el, values) {
- for (var v in values) {
- if (values.hasOwnProperty(v)) {
- setValue(el, v, values[v])
- }
+templater.set = function (el, values, valueNames) {
+ for (var v in values) {
+ if (values.hasOwnProperty(v)) {
+ valueNamesUtils.set(el, v, values[v], valueNames)
}
}
+}
- this.create = function (values) {
- var elm = templater.createItem(values)
- templater.set(elm, values)
- return elm
- }
- this.remove = function (el) {
- if (el !== undefined && el.parentNode === list.list) {
- list.list.removeChild(el)
- }
- }
- this.show = function (el) {
- list.list.appendChild(el)
+templater.create = function (values, valueNames, template) {
+ var elm = template(values)
+ templater.set(elm, values, valueNames)
+ return elm
+}
+templater.remove = function (el, parentEl) {
+ if (el !== undefined && el.parentNode === parentEl) {
+ parentEl.removeChild(el)
}
- this.clear = function () {
- /* .innerHTML = ''; fucks up IE */
- if (list.list.hasChildNodes()) {
- while (list.list.childNodes.length >= 1) {
- list.list.removeChild(list.list.firstChild)
- }
+}
+templater.show = function (el, parentEl) {
+ parentEl.appendChild(el)
+}
+templater.clear = function (parentEl) {
+ /* .innerHTML = ''; fucks up IE */
+ if (parentEl.hasChildNodes()) {
+ while (parentEl.childNodes.length >= 1) {
+ parentEl.removeChild(parentEl.firstChild)
}
}
-
- init()
- return templater
}
+
+module.exports = templater
diff --git a/src/utils/value-names.js b/src/utils/value-names.js
new file mode 100644
index 00000000..158d8595
--- /dev/null
+++ b/src/utils/value-names.js
@@ -0,0 +1,39 @@
+var getByClass = require('./get-by-class')
+
+const getDefinitionFromName = (name, valueNames) => {
+ for (var i = 0, il = valueNames.length; i < il; i++) {
+ var valueName = valueNames[i]
+ if (valueName.data) {
+ var data = valueName.data
+ for (var j = 0, jl = data.length; j < jl; j++) {
+ if (data[j] === name) {
+ return { data: name }
+ }
+ }
+ } else if (valueName.attr && valueName.name && valueName.name == name) {
+ return valueName
+ } else if (valueName === name) {
+ return name
+ }
+ }
+}
+const set = (el, name, value, valueNames) => {
+ var elm = undefined,
+ valueName = getDefinitionFromName(name, valueNames)
+ if (!valueName) return
+ if (valueName.data) {
+ el.setAttribute('data-' + valueName.data, value)
+ } else if (valueName.attr && valueName.name) {
+ elm = getByClass(el, valueName.name, true)
+ if (elm) {
+ elm.setAttribute(valueName.attr, value)
+ }
+ } else {
+ elm = getByClass(el, valueName, true)
+ if (elm) {
+ elm.innerHTML = value
+ }
+ }
+}
+
+module.exports = { getDefinitionFromName, set }
From 6d7c59088a81a39e25ac54f54d065e4fda94e965 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Sun, 29 Nov 2020 12:15:33 +0100
Subject: [PATCH 38/42] Begin decouple item from list and remove
item.visible/hide/show and change .matching
---
__test__/integration/filter.test.js | 25 ++--
__test__/integration/item.test.js | 46 ++----
__test__/integration/search-filter.test.js | 36 ++---
__test__/integration/search.test.js | 48 +++----
__test__/integration/show.test.js | 157 +++++++++++----------
__test__/unit/templater.test.js | 9 +-
__test__/utils/is-visible.js | 4 +
src/index.js | 23 +--
src/item.js | 33 ++---
src/templater.js | 25 ++--
10 files changed, 195 insertions(+), 211 deletions(-)
create mode 100644 __test__/utils/is-visible.js
diff --git a/__test__/integration/filter.test.js b/__test__/integration/filter.test.js
index 37760e20..78711dc0 100644
--- a/__test__/integration/filter.test.js
+++ b/__test__/integration/filter.test.js
@@ -1,4 +1,5 @@
const fixture = require('./fixtures')
+const isVisible = require('../utils/is-visible')
describe('Filter', function () {
var list, jonny, martina, angelica, sebastian, imma, hasse
@@ -68,34 +69,34 @@ describe('Filter', function () {
})
})
it('should match jonny', function () {
- expect(jonny.matching()).toBe(true)
+ expect(jonny.matching(list)).toBe(true)
expect(jonny.filtered).toBe(true)
- expect(jonny.visible()).toBe(true)
+ expect(isVisible(jonny.elm, list.list)).toBe(true)
})
it('should match martina', function () {
- expect(martina.matching()).toBe(true)
+ expect(martina.matching(list)).toBe(true)
expect(martina.filtered).toBe(true)
- expect(martina.visible()).toBe(true)
+ expect(isVisible(martina.elm, list.list)).toBe(true)
})
it('should match but not show angelica', function () {
- expect(angelica.matching()).toBe(true)
+ expect(angelica.matching(list)).toBe(true)
expect(angelica.filtered).toBe(true)
- expect(angelica.visible()).toBe(false)
+ expect(isVisible(angelica.elm, list.list)).toBe(false)
})
it('should match but not show sebastian', function () {
- expect(sebastian.matching()).toBe(true)
+ expect(sebastian.matching(list)).toBe(true)
expect(sebastian.filtered).toBe(true)
- expect(sebastian.visible()).toBe(false)
+ expect(isVisible(sebastian.elm, list.list)).toBe(false)
})
it('should not match imma', function () {
- expect(imma.matching()).toBe(false)
+ expect(imma.matching(list)).toBe(false)
expect(imma.filtered).toBe(false)
- expect(imma.visible()).toBe(false)
+ expect(isVisible(imma.elm, list.list)).toBe(false)
})
it('should not match hasse', function () {
- expect(hasse.matching()).toBe(false)
+ expect(hasse.matching(list)).toBe(false)
expect(hasse.filtered).toBe(false)
- expect(hasse.visible()).toBe(false)
+ expect(isVisible(hasse.elm, list.list)).toBe(false)
})
})
})
diff --git a/__test__/integration/item.test.js b/__test__/integration/item.test.js
index e1e6c464..07131c51 100644
--- a/__test__/integration/item.test.js
+++ b/__test__/integration/item.test.js
@@ -1,5 +1,6 @@
const $ = require('jquery'),
- fixture = require('./fixtures')
+ fixture = require('./fixtures'),
+ isVisible = require('../utils/is-visible')
describe('Item', function () {
var list, item
@@ -39,11 +40,8 @@ describe('Item', function () {
})
it('should have all default methods', function () {
- expect(item.hide).toBeInstanceOf(Function)
- expect(item.show).toBeInstanceOf(Function)
expect(item.values).toBeInstanceOf(Function)
expect(item.matching).toBeInstanceOf(Function)
- expect(item.visible).toBeInstanceOf(Function)
})
})
@@ -79,44 +77,28 @@ describe('Item', function () {
})
})
- describe('Hide, show, visible', function () {
- it('should be hidden', function () {
- expect($('#list li').length).toEqual(1)
- item.hide()
- expect(item.visible()).toBe(false)
- expect($('#list li').length).toEqual(0)
- })
- it('should be visible', function () {
- item.hide()
- expect($('#list li').length).toEqual(0)
- item.show()
- expect(item.visible()).toBe(true)
- expect($('#list li').length).toEqual(1)
- })
- })
-
describe('Matching, found, filtered', function () {
describe('Searching', function () {
it('should not be visible, match, found or filtered', function () {
list.search('Fredrik')
- expect(item.matching()).toBe(false)
+ expect(item.matching(list)).toBe(false)
expect(item.found).toBe(false)
expect(item.filtered).toBe(false)
- expect(item.visible()).toBe(false)
+ expect(isVisible(item.elm, list.list)).toBe(false)
})
it('should be visble, match and found but not filterd', function () {
var result = list.search('Sven')
- expect(item.matching()).toBe(true)
+ expect(item.matching(list)).toBe(true)
expect(item.found).toBe(true)
expect(item.filtered).toBe(false)
- expect(item.visible()).toBe(true)
+ expect(isVisible(item.elm, list.list)).toBe(true)
})
it('reset: should be visible and matching but not found or filtered', function () {
list.search()
- expect(item.matching()).toBe(true)
+ expect(item.matching(list)).toBe(true)
expect(item.found).toBe(false)
expect(item.filtered).toBe(false)
- expect(item.visible()).toBe(true)
+ expect(isVisible(item.elm, list.list)).toBe(true)
})
})
describe('Filtering', function () {
@@ -124,26 +106,26 @@ describe('Item', function () {
list.filter(function (item) {
return item.values().name == 'Fredrik'
})
- expect(item.matching()).toBe(false)
+ expect(item.matching(list)).toBe(false)
expect(item.found).toBe(false)
expect(item.filtered).toBe(false)
- expect(item.visible()).toBe(false)
+ expect(isVisible(item.elm, list.list)).toBe(false)
})
it('should be visble, match and filtered but not found', function () {
list.filter(function (item) {
return item.values().name == 'Sven'
})
- expect(item.matching()).toBe(true)
+ expect(item.matching(list)).toBe(true)
expect(item.found).toBe(false)
expect(item.filtered).toBe(true)
- expect(item.visible()).toBe(true)
+ expect(isVisible(item.elm, list.list)).toBe(true)
})
it('reset: should be visble and match but not filtered or found', function () {
list.filter()
- expect(item.matching()).toBe(true)
+ expect(item.matching(list)).toBe(true)
expect(item.found).toBe(false)
expect(item.filtered).toBe(false)
- expect(item.visible()).toBe(true)
+ expect(isVisible(item.elm, list.list)).toBe(true)
})
})
})
diff --git a/__test__/integration/search-filter.test.js b/__test__/integration/search-filter.test.js
index 73e7264f..e7f69b60 100644
--- a/__test__/integration/search-filter.test.js
+++ b/__test__/integration/search-filter.test.js
@@ -29,12 +29,12 @@ describe('Search and filter', function () {
return item.values().born == '1986'
})
expect(list.matchingItems.length).toEqual(3)
- expect(jonny.matching()).toBe(true)
- expect(martina.matching()).toBe(true)
- expect(angelica.matching()).toBe(true)
- expect(sebastian.matching()).toBe(false)
- expect(imma.matching()).toBe(false)
- expect(hasse.matching()).toBe(false)
+ expect(jonny.matching(list)).toBe(true)
+ expect(martina.matching(list)).toBe(true)
+ expect(angelica.matching(list)).toBe(true)
+ expect(sebastian.matching(list)).toBe(false)
+ expect(imma.matching(list)).toBe(false)
+ expect(hasse.matching(list)).toBe(false)
})
it('should find everyone born 1986 and containes "ö"', function () {
list.filter(function (item) {
@@ -42,12 +42,12 @@ describe('Search and filter', function () {
})
list.search('ö')
expect(list.matchingItems.length).toEqual(1)
- expect(jonny.matching()).toBe(true)
- expect(martina.matching()).toBe(false)
- expect(angelica.matching()).toBe(false)
- expect(sebastian.matching()).toBe(false)
- expect(imma.matching()).toBe(false)
- expect(hasse.matching()).toBe(false)
+ expect(jonny.matching(list)).toBe(true)
+ expect(martina.matching(list)).toBe(false)
+ expect(angelica.matching(list)).toBe(false)
+ expect(sebastian.matching(list)).toBe(false)
+ expect(imma.matching(list)).toBe(false)
+ expect(hasse.matching(list)).toBe(false)
})
it('should find everyone with a "ö"', function () {
list.filter(function (item) {
@@ -56,12 +56,12 @@ describe('Search and filter', function () {
list.search('ö')
list.filter()
expect(list.matchingItems.length).toEqual(4)
- expect(jonny.matching()).toBe(true)
- expect(martina.matching()).toBe(false)
- expect(angelica.matching()).toBe(false)
- expect(sebastian.matching()).toBe(true)
- expect(imma.matching()).toBe(true)
- expect(hasse.matching()).toBe(true)
+ expect(jonny.matching(list)).toBe(true)
+ expect(martina.matching(list)).toBe(false)
+ expect(angelica.matching(list)).toBe(false)
+ expect(sebastian.matching(list)).toBe(true)
+ expect(imma.matching(list)).toBe(true)
+ expect(hasse.matching(list)).toBe(true)
})
})
})
diff --git a/__test__/integration/search.test.js b/__test__/integration/search.test.js
index ec2abc7e..b2af0e62 100755
--- a/__test__/integration/search.test.js
+++ b/__test__/integration/search.test.js
@@ -30,22 +30,22 @@ describe('Search', function () {
it('should find jonny, martina, angelice', function () {
var result = list.search('1986')
expect(result.length).toEqual(3) // 3!!
- expect(jonny.matching()).toBe(true)
- expect(martina.matching()).toBe(true)
- expect(angelica.matching()).toBe(true)
- expect(sebastian.matching()).toBe(false)
- expect(imma.matching()).toBe(false)
- expect(hasse.matching()).toBe(false)
+ expect(jonny.matching(list)).toBe(true)
+ expect(martina.matching(list)).toBe(true)
+ expect(angelica.matching(list)).toBe(true)
+ expect(sebastian.matching(list)).toBe(false)
+ expect(imma.matching(list)).toBe(false)
+ expect(hasse.matching(list)).toBe(false)
})
it('should find all with utf-8 char ö', function () {
var result = list.search('ö')
expect(result.length).toEqual(4) // 4!!
- expect(jonny.matching()).toBe(true)
- expect(martina.matching()).toBe(false)
- expect(angelica.matching()).toBe(false)
- expect(sebastian.matching()).toBe(true)
- expect(imma.matching()).toBe(true)
- expect(hasse.matching()).toBe(true)
+ expect(jonny.matching(list)).toBe(true)
+ expect(martina.matching(list)).toBe(false)
+ expect(angelica.matching(list)).toBe(false)
+ expect(sebastian.matching(list)).toBe(true)
+ expect(imma.matching(list)).toBe(true)
+ expect(hasse.matching(list)).toBe(true)
})
it('should not break with weird searches', function () {
expect(function () {
@@ -159,22 +159,22 @@ describe('Search', function () {
it('should find jonny, hasse', function () {
var result = list.search('berg str')
expect(result.length).toEqual(2)
- expect(jonny.matching()).toBe(true)
- expect(martina.matching()).toBe(false)
- expect(angelica.matching()).toBe(false)
- expect(sebastian.matching()).toBe(false)
- expect(imma.matching()).toBe(false)
- expect(hasse.matching()).toBe(true)
+ expect(jonny.matching(list)).toBe(true)
+ expect(martina.matching(list)).toBe(false)
+ expect(angelica.matching(list)).toBe(false)
+ expect(sebastian.matching(list)).toBe(false)
+ expect(imma.matching(list)).toBe(false)
+ expect(hasse.matching(list)).toBe(true)
})
it('should find martina, angelica, sebastian, hasse', function () {
var result = list.search('a e')
expect(result.length).toEqual(4)
- expect(jonny.matching()).toBe(false)
- expect(martina.matching()).toBe(true)
- expect(angelica.matching()).toBe(true)
- expect(sebastian.matching()).toBe(true)
- expect(imma.matching()).toBe(false)
- expect(hasse.matching()).toBe(true)
+ expect(jonny.matching(list)).toBe(false)
+ expect(martina.matching(list)).toBe(true)
+ expect(angelica.matching(list)).toBe(true)
+ expect(sebastian.matching(list)).toBe(true)
+ expect(imma.matching(list)).toBe(false)
+ expect(hasse.matching(list)).toBe(true)
})
it('stripping whitespace should find martina', function () {
var result = list.search('martina elm ')
diff --git a/__test__/integration/show.test.js b/__test__/integration/show.test.js
index 60fa41cb..d32dcf46 100644
--- a/__test__/integration/show.test.js
+++ b/__test__/integration/show.test.js
@@ -1,4 +1,5 @@
const fixture = require('./fixtures')
+const isVisible = require('../utils/is-visible')
describe('Show', function () {
var list, a, b, c, d, e, f
@@ -36,52 +37,52 @@ describe('Show', function () {
it('should be 1, 2', function () {
list.show(1, 2)
expect(list.visibleItems.length).toEqual(2)
- expect(a.visible()).toBe(true)
- expect(b.visible()).toBe(true)
- expect(c.visible()).toBe(false)
- expect(d.visible()).toBe(false)
- expect(e.visible()).toBe(false)
- expect(f.visible()).toBe(false)
+ expect(isVisible(a.elm, list.list)).toBe(true)
+ expect(isVisible(b.elm, list.list)).toBe(true)
+ expect(isVisible(c.elm, list.list)).toBe(false)
+ expect(isVisible(d.elm, list.list)).toBe(false)
+ expect(isVisible(e.elm, list.list)).toBe(false)
+ expect(isVisible(f.elm, list.list)).toBe(false)
})
it('should show item 6', function () {
list.show(6, 2)
expect(list.visibleItems.length).toEqual(1)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(false)
- expect(d.visible()).toBe(false)
- expect(e.visible()).toBe(false)
- expect(f.visible()).toBe(true)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(false)
+ expect(isVisible(d.elm, list.list)).toBe(false)
+ expect(isVisible(e.elm, list.list)).toBe(false)
+ expect(isVisible(f.elm, list.list)).toBe(true)
})
it('should show item 1, 2, 3, 4, 5, 6', function () {
list.show(1, 200)
expect(list.visibleItems.length).toEqual(6)
- expect(a.visible()).toBe(true)
- expect(b.visible()).toBe(true)
- expect(c.visible()).toBe(true)
- expect(d.visible()).toBe(true)
- expect(e.visible()).toBe(true)
- expect(f.visible()).toBe(true)
+ expect(isVisible(a.elm, list.list)).toBe(true)
+ expect(isVisible(b.elm, list.list)).toBe(true)
+ expect(isVisible(c.elm, list.list)).toBe(true)
+ expect(isVisible(d.elm, list.list)).toBe(true)
+ expect(isVisible(e.elm, list.list)).toBe(true)
+ expect(isVisible(f.elm, list.list)).toBe(true)
})
it('should show item 3, 4, 5', function () {
list.show(3, 3)
expect(list.visibleItems.length).toEqual(3)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(true)
- expect(d.visible()).toBe(true)
- expect(e.visible()).toBe(true)
- expect(f.visible()).toBe(false)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(true)
+ expect(isVisible(d.elm, list.list)).toBe(true)
+ expect(isVisible(e.elm, list.list)).toBe(true)
+ expect(isVisible(f.elm, list.list)).toBe(false)
})
it('should show item 5, 6', function () {
list.show(5, 3)
expect(list.visibleItems.length).toEqual(2)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(false)
- expect(d.visible()).toBe(false)
- expect(e.visible()).toBe(true)
- expect(f.visible()).toBe(true)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(false)
+ expect(isVisible(d.elm, list.list)).toBe(false)
+ expect(isVisible(e.elm, list.list)).toBe(true)
+ expect(isVisible(f.elm, list.list)).toBe(true)
})
})
@@ -93,35 +94,35 @@ describe('Show', function () {
list.search('b')
list.show(1, 2)
expect(list.visibleItems.length).toEqual(2)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(true)
- expect(d.visible()).toBe(true)
- expect(e.visible()).toBe(false)
- expect(f.visible()).toBe(false)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(true)
+ expect(isVisible(d.elm, list.list)).toBe(true)
+ expect(isVisible(e.elm, list.list)).toBe(false)
+ expect(isVisible(f.elm, list.list)).toBe(false)
})
it('should show item 3,4,5,6', function () {
list.search('b')
list.show(1, 4)
expect(list.visibleItems.length).toEqual(4)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(true)
- expect(d.visible()).toBe(true)
- expect(e.visible()).toBe(true)
- expect(f.visible()).toBe(true)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(true)
+ expect(isVisible(d.elm, list.list)).toBe(true)
+ expect(isVisible(e.elm, list.list)).toBe(true)
+ expect(isVisible(f.elm, list.list)).toBe(true)
})
it('should not show any items but match two', function () {
list.search('a')
list.show(3, 2)
expect(list.visibleItems.length).toEqual(0)
expect(list.matchingItems.length).toEqual(2)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(false)
- expect(d.visible()).toBe(false)
- expect(e.visible()).toBe(false)
- expect(f.visible()).toBe(false)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(false)
+ expect(isVisible(d.elm, list.list)).toBe(false)
+ expect(isVisible(e.elm, list.list)).toBe(false)
+ expect(isVisible(f.elm, list.list)).toBe(false)
})
})
@@ -136,12 +137,12 @@ describe('Show', function () {
list.show(1, 2)
expect(list.visibleItems.length).toEqual(2)
expect(list.matchingItems.length).toEqual(2)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(true)
- expect(d.visible()).toBe(true)
- expect(e.visible()).toBe(false)
- expect(f.visible()).toBe(false)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(true)
+ expect(isVisible(d.elm, list.list)).toBe(true)
+ expect(isVisible(e.elm, list.list)).toBe(false)
+ expect(isVisible(f.elm, list.list)).toBe(false)
})
it('should show item 3,4,5,6', function () {
list.filter(function (item) {
@@ -150,12 +151,12 @@ describe('Show', function () {
list.show(1, 4)
expect(list.visibleItems.length).toEqual(2)
expect(list.matchingItems.length).toEqual(2)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(false)
- expect(d.visible()).toBe(false)
- expect(e.visible()).toBe(true)
- expect(f.visible()).toBe(true)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(false)
+ expect(isVisible(d.elm, list.list)).toBe(false)
+ expect(isVisible(e.elm, list.list)).toBe(true)
+ expect(isVisible(f.elm, list.list)).toBe(true)
})
it('should not show any items but match two', function () {
list.filter(function (item) {
@@ -164,12 +165,12 @@ describe('Show', function () {
list.show(3, 2)
expect(list.visibleItems.length).toEqual(0)
expect(list.matchingItems.length).toEqual(2)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(false)
- expect(d.visible()).toBe(false)
- expect(e.visible()).toBe(false)
- expect(f.visible()).toBe(false)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(false)
+ expect(isVisible(d.elm, list.list)).toBe(false)
+ expect(isVisible(e.elm, list.list)).toBe(false)
+ expect(isVisible(f.elm, list.list)).toBe(false)
})
})
@@ -185,12 +186,12 @@ describe('Show', function () {
list.search('b')
expect(list.visibleItems.length).toEqual(2)
expect(list.matchingItems.length).toEqual(3)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(false)
- expect(d.visible()).toBe(true)
- expect(e.visible()).toBe(true)
- expect(f.visible()).toBe(false)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(false)
+ expect(isVisible(d.elm, list.list)).toBe(true)
+ expect(isVisible(e.elm, list.list)).toBe(true)
+ expect(isVisible(f.elm, list.list)).toBe(false)
})
it('should show 5, 6', function () {
list.show(1, 2)
@@ -201,12 +202,12 @@ describe('Show', function () {
list.show(2, 2)
expect(list.visibleItems.length).toEqual(2)
expect(list.matchingItems.length).toEqual(3)
- expect(a.visible()).toBe(false)
- expect(b.visible()).toBe(false)
- expect(c.visible()).toBe(false)
- expect(d.visible()).toBe(false)
- expect(e.visible()).toBe(true)
- expect(f.visible()).toBe(true)
+ expect(isVisible(a.elm, list.list)).toBe(false)
+ expect(isVisible(b.elm, list.list)).toBe(false)
+ expect(isVisible(c.elm, list.list)).toBe(false)
+ expect(isVisible(d.elm, list.list)).toBe(false)
+ expect(isVisible(e.elm, list.list)).toBe(true)
+ expect(isVisible(f.elm, list.list)).toBe(true)
})
})
})
diff --git a/__test__/unit/templater.test.js b/__test__/unit/templater.test.js
index d56fd4db..f717f95c 100644
--- a/__test__/unit/templater.test.js
+++ b/__test__/unit/templater.test.js
@@ -7,14 +7,14 @@ describe('Templater', () => {
const item = `Foo
`
const valueNames = ['name']
const template = templater.getTemplate({ template: item, valueNames })
- const itemEl = template()
+ const itemEl = template.render()
expect(itemEl.outerHTML).toEqual('
')
})
it('should init with item string of a tr', () => {
const item = `Foo `
const valueNames = ['name']
const template = templater.getTemplate({ template: item, valueNames })
- const itemEl = template()
+ const itemEl = template.render()
expect(itemEl.outerHTML).toEqual(' ')
})
it('should init with item function', () => {
@@ -23,7 +23,7 @@ describe('Templater', () => {
}
const valueNames = ['name']
const template = templater.getTemplate({ template: item, valueNames })
- const itemEl = template()
+ const itemEl = template.render()
expect(itemEl.outerHTML).toEqual('
')
})
it('should init without item', () => {
@@ -35,7 +35,7 @@ describe('Templater', () => {
$(document.body).append(listEl)
const valueNames = ['name']
const template = templater.getTemplate({ parentEl: document.querySelector('.list'), valueNames })
- const itemEl = template()
+ const itemEl = template.render()
expect(itemEl.outerHTML).toEqual(' ')
})
})
@@ -69,7 +69,6 @@ describe('Templater', () => {
timestamp: '1337',
foo: 'hej',
},
- valueNames,
template
)
expect(itemEl.outerHTML).toEqual(
diff --git a/__test__/utils/is-visible.js b/__test__/utils/is-visible.js
new file mode 100644
index 00000000..dd00b255
--- /dev/null
+++ b/__test__/utils/is-visible.js
@@ -0,0 +1,4 @@
+module.exports = function (el, parent) {
+ if (!el) return false
+ return el && el.parentNode == parent
+}
diff --git a/src/index.js b/src/index.js
index 78754210..b037d610 100755
--- a/src/index.js
+++ b/src/index.js
@@ -6,7 +6,8 @@ var naturalSort = require('string-natural-compare'),
toString = require('./utils/to-string'),
classes = require('./utils/classes'),
getAttribute = require('./utils/get-attribute'),
- toArray = require('./utils/to-array')
+ toArray = require('./utils/to-array'),
+ templater = require('./templater')
module.exports = function (id, options, values) {
var self = this,
@@ -52,7 +53,7 @@ module.exports = function (id, options, values) {
self.list = getByClass(self.listContainer, self.listClass, true)
self.parse = require('./parse')(self)
- self.templater = require('./templater')
+ self.templater = templater
self.template = self.templater.getTemplate({
parentEl: self.list,
valueNames: self.valueNames,
@@ -128,13 +129,12 @@ module.exports = function (id, options, values) {
addAsync(values.slice(0), callback)
return
}
- var added = [],
- notCreate = false
+ var added = []
if (values[0] === undefined) {
values = [values]
}
for (var i = 0, il = values.length; i < il; i++) {
- var item = new Item(values[i], undefined, notCreate)
+ var item = new Item(values[i])
self.items.push(item)
added.push(item)
}
@@ -247,15 +247,18 @@ module.exports = function (id, options, values) {
self.matchingItems = []
self.templater.clear(self.list)
for (var i = 0; i < il; i++) {
- if (is[i].matching() && self.matchingItems.length + 1 >= self.i && self.visibleItems.length < self.page) {
- is[i].show()
+ if (is[i].matching(self) && self.matchingItems.length + 1 >= self.i && self.visibleItems.length < self.page) {
+ if (!is[i].elm) {
+ is[i].elm = templater.create(is[i].values(), self.template)
+ }
+ templater.show(is[i].elm, self.list)
self.visibleItems.push(is[i])
self.matchingItems.push(is[i])
- } else if (is[i].matching()) {
+ } else if (is[i].matching(self)) {
self.matchingItems.push(is[i])
- is[i].hide()
+ templater.remove(is[i].elm, self.list)
} else {
- is[i].hide()
+ templater.remove(is[i].elm, self.list)
}
}
self.trigger('updated')
diff --git a/src/item.js b/src/item.js
index 0d5a264d..2158ce9a 100644
--- a/src/item.js
+++ b/src/item.js
@@ -1,11 +1,13 @@
+const templater = require('./templater')
+
module.exports = function (list) {
return function (initValues, element, notCreate) {
var item = this
this._values = {}
- this.found = false // Show if list.searched == true and this.found == true
- this.filtered = false // Show if list.filtered == true and this.filtered == true
+ this.found = false
+ this.filtered = false
var init = function (initValues, element, notCreate) {
if (element === undefined) {
@@ -26,37 +28,22 @@ module.exports = function (list) {
item._values[name] = newValues[name]
}
if (item.elm) {
- list.templater.set(item.elm, item.values(), list.valueNames)
+ templater.set(item.elm, item.values(), list.valueNames)
}
} else {
return item._values
}
}
- this.show = function () {
- if (!item.elm) {
- item.elm = list.templater.create(item.values(), list.valueNames, list.template)
- }
- list.templater.show(item.elm, list.list)
- }
-
- this.hide = function () {
- list.templater.remove(item.elm, list.list)
- }
-
- this.matching = function () {
+ this.matching = function ({ searched, filtered }) {
return (
- (list.filtered && list.searched && item.found && item.filtered) ||
- (list.filtered && !list.searched && item.filtered) ||
- (!list.filtered && list.searched && item.found) ||
- (!list.filtered && !list.searched)
+ (filtered && searched && item.found && item.filtered) ||
+ (filtered && !searched && item.filtered) ||
+ (!filtered && searched && item.found) ||
+ (!filtered && !searched)
)
}
- this.visible = function () {
- return item.elm && item.elm.parentNode == list.list ? true : false
- }
-
init(initValues, element, notCreate)
}
}
diff --git a/src/templater.js b/src/templater.js
index 19af827d..a0e40bed 100644
--- a/src/templater.js
+++ b/src/templater.js
@@ -45,9 +45,13 @@ var templater = {}
templater.getTemplate = function ({ valueNames, parentEl, template }) {
if (typeof template === 'function') {
- return function (values) {
- var item = template(values)
- return stringToDOMElement(item)
+ return {
+ valueNames,
+ type: 'dynamic',
+ render: function (values) {
+ var item = template(values)
+ return stringToDOMElement(item)
+ },
}
}
@@ -73,8 +77,13 @@ templater.getTemplate = function ({ valueNames, parentEl, template }) {
itemSource = createCleanTemplateItem(itemSource, valueNames)
- return function () {
- return itemSource.cloneNode(true)
+ return {
+ valueNames,
+ render: function (values) {
+ var el = itemSource.cloneNode(true)
+ templater.set(el, values, valueNames)
+ return el
+ },
}
}
@@ -105,10 +114,8 @@ templater.set = function (el, values, valueNames) {
}
}
-templater.create = function (values, valueNames, template) {
- var elm = template(values)
- templater.set(elm, values, valueNames)
- return elm
+templater.create = function (values, template) {
+ return template.render(values)
}
templater.remove = function (el, parentEl) {
if (el !== undefined && el.parentNode === parentEl) {
From 5500a5c9204c8cbd80dbb6e55e67e8232fc9d626 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Sun, 29 Nov 2020 16:47:49 +0100
Subject: [PATCH 39/42] Complete decoupling of list from item
---
src/index.js | 6 ++---
src/item.js | 68 +++++++++++++++++++++++-----------------------------
src/parse.js | 6 ++---
3 files changed, 36 insertions(+), 44 deletions(-)
diff --git a/src/index.js b/src/index.js
index b037d610..ee43dd0c 100755
--- a/src/index.js
+++ b/src/index.js
@@ -7,12 +7,12 @@ var naturalSort = require('string-natural-compare'),
classes = require('./utils/classes'),
getAttribute = require('./utils/get-attribute'),
toArray = require('./utils/to-array'),
- templater = require('./templater')
+ templater = require('./templater'),
+ Item = require('./item')
module.exports = function (id, options, values) {
var self = this,
init,
- Item = require('./item')(self),
addAsync = require('./add-async')(self),
initPagination = require('./pagination')(self)
@@ -134,7 +134,7 @@ module.exports = function (id, options, values) {
values = [values]
}
for (var i = 0, il = values.length; i < il; i++) {
- var item = new Item(values[i])
+ var item = new Item(values[i], { template: self.template })
self.items.push(item)
added.push(item)
}
diff --git a/src/item.js b/src/item.js
index 2158ce9a..2e29847e 100644
--- a/src/item.js
+++ b/src/item.js
@@ -1,49 +1,41 @@
const templater = require('./templater')
-module.exports = function (list) {
- return function (initValues, element, notCreate) {
- var item = this
+module.exports = function (initValues, { element, template } = {}) {
+ var item = this
- this._values = {}
+ this._values = {}
- this.found = false
- this.filtered = false
+ this.found = false
+ this.filtered = false
- var init = function (initValues, element, notCreate) {
- if (element === undefined) {
- if (notCreate) {
- item.values(initValues, notCreate)
- } else {
- item.values(initValues)
- }
- } else {
- item.elm = element
- item.values(initValues)
- }
- }
+ var init = function (values, { element, template } = {}) {
+ if (element) item.elm = element
+ if (!template) throw new Error('no tempalte!')
+ item.template = template
+ item.values(values)
+ }
- this.values = function (newValues, notCreate) {
- if (newValues !== undefined) {
- for (var name in newValues) {
- item._values[name] = newValues[name]
- }
- if (item.elm) {
- templater.set(item.elm, item.values(), list.valueNames)
- }
- } else {
- return item._values
+ this.values = function (newValues) {
+ if (newValues !== undefined) {
+ for (var name in newValues) {
+ item._values[name] = newValues[name]
}
+ if (item.elm) {
+ templater.set(item.elm, item.values(), item.template.valueNames)
+ }
+ } else {
+ return item._values
}
+ }
- this.matching = function ({ searched, filtered }) {
- return (
- (filtered && searched && item.found && item.filtered) ||
- (filtered && !searched && item.filtered) ||
- (!filtered && searched && item.found) ||
- (!filtered && !searched)
- )
- }
-
- init(initValues, element, notCreate)
+ this.matching = function ({ searched, filtered }) {
+ return (
+ (filtered && searched && item.found && item.filtered) ||
+ (filtered && !searched && item.filtered) ||
+ (!filtered && searched && item.found) ||
+ (!filtered && !searched)
+ )
}
+
+ init(initValues, { element, template })
}
diff --git a/src/parse.js b/src/parse.js
index 7d7fe561..ab666589 100644
--- a/src/parse.js
+++ b/src/parse.js
@@ -1,6 +1,6 @@
-module.exports = function (list) {
- var Item = require('./item')(list)
+var Item = require('./item')
+module.exports = function (list) {
var getChildren = function (parent) {
var nodes = parent.childNodes,
items = []
@@ -16,7 +16,7 @@ module.exports = function (list) {
var parse = function (itemElements, valueNames) {
for (var i = 0, il = itemElements.length; i < il; i++) {
var values = list.templater.get(itemElements[i], list.valueNames)
- list.items.push(new Item(values, itemElements[i]))
+ list.items.push(new Item(values, { element: itemElements[i], template: list.template }))
}
}
var parseAsync = function (itemElements, valueNames) {
From 238b9001a3e19935ca36295bf184bc9ee23c01f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Mon, 30 Nov 2020 18:13:09 +0100
Subject: [PATCH 40/42] Add unit tests for Item
---
__test__/unit/item.test.js | 102 +++++++++++++++++++++++++++++++++++++
src/item.js | 2 +-
2 files changed, 103 insertions(+), 1 deletion(-)
create mode 100644 __test__/unit/item.test.js
diff --git a/__test__/unit/item.test.js b/__test__/unit/item.test.js
new file mode 100644
index 00000000..e4f1ce15
--- /dev/null
+++ b/__test__/unit/item.test.js
@@ -0,0 +1,102 @@
+const $ = require('jquery')
+
+const Item = require('../../src/item')
+const templater = require('../../src/templater')
+
+describe('item', function () {
+ beforeAll(() => {
+ this.templateParams = {
+ template: '
',
+ valueNames: ['name'],
+ }
+ })
+ describe('init', () => {
+ it('should throw error if missing template', () => {
+ expect(() => {
+ new Item({ name: 'Jonny' })
+ }).toThrowError('missing_item_template')
+ })
+ })
+ describe('create', () => {
+ it('should create item with only values', () => {
+ const template = templater.getTemplate(this.templateParams)
+ const item = new Item({ name: 'Jonny' }, { template })
+ expect(item._values).toEqual({ name: 'Jonny' })
+ expect(item.elm).toBeUndefined()
+ })
+ it('should create item with values and element', () => {
+ const template = templater.getTemplate(this.templateParams)
+ const element = document.createElement('div')
+ const item = new Item({ name: 'Jonny' }, { element, template })
+ expect(item._values).toEqual({ name: 'Jonny' })
+ expect(item.elm).not.toBeUndefined()
+ expect(item.elm).toBe(element)
+ })
+ })
+ describe('get', () => {
+ it('should get values', () => {
+ const template = templater.getTemplate(this.templateParams)
+ const item = new Item({ name: 'Jonny' }, { template })
+ expect(item.values()).toEqual({ name: 'Jonny' })
+ })
+ })
+ describe('set', () => {
+ it('should set values on item without element', () => {
+ const template = templater.getTemplate(this.templateParams)
+ const item = new Item({ name: 'Jonny' }, { template })
+ item.values({ name: 'Anna' })
+ expect(item.elm).toBeUndefined()
+ expect(item._values.name).toEqual('Anna')
+ })
+ it('should set values on item with element', () => {
+ const template = templater.getTemplate(this.templateParams)
+ const element = $('Jonny
')[0]
+ const item = new Item({ name: 'Jonny' }, { element, template })
+ expect(item.elm.querySelector('span').innerHTML).toEqual('Jonny')
+ item.values({ name: 'Anna' })
+ expect(item._values.name).toEqual('Anna')
+ expect(item.elm.querySelector('span').innerHTML).toEqual('Anna')
+ })
+ })
+ describe('matching', () => {
+ beforeAll(() => {
+ const template = templater.getTemplate(this.templateParams)
+ this.item = new Item({ name: 'Jonny' }, { template })
+ })
+ it('should handle not found or filtered', () => {
+ const item = this.item
+ item.found = false
+ item.filtered = false
+ expect(item.matching({ searched: false, filtered: false })).toEqual(true)
+ expect(item.matching({ searched: true, filtered: false })).toEqual(false)
+ expect(item.matching({ searched: true, filtered: false })).toEqual(false)
+ })
+ it('should handle found and not filtered', () => {
+ const item = this.item
+ item.found = true
+ item.filtered = false
+ expect(item.matching({ searched: false, filtered: false })).toEqual(true)
+ expect(item.matching({ searched: true, filtered: false })).toEqual(true)
+ expect(item.matching({ searched: true, filtered: true })).toEqual(false)
+ expect(item.matching({ searched: false, filtered: true })).toEqual(false)
+ })
+ it('should handle not found but filtered', () => {
+ const item = this.item
+ item.found = false
+ item.filtered = true
+ expect(item.matching({ searched: false, filtered: false })).toEqual(true)
+ expect(item.matching({ searched: true, filtered: false })).toEqual(false)
+ expect(item.matching({ searched: true, filtered: true })).toEqual(false)
+ expect(item.matching({ searched: false, filtered: true })).toEqual(true)
+ })
+ it('should handle both found and filtered', () => {
+ const item = this.item
+ item.found = true
+ item.filtered = true
+ expect(item.matching({ searched: false, filtered: false })).toEqual(true)
+ expect(item.matching({ searched: true, filtered: false })).toEqual(true)
+ expect(item.matching({ searched: true, filtered: true })).toEqual(true)
+ expect(item.matching({ searched: false, filtered: true })).toEqual(true)
+ })
+ })
+})
diff --git a/src/item.js b/src/item.js
index 2e29847e..9d7628af 100644
--- a/src/item.js
+++ b/src/item.js
@@ -10,7 +10,7 @@ module.exports = function (initValues, { element, template } = {}) {
var init = function (values, { element, template } = {}) {
if (element) item.elm = element
- if (!template) throw new Error('no tempalte!')
+ if (!template) throw new Error('missing_item_template')
item.template = template
item.values(values)
}
From d538d091cc187725f921dc81f48e747381ec4977 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Thu, 17 Dec 2020 00:29:22 +0100
Subject: [PATCH 41/42] Update string-natural-compare from 2.0.2 to 3.0.1
---
__test__/integration/sort.test.js | 10 +++-------
package-lock.json | 6 +++---
package.json | 2 +-
src/sort.js | 16 +++++++++++-----
4 files changed, 18 insertions(+), 16 deletions(-)
diff --git a/__test__/integration/sort.test.js b/__test__/integration/sort.test.js
index c30503f8..bcbcdd1c 100644
--- a/__test__/integration/sort.test.js
+++ b/__test__/integration/sort.test.js
@@ -286,24 +286,20 @@ describe('Sort', function () {
expect(list.items[11].values().val).toBe('z')
})
- it('should handle not longer (since 1.4.0) space and zero the same for desc and asc', function () {
+ it('should handle space and zero the same for desc and asc (worked 1.4.0 - 2.3.0 then string-natural-compare was updated)', function () {
list.clear()
list.add({ val: '' })
list.add({ val: '0' })
- list.add({ val: 0 })
list.sort('val', { order: 'asc' })
expect(list.items[0].values().val).toBe('')
expect(list.items[1].values().val).toBe('0')
- expect(list.items[2].values().val).toBe(0)
list.sort('val', { order: 'desc' })
- expect(list.items[0].values().val).toBe('0')
- expect(list.items[1].values().val).toBe(0)
- expect(list.items[2].values().val).toBe('')
+ expect(list.items[0].values().val).toBe('')
+ expect(list.items[1].values().val).toBe('0')
list.sort('val', { order: 'asc' })
expect(list.items[0].values().val).toBe('')
expect(list.items[1].values().val).toBe('0')
- expect(list.items[2].values().val).toBe(0)
})
})
diff --git a/package-lock.json b/package-lock.json
index fe1950dc..75cda77e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6529,9 +6529,9 @@
}
},
"string-natural-compare": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-2.0.3.tgz",
- "integrity": "sha512-4Kcl12rNjc+6EKhY8QyDVuQTAlMWwRiNbsxnVwBUKFr7dYPQuXVrtNU4sEkjF9LHY0AY6uVbB3ktbkIH4LC+BQ=="
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz",
+ "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw=="
},
"strip-bom": {
"version": "4.0.0",
diff --git a/package.json b/package.json
index ddf8dadb..29edf624 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"url": "https://github.com/javve/list.js/issues"
},
"dependencies": {
- "string-natural-compare": "^2.0.2"
+ "string-natural-compare": "^3.0.1"
},
"devDependencies": {
"@babel/core": "^7.12.7",
diff --git a/src/sort.js b/src/sort.js
index 4e4b415f..1020d0c7 100644
--- a/src/sort.js
+++ b/src/sort.js
@@ -77,12 +77,18 @@ module.exports = function (list) {
}
} else {
sortFunction = function (itemA, itemB) {
- var sort = list.utils.naturalSort
- sort.alphabet = list.alphabet || options.alphabet || undefined
- if (!sort.alphabet && options.insensitive) {
- sort = list.utils.naturalSort.caseInsensitive
+ var naturalSortOptions = {}
+ naturalSortOptions.alphabet = list.alphabet || options.alphabet || undefined
+ if (!naturalSortOptions.alphabet && options.insensitive) {
+ naturalSortOptions.caseInsensitive = true
}
- return sort(itemA.values()[options.valueName], itemB.values()[options.valueName]) * multi
+ return (
+ list.utils.naturalSort(
+ '' + itemA.values()[options.valueName],
+ '' + itemB.values()[options.valueName],
+ naturalSortOptions
+ ) * multi
+ )
}
}
From ae7e92962e3378f6ad9dbd25948190cbd1685942 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonny=20Str=C3=B6mberg?=
Date: Thu, 17 Dec 2020 00:32:47 +0100
Subject: [PATCH 42/42] Refactor sort and sort listeners + add unit tests
---
__test__/unit/sort-buttons.test.js | 210 ++++++++++++++++++++++++++
__test__/unit/sort.test.js | 230 +++++++++++++++++++++++++++++
src/index.js | 43 +++++-
src/sort-buttons.js | 75 ++++++++++
src/sort.js | 131 ++++------------
5 files changed, 581 insertions(+), 108 deletions(-)
create mode 100644 __test__/unit/sort-buttons.test.js
create mode 100644 __test__/unit/sort.test.js
create mode 100644 src/sort-buttons.js
diff --git a/__test__/unit/sort-buttons.test.js b/__test__/unit/sort-buttons.test.js
new file mode 100644
index 00000000..5666f293
--- /dev/null
+++ b/__test__/unit/sort-buttons.test.js
@@ -0,0 +1,210 @@
+const naturalSort = require('string-natural-compare')
+const $ = require('jquery')
+
+const Item = require('../../src/item')
+const templater = require('../../src/templater')
+const {
+ addSortListeners,
+ getInSensitive,
+ getNextSortOrder,
+ setSortOrder,
+ clearSortOrder,
+} = require('../../src/sort-buttons')
+
+describe('sort listeners', function () {
+ describe('getInSensitive', () => {
+ it('should be false if data-insensitive is false', () => {
+ const button = $(``)[0]
+ expect(getInSensitive(button)).toEqual(false)
+ })
+ it('should be true if data-insensitive is trye', () => {
+ const button = $(``)[0]
+ expect(getInSensitive(button)).toEqual(true)
+ })
+ it('should be true by default', () => {
+ const button = $(``)[0]
+ expect(getInSensitive(button)).toEqual(true)
+ })
+ })
+ describe('getNextSortOrder', () => {
+ it('should be asc by default', () => {
+ const button = $(``)[0]
+ expect(getNextSortOrder(button)).toEqual('asc')
+ })
+ it('should return asc if desc was set before', () => {
+ const button = $(``)[0]
+ expect(getNextSortOrder(button)).toEqual('asc')
+ })
+ it('should return desc if asc was set before', () => {
+ const button = $(``)[0]
+ expect(getNextSortOrder(button)).toEqual('desc')
+ })
+ it('should use predefined asc order if set', () => {
+ const button = $(``)[0]
+ expect(getNextSortOrder(button)).toEqual('asc')
+ })
+ it('should use predefined desc if set', () => {
+ const button = $(``)[0]
+ expect(getNextSortOrder(button)).toEqual('desc')
+ })
+ })
+ describe('setSortOrder', () => {
+ it('should set toggle between orders', () => {
+ const buttonName = $(``)[0]
+ const buttonAge = $(``)[0]
+ const buttons = [buttonName, buttonAge]
+
+ setSortOrder(buttons, 'name', 'asc')
+ expect(buttonName.classList.contains('asc')).toEqual(true)
+ expect(buttonName.classList.contains('desc')).toEqual(false)
+ expect(buttonAge.classList.contains('asc')).toEqual(false)
+ expect(buttonAge.classList.contains('desc')).toEqual(false)
+
+ setSortOrder(buttons, 'name', 'desc')
+ expect(buttonName.classList.contains('asc')).toEqual(false)
+ expect(buttonName.classList.contains('desc')).toEqual(true)
+ expect(buttonAge.classList.contains('asc')).toEqual(false)
+ expect(buttonAge.classList.contains('desc')).toEqual(false)
+ })
+ it('should respect predefined order', () => {
+ const buttonName = $(``)[0]
+ const buttonAge = $(``)[0]
+ const buttons = [buttonName, buttonAge]
+
+ setSortOrder(buttons, 'name', 'asc')
+ expect(buttonName.classList.contains('asc')).toEqual(true)
+ expect(buttonName.classList.contains('desc')).toEqual(false)
+ expect(buttonAge.classList.contains('asc')).toEqual(false)
+ expect(buttonAge.classList.contains('desc')).toEqual(false)
+
+ setSortOrder(buttons, 'name', 'desc')
+ expect(buttonName.classList.contains('asc')).toEqual(false)
+ expect(buttonName.classList.contains('desc')).toEqual(false)
+ expect(buttonAge.classList.contains('asc')).toEqual(false)
+ expect(buttonAge.classList.contains('desc')).toEqual(false)
+ })
+ })
+
+ describe('clearSortOrder', () => {
+ it('should set toggle between orders', () => {
+ const buttonName = $(``)[0]
+ const buttonAge = $(``)[0]
+ const buttons = [buttonName, buttonAge]
+
+ expect(buttonName.classList.contains('asc')).toEqual(false)
+ expect(buttonName.classList.contains('desc')).toEqual(true)
+ expect(buttonAge.classList.contains('asc')).toEqual(true)
+ expect(buttonAge.classList.contains('desc')).toEqual(false)
+
+ clearSortOrder(buttons)
+
+ expect(buttonName.classList.contains('asc')).toEqual(false)
+ expect(buttonName.classList.contains('desc')).toEqual(false)
+ expect(buttonAge.classList.contains('asc')).toEqual(false)
+ expect(buttonAge.classList.contains('desc')).toEqual(false)
+ })
+ })
+ describe('addSortListeners', function () {
+ beforeEach(() => {
+ const template = templater.getTemplate({
+ template: '
',
+ valueNames: ['name', 'age'],
+ })
+ this.items = [
+ new Item({ name: 'Jonny', age: '34' }, { template }),
+ new Item({ name: 'Jonas', age: '15' }, { template }),
+ new Item({ name: 'Martina', age: '17' }, { template }),
+ new Item({ name: 'Unn', age: '30' }, { template }),
+ ]
+ this.getValues = (valueName) => {
+ return this.items.map((i) => i._values[valueName])
+ }
+ })
+ it('should sort and handle classes when clicking buttons', () => {
+ const buttonName = $(``)[0]
+ const buttonAge = $(``)[0]
+ const elements = [buttonName, buttonAge]
+ addSortListeners(elements, { items: this.items })
+
+ $(buttonAge).click()
+ expect(buttonName.classList.contains('asc')).toEqual(false)
+ expect(buttonName.classList.contains('desc')).toEqual(false)
+ expect(buttonAge.classList.contains('asc')).toEqual(true)
+ expect(buttonAge.classList.contains('desc')).toEqual(false)
+ expect(this.getValues('age')).toEqual([
+ '15', //
+ '17',
+ '30',
+ '34',
+ ])
+
+ $(buttonAge).click()
+ expect(buttonName.classList.contains('asc')).toEqual(false)
+ expect(buttonName.classList.contains('desc')).toEqual(false)
+ expect(buttonAge.classList.contains('asc')).toEqual(false)
+ expect(buttonAge.classList.contains('desc')).toEqual(true)
+ expect(this.getValues('age')).toEqual([
+ '34', //
+ '30',
+ '17',
+ '15',
+ ])
+
+ $(buttonName).click()
+ expect(buttonName.classList.contains('asc')).toEqual(true)
+ expect(buttonName.classList.contains('desc')).toEqual(false)
+ expect(buttonAge.classList.contains('asc')).toEqual(false)
+ expect(buttonAge.classList.contains('desc')).toEqual(false)
+ expect(this.getValues('name')).toEqual([
+ 'Jonas', //
+ 'Jonny',
+ 'Martina',
+ 'Unn',
+ ])
+ })
+ it('should run before and after callbacks', (done) => {
+ const buttonAge = $(``)[0]
+ const elements = [buttonAge]
+ let beforeHasRun = false
+ const before = () => {
+ beforeHasRun = true
+ }
+ const after = () => {
+ expect(beforeHasRun).toEqual(true)
+ done()
+ }
+ addSortListeners(elements, { items: this.items, before, after })
+ $(buttonAge).click()
+ })
+ it('should use custom alphabeth', () => {
+ const buttonName = $(``)[0]
+ const elements = [buttonName]
+ const alphabet = 'UMJona'
+ addSortListeners(elements, { items: this.items, alphabet })
+ $(buttonName).click()
+ expect(this.getValues('name')).toEqual([
+ 'Unn', //
+ 'Martina',
+ 'Jonny',
+ 'Jonas',
+ ])
+ })
+ it('should use sort function', () => {
+ const buttonName = $(``)[0]
+ const elements = [buttonName]
+ const sortFunction = function (itemA, itemB, options) {
+ const reversNameA = itemA.values()[options.valueName].split('').reverse().join('')
+ const reversNameB = itemB.values()[options.valueName].split('').reverse().join('')
+ return naturalSort(reversNameA, reversNameB)
+ }
+ addSortListeners(elements, { items: this.items, sortFunction })
+ $(buttonName).click()
+ expect(this.getValues('name')).toEqual([
+ 'Martina', //
+ 'Unn',
+ 'Jonas',
+ 'Jonny',
+ ])
+ })
+ })
+})
diff --git a/__test__/unit/sort.test.js b/__test__/unit/sort.test.js
new file mode 100644
index 00000000..34d03206
--- /dev/null
+++ b/__test__/unit/sort.test.js
@@ -0,0 +1,230 @@
+/*
+sort(items, column, options = {
+ order: 'desc',
+ alphabet: undefined,
+ insensitive: true,
+ sortFunction: undefined
+})
+*/
+const naturalSort = require('string-natural-compare')
+const $ = require('jquery')
+
+const Item = require('../../src/item')
+const sort = require('../../src/sort')
+const templater = require('../../src/templater')
+
+describe('sort', function () {
+ beforeEach(() => {
+ const template = templater.getTemplate({
+ template: '
',
+ valueNames: ['v'],
+ })
+ this.items = []
+ this.setValues = (values) => {
+ if (!Array.isArray(values)) values = values.split('')
+ values.forEach((v, i) => {
+ this.items.push(new Item({ v }, { template }))
+ })
+ }
+ this.getValues = () => {
+ return this.items.map((i) => i._values.v)
+ }
+ })
+ describe('options', () => {
+ it('should sort asc by default', () => {
+ this.setValues('Babc')
+ sort(this.items, 'v')
+ expect(this.getValues().join('')).toEqual('aBbc')
+ })
+ it('should sort asc', () => {
+ this.setValues('Babc')
+ sort(this.items, 'v', { order: 'asc' })
+ expect(this.getValues().join('')).toEqual('aBbc')
+ })
+ it('should sort desc', () => {
+ this.setValues('Babc')
+ sort(this.items, 'v', { order: 'desc' })
+ expect(this.getValues().join('')).toEqual('cBba')
+ })
+ it('should be case sensitive', () => {
+ this.setValues('Babc')
+ sort(this.items, 'v', { insensitive: false })
+ expect(this.getValues().join('')).toEqual('Babc')
+ })
+ it('should use custom alphabeth', () => {
+ this.setValues('AaOoÅåÄäÖö')
+ sort(this.items, 'v', { order: 'asc' })
+ expect(this.getValues().join('')).toEqual('AaOoÄäÅåÖö') // Å & Ä is in wrong order
+ sort(this.items, 'v', { order: 'desc' })
+ expect(this.getValues().join('')).toEqual('ÖöÅåÄäOoAa') // Å & Ä is in wrong order
+ sort(this.items, 'v', {
+ order: 'asc',
+ alphabet: 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvXxYyZzÅåÄäÖö',
+ })
+ expect(this.getValues().join('')).toEqual('AaOoÅåÄäÖö')
+ sort(this.items, 'v', {
+ order: 'desc',
+ alphabet: 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvXxYyZzÅåÄäÖö',
+ })
+ expect(this.getValues().join('')).toEqual('öÖäÄåÅoOaA')
+ })
+ it('should use custom sort function', () => {
+ this.setValues([
+ " ", //
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ ])
+ sort(this.items, 'v', {
+ order: 'desc',
+ sortFunction: function (itemA, itemB, options = {}) {
+ const column = options.valueName
+ expect(column).toEqual('v')
+ return naturalSort($(itemA.values()[column]).val(), $(itemB.values()[column]).val())
+ },
+ })
+ expect(this.getValues()).toEqual([
+ " ", //
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ ])
+ })
+ })
+ describe('examples', () => {
+ it('should sort dates', () => {
+ this.setValues([
+ '2008-12-10', //
+ '2008-11-10',
+ '2007-11-10',
+ '2009-12-10',
+ '2007-01-4',
+ '2006-12-10',
+ ])
+ sort(this.items, 'v')
+ expect(this.getValues()).toEqual([
+ '2006-12-10', //
+ '2007-01-4',
+ '2007-11-10',
+ '2008-11-10',
+ '2008-12-10',
+ '2009-12-10',
+ ])
+ })
+ it('should sort file names (a bit wrong)', () => {
+ this.setValues([
+ 'car.mov', //
+ '01alpha.sgi',
+ '001alpha.sgi',
+ 'my.string_41299.tif',
+ '0003.zip',
+ '0002.asp',
+ ])
+ sort(this.items, 'v')
+ expect(this.getValues()).toEqual([
+ '01alpha.sgi', //
+ '001alpha.sgi',
+ '0002.asp',
+ '0003.zip',
+ 'car.mov',
+ 'my.string_41299.tif',
+ ])
+ })
+ it('should show order of sorted floates (a bit wrong)', () => {
+ this.setValues([
+ '10.0401', //
+ '10.022',
+ '10.021999',
+ '11.231',
+ '0003.123',
+ '09.2123',
+ ])
+ sort(this.items, 'v')
+ expect(this.getValues()).toEqual([
+ '0003.123', //
+ '09.2123',
+ '10.022',
+ '10.0401',
+ '10.021999',
+ '11.231',
+ ])
+ })
+ it('should sort IP addresses', () => {
+ this.setValues([
+ '192.168.1.1', //
+ '192.168.0.100',
+ '192.168.0.1',
+ '192.168.1.3',
+ '127.0.0.1',
+ '192.168.1.2',
+ ])
+ sort(this.items, 'v')
+ expect(this.getValues()).toEqual([
+ '127.0.0.1', //
+ '192.168.0.1',
+ '192.168.0.100',
+ '192.168.1.1',
+ '192.168.1.2',
+ '192.168.1.3',
+ ])
+ })
+ it('should handle values from issue 387', () => {
+ this.setValues([
+ 'Test', //
+ 'Test1Test2',
+ 'Bill-To Phone1 Extension',
+ 'z',
+ 's',
+ 'y',
+ ])
+ sort(this.items, 'v')
+ expect(this.getValues()).toEqual([
+ 'Bill-To Phone1 Extension', //
+ 's',
+ 'Test',
+ 'Test1Test2',
+ 'y',
+ 'z',
+ ])
+ })
+
+ it('should show how random values are sorted', () => {
+ this.setValues([
+ undefined, //
+ '',
+ null,
+ 'a',
+ '0',
+ true,
+ 0,
+ 'z',
+ '!',
+ '?',
+ 100,
+ false,
+ ])
+ sort(this.items, 'v')
+ sort(this.items, 'v', { order: 'desc' })
+ sort(this.items, 'v')
+
+ expect(this.getValues()).toEqual([
+ '', //
+ '!',
+ '0',
+ 0,
+ 100,
+ '?',
+ 'a',
+ false,
+ null,
+ true,
+ undefined,
+ 'z',
+ ])
+ })
+ })
+})
diff --git a/src/index.js b/src/index.js
index ee43dd0c..a06091fa 100755
--- a/src/index.js
+++ b/src/index.js
@@ -8,7 +8,9 @@ var naturalSort = require('string-natural-compare'),
getAttribute = require('./utils/get-attribute'),
toArray = require('./utils/to-array'),
templater = require('./templater'),
- Item = require('./item')
+ Item = require('./item'),
+ sort = require('./sort'),
+ { addSortListeners, clearSortOrder, setSortOrder } = require('./sort-buttons')
module.exports = function (id, options, values) {
var self = this,
@@ -61,12 +63,12 @@ module.exports = function (id, options, values) {
})
self.search = require('./search')(self)
self.filter = require('./filter')(self)
- self.sort = require('./sort')(self)
self.fuzzySearch = require('./fuzzy-search')(self, options.fuzzySearch)
this.handlers()
this.items()
this.pagination()
+ this.sort()
self.update()
},
@@ -96,6 +98,43 @@ module.exports = function (id, options, values) {
}
}
},
+ sort: function () {
+ const sortButtons = self.utils.getByClass(self.listContainer, self.sortClass)
+ const { items, sortFunction, alphabet } = self
+ const before = function () {
+ self.trigger('sortStart')
+ }
+ const after = function () {
+ self.update()
+ self.trigger('sortComplete')
+ }
+ addSortListeners(sortButtons, {
+ items,
+ sortFunction,
+ alphabet,
+ before,
+ after,
+ })
+
+ self.handlers.sortStart = self.handlers.sortStart || []
+ self.handlers.sortComplete = self.handlers.sortComplete || []
+ self.on('searchStart', function () {
+ clearSortOrder(sortButtons)
+ })
+ self.on('filterStart', function () {
+ clearSortOrder(sortButtons)
+ })
+ self.sort = function (valueName, options = {}) {
+ before()
+ setSortOrder(sortButtons, valueName, options.order)
+ options.alphabet = options.alphabet || self.alphabet
+ options.sortFunction = options.sortFunction || self.sortFunction
+ options.valueName = valueName
+ sort(items, valueName, options)
+ after()
+ return items
+ }
+ },
}
/*
diff --git a/src/sort-buttons.js b/src/sort-buttons.js
new file mode 100644
index 00000000..a41a13a5
--- /dev/null
+++ b/src/sort-buttons.js
@@ -0,0 +1,75 @@
+var getAttribute = require('./utils/get-attribute')
+var classes = require('./utils/classes')
+var events = require('./utils/events')
+var sorter = require('./sort')
+
+var getInSensitive = function (btn) {
+ var insensitive = getAttribute(btn, 'data-insensitive')
+ if (insensitive === 'false') {
+ return false
+ } else {
+ return true
+ }
+}
+var getNextSortOrder = function (btn) {
+ var predefinedOrder = getAttribute(btn, 'data-order')
+ if (predefinedOrder == 'asc' || predefinedOrder == 'desc') {
+ return predefinedOrder
+ } else if (classes(btn).has('desc')) {
+ return 'asc'
+ } else if (classes(btn).has('asc')) {
+ return 'desc'
+ } else {
+ return 'asc'
+ }
+}
+var clearSortOrder = function (els) {
+ for (var i = 0, il = els.length; i < il; i++) {
+ classes(els[i]).remove('asc')
+ classes(els[i]).remove('desc')
+ }
+}
+var setSortOrder = function (els, valueName, order) {
+ for (var i = 0, il = els.length; i < il; i++) {
+ var btn = els[i]
+ if (getAttribute(btn, 'data-sort') !== valueName) {
+ classes(btn).remove('asc')
+ classes(btn).remove('desc')
+ continue
+ }
+ var predefinedOrder = getAttribute(btn, 'data-order')
+ if (predefinedOrder == 'asc' || predefinedOrder == 'desc') {
+ if (predefinedOrder == order) {
+ classes(btn).add(order)
+ classes(btn).remove(order === 'asc' ? 'desc' : 'asc')
+ } else {
+ classes(btn).remove('asc')
+ classes(btn).remove('desc')
+ }
+ } else {
+ classes(btn).add(order)
+ classes(btn).remove(order === 'asc' ? 'desc' : 'asc')
+ }
+ }
+}
+
+var addSortListeners = function (elements, { items, sortFunction, alphabet, before, after } = {}) {
+ events.bind(elements, 'click', function () {
+ if (before) before()
+ var target = arguments[0].currentTarget || arguments[0].srcElement || undefined
+ var valueName = getAttribute(target, 'data-sort')
+ var order = getNextSortOrder(target)
+ var insensitive = getInSensitive(target)
+ var options = {
+ sortFunction,
+ insensitive,
+ alphabet,
+ order,
+ }
+ setSortOrder(elements, valueName, order)
+ sorter(items, valueName, options)
+ if (after) after()
+ })
+}
+
+module.exports = { addSortListeners, getInSensitive, getNextSortOrder, setSortOrder, clearSortOrder }
diff --git a/src/sort.js b/src/sort.js
index 1020d0c7..b792aa12 100644
--- a/src/sort.js
+++ b/src/sort.js
@@ -1,110 +1,29 @@
-module.exports = function (list) {
- var buttons = {
- els: undefined,
- clear: function () {
- for (var i = 0, il = buttons.els.length; i < il; i++) {
- list.utils.classes(buttons.els[i]).remove('asc')
- list.utils.classes(buttons.els[i]).remove('desc')
- }
- },
- getOrder: function (btn) {
- var predefinedOrder = list.utils.getAttribute(btn, 'data-order')
- if (predefinedOrder == 'asc' || predefinedOrder == 'desc') {
- return predefinedOrder
- } else if (list.utils.classes(btn).has('desc')) {
- return 'asc'
- } else if (list.utils.classes(btn).has('asc')) {
- return 'desc'
- } else {
- return 'asc'
- }
- },
- getInSensitive: function (btn, options) {
- var insensitive = list.utils.getAttribute(btn, 'data-insensitive')
- if (insensitive === 'false') {
- options.insensitive = false
- } else {
- options.insensitive = true
- }
- },
- setOrder: function (options) {
- for (var i = 0, il = buttons.els.length; i < il; i++) {
- var btn = buttons.els[i]
- if (list.utils.getAttribute(btn, 'data-sort') !== options.valueName) {
- continue
- }
- var predefinedOrder = list.utils.getAttribute(btn, 'data-order')
- if (predefinedOrder == 'asc' || predefinedOrder == 'desc') {
- if (predefinedOrder == options.order) {
- list.utils.classes(btn).add(options.order)
- }
- } else {
- list.utils.classes(btn).add(options.order)
- }
- }
- },
- }
-
- var sort = function () {
- list.trigger('sortStart')
- var options = {}
-
- var target = arguments[0].currentTarget || arguments[0].srcElement || undefined
-
- if (target) {
- options.valueName = list.utils.getAttribute(target, 'data-sort')
- buttons.getInSensitive(target, options)
- options.order = buttons.getOrder(target)
- } else {
- options = arguments[1] || options
- options.valueName = arguments[0]
- options.order = options.order || 'asc'
- options.insensitive = typeof options.insensitive == 'undefined' ? true : options.insensitive
- }
-
- buttons.clear()
- buttons.setOrder(options)
-
- // caseInsensitive
- // alphabet
- var customSortFunction = options.sortFunction || list.sortFunction || null,
- multi = options.order === 'desc' ? -1 : 1,
- sortFunction
-
- if (customSortFunction) {
- sortFunction = function (itemA, itemB) {
- return customSortFunction(itemA, itemB, options) * multi
- }
- } else {
- sortFunction = function (itemA, itemB) {
- var naturalSortOptions = {}
- naturalSortOptions.alphabet = list.alphabet || options.alphabet || undefined
- if (!naturalSortOptions.alphabet && options.insensitive) {
- naturalSortOptions.caseInsensitive = true
+var naturalSort = require('string-natural-compare')
+
+module.exports = (items, column, options = {}) => {
+ const { sortFunction, order, alphabet, insensitive } = options
+ const caseSensitive = insensitive !== false
+ const multi = order === 'desc' ? -1 : 1
+
+ if (sortFunction) {
+ return items.sort(function (itemA, itemB) {
+ return (
+ sortFunction(itemA, itemB, {
+ valueName: column,
+ alphabet,
+ caseSensitive,
+ }) * multi
+ )
+ })
+ } else {
+ return items.sort(function (itemA, itemB) {
+ const options = { alphabet }
+ if (!alphabet) {
+ if (caseSensitive) {
+ options.caseInsensitive = true
}
- return (
- list.utils.naturalSort(
- '' + itemA.values()[options.valueName],
- '' + itemB.values()[options.valueName],
- naturalSortOptions
- ) * multi
- )
}
- }
-
- list.items.sort(sortFunction)
- list.update()
- list.trigger('sortComplete')
+ return naturalSort('' + itemA.values()[column], '' + itemB.values()[column], options) * multi
+ })
}
-
- // Add handlers
- list.handlers.sortStart = list.handlers.sortStart || []
- list.handlers.sortComplete = list.handlers.sortComplete || []
-
- buttons.els = list.utils.getByClass(list.listContainer, list.sortClass)
- list.utils.events.bind(buttons.els, 'click', sort)
- list.on('searchStart', buttons.clear)
- list.on('filterStart', buttons.clear)
-
- return sort
}