Skip to content

Commit

Permalink
fix(VNumberInput): disable up/down controls for readonly state (#19906)
Browse files Browse the repository at this point in the history
Co-authored-by: jsek <im68889094144@outlook.com>
Co-authored-by: Yuchao Wu <yuchao.sydneyuni@gmail.com>
  • Loading branch information
3 people authored May 28, 2024
1 parent 2877f4f commit 4ceaaa5
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 0 deletions.
10 changes: 10 additions & 0 deletions packages/vuetify/src/labs/VNumberInput/VNumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { VDivider } from '../../components/VDivider'
import { makeVTextFieldProps, VTextField } from '@/components/VTextField/VTextField'

// Composables
import { useForm } from '@/composables/form'
import { useProxiedModel } from '@/composables/proxiedModel'

// Utilities
Expand Down Expand Up @@ -71,16 +72,24 @@ export const VNumberInput = genericComponent<VNumberInputSlots>()({
const stepDecimals = computed(() => getDecimals(props.step))
const modelDecimals = computed(() => model.value != null ? getDecimals(model.value) : 0)

const form = useForm()
const controlsDisabled = computed(() => (
props.disabled || props.readonly || form?.isReadonly.value
))

const canIncrease = computed(() => {
if (controlsDisabled.value) return false
if (model.value == null) return true
return model.value + props.step <= props.max
})
const canDecrease = computed(() => {
if (controlsDisabled.value) return false
if (model.value == null) return true
return model.value - props.step >= props.min
})

watchEffect(() => {
if (controlsDisabled.value) return
if (model.value != null && (model.value < props.min || model.value > props.max)) {
model.value = clamp(model.value, props.min, props.max)
}
Expand All @@ -95,6 +104,7 @@ export const VNumberInput = genericComponent<VNumberInputSlots>()({
const decrementSlotProps = computed(() => ({ click: onClickDown }))

function toggleUpDown (increment = true) {
if (controlsDisabled.value) return
if (model.value == null) {
model.value = 0
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,115 @@

// Components
import { VNumberInput } from '../VNumberInput'
import { VForm } from '@/components/VForm'

// Utilities
import { ref } from 'vue'

describe('VNumberInput', () => {
describe('readonly', () => {
it('should prevent mutation when readonly applied', () => {
const value = ref(1)

cy.mount(() => (
<>
<VNumberInput
class="standalone-input"
v-model={ value.value }
readonly
/>
</>
))

cy.get('.v-field input').as('input')
cy.get('.v-number-input__control .v-btn:first-child').click({ force: true })
cy.get('@input').should('have.value', '1')

cy.get('.v-number-input__control .v-btn:last-child').click({ force: true })
cy.get('@input').should('have.value', '1')

cy.get('@input')
.focus()
.type('{uparrow}', { force: true })
.should('have.value', '1')
.type('{downarrow}', { force: true })
.should('have.value', '1')
})
it('should prevent mutation when readonly applied to parent form', () => {
const value = ref(1)

cy.mount(() => (
<>
<VForm readonly>
<VNumberInput
class="input-in-form"
v-model={ value.value }
/>
</VForm>
</>
))

cy.get('.v-field input').as('input')
cy.get('.v-number-input__control .v-btn:first-child').click({ force: true })
cy.get('@input').should('have.value', '1')

cy.get('.v-number-input__control .v-btn:last-child').click({ force: true })
cy.get('@input').should('have.value', '1')

cy.get('@input')
.focus()
.type('{uparrow}', { force: true })
.should('have.value', '1')
.type('{downarrow}', { force: true })
.should('have.value', '1')
})

it('should keep original value when readonly or disabled', () => {
const value1 = ref(120)
const value2 = ref(-15)
const value3 = ref(40.4)
const value4 = ref(-8.6)

cy.mount(() => (
<>
<VNumberInput
class="readonly-input-1"
v-model={ value1.value }
min={ 0 }
max={ 50 }
readonly
/>
<VNumberInput
class="readonly-input-2"
v-model={ value2.value }
min={ 0 }
max={ 50 }
readonly
/>
<VNumberInput
class="disabled-input-1"
v-model={ value3.value }
min={ 0 }
max={ 10 }
disabled
/>
<VNumberInput
class="disabled-input-2"
v-model={ value4.value }
min={ 0 }
max={ 10 }
disabled
/>
</>
))

cy.get('.readonly-input-1 input').should('have.value', '120')
cy.get('.readonly-input-2 input').should('have.value', '-15')
cy.get('.disabled-input-1 input').should('have.value', '40.4')
cy.get('.disabled-input-2 input').should('have.value', '-8.6')
})
})

describe('native number input quirks', () => {
it('should not bypass min', () => {
const numberInputValue = ref(1)
Expand Down

0 comments on commit 4ceaaa5

Please sign in to comment.