Skip to content

Commit

Permalink
fix(percent format style): fix calculation of default step (closes #20)
Browse files Browse the repository at this point in the history
  • Loading branch information
dm4t2 committed Oct 29, 2023
1 parent 4886407 commit b724817
Show file tree
Hide file tree
Showing 9 changed files with 1,873 additions and 665 deletions.
2 changes: 1 addition & 1 deletion api-extractor.json
Expand Up @@ -45,7 +45,7 @@
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
*/
"mainEntryPointFilePath": "<projectFolder>/temp/types/index.d.ts",
"mainEntryPointFilePath": "<projectFolder>/temp/types/src/index.d.ts",

/**
* A list of NPM package names whose exports should be treated as part of this package.
Expand Down
6 changes: 2 additions & 4 deletions docs/.vitepress/theme/components/Demo.vue
Expand Up @@ -271,7 +271,7 @@ export default defineComponent({
Dialog
},
setup() {
const range = (from, to) =>
const range = (from: number, to: number) =>
Array(to - from)
.fill(from)
.map((x, y) => x + y)
Expand Down Expand Up @@ -360,7 +360,7 @@ export default defineComponent({
precisionRangeMaxValue: 5,
precision: 2,
precisionOptions: computed(() => range(1, 16)),
precisionRangeMinOptions: computed(() => range(1, state.precisionRangeMaxValue + 1)),
precisionRangeMinOptions: computed(() => range(0, state.precisionRangeMaxValue + 1)),
precisionRangeMaxOptions: computed(() => range(state.precisionRangeMinValue, 16)),
valueRangeEnabled: false,
minValue: undefined,
Expand Down Expand Up @@ -406,5 +406,3 @@ export default defineComponent({
}
})
</script>

<style scoped></style>
32 changes: 18 additions & 14 deletions package.json
Expand Up @@ -43,30 +43,34 @@
"release": "run-s build:api build:bundle build:docs"
},
"devDependencies": {
"@microsoft/api-documenter": "^7.19.24",
"@microsoft/api-extractor": "^7.33.6",
"@microsoft/api-documenter": "^7.23.9",
"@microsoft/api-extractor": "^7.38.0",
"@rushstack/eslint-patch": "^1.2.0",
"@testing-library/dom": "^9.3.3",
"@testing-library/user-event": "^14.5.1",
"@types/node": "^20.8.9",
"@types/stringify-object": "^3.3.1",
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^5.42.1",
"@vitest/coverage-c8": "^0.25.1",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.2",
"eslint": "^8.27.0",
"eslint-config-prettier": "^8.5.0",
"@typescript-eslint/eslint-plugin": "^6.9.0",
"@typescript-eslint/parser": "^6.9.0",
"@vitest/coverage-v8": "^0.34.6",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
"eslint": "^8.52.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-tsdoc": "^0.2.17",
"eslint-plugin-vue": "^9.7.0",
"eslint-plugin-vue": "^9.18.1",
"jsdom": "^22.1.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.7.1",
"prettier": "^3.0.3",
"rimraf": "^3.0.2",
"rollup": "^2.79.1",
"rollup-plugin-typescript2": "^0.34.1",
"rollup-plugin-typescript2": "^0.36.0",
"stringify-object": "^3.3.0",
"typescript": "^4.8.4",
"typescript": "^5.2.2",
"vite-plugin-components": "^0.13.3",
"vite-plugin-windicss": "^0.17.1",
"vitepress": "^0.22.4",
"vitest": "^0.25.1",
"vitest": "^0.34.6",
"vue": "^3.2.41",
"windicss": "^3.5.6"
}
Expand Down
4 changes: 3 additions & 1 deletion src/api.ts
Expand Up @@ -209,7 +209,9 @@ export interface NumberInputOptions {
valueRange?: NumberRange
/**
* Step size which is used to {@link NumberInput.increment|increment} or {@link NumberInput.decrement|decrement} the value.
* Default is `1`.
* Default is the smallest possible value depending on the maximum fraction digits.
*
* @example `0.01` with 2 fraction digits
*/
step?: number
/**
Expand Down
12 changes: 10 additions & 2 deletions src/numberInput.ts
Expand Up @@ -108,7 +108,7 @@ export class NumberInput {
}
this.numberFormat = new NumberFormat(this.options)
this.numberMask = this.options.autoDecimalDigits ? new AutoDecimalDigitsNumberMask(this.numberFormat) : new DefaultNumberMask(this.numberFormat)
this.step = options.step && options.step > 0 ? Math.max(options.step, this.toFloat(1)) : this.toFloat(1)
this.step = Math.max(options.step ?? 0, this.getDefaultStep())
this.minValue = this.getMinValue()
this.maxValue = this.getMaxValue()
}
Expand Down Expand Up @@ -136,6 +136,14 @@ export class NumberInput {
return max
}

private getDefaultStep(): number {
let defaultStep = 1
if (this.options.formatStyle === NumberFormatStyle.Percent) {
defaultStep /= 100
}
return this.toFloat(defaultStep)
}

private validateStep(value: number): number {
return this.toInteger(value) % this.toInteger(this.step) !== 0 ? this.getNextStep(value) : value
}
Expand All @@ -145,7 +153,7 @@ export class NumberInput {
}

private toFloat(value: number): number {
return value / Math.pow(10, this.numberFormat.maximumFractionDigits)
return value / 10 ** this.numberFormat.maximumFractionDigits
}

private toInteger(value: number) {
Expand Down
104 changes: 104 additions & 0 deletions tests/unit/numberInput.spec.ts
@@ -0,0 +1,104 @@
// @vitest-environment jsdom
import { NumberFormatStyle } from '@/api'
import { beforeEach, describe, expect, it } from 'vitest'
import { NumberInput } from '@/numberInput'
import { fireEvent } from '@testing-library/dom'

describe('Number Input', () => {
let el: HTMLInputElement

beforeEach(() => {
document.body.innerHTML = `<input type="text">`
el = document.querySelector('input') as HTMLInputElement
})

describe('init', () => {
describe('inputmode attribute', () => {
it('should be "decimal" by default', () => {
new NumberInput({
el,
options: { locale: 'en' }
})
expect(el.getAttribute('inputmode')).toBe('decimal')
})

it('should be "numeric" if autoDecimalDigits is active', () => {
new NumberInput({
el,
options: { locale: 'en', autoDecimalDigits: true }
})
expect(el.getAttribute('inputmode')).toBe('numeric')
})
})
})

describe('setValue', () => {
it('should update the input value', () => {
const numberInput = new NumberInput({
el,
options: {
locale: 'en',
formatStyle: NumberFormatStyle.Currency,
currency: 'EUR'
}
})
numberInput.setValue(1)

expect(el.value).toBe('€1')
})

it('should consider the value range', () => {
const numberInput = new NumberInput({
el,
options: {
locale: 'en',
formatStyle: NumberFormatStyle.Percent,
valueRange: { min: 0.2, max: 0.5 }
}
})
numberInput.setValue(0.19)
expect(el.value).toBe('20%')
numberInput.setValue(0.2)
expect(el.value).toBe('20%')
numberInput.setValue(0.51)
expect(el.value).toBe('50%')
})
})

describe('increment', () => {
it('should increment the current value by the smallest possible step depending on the used precision', () => {
const numberInput = new NumberInput({
el,
options: { locale: 'en' }
})
numberInput.setValue(1)
numberInput.increment()
expect(el.value).toBe('1.001')
})

it('should consider a custom step', () => {
const numberInput = new NumberInput({
el,
options: { locale: 'en', step: 2.5 }
})
numberInput.increment()
expect(el.value).toBe('2.5')
})
})

describe('on input', () => {
it('should update the input value', () => {
new NumberInput({
el,
options: {
locale: 'en',
formatStyle: NumberFormatStyle.Unit,
unit: 'gigabit'
}
})
fireEvent.input(el, { target: { value: '1234567' } })

expect(el.value).toBe('1,234,567 Gb')
})
})
})
6 changes: 3 additions & 3 deletions tsconfig.json
Expand Up @@ -10,7 +10,7 @@
"paths": {
"@/*": ["src/*"]
},
"esModuleInterop": true
},
"include": ["src/*.ts"]
"esModuleInterop": true,
"skipLibCheck": true
}
}
5 changes: 4 additions & 1 deletion vitest.config.ts
@@ -1,6 +1,9 @@
import { defineConfig } from 'vitest/config'

import { fileURLToPath, URL } from 'url'
export default defineConfig({
resolve: {
alias: [{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }]
},
test: {
coverage: {
all: true,
Expand Down

0 comments on commit b724817

Please sign in to comment.