Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for percentages in NumericInput #385

Merged
merged 5 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 41 additions & 22 deletions src/components/NumericInput/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,15 @@
// remove spaces
value = value.replace(/\s/g, '');

// sanitize input to only allow short mathematical expressions to be evaluated
// Handle percentages by replacing them with their calculated values
const currentValue = this._oldValue || 0;

value = value.replace(/(\d+(?:\.\d+)?)%/g, (match: string, percent: string) => {
const calculatedValue = (parseFloat(percent) / 100) * currentValue;
return calculatedValue.toString();
});
Fixed Show fixed Hide fixed

// sanitize input to only allow short mathematical expressions
value = value.match(/^[*/+\-0-9().]+$/);
if (value !== null && value[0].length < 20) {
let expression = value[0];
Expand All @@ -267,35 +275,46 @@
expression = expressionArr.join(operator);
});
// eslint-disable-next-line
value = Function('"use strict";return (' + expression + ')')();
value = Function(`"use strict";return (${expression})`)();
}
}
} catch (error) {
value = null;
}

if (value === null || isNaN(value)) {
if (this._allowNull) {
return null;
if (value === null || value === undefined || value === '') {
if (this._allowNull) {
return null;
}
value = 0;
}

value = 0;
}
value = Number(value);

// clamp between min max
if (this.min !== null && value < this.min) {
value = this.min;
}
if (this.max !== null && value > this.max) {
value = this.max;
}
if (isNaN(value)) {
if (this._allowNull) {
return null;
}
value = 0;
}

// fix precision
if (this.precision !== null) {
value = parseFloat(Number(value).toFixed(this.precision));
}
// clamp between min max
if (this.min !== null && value < this.min) {
value = this.min;
}
if (this.max !== null && value > this.max) {
value = this.max;
}

// fix precision
if (this.precision !== null) {
value = parseFloat(Number(value).toFixed(this.precision));
}

return value;
return value;
} catch (error) {
if (this._allowNull) {
return null;
}
return 0;
}
}

protected _updateValue(value: number, force?: boolean) {
Expand Down
78 changes: 78 additions & 0 deletions test/components/numericinput.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,83 @@ describe('NumericInput', () => {
numericInput.value = "1+1+1+1+1+1+1+1+1+10";
strictEqual(numericInput.value, 0);
});

describe('percentages', () => {
it('basic percentage', () => {
const numericInput = new NumericInput();

numericInput.value = 200;
numericInput.value = "50%";
strictEqual(numericInput.value, 100); // 50% of 200

numericInput.value = 200;
numericInput.value = "150%";
strictEqual(numericInput.value, 300); // 150% of 200
});

it('percentage in expressions', () => {
const numericInput = new NumericInput();

numericInput.value = 100;
numericInput.value = "50% + 10";
strictEqual(numericInput.value, 60); // (50% of 100) + 10

numericInput.value = 100;
numericInput.value = "25% * 2";
strictEqual(numericInput.value, 50); // (25% of 100) * 2
});

it('multiple percentages', () => {
const numericInput = new NumericInput();

numericInput.value = 100;
numericInput.value = "25% + 50%";
strictEqual(numericInput.value, 75); // (25% of 100) + (50% of 100)
});

it('invalid percentages', () => {
const numericInput = new NumericInput();

numericInput.value = 100;
numericInput.value = "abc%";
strictEqual(numericInput.value, 0);

numericInput.value = 100;
numericInput.value = "%50";
strictEqual(numericInput.value, 0);

numericInput.value = 100;
numericInput.value = "50%%";
strictEqual(numericInput.value, 0);
});

it('percentage with zero base value', () => {
const numericInput = new NumericInput();

numericInput.value = 0;
numericInput.value = "50%";
strictEqual(numericInput.value, 0); // 50% of 0 is 0
});

it('percentage with negative base value', () => {
const numericInput = new NumericInput();

numericInput.value = -100;
numericInput.value = "50%";
strictEqual(numericInput.value, -50); // 50% of -100

numericInput.value = -100;
numericInput.value = "150%";
strictEqual(numericInput.value, -150); // 150% of -100
});

it('percentage with decimal base value', () => {
const numericInput = new NumericInput();

numericInput.value = 0.5;
numericInput.value = "200%";
strictEqual(numericInput.value, 1); // 200% of 0.5
});
});
});
});
Loading