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

[EuiSuperDatePicker] Support onFocus (#4924) #6320

Merged
merged 8 commits into from
Nov 7, 2022

Conversation

UzairNoman
Copy link
Contributor

@UzairNoman UzairNoman commented Oct 22, 2022

Summary

I have made a PR for onFocus support on EuiSuperDatePicker (Issue: #4924). In addition, I have written a test to verify if the focus is infact triggering once when the input is focused.

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
- [ ] Checked for breaking changes and labeled appropriately
- [ ] Checked for accessibility including keyboard-only and screenreader modes
- [ ] Added documentation

@kibanamachine
Copy link

Since this is a community submitted pull request, a Jenkins build has not been kicked off automatically. Can an Elastic organization member please verify the contents of this patch and then kick off a build manually?

@cla-checker-service
Copy link

cla-checker-service bot commented Oct 22, 2022

💚 CLA has been signed

@elizabetdev
Copy link
Contributor

Hi @UzairNoman,

Thank you for opening this PR to fix the issue #4924.

Could you please sign the Contributor Agreement so we can review your PR?

@UzairNoman
Copy link
Contributor Author

UzairNoman commented Oct 24, 2022

Hi @miukimiu, sorry about that. I had signed it earlier as well. I have re-signed it again. Let me know if you guys received it. Thanks :)

@elizabetdev
Copy link
Contributor

Thanks, @UzairNoman. Now it's signed. ✅

An engineer will do the review as soon as possible.

@UzairNoman UzairNoman force-pushed the onfocus-super-date-picker branch from 79cca5b to 12c045e Compare October 24, 2022 16:27
Comment on lines 630 to 631
onClick={handleInputActivity}
onKeyUp={handleInputActivity}
Copy link
Contributor

Choose a reason for hiding this comment

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

Setting an onClick/onKeyUp on EuiFormControlLayout isn't the approach I would go with. I would strongly prefer that we instead pass the onFocus callback directly to each underlying EuiDatePickerRange (which now accepts onFocus as of #6136)

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 tried to follow this approach from ChoicePicker. But I agree its not the right approach. I have tried to introduce onFocus with buttonProps on EuiDatePopoverButton but it was invoking the function twice. I then introduce it directly on the <button> tag on date_popover_button.tsx to look at the issue more closely (you can check the latest commit update) but to no avail.

I suspect that this issue is linked to opening of Panel and therefore peeked into _popover_panel.tsx code:

return (
    <EuiPopoverPanelContext.Provider value={panelContext}>
      <EuiPanel
        className={classes}
        css={panelCSS}
        data-popover-panel
        data-popover-open={isOpen || undefined}
        {...rest}
      >
        {children}
      </EuiPanel>
    </EuiPopoverPanelContext.Provider> 

Specifically, if I comment {...rest} and {children} both, the onFocus call is then made once, but I wasn't able to find the exact cause of the issue. Any suggestion would be appreciated.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi Uzair! After a little bit of testing locally, the "double" focus events appear to primarily occur when the super date picker popover that each button toggles closes. This is due to the returnFocus logic used by our focus trap library & logic.

Whenever a dialogue that traps focus within itself (popovers, modals, and flyouts) closes, EUI attempts to return focus to the button that toggled the popover. This works great for keyboard users and escape key presses, but provides an extra "noisy" focus event in scenarios like this for mouse users who click out of a popover into another focusable item (e.g. clicking between the start date and end date button/inputs). You can see this behavior more clearly by changing your onFocus to log the event target, e.g. onFocus: (e) => console.log(e.target, new Date()).

To be honest, the extra onFocus events don't totally bother me and it feels like it is 'working as intended' (programmatically, the button is briefly recapturing focus on popover close). Do you feel like the extra event is prohibitively annoying to consumers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi Constance, thanks for putting your time into this. Let's say a user wants to attach an API call to this onFocus event or maybe just as simple as popping an element from some stack. How would he prevent it from happening twice then?

It seems like, then still the solution through onClick/onKeyUp (although a very bad workaround) creates an expected behavior from the user's standpoint, rather than this one.

Copy link
Contributor

@cee-chen cee-chen Nov 1, 2022

Choose a reason for hiding this comment

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

Let's say a user wants to attach an API call to this onFocus event or maybe just as simple as popping an element from some stack. How would he prevent it from happening twice then?

I would personally suggest wrapping a requestAnimationFrame(() => { if (e.target === document.activeElement) ... } in their logic to ensure the event trigger is still the current focus/active element before continuing.

The dupe event may not be an issue for consumers depending on what they're doing (e.g. just checking that any button within EuiSuperDatePicker has focus), but I consider it a lesser evil over conflating event types.

To be honest we have other "dupe"/empty focus/blur/change events in other places in EUI - this would not be the first and in my opinion is not a bug / can be worked around. It is simply accurately representing the events natively occurring under the hood, and I tend to err on the side of exposing/relying on native events over smoothing them over if there isn't a specific UX goal in mind.

It seems like, then still the solution through onClick/onKeyUp (although a very bad workaround) creates an expected behavior from the user's standpoint, rather than this one.

It depends on what the consumer is trying to accomplish - "expected behavior" is entirely dependent on the goal in mind. Honestly, the concept of 'focusing into EuiSuperDatePicker' is an inherently questionable one, because there's 4-5 different items within what we consider "the EuiSuperDatePicker component" that can all individually take focus. This PR considers the "date/time inputs" (not really inputs, they're actually buttons toggling popovers) to be what matters for focus, but another consumer might want to know about focus on every single button. Without an actual UX story/case in mind, it's difficult to write code in a vacuum for the unknown.

I do feel very strongly about not conflating onClick/onKeyUp with onFocus. They're simply not the same type of event whatsoever and will end up creating event type headaches down the road vs sticking to a consistent API/event type.

If we want to really make a super robust focus behavior, my suggestion would be setting a custom onFocus and onBlur handler on the parent EuiFlexGroup that checks the current document.activeElement and stores an isFocused flag in state, and call props.onFocus if that state changes from false to true. Doing so will let you more granularly examine whether focus has moved between child buttons within the EuiSuperDatePicker and not fire props.onFocus if so.

Again, that's a lot more manual JS & logic than simply letting consumers deal with duplicate onFocus events however, so without a use-case in mind I would lean towards the simpler option. But if you're interested in trying it out, feel free!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright, make sense, I updated the code with buttonProps logic, and also removed the previous workaround. Please review whenever you have time :)

[EuiSuperDatePicker] Added code suggestions for onFocus (elastic#4924)

Co-authored-by: Constance <constancecchen@users.noreply.github.com>
@cee-chen
Copy link
Contributor

cee-chen commented Nov 2, 2022

jenkins test this

@kibanamachine
Copy link

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

@UzairNoman
Copy link
Contributor Author

UzairNoman commented Nov 2, 2022

jenkins test this

We have to re-run it again, made the corrections in the test file, suggested snippet in the test file had minor stuff missing.

Comment on lines +180 to +182
component
.find('button[data-test-subj="superDatePickerShowDatesButton"]')
.simulate('click');
Copy link
Contributor

Choose a reason for hiding this comment

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

This button should also be triggering the focus handler, so we should simulate focus on it as well

Suggested change
component
.find('button[data-test-subj="superDatePickerShowDatesButton"]')
.simulate('click');
component
.find('button[data-test-subj="superDatePickerShowDatesButton"]')
.simulate('click')
.simulate('focus');
expect(focusMock).toBeCalledTimes(1);

@cee-chen
Copy link
Contributor

cee-chen commented Nov 2, 2022

Changes look good! One minor comment remaining. Once staging/CI finishes, I'll quickly check the props table and check off the last QA item.

@UzairNoman UzairNoman requested a review from cee-chen November 6, 2022 15:31
@UzairNoman
Copy link
Contributor Author

Changes look good! One minor comment remaining. Once staging/CI finishes, I'll quickly check the props table and check off the last QA item.

Hi Constance, I have updated the test cases, hopefully it should look fine now.

@cee-chen
Copy link
Contributor

cee-chen commented Nov 7, 2022

jenkins test this

@kibanamachine
Copy link

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

@UzairNoman
Copy link
Contributor Author

jenkins test this

@constancecchen , sorry can you re-run the tests again, my bad :/

@cee-chen
Copy link
Contributor

cee-chen commented Nov 7, 2022

jenkin test this

@cee-chen
Copy link
Contributor

cee-chen commented Nov 7, 2022

No worries, apologies for the CI slowness/slow turnaround - we're currently babysitting a Kibana upgrade and that's taking up most of our time. Props table looks good to me! I'll approve and merge once CI passes!

Copy link
Contributor

@cee-chen cee-chen left a comment

Choose a reason for hiding this comment

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

Thank you for your time on this - we really appreciate your community contribution!

@kibanamachine
Copy link

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

@cee-chen cee-chen enabled auto-merge (squash) November 7, 2022 20:00
@cee-chen cee-chen merged commit 787a47c into elastic:main Nov 7, 2022
jbudz pushed a commit to elastic/kibana that referenced this pull request Nov 18, 2022
## Summary
`eui@67.1.8` ⏩ `eui@70.2.2`

⚠️ Note: This upgrade contains breaking changes to `EuiFlexGroup` and
`EuiFlexGrid`, primarily around switching margins and negative margins
to `gap`. Please do a quick QA pass of your app to scan for any issues.
We're happy to help resolve minor fixes, or potentially follow up after
PR merges. You can find us over in #eui!

## [`70.2.4`](https://github.com/elastic/eui/tree/v70.2.4)

**Bug fixes**

- Fixed visual bug in nested `EuiFlexGroup`s, where the parent
`EuiFlexGroup` is responsive but a child `EuiFlexGroup` is not
([#6381](elastic/eui#6381))

## [`70.2.3`](https://github.com/elastic/eui/tree/v70.2.3)

**Bug fixes**

- Fixed incorrect margins in `EuiSuperDatePicker` caused by `EuiFlex`
CSS gap change ([#6380](elastic/eui#6380))

## [`70.2.2`](https://github.com/elastic/eui/tree/v70.2.2)

- `EuiButton` now accepts `minWidth={false}`
([#6373](elastic/eui#6373))

**Bug fixes**

- `EuiButton` no longer outputs unnecessary inline styles for
`minWidth={0}` or `minWidth={false}`
([#6373](elastic/eui#6373))
- `EuiFacetButton` no longer reports type issues when passing props
accepted by `EuiButton`
([#6373](elastic/eui#6373))
- Fixed the shadow sizes of `.eui-yScrollWithShadows` and
`.eui-xScrollWithShadows`
([#6374](elastic/eui#6374))


## [`70.2.1`](https://github.com/elastic/eui/tree/v70.2.1)

**Bug fixes**

- Re-fixed `EuiPageSection` not correctly merging `contentProps.css`
([#6365](elastic/eui#6365))
- Fixed `EuiTab` not defaulting to size `m`
([#6366](elastic/eui#6366))

## [`70.2.0`](https://github.com/elastic/eui/tree/v70.2.0)

- Added a keyboard shortcuts popover to `EuiDataGrid`'s toolbar. This
can be visually hidden via `toolbarVisibility.showKeyboardShortcuts`,
but will always remain accessible to keyboard and screen reader users.
([#6036](elastic/eui#6036))
- `EuiScreenReaderOnly`'s `showOnFocus` prop now also shows on focus
within its children ([#6036](elastic/eui#6036))
- Added `onFocus` prop callback to `EuiSuperDatePicker`
([#6320](elastic/eui#6320))

**Bug fixes**

- Fixed `EuiSelectable` to ensure the full options list is re-displayed
when the search bar is controlled and cleared using `searchProps.value`
([#6317](elastic/eui#6317))
- Fixed incorrect padding on `xl`-sized `EuiTabs`
([#6336](elastic/eui#6336))
- Fixed `EuiCard` not correctly merging `css` on its child `icon`s
([#6341](elastic/eui#6341))
- Fixed `EuiCheckableCard` not setting `css` on the correct DOM node
([#6341](elastic/eui#6341))
- Fixed a webkit rendering issue with `EuiModal`s containing
`EuiBasicTable`s tall enough to scroll
([#6343](elastic/eui#6343))
- Fixed bug in `to_initials` that truncates custom initials
([#6346](elastic/eui#6346))
- Fix bug in `EuiCard` where layout breaks when `horizontal` and
`selectable` are both passed
([#6348](elastic/eui#6348))

## [`70.1.0`](https://github.com/elastic/eui/tree/v70.1.0)

- Added the `hint` prop to the `<EuiSearchBar />`. This prop lets the
consumer render a hint below the search bar that will be displayed on
focus. ([#6319](elastic/eui#6319))
- Added the `hasDragDrop` prop to `EuiPopover`. Use this prop if your
popover contains `EuiDragDropContext`.
([#6329](elastic/eui#6329))

**Bug fixes**

- Fixed `EuiButton`'s cursor style when the button is disabled
([#6323](elastic/eui#6323))
- Fixed `EuiPageTemplate` not recognizing child
`EuiPageSidebar`s/`EuiPageTemplate.Sidebar`s with `css` props
([#6324](elastic/eui#6324))
- Fixed `EuiBetaBadge` to always respect its `anchorProps` values,
including when there is no tooltip content
([#6326](elastic/eui#6326))
- Temporarily patched `EuiModal` to not cause scroll-jumping issues on
modal open ([#6327](elastic/eui#6327))
- Fixed buggy drag & drop behavior within `EuiDataGrid`'s columns &
sorting toolbar popovers
([#6329](elastic/eui#6329))
- Fixed `EuiButton` not correctly passing `textProps` for children
inside fragments or i18n components
([#6332](elastic/eui#6332))
- Fixed `EuiButton` not correctly respecting `minWidth={0}`
([#6332](elastic/eui#6332))

**CSS-in-JS conversions**

- Converted `EuiTabs` to Emotion
([#6311](elastic/eui#6311))

## [`70.0.0`](https://github.com/elastic/eui/tree/v70.0.0)

- Added the `enabled` option to the `<EuiInMemoryTable />`
`executeQueryOptions` prop. This option prevents the Query from being
executed when controlled by the consumer.
([#6284](elastic/eui#6284))

**Bug fixes**

- Fixed `EuiOverlayMask` to set a
`[data-relative-to-header=above|below]` attribute to replace the
`--aboveHeader` and `--belowHeader` classNames removed in its Emotion
conversion ([#6289](elastic/eui#6289))
- Fixed `EuiHeader` CSS using removed `EuiOverlayMask` class modifiers
([#6293](elastic/eui#6293))
- Fixed `EuiToolTip` not respecting reduced motion preferences
([#6295](elastic/eui#6295))
- Fixed a bug with `EuiTour` where passing any `panelProps` would cause
the beacon to disappear
([#6298](elastic/eui#6298))

**Breaking changes**

- `@emotion/css` is now a required peer dependency, alongside
`@emotion/react` ([#6288](elastic/eui#6288))
- `@emotion/cache` is no longer required peer dependency, although your
project must still use it if setting custom cache/injection locations
([#6288](elastic/eui#6288))

**CSS-in-JS conversions**

- Converted `EuiCode` and `EuiCodeBlock` to Emotion; Removed
`euiCodeSyntaxTokens` Sass mixin and `$euiCodeBlockPaddingModifiers`;
([#6263](elastic/eui#6263))
- Converted `EuiResizableContainer` and `EuiResizablePanel` to Emotion
([#6287](elastic/eui#6287))

## [`69.0.0`](https://github.com/elastic/eui/tree/v69.0.0)

- Added support for `fullWidth` prop on EuiForm, which will be the
default for all rows/controls within
([#6229](elastic/eui#6229))
- Added support for `onResizeStart` and `onResizeEnd` callbacks to
`EuiResizableContainer`
([#6236](elastic/eui#6236))
- Added optional case sensitive option matching to `EuiComboBox` with
the `isCaseSensitive` prop
([#6268](elastic/eui#6268))
- `EuiFlexItem` now supports `grow={0}`
([#6270](elastic/eui#6270))
- Added the `alignItems` prop to `EuiFlexGrid`
([#6281](elastic/eui#6281))
- Added `filter`, `filterExclude`, `filterIgnore`, `filterInclude`,
`indexTemporary`, `infinity`, `sortAscending`, and `sortDescending`
glyphs to `EuiIcon` ([#6282](elastic/eui#6282))

**Bug fixes**

- Fixed `EuiTextProps` to show the `color` type option `inherit` as
default ([#6267](elastic/eui#6267))
- `EuiFlexGroup` now correctly respects `gutterSize` when responsive
([#6270](elastic/eui#6270))
- Fixed the last breadcrumb in `EuiBreadcrumbs`'s `breadcrumbs` array
not respecting `truncate` overrides
([#6280](elastic/eui#6280))

**Breaking changes**

- `EuiFlexGrid` no longer supports `columns={0}`. Use `EuiFlexGroup`
instead for normal flex display
([#6270](elastic/eui#6270))
- `EuiFlexGrid` now uses modern `display: grid` CSS
([#6270](elastic/eui#6270))
- `EuiFlexGroup`, `EuiFlexGrid`, and `EuiFlexItem` now use modern `gap`
CSS instead of margins and negative margins
([#6270](elastic/eui#6270))
- `EuiFlexGroup` no longer applies responsive styles to `column` or
`columnReverse` directions
([#6270](elastic/eui#6270))

**CSS-in-JS conversions**

- Converted `EuiFlexGroup`, `EuiFlexGrid`, and `EuiFlexItem` to Emotion
([#6270](elastic/eui#6270))

## [`68.0.0`](https://github.com/elastic/eui/tree/v68.0.0)

- Added `beta` glyph to `EuiIcon`
([#6250](elastic/eui#6250))
- Added `launch` and `spaces` glyphs to `EuiIcon`
([#6260](elastic/eui#6260))
- Added the `fallbackDestination` prop to `EuiSkipLink`, which accepts a
string of query selectors to fall back to if the `destinationId` does
not have a valid target. Defaults to `main`
([#6261](elastic/eui#6261))
- `EuiSkipLink` is now always an `a` tag to ensure that it is always
placed within screen reader link menus.
([#6261](elastic/eui#6261))

**Bug fixes**

- Fixed `EuiSuperDatePicker` not correctly merging passed `className`s
([#6253](elastic/eui#6253))
- Fixed `EuiColorStops` not correctly merging in passed
`data-test-subj`s, `style`s, or `...rest`
([#6255](elastic/eui#6255))
- Fixed `EuiResizablePanel` incorrectly passing `style` to the wrapper
instead of the panel. Use `wrapperProps.style` to pass styles to the
wrapper. ([#6255](elastic/eui#6255))
- Fixed custom `onClick`s passed to `EuiSkipLink` overriding
`overrideLinkBehavior`
([#6261](elastic/eui#6261))

**Breaking changes**

- Removed `inherit` and `ghost` color from `EuiListGroupItem`
([#6207](elastic/eui#6207))
- Changed default color to `text` instead of `inherit`
([#6207](elastic/eui#6207))

**CSS-in-JS conversions**

- Converted `EuiListGroup` and `EuiListGroupItem` to Emotion; Removed
`$euiListGroupGutterTypes`, `$euiListGroupItemColorTypes` and
`$euiListGroupItemSizeTypes`;
([#6207](elastic/eui#6207))
- Converted `EuiBadgeGroup` to Emotion
([#6258](elastic/eui#6258))
- Converted `EuiBetaBadge` to Emotion
([#6258](elastic/eui#6258))
- Converted `EuiNotificationBadge` to Emotion
([#6258](elastic/eui#6258))

Co-authored-by: Elizabet Oliveira <elizabet.oliveira@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
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