Skip to content

Commit

Permalink
feat: get checkboxes with same value as array fp-96 (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
what1s1ove authored Dec 11, 2023
1 parent 64dd17a commit 397680a
Show file tree
Hide file tree
Showing 15 changed files with 217 additions and 68 deletions.
13 changes: 7 additions & 6 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,21 +165,22 @@ const overridesConfigs = [
},
},
{
files: ['lint-staged.config.js'],
files: ['src/libs/enums/control-type.enum.js'],
rules: {
quotes: ['off'],
'perfectionist/sort-objects': ['off'],
},
},
{
files: ['tests/**/*.test.js'],
files: ['lint-staged.config.js'],
rules: {
'perfectionist/sort-imports': ['off'],
quotes: ['off'],
},
},
{
files: ['src/libs/enums/control-type.enum.js'],
files: ['tests/**/*.test.js'],
rules: {
'perfectionist/sort-objects': ['off'],
'perfectionist/sort-imports': ['off'],
'sonarjs/cognitive-complexity': ['off'],
},
},
];
Expand Down
67 changes: 34 additions & 33 deletions readme.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/libs/constants/constants.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { BANNED_CONTROL_TYPES } from './banned-control-types.constant.js';
export { VALUE_AS_ARRAY_CUSTOM_CONTROL_TYPES } from './value-as-array-custom-control-types.constant.js';
export { VALUE_AS_ARRAY_IDENTIFIER } from './value-as-array-identifier.constant.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ControlType } from '../enums/enums.js';

const VALUE_AS_ARRAY_CUSTOM_CONTROL_TYPES = [ControlType.CHECKBOX];

export { VALUE_AS_ARRAY_CUSTOM_CONTROL_TYPES };
3 changes: 3 additions & 0 deletions src/libs/constants/value-as-array-identifier.constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const VALUE_AS_ARRAY_IDENTIFIER = '[]';

export { VALUE_AS_ARRAY_IDENTIFIER };
2 changes: 1 addition & 1 deletion src/libs/maps/maps.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { bannedElementNameToElementInstance } from './element-name-to-element-instance.map.js';
export { bannedElementNameToElementInstance } from './banned-element-name-to-element-instance.map.js';
24 changes: 18 additions & 6 deletions src/packages/get-form-control-payload/get-form-control-payload.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ControlType } from '../../libs/enums/enums.js';
import { FormPayloadError } from '../../libs/exceptions/exceptions.js';
import {
checkHasValueAsArray,
checkIsReferToAnotherNode,
getAllowedElements,
getCheckboxValue,
getCleanedValueAsArrayControlName,
getDatetimeLocalValue,
getFormControlValue,
getInputDateValue,
Expand All @@ -24,22 +26,32 @@ import {
const getFormControlsPayload = (...controlElements) => {
const allowedElements = getAllowedElements(controlElements);

let elementsValues = /** @type {T} */ ({});
const elementsValues = /** @type {T} */ ({});

for (const element of allowedElements) {
const isReferToAnotherNode = checkIsReferToAnotherNode(
element,
...allowedElements,
allowedElements,
);

if (isReferToAnotherNode) {
continue;
}

elementsValues = {
...elementsValues,
[element.name]: getFormControlPayload(element),
};
let key = /** @type {keyof T} */ (element.name);
let value = /** @type {T[keyof T]} */ (getFormControlPayload(element));
const hasValueAsArray = checkHasValueAsArray(element);

if (hasValueAsArray) {
key = getCleanedValueAsArrayControlName(element);

value = /** @type {T[keyof T]} */ ([
.../** @type {unknown[]} */ (elementsValues[key] ?? []),
.../** @type {unknown[]} */ (getFormControlPayload(element)),
]);
}

elementsValues[key] = value;
}

return elementsValues;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
VALUE_AS_ARRAY_CUSTOM_CONTROL_TYPES,
VALUE_AS_ARRAY_IDENTIFIER,
} from '../../../../libs/constants/constants.js';

/** @typedef {import('../../../../libs/types/types.js').HTMLFormOperationalControlElement} HTMLFormOperationalControlElement */

/**
* @param {HTMLFormOperationalControlElement} element
* @returns {boolean}
*/
const checkHasValueAsArray = (element) => {
return (
element.name.endsWith(VALUE_AS_ARRAY_IDENTIFIER) &&
/** @type {readonly string[]} */ (
VALUE_AS_ARRAY_CUSTOM_CONTROL_TYPES
).includes(element.type)
);
};

export { checkHasValueAsArray };
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
/**
* @template {HTMLFormOperationalControlElement} T
* @param {T} currentNode
* @param {...T} checkNodes
* @param {T[]} checkNodes
* @returns {boolean}
*/
const checkIsReferToAnotherNode = (currentNode, ...checkNodes) => {
const checkIsReferToAnotherNode = (currentNode, checkNodes) => {
return checkNodes.some((checkNode) => {
const hasElements =
'elements' in checkNode && checkNode.elements.length > 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { VALUE_AS_ARRAY_IDENTIFIER } from '../../../../libs/constants/constants.js';

/**
* @param {HTMLInputElement} element
* @returns {boolean}
* @returns {boolean | string[]}
*/
const getCheckboxValue = (element) => {
const hasArrayValue = element.name.endsWith(VALUE_AS_ARRAY_IDENTIFIER);

if (hasArrayValue) {
return element.checked ? [element.value] : [];
}

return element.checked;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { VALUE_AS_ARRAY_IDENTIFIER } from '../../../../libs/constants/constants.js';

/** @typedef {import('../../../../libs/types/types.js').HTMLFormOperationalControlElement} HTMLFormOperationalControlElement */

/**
* @param {HTMLFormOperationalControlElement} element
* @returns {string}
*/
const getCleanedValueAsArrayControlName = (element) => {
return element.name.replace(VALUE_AS_ARRAY_IDENTIFIER, '');
};

export { getCleanedValueAsArrayControlName };
2 changes: 2 additions & 0 deletions src/packages/get-form-control-payload/helpers/helpers.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export { checkHasValueAsArray } from './check-has-value-as-array/check-has-value-as-array.helper.js';
export { checkIsReferToAnotherNode } from './check-is-refer-to-another-node/check-is-refer-to-another-node.helper.js';
export { getAllowedElements } from './get-allowed-elements/get-allowed-elements.helper.js';
export { getCheckboxValue } from './get-checkbox-value/get-checkbox-value.helper.js';
export { getCleanedValueAsArrayControlName } from './get-cleaned-value-as-array-control-name/get-cleaned-value-as-array-control-name.helper.js';
export { getFormControlValue } from './get-control-value/get-control-value.helper.js';
export { getDatetimeLocalValue } from './get-datatime-local-value/get-datatime-local-value.helper.js';
export { getInputDateValue } from './get-input-date-value/get-input-date-value.helper.js';
Expand Down
75 changes: 57 additions & 18 deletions tests/get-form-control-payload.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { beforeEach, describe, test } from 'node:test';
import { fireEvent, waitFor } from '@testing-library/dom';

import { FormPayloadError, getFormControlPayload } from '../src/index.js';
import { VALUE_AS_ARRAY_IDENTIFIER } from '../src/libs/constants/constants.js';
import { ControlType } from '../src/libs/enums/enums.js';

describe('getFormControlPayload should work correctly', () => {
Expand Down Expand Up @@ -72,30 +73,68 @@ describe('getFormControlPayload should work correctly', () => {
}
});

test('should get value from boolean inputs correctly', () => {
const inputs = /** @type {const} */ ([
[ControlType.CHECKBOX, false],
]);
describe('should get value from boolean inputs correctly', () => {
test('should get value from regular boolean input correctly', () => {
const inputs = /** @type {const} */ ([
[ControlType.CHECKBOX, false],
]);

for (const [type, value] of inputs) {
document.body.innerHTML = /* HTML */ `
<form>
<input type="${type}" value="${value}" />
</form>
`;
for (const [type, isChecked] of inputs) {
document.body.innerHTML = /* HTML */ `
<form>
<input
type="${type}"
${isChecked ? 'checked' : ''}
/>
</form>
`;

const control = /** @type {HTMLInputElement} */ (
document.querySelector('input')
);
const control = /** @type {HTMLInputElement} */ (
document.querySelector('input')
);

const controlValue = getFormControlPayload(control);
const controlValue = getFormControlPayload(control);

equal(typeof controlValue, 'boolean');
equal(typeof controlValue, 'boolean');

equal(controlValue, value);
equal(controlValue, isChecked);

document.body.innerHTML = '';
}
document.body.innerHTML = '';
}
});

test('should get single value from boolean input with collection identifier correctly', () => {
const inputs = /** @type {const} */ ([
[ControlType.CHECKBOX, 'banana', true],
[ControlType.CHECKBOX, 'apple', false],
[ControlType.CHECKBOX, 'apple', true],
]);

for (const [type, value, isChecked] of inputs) {
document.body.innerHTML = /* HTML */ `
<form>
<input
name="fruits${VALUE_AS_ARRAY_IDENTIFIER}"
type="${type}"
value="${value}"
${isChecked ? 'checked' : ''}
/>
</form>
`;

const control = /** @type {HTMLInputElement} */ (
document.querySelector('input')
);

const controlValue = getFormControlPayload(control);

equal(Array.isArray(controlValue), true);

deepEqual(controlValue, isChecked ? [value] : []);

document.body.innerHTML = '';
}
});
});

test('should get value from date inputs correctly', () => {
Expand Down
44 changes: 43 additions & 1 deletion tests/get-form-payload.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { deepEqual } from 'node:assert/strict';
import { beforeEach, describe, test } from 'node:test';

import { getFormPayload } from '../src/index.js';
import { BANNED_CONTROL_TYPES } from '../src/libs/constants/constants.js';
import {
BANNED_CONTROL_TYPES,
VALUE_AS_ARRAY_IDENTIFIER,
} from '../src/libs/constants/constants.js';
import { ControlType } from '../src/libs/enums/enums.js';
import { bannedElementNameToElementInstance } from '../src/libs/maps/maps.js';

Expand Down Expand Up @@ -120,4 +123,43 @@ describe('getFormPayload should work correctly', () => {

deepEqual(getFormPayload(formNode), formPayload);
});

describe('should get multiple value from controls with collection identifier correctly', () => {
test('should get multiple value from boolean inputs with collection identifier correctly', () => {
const Fruit = {
APPLE: 'apple',
BANANA: 'banana',
ORANGE: 'orange',
};

const formPayload = {
fruits: [Fruit.BANANA, Fruit.ORANGE],
};

document.body.innerHTML = /* HTML */ `
<form>
${Object.values(Fruit)
.map(
(fruit) => /* HTML */ `
<input
name="fruits${VALUE_AS_ARRAY_IDENTIFIER}"
type="${ControlType.CHECKBOX}"
value="${fruit}"
${formPayload.fruits.includes(fruit)
? 'checked'
: ''}
/>
`,
)
.join('')}
</form>
`;

const formNode = /** @type {HTMLFormElement} */ (
document.querySelector('form')
);

deepEqual(getFormPayload(formNode), formPayload);
});
});
});

0 comments on commit 397680a

Please sign in to comment.