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