Skip to content

[WIP] [EuiInlineEdit] Allow EuiInlineEdit to be used as a controlled component#7117

Closed
breehall wants to merge 3 commits intoelastic:mainfrom
breehall:inline-edit/controlled
Closed

[WIP] [EuiInlineEdit] Allow EuiInlineEdit to be used as a controlled component#7117
breehall wants to merge 3 commits intoelastic:mainfrom
breehall:inline-edit/controlled

Conversation

@breehall
Copy link
Contributor

@breehall breehall commented Aug 23, 2023

🛑 WIP - DO NOT MERGE

#7084

Summary

Allows EuiInlineEdit to be used as a controlled component with the addition of three new props
More to come (this is a WIP)

Feature Checklist

  1. Create three new props
    1. value - controlled value for consumers
    2. onChange - onChange callback that returns the HTML event
    3. onCancel - onCancel callback that returns the previous value
  2. Create exclusive union between defaultValue and {value, onChange}
  3. Create useMemo to return valueToUse
    1. Should return which value should be used in edit mode and read mode elements.
      1. If ( value ) - use it | Else Fall back to readModeValue / editModeValue based on isEditing
    2. Replace instances of readModeValue & editModeValue with valueToUse
  4. Pass onChange from props to edit mode form element (ln. 238)

QA

Remove or strikethrough items that do not apply to your PR.

General checklist

  • Checked in both light and dark modes
  • Checked in mobile
  • Checked in Chrome, Safari, Edge, and Firefox
  • Props have proper autodocs (using @default if default values are missing) and playground toggles
  • Added documentation
  • Checked Code Sandbox works for any docs examples
  • Added or updated jest and cypress tests
  • Checked for breaking changes and labeled appropriately
  • Checked for accessibility including keyboard-only and screenreader modes
  • Updated the Figma library counterpart
  • A changelog entry exists and is marked appropriately

…as a controlled component.

- Added the value and onChange props to be used when inline edit is a controlled component
- Separated defaultValue and controlled props (value & onChange) into an exclusive union to ensure inline edit cannot be used in both states at once
- Add the onCancel callback prop to allow consumers to fallback to the previous inline edit value upon cancel
- Create a DRY helper to switch between readModeValue, editModeValue, and value appropriately
@elasticmachine
Copy link
Collaborator

elasticmachine commented Aug 23, 2023

💔 Build Failed

Failed CI Steps

History


const cancelInlineEdit = () => {
setEditModeValue(readModeValue);
onCancel && onCancel(readModeValue);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am using readModeValue instead of valueToUse here because is contains the prior text value

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_7117/

Comment on lines +71 to +77
value={inlineEditValue}
onChange={(e) => {
setInlineEditValue(e.target.value);
}}
onCancel={(previousValue) => {
setInlineEditValue(previousValue);
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

👌💋 B-E-A-utiful developer experience!

// If an onSave callback is present, and returns false, stay in edit mode
if (onSave) {
const onSaveReturn = onSave(editModeValue);
const onSaveReturn = onSave(valueToUse);
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll probably also want to test that onSave works with a controlled value as well. How you prefer to test that is up to you; whether via docs, Jest test, or Storybook

Comment on lines +190 to +191
const [editModeValue, setEditModeValue] = useState(value || defaultValue);
const [readModeValue, setReadModeValue] = useState(value || defaultValue);
Copy link
Contributor

@cee-chen cee-chen Aug 23, 2023

Choose a reason for hiding this comment

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

My 2c is I don't think we need to use value || here for our internal state. Since they're only used for uncontrolled behavior, they only need to listen for defaultValue.

Suggested change
const [editModeValue, setEditModeValue] = useState(value || defaultValue);
const [readModeValue, setReadModeValue] = useState(value || defaultValue);
const [editModeValue, setEditModeValue] = useState(defaultValue);
const [readModeValue, setReadModeValue] = useState(defaultValue);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This logic is actually how I'm able to pass readModeValue to the onCancel function. Otherwise, the readModeValue falls out of sync and isn't a reliable backup of the previous value.
Link to code sample mentioned: #7117 (comment)

Copy link
Contributor

@cee-chen cee-chen Aug 25, 2023

Choose a reason for hiding this comment

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

Ahh super interesting! If readModeValue is the only one that needs this logic, let's set it just for that and add an inline comment noting why

Comment on lines +193 to +194
const valueToUse = useMemo(() => {
if (value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

[naming things is hard] how do you feel about renaming the consumer-passed value prop to value: controlledValue or possibly just _value, and then just naming this value?

if (value) {
return value;
} else {
return isEditing ? editModeValue : readModeValue || placeholder;
Copy link
Contributor

Choose a reason for hiding this comment

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

I still can't get over how elegant this ternary is haha. Super awesome!

inputRef={setEditModeRefs}
onChange={(e) => {
setEditModeValue(e.target.value);
onChange && onChange(e);
Copy link
Contributor

Choose a reason for hiding this comment

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

quick nit while I'm here: onChange?.(e) for brevity

@breehall breehall closed this Sep 5, 2023
@breehall
Copy link
Contributor Author

breehall commented Sep 5, 2023

Closing this PR in favor #7157

@breehall breehall deleted the inline-edit/controlled branch October 6, 2023 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants