Skip to content

Commit

Permalink
refactor(Editable): update model value only on submit (#1538)
Browse files Browse the repository at this point in the history
* docs(Editable): add `Change only on submit` example

* feat(editable): update flow to update:model-value only on submit

* test(Editable): update `enter` submit props because we have `blur` by default

* test: fix test

* docs: update example

---------

Co-authored-by: zernonia <zernonia@gmail.com>
  • Loading branch information
hrynevychroman and zernonia authored Jan 28, 2025
1 parent d0077d7 commit 8d98c2e
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 9 deletions.
21 changes: 21 additions & 0 deletions docs/content/components/editable.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,27 @@ Contains the cancel trigger of the editable component.

<!-- @include: @/meta/EditableCancelTrigger.md -->

## Examples

### Change only on submit

By default the component will submit when `blur` event triggers. We can modify the `submit-mode` prop to alter this behavior.
In this case, we want to submit only when user click on `EditableSubmitTrigger`, so we change the submit mode to `none`.

```vue line=2,8
<template>
<EditableRoot submit-mode="none">
<EditableArea>
<EditablePreview />
<EditableInput />
</EditableArea>
<EditableEditTrigger />
<EditableSubmitTrigger />
<EditableCancelTrigger />
</EditableRoot>
</template>
```

## Accessibility

### Keyboard Interactions
Expand Down
10 changes: 7 additions & 3 deletions packages/radix-vue/src/Editable/Editable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('editable', () => {
})

it('submits the value when pressing enter', async () => {
const { input, preview, rerender } = setup({ editableProps: { modelValue: '' }, emits: { 'onUpdate:modelValue': (data: string) => rerender({ modelValue: data }) } })
const { input, preview, rerender } = setup({ editableProps: { modelValue: '', submitMode: 'enter' }, emits: { 'onUpdate:modelValue': (data: string) => rerender({ modelValue: data }) } })

await userEvent.type(input, 'New Value')
await userEvent.keyboard(kbd.ENTER)
Expand All @@ -94,8 +94,10 @@ describe('editable', () => {
it('submits the value on blur', async () => {
const { input, preview, rerender } = setup({ editableProps: { modelValue: '', submitMode: 'blur' }, emits: { 'onUpdate:modelValue': (data: string) => rerender({ modelValue: data, submitMode: 'blur' }) } })

await userEvent.dblClick(preview)
await userEvent.type(input, 'New Value')
await userEvent.tab()
await userEvent.click(document.children[0])

expect(preview).toBeVisible()
expect(preview).toHaveTextContent('New Value')
})
Expand All @@ -112,8 +114,10 @@ describe('editable', () => {
it('submits the value on blur if submitMode is both', async () => {
const { input, preview, rerender } = setup({ editableProps: { modelValue: '', submitMode: 'both' }, emits: { 'onUpdate:modelValue': (data: string) => rerender({ modelValue: data, submitMode: 'blur' }) } })

await userEvent.dblClick(preview)
await userEvent.type(input, 'New Value')
await userEvent.tab()
await userEvent.click(document.children[0])

expect(preview).toBeVisible()
expect(preview).toHaveTextContent('New Value')
})
Expand Down
4 changes: 2 additions & 2 deletions packages/radix-vue/src/Editable/EditableInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function handleSubmitKeyDown(event: KeyboardEvent) {
<Primitive
ref="primitiveElement"
v-bind="props"
:value="context.modelValue.value"
:value="context.inputValue.value"
:placeholder="placeholder"
:disabled="disabled"
:maxlength="context.maxLength.value"
Expand All @@ -63,7 +63,7 @@ function handleSubmitKeyDown(event: KeyboardEvent) {
aria-label="editable input"
:hidden="context.autoResize.value ? undefined : !context.isEditing.value"
:style="context.autoResize.value ? { all: 'unset', gridArea: '1 / 1 / auto / auto', visibility: !context.isEditing.value ? 'hidden' : undefined } : undefined"
@input="context.modelValue.value = $event.target.value"
@input="context.inputValue.value = $event.target.value"
@keydown.enter.space="handleSubmitKeyDown"
@keydown.esc="context.cancel"
>
Expand Down
15 changes: 11 additions & 4 deletions packages/radix-vue/src/Editable/EditableRoot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type EditableRootContext = {
maxLength: Ref<number | undefined>
disabled: Ref<boolean>
modelValue: Ref<string | undefined>
inputValue: Ref<string | undefined>
placeholder: Ref<{ edit: string, preview: string }>
isEditing: Ref<boolean>
submitMode: Ref<SubmitMode>
Expand Down Expand Up @@ -75,7 +76,7 @@ export const [injectEditableRootContext, provideEditableRootContext]
</script>

<script setup lang="ts">
import { type Ref, computed, ref, toRefs } from 'vue'
import { type Ref, computed, ref, toRefs, watch } from 'vue'
import { Primitive, usePrimitiveElement } from '@/Primitive'
import { useVModel } from '@vueuse/core'
Expand Down Expand Up @@ -146,21 +147,26 @@ const placeholder = computed(() => {
return typeof propPlaceholder.value === 'string' ? { edit: propPlaceholder.value, preview: propPlaceholder.value } : propPlaceholder.value
})
const previousValue = ref(modelValue.value)
const inputValue = ref(modelValue.value)
watch(() => modelValue.value, () => {
inputValue.value = modelValue.value
}, { immediate: true, deep: true })
function cancel() {
modelValue.value = previousValue.value
isEditing.value = false
emits('update:state', 'cancel')
}
function edit() {
isEditing.value = true
inputValue.value = modelValue.value
emits('update:state', 'edit')
}
function submit() {
previousValue.value = modelValue.value
modelValue.value = inputValue.value
isEditing.value = false
emits('update:state', 'submit')
Expand Down Expand Up @@ -196,6 +202,7 @@ provideEditableRootContext({
isEditing,
maxLength,
modelValue,
inputValue,
placeholder,
edit,
cancel,
Expand Down

0 comments on commit 8d98c2e

Please sign in to comment.