-
Notifications
You must be signed in to change notification settings - Fork 435
Make Dialog positioning more robust #1404
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
Conversation
@@ -12,6 +12,7 @@ import { DropdownNubbinPositions } from '../../menu-dropdown/menu-dropdown'; | |||
import List from '../../utilities/menu-list'; | |||
import Button from '../../button'; | |||
import Trigger from '../../menu-dropdown/button-trigger'; | |||
import Popover from '../../popover'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore this file. Just making more complex test cases.
@@ -0,0 +1,31 @@ | |||
// TODO - we should make stories to see everything on one page |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore this. Was looking into having a story of all things that use dialog to see all cases on one page, but didn't have the time for this.
|
||
import { DIALOG } from '../../../utilities/constants'; | ||
|
||
// #### Dialog doesn't pass down <IconSettings> context so repassing it here. | ||
import IconSettings from '../../icon-settings'; | ||
// import { , getNubbinClassName } from '../../utilities/dialog-helpers'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will remove
this.handleOpen(); | ||
} | ||
}, | ||
|
||
componentDidUpdate (prevProps, prevState) { | ||
if ( | ||
this.state.triggerPopperJS === true && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore these formatting changes, i'll change back. I can't read the code as linted. It hurts my eyes, but I'll change back before committing.
return { ...popperData.style, left, top, position }; | ||
const propOffsets = this.getPropOffsetsInPixels(this.props.offset); | ||
|
||
// FIXME before merge - gotta rename from margin to offset |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got to address fixme and rename to getNubbinOffsets
|
||
if (this.props.position === 'absolute' || this.props.position === 'overflowBoundaryElement') { | ||
Object.assign(style, { | ||
outline: 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the outline: 0 is actually needed. Will remove.
style={style} | ||
onKeyDown={this.handleKeyDown} | ||
onMouseEnter={this.props.onMouseEnter} | ||
onMouseLeave={this.props.onMouseLeave} | ||
{...this.props.containerProps} | ||
ref={this.setDialogContent} | ||
role={this.props.variant} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how to address this properly and whether this can be done, but seemed sane to me that the role would be based off the variant, or if we want we can have some sort of mapping, but it's basically the same thing with different labels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine. We may want to do an enum object though incase we add more variants in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah was my thinking, but only if the direct variant -> role mapping ends up insufficient.
components/popover/popover.jsx
Outdated
@@ -478,7 +475,6 @@ const Popover = createReactClass({ | |||
aria-describedby={`${this.getId()}-dialog-body`} | |||
className={classNames( | |||
'slds-popover', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incredibly annoying since it can't be moved up to the Dialog
classnames level or at least I'm not sure how to go about it since it's difficult to follow all the logic of trapping the focus in the popover and following this up and down through the components, life cycle methods, and refs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's easier to follow from a comparing the HTML nodes here to SLDS if you pass in the whole popover HTML in as a child.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now leaving it as is, but after thinking about this some more, I think that I disagree, the Dialog is the Popover. You should be able to render an empty popover. Why should it be div div.popover #contents
when it can just be div.popover #contents
. It's a bit annoying because we're trying to unify all these components under the concept of a "Dialog" but SLDS limits us in this fashion. Leaving it as is, because I'm already rushed for time as is.
The output definitly looks nicer: On the subject of trapping focus, the first tabbable node in the dialog contents should receive focus. I made the dialog I think it's reasonable to ask folks to be inline block if they want to absolutely align something to it. I'm not sure what you mean by |
@vintik, @davidlygagnon will be working on Popover's nubbin flip #1211 probably next week and be in the Dialog codebase also. Just FYI. |
I was thinking about adding it in this week if @davidlygagnon is okay with that. I finally have some time cleared up and we need these dialog changes merged within the next 2 weeks. About to have sprint planning. Hopefully this remains my top priority. |
Sounds good @vintik . I'll take another issue that @interactivellama assigned to me. Thanks! |
DOMElementFocus.storeActiveElement(); | ||
DOMElementFocus.setupScopedFocus({ | ||
ancestorElement: scopedElement.querySelector('.slds-popover'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was kind of weird that we used to do a search for slds-popover, even though slds-popover is a class we control. Now since the Dialog no longer receives a div child that it's tightly bound to, and the Dialog is of a type of popover, we can just use the dialogContent reference directly.
If we want to be a bit defensive, we can do the scan if the class isn't on dialogContent, but then there'd be a bug anyways and the tests should catch that. I don't think this is an appropriate time for defensive checks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The focusing helpers were originally taken from jQuery UI, so DOM query snuck in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still uses a dom reference, there's just no need to query, there's only one possible element it can be.
this.props.contentsClassName | ||
) || undefined | ||
} | ||
id={this.props.id} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be removed. This should be passed as containerProps instead.
* | ||
* | ||
*/ | ||
// FIXME - still need to account for border shadow of 2px. probably only needs to be added to the rotated height. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure this was ever accounted for previously.
Not exactly sure how I resolve the conflicts? :) |
Just pull down master and overwrite what you have locally. These images are unrelated to what you are doing. |
components/popover/popover.jsx
Outdated
@@ -570,7 +549,7 @@ const Popover = createReactClass({ | |||
|
|||
this.renderOverlay(this.getIsOpen()); | |||
|
|||
const containerStyles = { display: 'inline' }; | |||
const containerStyles = { display: 'inline-block' }; // ATTN FIXME - can we make this change??! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will be OK. slds-button
should already be inline block
/**
* @summary This neutralizes all the base styles making it look like a text link
*
* @name base
* @selector .slds-button
* @restrict button, a, span
* @variant
*/
.slds-button {
position: relative;
display: inline-block;
padding: 0;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh i need to remove the comment
@@ -372,13 +389,16 @@ const Dialog = createReactClass({ | |||
}, | |||
|
|||
render () { | |||
let style = {}; | |||
const style = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See master branch. This needs to let
once master branch is merged in. Object.assign
was removed and spread operator was put in it's place.
|
||
if ( | ||
this.props.position === 'absolute' || | ||
this.props.position === 'overflowBoundaryElement' | ||
) { | ||
style = this.getPopperStyles(); | ||
Object.assign(style, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use spread operator here.
Tooltips are not being read on focus of trigger anymore Cmd F5 will enable Voiceover on a Mac. It should read the tooltip when the button is focused. This will need to be fixed. The button and the dialog should be connected by the aria attributes which will cause the button to read the tool contents when focued. |
We'll need to make sure we mention the initial offset orientation change in the release notes. A checkProps would be nice, but it wouldn't go away after the offset numbers were updated. |
@interactivellama So finally got time again :) lol it's been a crazy couple of months. Looked into it and realize now why tooltip trap focus doesn't work. It was never supposed to work in the first place and according to the original design would probably be a bug. Frankly, have no idea how it's trapping focus on master branch. If we want I can switch the variant from tooltip to popover and it should work as expected. |
Fixes #1211 |
This pull request is meant to make dialog positioning more robust and move responsibility closer to the Dialog component instead of having the parent/consumer be responsible for the dialog logic.
In my eyes, the responsibility of the parent should be only what gets rendered in the Dialog. Not how the dialog renders. It can use props to change that on a case by case basis. Currently the coupling between parent and Dialog is very tight and needs to be loosened.
Our internal utility Dialog component now receives a
hasNubbin
which will add a nubbin. The placement of the nubbin will be determined from the overallalign
. Nubbins can only be added ontoposition="absolute"
orposition="overflowBoundaryElement"
Take a look at any of the alignments for PopoverTooltips:
https://design-system-react-components.herokuapp.com/?selectedKind=SLDSPopoverTooltip&selectedStory=Alignment%20%28Button%29&full=0&addons=0&stories=1&panelRight=0&addonPanel=storybook%2Factions%2Factions-panel
Big things to figure out:
Getting snapshot tests to pass
Due to reasons for trapping focus, the
slds-popover
class is required to be a child ofDialog
and not on dialog, otherwise it breaks. I'd much rather prefer to pass these higher level classes to dialog and not have special divs that try to control the internals of Dialog.After these changes the nubbin always points at the center of the element instead of being on the side of the element. Unless we require the user to explicitly provide the exact nubbin position, I think this is the much more reasonable default because it works consistently in many more cases. Otherwise, what would a top left nubbin position on a top left align mean? The nubbin would point up even though the reference element is below the dialog.
In a few of the parents I change the wrapper from
display: inline
todisplay: inline-block
. Can it be done? Is this a breaking change? We want the "container" to take up the full height in whatever the user renders it in. Otherwise the nubbin will always point at the wrong part.preventOverflow. Pretty sure this has never worked properly, but if we add offsets this may not work or the addition of offsets may need to move to a different time in the popper cycle. Either way I think this part would be out of scope unless this actually works for anybody currently using Dialog.
Pull Request Review checklist (do not remove)
npm run lint:fix
has been run and linting passes.components/component-docs.json
CI tests pass (npm test
).last-slds-markup-review
inpackage.json
and push.last-accessibility-review
, topackage.json
and push.npm run local-update
within locally cloned site repo to confirm the site will function correctly at the next release.