Skip to content

Commit 526d3c7

Browse files
committed
[EuiDualRange] Fix incorrect width calculations caused by race condition
- this is the ultimate fix for the opened issue - the conversion to a function component apparently is causing race conditions for the `ref` behavior - wrapping the `.clientWidth` call in a rAF fixes the issue + bonus - DRY out `this.props.showInput === 'inputWithPopover'` logic with a getter
1 parent 3f09ca1 commit 526d3c7

3 files changed

Lines changed: 36 additions & 8 deletions

File tree

src/components/form/range/dual_range.test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ describe('EuiDualRange', () => {
191191
});
192192

193193
it('renders inputs with popover slider when showInput="inputWithPopover"', () => {
194+
// Mock requestAnimationFrame to run immediately
195+
jest
196+
.spyOn(window, 'requestAnimationFrame')
197+
.mockImplementation((cb: Function) => cb());
198+
194199
render(
195200
<EuiDualRange
196201
{...props}
@@ -209,6 +214,8 @@ describe('EuiDualRange', () => {
209214
expect(screen.getByRole('dialog')).toBeDefined();
210215
expect(screen.getAllByRole('slider')).toHaveLength(2);
211216
expect(screen.getByTestSubject('test')).toBeInTheDocument();
217+
218+
jest.restoreAllMocks();
212219
});
213220
});
214221

src/components/form/range/dual_range.tsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,36 @@ export class EuiDualRangeClass extends Component<
6565
isVisible: true, // used to trigger a rerender if initial element width is 0
6666
};
6767

68+
get isInPopover() {
69+
return this.props.showInput === 'inputWithPopover';
70+
}
6871
preventPopoverClose = false;
72+
6973
rangeSliderRef: HTMLInputElement | null = null;
7074
handleRangeSliderRefUpdate = (ref: HTMLInputElement | null) => {
7175
this.rangeSliderRef = ref;
72-
this.setState({
73-
rangeSliderRefAvailable: !!ref,
74-
rangeWidth: !!ref ? ref.clientWidth : null,
75-
});
76+
if (ref) {
77+
if (this.isInPopover) {
78+
// Wait a tick for popover rendering to settle
79+
requestAnimationFrame(() => {
80+
this.setState({
81+
rangeSliderRefAvailable: true,
82+
rangeWidth: ref.clientWidth,
83+
});
84+
});
85+
} else {
86+
// If not in a popover, no need to wait
87+
this.setState({
88+
rangeSliderRefAvailable: true,
89+
rangeWidth: ref.clientWidth,
90+
});
91+
}
92+
} else {
93+
this.setState({
94+
rangeSliderRefAvailable: false,
95+
rangeWidth: 0,
96+
});
97+
}
7698
};
7799
private leftPosition = 0;
78100
private dragAcc = 0;
@@ -319,9 +341,7 @@ export class EuiDualRangeClass extends Component<
319341

320342
calculateThumbPositionStyle = (value: number, width?: number) => {
321343
const trackWidth =
322-
this.props.showInput === 'inputWithPopover' && !!width
323-
? width
324-
: this.rangeSliderRef!.clientWidth;
344+
this.isInPopover && !!width ? width : this.state.rangeWidth;
325345

326346
const position = calculateThumbPosition(
327347
value,
@@ -462,7 +482,7 @@ export class EuiDualRangeClass extends Component<
462482

463483
const { id } = this.state;
464484

465-
const showInputOnly = showInput === 'inputWithPopover';
485+
const showInputOnly = this.isInPopover;
466486
const canShowDropdown = showInputOnly && !readOnly && !disabled;
467487

468488
const rangeStyles = euiRangeStyles(theme);

upcoming_changelogs/7305.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
**Bug fixes**
22

33
- Fixed `EuiInputPopover` not calling `onPanelResize` callback prop
4+
- Fixed `EuiDualRange` incorrectly positioning highlights when rendered with `showInput="inputWithPopover"`

0 commit comments

Comments
 (0)