Skip to content
Snippets Groups Projects
Verified Commit 4630179b authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: new function initOptionsFromAttributes

parent 09627c65
No related branches found
No related tags found
No related merge requests found
/**
* Copyright schukai GmbH and contributors 2023. All Rights Reserved.
* Node module: @schukai/monster
* This file is licensed under the AGPLv3 License.
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/
import {Pathfinder} from '../../data/pathfinder.mjs';
import {isFunction} from '../../types/is.mjs';
export {initOptionsFromAttributes };
/**
* Initializes the given options object based on the attributes of the current DOM element.
* The function looks for attributes with the prefix 'data-monster-option-', and maps them to
* properties in the options object. It replaces the dashes with dots to form the property path.
* For example, the attribute 'data-monster-option-url' maps to the 'url' property in the options object.
*
* With the mapping parameter, the attribute value can be mapped to a different value.
* For example, the attribute 'data-monster-option-foo' maps to the 'bar' property in the options object.
*
* The mapping object would look like this:
* {
* 'foo': (value) => value + 'bar'
* // the value of the attribute 'data-monster-option-foo' is appended with 'bar'
* // and assigned to the 'bar' property in the options object.
* // e.g. <div data-monster-option-foo="foo"></div>
* 'bar.baz': (value) => value + 'bar'
* // the value of the attribute 'data-monster-option-bar-baz' is appended with 'bar'
* // and assigned to the 'bar.baz' property in the options object.
* // e.g. <div data-monster-option-bar-baz="foo"></div>
* }
*
* @param {HTMLElement} element - The DOM element to be used as the source of the attributes.
* @param {Object} options - The options object to be initialized.
* @param {Object} mapping - A mapping between the attribute value and the property value.
* @param {string} prefix - The prefix of the attributes to be considered.
* @returns {Object} - The initialized options object.
* @this HTMLElement - The context of the DOM element.
*/
function initOptionsFromAttributes(element, options, mapping={},prefix = 'data-monster-option-') {
if (!(element instanceof HTMLElement)) return options;
if (!element.hasAttributes()) return options;
const finder = new Pathfinder(options);
element.getAttributeNames().forEach((name) => {
if (!name.startsWith(prefix)) return;
// check if the attribute name is a valid option.
// the mapping between the attribute is simple. The dash is replaced by a dot.
// e.g. data-monster-url => url
const optionName = name.replace(prefix, '').replace(/-/g, '.');
if (!finder.exists(optionName)) return;
if (element.hasAttribute(name)) {
let value = element.getAttribute(name);
if (mapping.hasOwnProperty(optionName)&&isFunction(mapping[optionName])) {
value = mapping[optionName](value);
}
const typeOfOptionValue = typeof finder.getVia(optionName);
if (typeOfOptionValue === 'boolean') {
value = value === 'true';
} else if (typeOfOptionValue === 'number') {
value = Number(value);
} else if (typeOfOptionValue === 'string') {
value = String(value);
} else if (typeOfOptionValue === 'object') {
value = JSON.parse(value);
}
finder.setVia(optionName, value);
}
})
return options;
}
\ No newline at end of file
...@@ -1611,7 +1611,7 @@ packages: ...@@ -1611,7 +1611,7 @@ packages:
execa: 4.1.0 execa: 4.1.0
polyfill-library: 3.111.0 polyfill-library: 3.111.0
semver: 7.3.8 semver: 7.3.8
snyk: 1.1133.0 snyk: 1.1134.0
yargs: 15.4.1 yargs: 15.4.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
...@@ -4091,8 +4091,8 @@ packages: ...@@ -4091,8 +4091,8 @@ packages:
supports-color: 7.2.0 supports-color: 7.2.0
dev: true dev: true
/snyk@1.1133.0: /snyk@1.1134.0:
resolution: {integrity: sha512-op/OCcfZZcR1nZDwKS4sVYB51Dqc327/t47a0nyzVTtB8qVQJyujnIcsWaOeY4uEoaJnUNxp1Mp5kgN6jhwJFA==} resolution: {integrity: sha512-gy+Aas1F10AEkAH40f8ewPavFCVjUW/izTv1RJNRwuOt52wqFgb4mkW3F0U6xq4HCNiEzmWCkz8BN21uzdMPUA==}
engines: {node: '>=12'} engines: {node: '>=12'}
hasBin: true hasBin: true
requiresBuild: true requiresBuild: true
......
...@@ -8,7 +8,7 @@ import {initJSDOM} from "../../util/jsdom.mjs"; ...@@ -8,7 +8,7 @@ import {initJSDOM} from "../../util/jsdom.mjs";
describe('Events', function () { describe('Events', function () {
before(async function () { before(async function () {
initJSDOM(); await initJSDOM();
}) })
describe('findTargetElementFromEvent()', function () { describe('findTargetElementFromEvent()', function () {
......
import {expect} from "chai"
import {initOptionsFromAttributes} from "../../../../..//application/source/dom/util/init-options-from-attributes.mjs";
import {initJSDOM} from "../../../util/jsdom.mjs";
describe('initOptionsFromAttributes', () => {
let element;
let options;
before(async function () {
await initJSDOM();
})
beforeEach(() => {
options = { url: "", key: { subkey: "" } };
element = document.createElement('div');
});
it('should initialize options with matching attributes', () => {
element.setAttribute('data-monster-option-url', 'https://example.com');
element.setAttribute('data-monster-option-key.subkey', 'test');
const result = initOptionsFromAttributes(element, options);
expect(result.url).to.equal('https://example.com');
expect(result.key.subkey).to.equal('test');
});
it('should not modify options without matching attributes', () => {
const result = initOptionsFromAttributes(element, options);
expect(result.url).to.equal('');
expect(result.key.subkey).to.equal('');
});
it('should ignore attributes without the correct prefix', () => {
element.setAttribute('data-some-option-url', 'https://example.com');
const result = initOptionsFromAttributes(element, options);
expect(result.url).to.equal('');
});
it('should ignore attributes with invalid option paths', () => {
element.setAttribute('data-monster-option-nonexistent', 'value');
const result = initOptionsFromAttributes(element, options);
expect(result).to.deep.equal(options);
});
it('should apply mapping for a single attribute', () => {
element.setAttribute('data-monster-option-url', 'example');
const mapping = {
'url': (value) => 'https://' + value + '.com'
};
const result = initOptionsFromAttributes(element, options, mapping);
expect(result.url).to.equal('https://example.com');
});
it('should apply mapping for a nested attribute', () => {
element.setAttribute('data-monster-option-key-subkey', '123');
const mapping = {
'key.subkey': (value) => parseInt(value, 10) * 2
};
const result = initOptionsFromAttributes(element, options, mapping);
expect(result.key.subkey).to.equal("246");
});
it('should apply multiple mappings', () => {
element.setAttribute('data-monster-option-url', 'example');
element.setAttribute('data-monster-option-key.subkey', '123');
const mapping = {
'url': (value) => 'https://' + value + '.com',
'key.subkey': (value) => parseInt(value, 10) * 2
};
const result = initOptionsFromAttributes(element, options, mapping);
expect(result.url).to.equal('https://example.com');
expect(result.key.subkey).to.equal("246");
});
it('should ignore mappings for non-existing attributes', () => {
const mapping = {
'url': (value) => 'https://' + value + '.com'
};
const result = initOptionsFromAttributes(element, options, mapping);
expect(result.url).to.equal('');
});
it('should ignore mappings for invalid option paths', () => {
element.setAttribute('data-monster-option-nonexistent', 'value');
const mapping = {
'nonexistent': (value) => value + 'bar'
};
const result = initOptionsFromAttributes(element, options, mapping);
expect(result).to.deep.equal(options);
});
it('should apply mapping only to specified attributes', () => {
element.setAttribute('data-monster-option-url', 'example');
element.setAttribute('data-monster-option-key.subkey', '123');
const mapping = {
'url': (value) => 'https://' + value + '.com'
};
const result = initOptionsFromAttributes(element, options, mapping);
expect(result.url).to.equal('https://example.com');
expect(result.key.subkey).to.equal('123');
});
it('should not apply mapping if not a function', () => {
element.setAttribute('data-monster-option-url', 'example');
const mapping = {
'url': 'https://example.com'
};
const result = initOptionsFromAttributes(element, options, mapping);
expect(result.url).to.equal('example');
});
it('should apply mapping with custom prefix', () => {
element.setAttribute('data-custom-option-url', 'example');
const mapping = {
'url': (value) => 'https://' + value + '.com'
};
const result = initOptionsFromAttributes(element, options, mapping, 'data-custom-option-');
expect(result.url).to.equal('https://example.com');
});
it('should not apply mapping with incorrect custom prefix', () => {
element.setAttribute('data-custom-option-url', 'example');
const mapping = {
'url': (value) => 'https://' + value + '.com'
};
const result = initOptionsFromAttributes(element, options, mapping);
expect(result.url).to.equal('');
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment