Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HOLD for payment 2023-11-09] [HOLD for #24198] [$500] Pressing Enter on the state search page submits the form instead of only selecting the state. #26345

Closed
1 of 6 tasks
kavimuru opened this issue Aug 30, 2023 · 51 comments
Assignees
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 Engineering Internal Requires API changes or must be handled by Expensify staff

Comments

@kavimuru
Copy link

kavimuru commented Aug 30, 2023

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Action Performed:

1-Go to settings -> workspaces -> your workspace -> add back 2- account -> connect manually.
3- Fill in the data.
4- Click on the "Connect" button.
5- Open the state search page.
6- Use the arrow keys to navigate to a state.
7- Press Enter.

Expected Result:

Pressing Enter should only select the state and return the user to the form.

Actual Result:

Pressing Enter selects the state and submits the form.

Workaround:

Can the user still use Expensify without this being fixed? Have you informed them of the workaround?

Platforms:

Which of our officially supported platforms is this issue occurring on?

  • Android / native
  • Android / Chrome
  • iOS / native
  • iOS / Safari
  • MacOS / Chrome / Safari
  • MacOS / Desktop

Version Number: 1.3.59-1
Reproducible in staging?: y
Reproducible in production?: y
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Notes/Photos/Videos: Any additional supporting documentation

Screen.Recording.2023-08-21.at.2.52.27.PM.mov
Recording.1542.mp4

Expensify/Expensify Issue URL:
Issue reported by: @ahmdshrif
Slack conversation: https://expensify.slack.com/archives/C049HHMV9SM/p1692629203062389

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~01bf4700c16d6a333f
  • Upwork Job ID: 1699765868129120256
  • Last Price Increase: 2023-09-21
@kavimuru kavimuru added Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. labels Aug 30, 2023
@melvin-bot
Copy link

melvin-bot bot commented Aug 30, 2023

Triggered auto assignment to @sonialiap (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details.

@melvin-bot
Copy link

melvin-bot bot commented Aug 30, 2023

Bug0 Triage Checklist (Main S/O)

  • This "bug" occurs on a supported platform (ensure Platforms in OP are ✅)
  • This bug is not a duplicate report (check E/App issues and #expensify-bugs)
    • If it is, comment with a link to the original report, close the issue and add any novel details to the original issue instead
  • This bug is reproducible using the reproduction steps in the OP. S/O
    • If the reproduction steps are clear and you're unable to reproduce the bug, check with the reporter and QA first, then close the issue.
    • If the reproduction steps aren't clear and you determine the correct steps, please update the OP.
  • This issue is filled out as thoroughly and clearly as possible
    • Pay special attention to the title, results, platforms where the bug occurs, and if the bug happens on staging/production.
  • I have reviewed and subscribed to the linked Slack conversation to ensure Slack/Github stay in sync

@akinwale
Copy link
Contributor

akinwale commented Aug 30, 2023

Proposal

Please re-state the problem that we are trying to solve in this issue.

Pressing Enter on the state search page submits the form instead of only selecting the state.

What is the root cause of that problem?

The form submit button in the CompanyStep component is receiving key press events even when the StatePicker modal is visible.

What changes do you think we should make in order to solve the problem?

Add props to the StatePicker and AddressForm modules to handle show / hide events for the state picker modal, and then set the a flag using the state to indicate whether or not the form should be submitted.

We should also do this for the RequestorStep page, forwarding the onShowStatePicker / onHideStatePicker props through IdentityForm (since this contains the AddressForm).

The solution can be achieved with the following steps:

  1. Add an onShow prop to StateSelectorModal, and set it to the onModalShow prop for the root Modal.
onShow: PropTypes.func, // prop type
...
onModalShow={onShow}
  1. Add onShow and onClose props to the StatePicker component and update the prop types and default props accordingly. Also update the hidePickerModal handler.
<StateSelectorModal
    ...
    onShow={onShow}
/>
...
const hidePickerModal = () => {
    if (onClose) {
        onClose();
    }
    setIsPickerVisible(false);
};
  1. Handle the onShow and onHide props for the StatePicker in the AddressForm component. Also add the corresponding prop types and default props (onShowStatePicker / onHideStatePicker).
<StatePicker
    ...
    onShow={props.onShowStatePicker}
    onHide={props.onHideStatePicker}
    ...
/>
  1. Add onShow and onHide props for the StatePicker in the CompanyStep component, as well as the handlers.
const [shouldDisableFormSubmit, setShouldDisableFormSubmit] = useState(false);
const showStatePicker = () => {
    setShouldDisableFormSubmit(true);
}
const hideStatePicker = () => {
    setShouldDisableFormSubmit(false);
}
...
<StatePicker
    ...
    onShow={showStatePicker}
    onClose={hideStatePicker}
/>
  1. Update the submit handler in the CompanyStep component.
const submit = (values) => {
      if (shouldDisableFormSubmit) {
          return;
      }
    // the rest of the submit handler implementation
}

What alternative solutions did you explore?

We could also consider making the button disabled when the state picker is visible, but this will require updating the Form and FormAlertWithSubmitButton components by adding the required prop and its prop type.

@tamdao
Copy link
Contributor

tamdao commented Aug 30, 2023

Proposal

Please re-state the problem that we are trying to solve in this issue.

Pressing Enter on the state search page submits the form instead of only selecting the state

What is the root cause of that problem?

We have logic onKeyDownCapture to call onSubmit function when user type Enter key, but didn't check the input is belong form or not. So, any input event always calls onSubmit function if user type Enter and happen for all form has input not belong from.

In this case, the search input is not belonged to form, it uses for search state

const submitForm = (event) => {
// ENTER is pressed with modifier key or during text composition, do not submit the form
if (event.shiftKey || event.key !== CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey || isEnterWhileComposition(event)) {
return;
}
const tagName = lodashGet(event, 'target.tagName', '');
// ENTER is pressed on INPUT or SELECT element, call the submit callback.
if (tagName === 'INPUT' || tagName === 'SELECT') {
onSubmit();
return;
}
// Pressing Enter on TEXTAREA element adds a new line. When `dataset.submitOnEnter` prop is passed, call the submit callback.
if (tagName === 'TEXTAREA' && lodashGet(event, 'target.dataset.submitOnEnter', 'false') === 'true') {
onSubmit();
return;
}
// ENTER is pressed on checkbox element, call the submit callback.
if (lodashGet(event, 'target.role') === 'checkbox') {
onSubmit();
}
};

What alternative solutions did you explore?

Update childrenWrapperWithProps in Form component to add dataSet when call React.cloneElement line 294

App/src/components/Form.js

Lines 219 to 357 in 1b63c76

const childrenWrapperWithProps = useCallback(
(childNodes) => {
const childrenElements = React.Children.map(childNodes, (child) => {
// Just render the child if it is not a valid React element, e.g. text within a <Text> component
if (!React.isValidElement(child)) {
return child;
}
// Depth first traversal of the render tree as the input element is likely to be the last node
if (child.props.children) {
return React.cloneElement(child, {
children: childrenWrapperWithProps(child.props.children),
});
}
// Look for any inputs nested in a custom component, e.g AddressForm or IdentityForm
if (_.isFunction(child.type)) {
const childNode = new child.type(child.props);
// If the custom component has a render method, use it to get the nested children
const nestedChildren = _.isFunction(childNode.render) ? childNode.render() : childNode;
// Render the custom component if it's a valid React element
// If the custom component has nested children, Loop over them and supply From props
if (React.isValidElement(nestedChildren) || lodashGet(nestedChildren, 'props.children')) {
return childrenWrapperWithProps(nestedChildren);
}
// Just render the child if it's custom component not a valid React element, or if it hasn't children
return child;
}
// We check if the child has the inputID prop.
// We don't want to pass form props to non form components, e.g. View, Text, etc
if (!child.props.inputID) {
return child;
}
// We clone the child passing down all form props
const inputID = child.props.inputID;
let defaultValue;
// We need to make sure that checkboxes have correct
// value assigned from the list of draft values
// https://github.com/Expensify/App/issues/16885#issuecomment-1520846065
if (_.isBoolean(props.draftValues[inputID])) {
defaultValue = props.draftValues[inputID];
} else {
defaultValue = props.draftValues[inputID] || child.props.defaultValue;
}
// We want to initialize the input value if it's undefined
if (_.isUndefined(inputValues[inputID])) {
inputValues[inputID] = defaultValue || '';
}
// We force the form to set the input value from the defaultValue props if there is a saved valid value
if (child.props.shouldUseDefaultValue) {
inputValues[inputID] = child.props.defaultValue;
}
if (!_.isUndefined(child.props.value)) {
inputValues[inputID] = child.props.value;
}
const errorFields = lodashGet(props.formState, 'errorFields', {});
const fieldErrorMessage =
_.chain(errorFields[inputID])
.keys()
.sortBy()
.reverse()
.map((key) => errorFields[inputID][key])
.first()
.value() || '';
return React.cloneElement(child, {
ref: (node) => {
inputRefs.current[inputID] = node;
const {ref} = child;
if (_.isFunction(ref)) {
ref(node);
}
},
value: inputValues[inputID],
// As the text input is controlled, we never set the defaultValue prop
// as this is already happening by the value prop.
defaultValue: undefined,
errorText: errors[inputID] || fieldErrorMessage,
onBlur: (event) => {
// Only run validation when user proactively blurs the input.
if (Visibility.isVisible() && Visibility.hasFocus()) {
// We delay the validation in order to prevent Checkbox loss of focus when
// the user are focusing a TextInput and proceeds to toggle a CheckBox in
// web and mobile web platforms.
setTimeout(() => {
setTouchedInput(inputID);
if (props.shouldValidateOnBlur) {
onValidate(inputValues);
}
}, 200);
}
if (_.isFunction(child.props.onBlur)) {
child.props.onBlur(event);
}
},
onTouched: () => {
setTouchedInput(inputID);
},
onInputChange: (value, key) => {
const inputKey = key || inputID;
setInputValues((prevState) => {
const newState = {
...prevState,
[inputKey]: value,
};
if (props.shouldValidateOnChange) {
onValidate(newState);
}
return newState;
});
if (child.props.shouldSaveDraft) {
FormActions.setDraftValues(props.formID, {[inputKey]: value});
}
if (child.props.onValueChange) {
child.props.onValueChange(value, inputKey);
}
},
});
});
return childrenElements;
},
[errors, inputRefs, inputValues, onValidate, props.draftValues, props.formID, props.formState, setTouchedInput, props.shouldValidateOnBlur, props.shouldValidateOnChange],
);

like this:

dataSet: {formInputId: inputID},

Update BaseTextInput to set dataSet line 350

dataSet={{submitOnEnter: isMultiline && props.submitOnEnter}}

dataSet={{...props.dataSet, submitOnEnter: isMultiline && props.submitOnEnter}}

Update AddressSearch to set props.dataSet into textInputProps

textInputProps={{
InputComp: TextInput,
ref: (node) => {
if (!props.innerRef) {
return;
}
if (_.isFunction(props.innerRef)) {
props.innerRef(node);
return;
}
// eslint-disable-next-line no-param-reassign
props.innerRef.current = node;
},
label: props.label,
containerStyles: props.containerStyles,
errorText: props.errorText,
hint: displayListViewBorder ? undefined : props.hint,
value: props.value,
defaultValue: props.defaultValue,
inputID: props.inputID,
shouldSaveDraft: props.shouldSaveDraft,
onBlur: (event) => {
resetDisplayListViewBorderOnBlur(setDisplayListViewBorder, event, containerRef);
props.onBlur();
},
autoComplete: 'off',
onInputChange: (text) => {
if (props.inputID) {
props.onInputChange(text);
} else {
props.onInputChange({street: text});
}
// If the text is empty, we set displayListViewBorder to false to prevent UI flickering
if (_.isEmpty(text)) {
setDisplayListViewBorder(false);
}
},
maxLength: props.maxInputLength,
spellCheck: false,
}}

Update submitForm function in FromSubmit component to check the formInputId dataset

const submitForm = (event) => {
// ENTER is pressed with modifier key or during text composition, do not submit the form
if (event.shiftKey || event.key !== CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey || isEnterWhileComposition(event)) {
return;
}
const tagName = lodashGet(event, 'target.tagName', '');
// ENTER is pressed on INPUT or SELECT element, call the submit callback.
if (tagName === 'INPUT' || tagName === 'SELECT') {
onSubmit();
return;
}
// Pressing Enter on TEXTAREA element adds a new line. When `dataset.submitOnEnter` prop is passed, call the submit callback.
if (tagName === 'TEXTAREA' && lodashGet(event, 'target.dataset.submitOnEnter', 'false') === 'true') {
onSubmit();
return;
}
// ENTER is pressed on checkbox element, call the submit callback.
if (lodashGet(event, 'target.role') === 'checkbox') {
onSubmit();
}
};

like this

...

if (event.shiftKey || event.key !== CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey || isEnterWhileComposition(event)) { 
    return; 
} 

if (!lodashGet(event, 'target.dataset.formInputId')) {
    return;
}
...

@melvin-bot melvin-bot bot added the Overdue label Sep 4, 2023
@melvin-bot
Copy link

melvin-bot bot commented Sep 5, 2023

@sonialiap Huh... This is 4 days overdue. Who can take care of this?

@sonialiap
Copy link
Contributor

Triaging to external ⏭️

@melvin-bot melvin-bot bot removed the Overdue label Sep 7, 2023
@sonialiap sonialiap added the External Added to denote the issue can be worked on by a contributor label Sep 7, 2023
@melvin-bot melvin-bot bot changed the title Pressing Enter on the state search page submits the form instead of only selecting the state. [$500] Pressing Enter on the state search page submits the form instead of only selecting the state. Sep 7, 2023
@melvin-bot
Copy link

melvin-bot bot commented Sep 7, 2023

Job added to Upwork: https://www.upwork.com/jobs/~01bf4700c16d6a333f

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Sep 7, 2023
@melvin-bot
Copy link

melvin-bot bot commented Sep 7, 2023

Current assignee @sonialiap is eligible for the External assigner, not assigning anyone new.

@melvin-bot
Copy link

melvin-bot bot commented Sep 7, 2023

Triggered auto assignment to Contributor-plus team member for initial proposal review - @sobitneupane (External)

@ramachoon
Copy link

There should be listener what listens button actions.
When it listens Enter action of keyboard, it should check and validate, and next process will be submitting.
If you give me choice, I will fix it in this way.

@melvin-bot
Copy link

melvin-bot bot commented Sep 7, 2023

📣 @edward-potter818! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
Please follow these steps:

  1. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
  2. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
  3. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
    Screen Shot 2022-11-16 at 4 42 54 PM
    Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>

@sicarius97
Copy link

Proposal

Please re-state the problem that we are trying to solve in this issue.

Pressing enter while in state selector submits the form rather than selecting the state of choice

What is the root cause of that problem?

The state select dropdown doesn't lock the proper focus when open so actions that should occur inside the focus context of the dropdown instead happen outside of it, as is default behavior for forms, but not very good by accessibility standards

What changes do you think we should make in order to solve the problem?

While some of the previously proposed solutions mentioned capturing the event and altering its logic to prevent the default behavior, I think it was being altered at the wrong level in the previous proposals in a way that was not as composable. There is already a component for capturing the focus of the arrow keys here:

class ArrowKeyFocusManager extends Component {

Which is used by multiple selects to capture the focus of the arrow keys. This component can be extended to also capture the focus of the 'ENTER' key stroke and would fix this problem for not only this selection box, but any future selection boxes as well, making it a more extensible solution than previous proposals

@melvin-bot
Copy link

melvin-bot bot commented Sep 8, 2023

📣 @sicarius97! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
Please follow these steps:

  1. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
  2. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
  3. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
    Screen Shot 2022-11-16 at 4 42 54 PM
    Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>

@sicarius97
Copy link

Contributor details
Your Expensify account email: icroatt97@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~01dbdc4055191721a0

@melvin-bot
Copy link

melvin-bot bot commented Sep 8, 2023

✅ Contributor details stored successfully. Thank you for contributing to Expensify!

@melvin-bot
Copy link

melvin-bot bot commented Sep 11, 2023

@sonialiap, @sobitneupane Whoops! This issue is 2 days overdue. Let's get this updated quick!

@melvin-bot melvin-bot bot added the Overdue label Sep 11, 2023
@melvin-bot
Copy link

melvin-bot bot commented Sep 11, 2023

@sonialiap, @sobitneupane Whoops! This issue is 2 days overdue. Let's get this updated quick!

@sonialiap
Copy link
Contributor

@sobitneupane what do you think of the existing proposal?

@melvin-bot melvin-bot bot removed the Overdue label Sep 12, 2023
@sobitneupane
Copy link
Contributor

sobitneupane commented Sep 13, 2023

Thanks for the proposal everyone.

This component can be extended to also capture the focus of the 'ENTER' key stroke

@sicarius97 Can you please share more details on proposal. How are you planning to extend the component to manage ENTER key?

@melvin-bot melvin-bot bot added the Overdue label Oct 9, 2023
@melvin-bot
Copy link

melvin-bot bot commented Oct 10, 2023

@sonialiap, @sobitneupane Eep! 4 days overdue now. Issues have feelings too...

@sonialiap
Copy link
Contributor

On hold for #24198

@melvin-bot melvin-bot bot added Overdue and removed Overdue labels Oct 10, 2023
@sonialiap
Copy link
Contributor

Still on hold for #24198

That issue isn't super close to being resolved and I'll be OOO until Oct 22 so I'm going to update this issue to "weekly" for now.

@melvin-bot melvin-bot bot removed the Overdue label Oct 13, 2023
@sonialiap sonialiap added Weekly KSv2 and removed Daily KSv2 labels Oct 13, 2023
@melvin-bot melvin-bot bot added the Overdue label Oct 23, 2023
@sonialiap
Copy link
Contributor

Still on hold for #24198

@melvin-bot melvin-bot bot removed the Overdue label Oct 23, 2023
@melvin-bot melvin-bot bot added Reviewing Has a PR in review Weekly KSv2 and removed Weekly KSv2 labels Oct 24, 2023
@melvin-bot melvin-bot bot added Weekly KSv2 Awaiting Payment Auto-added when associated PR is deployed to production and removed Weekly KSv2 labels Nov 2, 2023
@melvin-bot melvin-bot bot changed the title [HOLD for #24198] [$500] Pressing Enter on the state search page submits the form instead of only selecting the state. [HOLD for payment 2023-11-09] [HOLD for #24198] [$500] Pressing Enter on the state search page submits the form instead of only selecting the state. Nov 2, 2023
@melvin-bot melvin-bot bot removed the Reviewing Has a PR in review label Nov 2, 2023
Copy link

melvin-bot bot commented Nov 2, 2023

Reviewing label has been removed, please complete the "BugZero Checklist".

Copy link

melvin-bot bot commented Nov 2, 2023

The solution for this issue has been 🚀 deployed to production 🚀 in version 1.3.94-2 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

If no regressions arise, payment will be issued on 2023-11-09. 🎊

After the hold period is over and BZ checklist items are completed, please complete any of the applicable payments for this issue, and check them off once done.

  • External issue reporter
  • Contributor that fixed the issue
  • Contributor+ that helped on the issue and/or PR

For reference, here are some details about the assignees on this issue:

Copy link

melvin-bot bot commented Nov 2, 2023

BugZero Checklist: The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed:

  • [@sobitneupane] The PR that introduced the bug has been identified. Link to the PR:
  • [@sobitneupane] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake. Link to comment:
  • [@sobitneupane] A discussion in #expensify-bugs has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner. Link to discussion:
  • [@sobitneupane] Determine if we should create a regression test for this bug.
  • [@sobitneupane] If we decide to create a regression test for the bug, please propose the regression test steps to ensure the same bug will not reach production again.
  • [@sonialiap] Link the GH issue for creating/updating the regression test once above steps have been agreed upon:

@melvin-bot melvin-bot bot added Daily KSv2 Overdue and removed Weekly KSv2 labels Nov 8, 2023
@sonialiap
Copy link
Contributor

This is Dupe. Resolved by #24198

@melvin-bot melvin-bot bot removed the Overdue label Nov 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 Engineering Internal Requires API changes or must be handled by Expensify staff
Projects
None yet
Development

No branches or pull requests