-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Fixes #7303 - Panel: When IsHiddenOnDismiss is true, Focus does not automatically enter Panel and is not returned when Panel is dismissed #7362
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "office-ui-fabric-react", | ||
| "comment": "FocusTrapZone - Make forceFocusInsideTrap prop changes modify focus", | ||
| "type": "patch" | ||
| } | ||
| ], | ||
| "packageName": "office-ui-fabric-react", | ||
| "email": "yubojia@microsoft.com" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,15 +26,7 @@ export class FocusTrapZone extends BaseComponent<IFocusTrapZoneProps, {}> implem | |
| } | ||
|
|
||
| public componentDidMount(): void { | ||
| const { elementToFocusOnDismiss, disableFirstFocus = false } = this.props; | ||
|
|
||
| this._previouslyFocusedElementOutsideTrapZone = elementToFocusOnDismiss | ||
| ? elementToFocusOnDismiss | ||
| : (document.activeElement as HTMLElement); | ||
| if (!elementContains(this._root.current, this._previouslyFocusedElementOutsideTrapZone) && !disableFirstFocus) { | ||
| this.focus(); | ||
| } | ||
|
|
||
| this._bringFocusIntoZone(); | ||
| this._updateEventHandlers(this.props); | ||
| } | ||
|
|
||
|
|
@@ -47,23 +39,30 @@ export class FocusTrapZone extends BaseComponent<IFocusTrapZoneProps, {}> implem | |
| this._updateEventHandlers(nextProps); | ||
| } | ||
|
|
||
| public componentWillUnmount(): void { | ||
| const { ignoreExternalFocusing } = this.props; | ||
| public componentDidUpdate(prevProps: IFocusTrapZoneProps) { | ||
| const prevForceFocusInsideTrap = prevProps.forceFocusInsideTrap !== undefined ? prevProps.forceFocusInsideTrap : true; | ||
| const newForceFocusInsideTrap = this.props.forceFocusInsideTrap !== undefined ? this.props.forceFocusInsideTrap : true; | ||
|
|
||
| if (!prevForceFocusInsideTrap && newForceFocusInsideTrap) { | ||
| // Transition from forceFocusInsideTrap disabled to enabled. Emulate what happens when a FocusTrapZone gets mounted | ||
| FocusTrapZone._focusStack.push(this); | ||
| this._bringFocusIntoZone(); | ||
| } else if (prevForceFocusInsideTrap && !newForceFocusInsideTrap) { | ||
| // Transition from forceFocusInsideTrap enabled to disabled. Emulate what happens when a FocusTrapZone gets unmounted | ||
| FocusTrapZone._focusStack = FocusTrapZone._focusStack.filter((value: FocusTrapZone) => { | ||
| return this !== value; | ||
| }); | ||
| this._returnFocusToInitiator(); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block of code is exactly the same as what's in componentWillUnmount. This should be pulled out into a helper function that both places call
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The helper function that both places call is this._returnFocusToInitiator().
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I'm bringing the _focusStack removing logic into returnFocusToInitator() as well |
||
| } | ||
|
|
||
| public componentWillUnmount(): void { | ||
| this._events.dispose(); | ||
| FocusTrapZone._focusStack = FocusTrapZone._focusStack.filter((value: FocusTrapZone) => { | ||
| return this !== value; | ||
| }); | ||
|
|
||
| const activeElement = document.activeElement as HTMLElement; | ||
| if ( | ||
| !ignoreExternalFocusing && | ||
| this._previouslyFocusedElementOutsideTrapZone && | ||
| typeof this._previouslyFocusedElementOutsideTrapZone.focus === 'function' && | ||
| (elementContains(this._root.current, activeElement) || activeElement === document.body) | ||
| ) { | ||
| focusAsync(this._previouslyFocusedElementOutsideTrapZone); | ||
| } | ||
| this._returnFocusToInitiator(); | ||
| } | ||
|
|
||
| public render(): JSX.Element { | ||
|
|
@@ -114,6 +113,31 @@ export class FocusTrapZone extends BaseComponent<IFocusTrapZoneProps, {}> implem | |
| } | ||
| } | ||
|
|
||
| private _bringFocusIntoZone(): void { | ||
| const { elementToFocusOnDismiss, disableFirstFocus = false } = this.props; | ||
|
|
||
| this._previouslyFocusedElementOutsideTrapZone = elementToFocusOnDismiss | ||
| ? elementToFocusOnDismiss | ||
| : (document.activeElement as HTMLElement); | ||
| if (!elementContains(this._root.current, this._previouslyFocusedElementOutsideTrapZone) && !disableFirstFocus) { | ||
| this.focus(); | ||
| } | ||
| } | ||
|
|
||
| private _returnFocusToInitiator(): void { | ||
| const { ignoreExternalFocusing } = this.props; | ||
|
|
||
| const activeElement = document.activeElement as HTMLElement; | ||
| if ( | ||
| !ignoreExternalFocusing && | ||
| this._previouslyFocusedElementOutsideTrapZone && | ||
| typeof this._previouslyFocusedElementOutsideTrapZone.focus === 'function' && | ||
| (elementContains(this._root.current, activeElement) || activeElement === document.body) | ||
| ) { | ||
| focusAsync(this._previouslyFocusedElementOutsideTrapZone); | ||
| } | ||
| } | ||
|
|
||
| private _updateEventHandlers(newProps: IFocusTrapZoneProps): void { | ||
| const { isClickableOutsideFocusTrap = false, forceFocusInsideTrap = true } = newProps; | ||
|
|
||
|
|
||
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.
Seems a little odd that we are not pushing every time we are going to bringFocusIntoZone. Is that expected?
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 looked closer into this. We do push every time before going into bringFocusIntoZone; however, the first _focusStack.push happens in componentWillMount(), so it's not immediately noticeable.
I looked through the code and I don't think there's a reason it NEEDS to be in componentWillMount() as opposed to componentDidMount(), so I'm bringing the _focusStack.push into the bringFocusIntoZone helper function.