mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(editor): Add smart decimals directive (#14054)
This commit is contained in:
35
packages/@n8n/utils/src/number/smartDecimal.test.ts
Normal file
35
packages/@n8n/utils/src/number/smartDecimal.test.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { smartDecimal } from './smartDecimal';
|
||||||
|
|
||||||
|
describe('smartDecimal', () => {
|
||||||
|
it('should return the same value if it is an integer', () => {
|
||||||
|
expect(smartDecimal(42)).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the same value if it has only one decimal place', () => {
|
||||||
|
expect(smartDecimal(42.5)).toBe(42.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should round to two decimal places by default', () => {
|
||||||
|
expect(smartDecimal(42.567)).toBe(42.57);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should round to the specified number of decimal places', () => {
|
||||||
|
expect(smartDecimal(42.567, 1)).toBe(42.6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle negative numbers correctly', () => {
|
||||||
|
expect(smartDecimal(-42.567, 2)).toBe(-42.57);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle zero correctly', () => {
|
||||||
|
expect(smartDecimal(0)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle very small numbers correctly', () => {
|
||||||
|
expect(smartDecimal(0.000567, 5)).toBe(0.00057);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should round to two decimal if it is smaller than the given one', () => {
|
||||||
|
expect(smartDecimal(42.56, 3)).toBe(42.56);
|
||||||
|
});
|
||||||
|
});
|
||||||
13
packages/@n8n/utils/src/number/smartDecimal.ts
Normal file
13
packages/@n8n/utils/src/number/smartDecimal.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export const smartDecimal = (value: number, decimals = 2): number => {
|
||||||
|
// Check if integer
|
||||||
|
if (Number.isInteger(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it has only one decimal place
|
||||||
|
if (value.toString().split('.')[1].length <= decimals) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Number(value.toFixed(decimals));
|
||||||
|
};
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
import { render } from '@testing-library/vue';
|
||||||
|
|
||||||
|
import { n8nSmartDecimal } from './n8n-smart-decimal';
|
||||||
|
|
||||||
|
describe('Directive n8n-truncate', () => {
|
||||||
|
it('should leave number as is without decimals', async () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '42',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>42</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should leave number as is without decimals with binding arg', async () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal:3>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '42',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>42</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should leave the number with 1 decimal', async () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '42.1',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>42.1</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format number to 2 decimal places by default', async () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '42.123456',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>42.12</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format number to 1 decimal place', async () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal:1>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '42.123456',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>42.1</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format number to 3 decimal places', async () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal:3>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '42.123456',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>42.123</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle negative numbers correctly', () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '-42.123456',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>-42.12</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle zero correctly', () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '0',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>0</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle very small numbers correctly', () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal:5>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '0.000567',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>0.00057</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format number to 2 decimal places if that has fewer decimals than the desired', async () => {
|
||||||
|
const { html } = render(
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<p v-n8n-smart-decimal:3>{{text}}</p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
text: '42.12',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
directives: {
|
||||||
|
n8nSmartDecimal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(html()).toBe('<p>42.12</p>');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { smartDecimal } from '@n8n/utils/number/smartDecimal';
|
||||||
|
import type { DirectiveBinding, FunctionDirective } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom directive `n8nSmartDecimal` to format numbers with smart decimal places.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* In your Vue template, use the directive `v-n8n-smart-decimal` passing the number and optionally the decimal places.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <p v-n8n-smart-decimal>42.567</p>
|
||||||
|
*
|
||||||
|
* Compiles to: <p>42.57</p>
|
||||||
|
*
|
||||||
|
* Example with specified decimal places:
|
||||||
|
* <p v-n8n-smart-decimal:4>42.56789</p>
|
||||||
|
*
|
||||||
|
* Compiles to: <p>42.5679</p>
|
||||||
|
*
|
||||||
|
* Function Shorthand:
|
||||||
|
* https://vuejs.org/guide/reusability/custom-directives#function-shorthand
|
||||||
|
*
|
||||||
|
* Hint: Do not use it on components
|
||||||
|
* https://vuejs.org/guide/reusability/custom-directives#usage-on-components
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const n8nSmartDecimal: FunctionDirective = (
|
||||||
|
el: HTMLElement,
|
||||||
|
binding: DirectiveBinding<number | undefined>,
|
||||||
|
) => {
|
||||||
|
const value = parseFloat(el.textContent ?? '');
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
const decimals = isNaN(Number(binding.arg)) ? undefined : Number(binding.arg);
|
||||||
|
const formattedValue = smartDecimal(value, decimals);
|
||||||
|
el.textContent = formattedValue.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user