Skip to content

Commit b37554b

Browse files
authored
Revert "fix: MessageBar auto reflow should handle document reflow with min-content" (#33797)
1 parent 0e8b2a0 commit b37554b

File tree

3 files changed

+73
-77
lines changed

3 files changed

+73
-77
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "fix: Revert MessageBar auto reflow changes from #33409",
4+
"packageName": "@fluentui/react-message-bar",
5+
"email": "lingfangao@hotmail.com",
6+
"dependentChangeType": "patch"
7+
}

packages/react-components/react-message-bar/library/src/components/MessageBar/MessageBar.test.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,6 @@ describe('MessageBar', () => {
2323
// do nothing
2424
}
2525
};
26-
27-
// @ts-expect-error https://github.com/jsdom/jsdom/issues/2032
28-
global.IntersectionObserver = class IntersectionObserver {
29-
public observe() {
30-
// do nothing
31-
}
32-
public disconnect() {
33-
// do nothing
34-
}
35-
};
3626
});
3727

3828
beforeEach(() => {
Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,91 @@
11
import * as React from 'react';
22
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
3-
import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities';
3+
import { isHTMLElement } from '@fluentui/react-utilities';
44

55
export function useMessageBarReflow(enabled: boolean = false) {
66
const { targetDocument } = useFluent();
7-
const prevInlineSizeRef = React.useRef(-1);
8-
const messageBarRef = React.useRef<HTMLElement | null>(null);
9-
10-
const [reflowing, setReflowing] = React.useState(false);
7+
const forceUpdate = React.useReducer(() => ({}), {})[1];
8+
const reflowingRef = React.useRef(false);
9+
// TODO: exclude types from this lint rule: https://github.com/microsoft/fluentui/issues/31286
1110

12-
// This layout effect 'sanity checks' what observers have done
13-
// since DOM has not been flushed when observers run
14-
useIsomorphicLayoutEffect(() => {
15-
if (!messageBarRef.current) {
16-
return;
17-
}
11+
const resizeObserverRef = React.useRef<ResizeObserver | null>(null);
12+
const prevInlineSizeRef = React.useRef(-1);
1813

19-
setReflowing(prevReflowing => {
20-
if (!prevReflowing && messageBarRef.current && isReflowing(messageBarRef.current)) {
21-
return true;
14+
const handleResize: ResizeObserverCallback = React.useCallback(
15+
entries => {
16+
// Resize observer is only owned by this component - one resize observer entry expected
17+
// No need to support multiple fragments - one border box entry expected
18+
if (process.env.NODE_ENV !== 'production' && entries.length > 1) {
19+
// eslint-disable-next-line no-console
20+
console.error(
21+
[
22+
'useMessageBarReflow: Resize observer should only have one entry. ',
23+
'If multiple entries are observed, the first entry will be used.',
24+
'This is a bug, please report it to the Fluent UI team.',
25+
].join(' '),
26+
);
2227
}
2328

24-
return prevReflowing;
25-
});
26-
}, [reflowing]);
29+
const entry = entries[0];
30+
// `borderBoxSize` is not supported before Chrome 84, Firefox 92, nor Safari 15.4
31+
const inlineSize = entry?.borderBoxSize?.[0]?.inlineSize ?? entry?.target.getBoundingClientRect().width;
2732

28-
const handleResize: ResizeObserverCallback = React.useCallback(() => {
29-
if (!messageBarRef.current) {
30-
return;
31-
}
32-
33-
const inlineSize = messageBarRef.current.getBoundingClientRect().width;
34-
const scrollWidth = messageBarRef.current.scrollWidth;
33+
if (inlineSize === undefined || !entry) {
34+
return;
35+
}
3536

36-
const expanding = prevInlineSizeRef.current < inlineSize;
37-
const overflowing = inlineSize < scrollWidth;
37+
const { target } = entry;
3838

39-
setReflowing(!expanding || overflowing);
40-
}, []);
39+
if (!isHTMLElement(target)) {
40+
return;
41+
}
4142

42-
const handleIntersection: IntersectionObserverCallback = React.useCallback(entries => {
43-
if (entries[0].intersectionRatio < 1) {
44-
setReflowing(true);
45-
}
46-
}, []);
43+
let nextReflowing: boolean | undefined;
44+
45+
// No easy way to really determine when the single line layout will fit
46+
// Just keep try to set single line layout as long as the size is growing
47+
// Will cause flickering when size is being adjusted gradually (i.e. drag) - but this should not be a common case
48+
if (reflowingRef.current) {
49+
if (prevInlineSizeRef.current < inlineSize) {
50+
nextReflowing = false;
51+
}
52+
} else {
53+
const scrollWidth = target.scrollWidth;
54+
if (inlineSize < scrollWidth) {
55+
nextReflowing = true;
56+
}
57+
}
4758

48-
const ref = React.useMemo(() => {
49-
let resizeObserver: ResizeObserver | null = null;
50-
let intersectionObserer: IntersectionObserver | null = null;
59+
prevInlineSizeRef.current = inlineSize;
60+
if (typeof nextReflowing !== 'undefined' && reflowingRef.current !== nextReflowing) {
61+
reflowingRef.current = nextReflowing;
62+
forceUpdate();
63+
}
64+
},
65+
[forceUpdate],
66+
);
5167

52-
return (el: HTMLElement | null) => {
68+
const ref = React.useCallback(
69+
(el: HTMLElement | null) => {
5370
if (!enabled || !el || !targetDocument?.defaultView) {
54-
resizeObserver?.disconnect();
55-
intersectionObserer?.disconnect();
5671
return;
5772
}
5873

59-
messageBarRef.current = el;
74+
resizeObserverRef.current?.disconnect();
6075

6176
const win = targetDocument.defaultView;
62-
resizeObserver = new win.ResizeObserver(handleResize);
63-
intersectionObserer = new win.IntersectionObserver(handleIntersection, { threshold: 1 });
64-
65-
intersectionObserer.observe(el);
77+
const resizeObserver = new win.ResizeObserver(handleResize);
78+
resizeObserverRef.current = resizeObserver;
6679
resizeObserver.observe(el, { box: 'border-box' });
80+
},
81+
[targetDocument, handleResize, enabled],
82+
);
83+
84+
React.useEffect(() => {
85+
return () => {
86+
resizeObserverRef.current?.disconnect();
6787
};
68-
}, [handleResize, handleIntersection, enabled, targetDocument]);
88+
}, []);
6989

70-
return { ref, reflowing };
90+
return { ref, reflowing: reflowingRef.current };
7191
}
72-
73-
const isReflowing = (el: HTMLElement) => {
74-
return el.scrollWidth > el.offsetWidth || !isFullyInViewport(el);
75-
};
76-
77-
const isFullyInViewport = (el: HTMLElement) => {
78-
const rect = el.getBoundingClientRect();
79-
const doc = el.ownerDocument;
80-
const win = doc.defaultView;
81-
82-
if (!win) {
83-
return true;
84-
}
85-
86-
return (
87-
rect.top >= 0 &&
88-
rect.left >= 0 &&
89-
rect.bottom <= (win.innerHeight || doc.documentElement.clientHeight) &&
90-
rect.right <= (win.innerWidth || doc.documentElement.clientWidth)
91-
);
92-
};

0 commit comments

Comments
 (0)