Skip to content

Commit

Permalink
fix(editable): #5670 call setPrevValue in onFocus to avoid outdated d…
Browse files Browse the repository at this point in the history
…ata when controlled (#5684)
  • Loading branch information
takethefake authored Mar 15, 2022
1 parent 2328530 commit 6ec0974
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 3 deletions.
6 changes: 6 additions & 0 deletions .changeset/dry-worms-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@chakra-ui/editable": patch
---

Call `setPrevValue` `onFocus` to avoid an outdated prev value when the field is
controlled
21 changes: 19 additions & 2 deletions packages/editable/src/use-editable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ export function useEditable(props: UseEditableProps = {}) {
}
}, [isInteractive])

const onUpdatePrevValue = useCallback(() => {
setPrevValue(value)
}, [value])

const onCancel = useCallback(() => {
setIsEditing(false)
setValue(prevValue)
Expand Down Expand Up @@ -247,7 +251,7 @@ export function useEditable(props: UseEditableProps = {}) {
hidden: isEditing,
"aria-disabled": ariaAttr(isDisabled),
tabIndex,
onFocus: callAllHandlers(props.onFocus, onEdit),
onFocus: callAllHandlers(props.onFocus, onEdit, onUpdatePrevValue),
}
},
[
Expand All @@ -257,6 +261,7 @@ export function useEditable(props: UseEditableProps = {}) {
isPreviewFocusable,
isValueEmpty,
onEdit,
onUpdatePrevValue,
placeholder,
value,
],
Expand All @@ -277,8 +282,18 @@ export function useEditable(props: UseEditableProps = {}) {
onBlur: callAllHandlers(props.onBlur, onBlur),
onChange: callAllHandlers(props.onChange, onChange),
onKeyDown: callAllHandlers(props.onKeyDown, onKeyDown),
onFocus: callAllHandlers(props.onFocus, onUpdatePrevValue),
}),
[isDisabled, isEditing, onBlur, onChange, onKeyDown, placeholder, value],
[
isDisabled,
isEditing,
onBlur,
onChange,
onKeyDown,
onUpdatePrevValue,
placeholder,
value,
],
)

const getTextareaProps: PropGetterV2<
Expand All @@ -296,13 +311,15 @@ export function useEditable(props: UseEditableProps = {}) {
onBlur: callAllHandlers(props.onBlur, onBlur),
onChange: callAllHandlers(props.onChange, onChange),
onKeyDown: callAllHandlers(props.onKeyDown, onKeyDownWithoutSubmit),
onFocus: callAllHandlers(props.onFocus, onUpdatePrevValue),
}),
[
isDisabled,
isEditing,
onBlur,
onChange,
onKeyDownWithoutSubmit,
onUpdatePrevValue,
placeholder,
value,
],
Expand Down
35 changes: 35 additions & 0 deletions packages/editable/stories/editable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
EditableTextarea,
useEditableControls,
} from "../src"
import { Heading } from "@chakra-ui/layout"

export default {
title: "Components / Forms / Editable",
Expand Down Expand Up @@ -125,3 +126,37 @@ export const TextareaAsInput = () => {
</Editable>
)
}

export const EditableEventHandler = () => {
const [name, setName] = React.useState("")
console.log("State 'name' is ", name)

React.useEffect(() => {
setName("John")
}, [])

return (
<>
<Heading>Name State=[{name}]</Heading>
<Editable
value={name}
onChange={(value) => {
console.log("onChange called with ", value)
setName(value)
}}
onSubmit={(value) => {
console.log("onSubmit called with ", value)
setName(value)
}}
onCancel={(value) => {
console.log("onCancel called with ", value)
setName(value)
}}
placeholder="Enter your name"
>
<EditablePreview />
<EditableInput />
</Editable>
</>
)
}
53 changes: 53 additions & 0 deletions packages/editable/tests/editable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,56 @@ test("startWithEditView when true focuses on the input ", () => {

expect(document.activeElement === input).toBe(true)
})

test.each([
{ startWithEditView: true, text: undefined },
{ startWithEditView: false, text: undefined },
{ startWithEditView: true, text: "Bob" },
{ startWithEditView: false, text: "Bob" },
])(
"controlled: sets value toPrevValue onCancel, startWithEditView: $startWithEditView",
({ startWithEditView, text }) => {
const Component = () => {
const [name, setName] = React.useState("")

React.useEffect(() => {
setName("John")
}, [])

return (
<Editable
value={name}
startWithEditView={startWithEditView}
onChange={(value) => {
setName(value)
}}
onSubmit={(value) => {
setName(value)
}}
onCancel={(value) => {
setName(value)
}}
placeholder="Enter your name"
>
<EditablePreview data-testid="preview" />
<EditableInput data-testid="input" />
</Editable>
)
}

render(<Component />)
const input = screen.getByTestId("input")
const preview = screen.getByTestId("preview")
if (!startWithEditView) {
fireEvent.focus(preview)
} else {
fireEvent.focus(input)
}
if (text) {
userEvent.type(input, text)
}
fireEvent.keyDown(input, { key: "Escape" })

expect(preview).toHaveTextContent("John")
},
)
59 changes: 58 additions & 1 deletion packages/editable/tests/editableTextarea.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import {
userEvent,
} from "@chakra-ui/test-utils"
import * as React from "react"
import { Editable, EditablePreview, EditableTextarea } from "../src"
import {
Editable,
EditableInput,
EditablePreview,
EditableTextarea,
} from "../src"

test("matches snapshot", () => {
render(
Expand Down Expand Up @@ -209,3 +214,55 @@ test("editable textarea can submit on blur", () => {
fireEvent.blur(textarea)
expect(onSubmit).toHaveBeenCalledWith("testing")
})
test.each([
{ startWithEditView: true, text: undefined },
{ startWithEditView: false, text: undefined },
{ startWithEditView: true, text: "Bob" },
{ startWithEditView: false, text: "Bob" },
])(
"controlled: sets value toPrevValue onCancel, startWithEditView: $startWithEditView",
({ startWithEditView, text }) => {
const Component = () => {
const [name, setName] = React.useState("")

React.useEffect(() => {
setName("John")
}, [])

return (
<Editable
value={name}
startWithEditView={startWithEditView}
onChange={(value) => {
setName(value)
}}
onSubmit={(value) => {
setName(value)
}}
onCancel={(value) => {
setName(value)
}}
placeholder="Enter your name"
>
<EditablePreview data-testid="preview" />
<EditableTextarea data-testid="input" />
</Editable>
)
}

render(<Component />)
const input = screen.getByTestId("input")
const preview = screen.getByTestId("preview")
if (!startWithEditView) {
fireEvent.focus(preview)
} else {
fireEvent.focus(input)
}
if (text) {
userEvent.type(input, text)
}
fireEvent.keyDown(input, { key: "Escape" })

expect(preview).toHaveTextContent("John")
},
)

1 comment on commit 6ec0974

@vercel
Copy link

@vercel vercel bot commented on 6ec0974 Mar 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.