diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2f2eaa3e..5f705493c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,14 @@
+# Changelog
+
-## v0.15.0 · 03 11 2022
+## v0.15.1 · 17 11 2022
+
+- New feature: dynamically add option groups
+ _@jackbentley_
+
+## v0.15.0 · 14 11 2022
### Breaking changes
diff --git a/examples/optgroups.html b/examples/optgroups.html
index 174d11873..3f76fc40f 100644
--- a/examples/optgroups.html
+++ b/examples/optgroups.html
@@ -2,201 +2,253 @@
-
-
-
-
- Selectize.js Demo
-
-
-
-
-
-
-
-
-
-
-
-
-
Selectize.js
+
+
+
-
-
Optgroups (basic)
-
-
-
-
-
-
+
+
+
+
Selectize.js Demo
+
+
+
+
+
+
+
+
+
+
-
-
Optgroups (programmatic)
-
-
-
-
-
-
+
+ < id="wrapper">
+
Selectize.js
+
+
+
Optgroups (basic)
+
+
+
+
+
+
+
+
+
Optgroups (disabled)
+
+
+
+
+
+
+
+
+
Optgroups (repeated options)
+
+
+
+
+
+
+
+
+
Optgroups (programmatic)
+
+
+
+
+
+
+
+
+
Plugin: "optgroup_columns"
+
+
+
+
+
+
+
+
+
Optgroups (dynamic)
+
+
+
+
+
+
+
-
-
Plugin: "optgroup_columns"
-
-
-
-
-
-
-
diff --git a/src/defaults.js b/src/defaults.js
index d69f7701f..34098ee2a 100644
--- a/src/defaults.js
+++ b/src/defaults.js
@@ -62,26 +62,28 @@ Selectize.defaults = {
normalize: false,
ignoreOnDropwdownHeight: 'img, i',
search: true,
- /*
- load : null, // function(query, callback) { ... }
- score : null, // function(search) { ... }
- formatValueToKey : null, // function(key) { ... }
- onInitialize : null, // function() { ... }
- onChange : null, // function(value) { ... }
- onItemAdd : null, // function(value, $item) { ... }
- onItemRemove : null, // function(value, $item) { ... }
- onClear : null, // function() { ... }
- onOptionAdd : null, // function(value, data) { ... }
- onOptionRemove : null, // function(value) { ... }
- onOptionClear : null, // function() { ... }
- onOptionGroupAdd : null, // function(id, data) { ... }
- onOptionGroupRemove : null, // function(id) { ... }
- onOptionGroupClear : null, // function() { ... }
- onDropdownOpen : null, // function($dropdown) { ... }
- onDropdownClose : null, // function($dropdown) { ... }
- onType : null, // function(str) { ... }
- onDelete : null, // function(values) { ... }
- */
+
+ /*
+ load : null, // function(query, callback) { ... }
+ score : null, // function(search) { ... }
+ formatValueToKey : null, // function(key) { ... }
+ optionGroupRegister : null, // function(optgroup) to register dynamically created option groups
+ onInitialize : null, // function() { ... }
+ onChange : null, // function(value) { ... }
+ onItemAdd : null, // function(value, $item) { ... }
+ onItemRemove : null, // function(value, $item) { ... }
+ onClear : null, // function() { ... }
+ onOptionAdd : null, // function(value, data) { ... }
+ onOptionRemove : null, // function(value) { ... }
+ onOptionClear : null, // function() { ... }
+ onOptionGroupAdd : null, // function(id, data) { ... }
+ onOptionGroupRemove : null, // function(id) { ... }
+ onOptionGroupClear : null, // function() { ... }
+ onDropdownOpen : null, // function($dropdown) { ... }
+ onDropdownClose : null, // function($dropdown) { ... }
+ onType : null, // function(str) { ... }
+ onDelete : null, // function(values) { ... }
+ */
render: {
/*
diff --git a/src/selectize.js b/src/selectize.js
index 4aef82d83..fde40d962 100644
--- a/src/selectize.js
+++ b/src/selectize.js
@@ -1172,7 +1172,13 @@ $.extend(Selectize.prototype, {
for (j = 0, k = optgroups && optgroups.length; j < k; j++) {
optgroup = optgroups[j];
- if (!self.optgroups.hasOwnProperty(optgroup)) {
+ if (!self.optgroups.hasOwnProperty(optgroup) && typeof self.settings.optionGroupRegister === 'function') {
+ var regGroup;
+ if (regGroup = self.settings.optionGroupRegister.apply(self, [optgroup])) {
+ self.registerOptionGroup(regGroup);
+ }
+ }
+ if (!self.optgroups.hasOwnProperty(optgroup)) {
optgroup = '';
}
if (!groups.hasOwnProperty(optgroup)) {
diff --git a/test/setup.js b/test/setup.js
index 9f8cbf892..4a64fdded 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -1,226 +1,262 @@
-(function() {
-
- describe('Setup', function() {
-
- it('should not allow duplicate initialization', function() {
- var instance_before, instance_after, test;
-
- test = setup_test('', {});
- instance_before = test.$select[0].selectize;
- test.$select.selectize();
- instance_after = test.$select[0].selectize;
-
- expect(instance_before).to.be.equal(instance_after);
- });
-
- describe('', function() {
- it('should complete without exceptions', function() {
- var test = setup_test('', {});
- });
- it('should populate items,options from "dataAttr" if available', function() {
- var data = [{val: 'a', lbl: 'Hello'}, {val: 'b', lbl: 'World'}];
- var test = setup_test('', {
- dataAttr: 'data-hydrate',
- valueField: 'val',
- labelField: 'lbl'
- });
- expect(test.selectize.getValue()).to.be.equal('a,b');
- assert.deepEqual(test.selectize.items, ['a','b']);
- assert.deepEqual(test.selectize.options, {
- 'a': {val: 'a', lbl: 'Hello', $order: 1},
- 'b': {val: 'b', lbl: 'World', $order: 2}
- });
- });
- describe('getValue()', function() {
- it('should return value as a string', function() {
- var test = setup_test('', {delimiter: ','});
- expect(test.selectize.getValue()).to.be.a('string');
- });
- it('should return "" when empty', function() {
- var test = setup_test('', {delimiter: ','});
- expect(test.selectize.getValue()).to.be.equal('');
- });
- it('should return proper value when not empty', function() {
- var test = setup_test('', {delimiter: ','});
- expect(test.selectize.getValue()).to.be.equal('a,b');
- });
- });
- describe('', function() {
- it('should propagate original input attributes to the generated input', function() {
- var test = setup_test('', {});
- expect(test.selectize.$control_input.attr('autocorrect')).to.be.equal('off');
- expect(test.selectize.$control_input.attr('autocapitalize')).to.be.equal('none');
- });
- it('should not add attributes if not present in the original', function() {
- var test = setup_test('', {});
- expect(test.selectize.$control_input.attr('autocorrect')).to.be.equal(undefined);
- expect(test.selectize.$control_input.attr('autocapitalize')).to.be.equal(undefined);
- });
- });
- });
-
- describe('', function() {
- it('should complete without exceptions', function(done) {
- var test = setup_test('', {});
- window.setTimeout(function() {
- assert.equal(test.selectize.$control_input.attr('type'), 'number');
- done();
- }, 0);
- });
- });
-
- describe('