Skip to content

Commit a0087a8

Browse files
committed
feat: modify size logic and test units
1 parent ded0c52 commit a0087a8

File tree

2 files changed

+214
-13
lines changed

2 files changed

+214
-13
lines changed

src/DrawerPopup.tsx

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -284,30 +284,59 @@ const DrawerPopup: React.ForwardRefRenderFunction<
284284
break;
285285
}
286286
}
287-
// Use currentSize if available (from resizing), otherwise use width/height with proper fallback to defaults
287+
// Track if props changed to detect controlled mode
288+
const prevSizeRef = React.useRef<{
289+
width?: number | string;
290+
height?: number | string;
291+
}>({});
292+
const isControlledMode = React.useMemo(() => {
293+
// Consider controlled if width/height changes between renders (external updates)
294+
const currentSize = isHorizontal ? width : height;
295+
const prevSize = isHorizontal
296+
? prevSizeRef.current.width
297+
: prevSizeRef.current.height;
298+
299+
const hasExternalUpdate =
300+
currentSize !== undefined &&
301+
currentSize !== prevSize &&
302+
prevSize !== undefined;
303+
304+
// Update ref for next comparison
305+
prevSizeRef.current = { width, height };
306+
307+
return hasExternalUpdate;
308+
}, [width, height, isHorizontal]);
309+
310+
// Size calculation with controlled mode consideration
288311
if (isHorizontal) {
289312
let finalWidth: number | string;
290-
if (currentSize !== undefined) {
291-
// Highest priority: user dragged size
313+
if (isControlledMode) {
314+
// In controlled mode, prioritize external width
315+
finalWidth = width !== undefined ? width : defaultWidth;
316+
} else if (currentSize !== undefined) {
317+
// In uncontrolled mode, user dragged size takes precedence
292318
finalWidth = currentSize;
293319
} else if (width !== undefined) {
294-
// Second priority: explicitly provided width
320+
// Fallback to provided width
295321
finalWidth = width;
296322
} else {
297-
// Lowest priority: default width fallback
323+
// Final fallback to default
298324
finalWidth = defaultWidth;
299325
}
300326
wrapperStyle.width = parseWidthHeight(finalWidth);
301327
} else {
302328
let finalHeight: number | string;
303-
if (currentSize !== undefined) {
304-
// Highest priority: user dragged size
329+
if (isControlledMode) {
330+
// In controlled mode, prioritize external height
331+
finalHeight = height !== undefined ? height : defaultHeight;
332+
} else if (currentSize !== undefined) {
333+
// In uncontrolled mode, user dragged size takes precedence
305334
finalHeight = currentSize;
306335
} else if (height !== undefined) {
307-
// Second priority: explicitly provided height
336+
// Fallback to provided height
308337
finalHeight = height;
309338
} else {
310-
// Lowest priority: default height fallback
339+
// Final fallback to default
311340
finalHeight = defaultHeight;
312341
}
313342
wrapperStyle.height = parseWidthHeight(finalHeight);
@@ -358,10 +387,13 @@ const DrawerPopup: React.ForwardRefRenderFunction<
358387

359388
const handleResize = React.useCallback(
360389
(size: number) => {
361-
setCurrentSize(size);
390+
// In controlled mode, only trigger callback without updating internal state
391+
if (!isControlledMode) {
392+
setCurrentSize(size);
393+
}
362394
resizable?.onResize?.(size);
363395
},
364-
[resizable],
396+
[resizable, isControlledMode],
365397
);
366398

367399
const handleResizeStart = React.useCallback(() => {

tests/index.spec.tsx

Lines changed: 171 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { resetWarned } from '@rc-component/util/lib/warning';
44
import React from 'react';
55
import type { DrawerProps } from '../src';
66
import Drawer from '../src';
7+
import useDrag from '../src/hooks/useDrag';
78

89
describe('rc-drawer-menu', () => {
910
beforeEach(() => {
@@ -477,9 +478,9 @@ describe('rc-drawer-menu', () => {
477478
});
478479

479480
it('should support resizable horizontal', () => {
480-
let currentWidth = 200;
481+
let currentWidth = '200px';
481482
const setWidth = (newWidth: number) => {
482-
currentWidth = newWidth;
483+
currentWidth = newWidth + 'px';
483484
};
484485
const onResizeStart = jest.fn();
485486
const onResizeEnd = jest.fn();
@@ -683,4 +684,172 @@ describe('rc-drawer-menu', () => {
683684
});
684685
unmount();
685686
});
687+
688+
it('should handle default size fallbacks for both directions', () => {
689+
// Test horizontal default width (number type)
690+
const { unmount: unmount1 } = render(
691+
<Drawer open placement="right" defaultWidth={250}>
692+
<div>Test content</div>
693+
</Drawer>,
694+
);
695+
let contentWrapper = document.querySelector('.rc-drawer-content-wrapper');
696+
expect(contentWrapper).toHaveStyle({ width: '250px' });
697+
unmount1();
698+
699+
// Test vertical default height (number type)
700+
const { unmount: unmount2 } = render(
701+
<Drawer open placement="top" defaultHeight={150}>
702+
<div>Test content</div>
703+
</Drawer>,
704+
);
705+
contentWrapper = document.querySelector('.rc-drawer-content-wrapper');
706+
expect(contentWrapper).toHaveStyle({ height: '150px' });
707+
unmount2();
708+
709+
// Test string default width (triggers different code path)
710+
const { unmount: unmount3 } = render(
711+
<Drawer open placement="left" defaultWidth="300px">
712+
<div>Test content</div>
713+
</Drawer>,
714+
);
715+
contentWrapper = document.querySelector('.rc-drawer-content-wrapper');
716+
expect(contentWrapper).toHaveStyle({ width: '300px' });
717+
unmount3();
718+
});
719+
720+
it('should prioritize props over defaults when currentSize is undefined', () => {
721+
// Test width prop priority over defaultWidth
722+
const { unmount: unmount1 } = render(
723+
<Drawer
724+
open
725+
placement="left"
726+
width={280}
727+
defaultWidth="350px" // string to keep currentSize undefined
728+
>
729+
<div>Test content</div>
730+
</Drawer>,
731+
);
732+
let contentWrapper = document.querySelector('.rc-drawer-content-wrapper');
733+
expect(contentWrapper).toHaveStyle({ width: '280px' });
734+
unmount1();
735+
736+
// Test height prop priority over defaultHeight
737+
const { unmount: unmount2 } = render(
738+
<Drawer
739+
open
740+
placement="top"
741+
height={180}
742+
defaultHeight="250px" // string to keep currentSize undefined
743+
>
744+
<div>Test content</div>
745+
</Drawer>,
746+
);
747+
contentWrapper = document.querySelector('.rc-drawer-content-wrapper');
748+
expect(contentWrapper).toHaveStyle({ height: '180px' });
749+
unmount2();
750+
});
751+
752+
it('should handle controlled mode for both width and height', () => {
753+
// Test controlled mode for width
754+
const { rerender: rerender1, unmount: unmount1 } = render(
755+
<Drawer
756+
open
757+
placement="left"
758+
width={200}
759+
resizable={{ onResize: () => {} }}
760+
>
761+
<div>Test content</div>
762+
</Drawer>,
763+
);
764+
rerender1(
765+
<Drawer
766+
open
767+
placement="left"
768+
width={300}
769+
resizable={{ onResize: () => {} }}
770+
>
771+
<div>Test content</div>
772+
</Drawer>,
773+
);
774+
let contentWrapper = document.querySelector('.rc-drawer-content-wrapper');
775+
expect(contentWrapper).toHaveStyle({ width: '300px' });
776+
unmount1();
777+
778+
// Test controlled mode for height
779+
const { rerender: rerender2, unmount: unmount2 } = render(
780+
<Drawer
781+
open
782+
placement="top"
783+
height={150}
784+
resizable={{ onResize: () => {} }}
785+
>
786+
<div>Test content</div>
787+
</Drawer>,
788+
);
789+
rerender2(
790+
<Drawer
791+
open
792+
placement="top"
793+
height={250}
794+
resizable={{ onResize: () => {} }}
795+
>
796+
<div>Test content</div>
797+
</Drawer>,
798+
);
799+
contentWrapper = document.querySelector('.rc-drawer-content-wrapper');
800+
expect(contentWrapper).toHaveStyle({ height: '250px' });
801+
unmount2();
802+
});
803+
804+
it('should fallback to container size when currentSize is undefined', () => {
805+
const onResizeStart = jest.fn();
806+
const onResize = jest.fn();
807+
const onResizeEnd = jest.fn();
808+
809+
const mockContainer = {
810+
getBoundingClientRect: jest.fn(() => ({
811+
width: 350,
812+
height: 250,
813+
top: 0,
814+
left: 0,
815+
bottom: 250,
816+
right: 350,
817+
x: 0,
818+
y: 0,
819+
toJSON: () => ({}),
820+
})),
821+
} as any;
822+
823+
const TestComponent = () => {
824+
const { dragElementProps } = useDrag({
825+
prefixCls: 'rc-drawer-resizable',
826+
direction: 'left',
827+
minSize: 0,
828+
containerRef: { current: mockContainer },
829+
currentSize: undefined,
830+
onResizeStart,
831+
onResize,
832+
onResizeEnd,
833+
});
834+
835+
return <div {...dragElementProps}>Dragger</div>;
836+
};
837+
838+
render(<TestComponent />);
839+
840+
const dragger = document.querySelector('.rc-drawer-resizable-dragger');
841+
expect(dragger).toBeTruthy();
842+
843+
fireEvent.mouseDown(dragger, { clientX: 200 });
844+
845+
expect(mockContainer.getBoundingClientRect).toHaveBeenCalled();
846+
847+
expect(onResizeStart).toHaveBeenCalledWith(350);
848+
849+
fireEvent.mouseMove(document, { clientX: 250 });
850+
expect(onResize).toHaveBeenCalledWith(400);
851+
852+
fireEvent.mouseUp(document, { clientX: 250 });
853+
expect(onResizeEnd).toHaveBeenCalledWith(350);
854+
});
686855
});

0 commit comments

Comments
 (0)