Skip to content

Commit a7801a8

Browse files
author
刘欢
committed
feat:Using the showScrollBar property of the List component
1 parent 3ff731d commit a7801a8

File tree

4 files changed

+172
-2
lines changed

4 files changed

+172
-2
lines changed

src/BaseSelect/index.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export interface BaseSelectProps extends BaseSelectPrivateProps, React.AriaAttri
136136
tagRender?: (props: CustomTagProps) => React.ReactElement;
137137
direction?: 'ltr' | 'rtl';
138138
maxLength?: number;
139-
139+
showScrollBar?: boolean | 'optional';
140140
// MISC
141141
tabIndex?: number;
142142
autoFocus?: boolean;
@@ -223,6 +223,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
223223
className,
224224
showSearch,
225225
tagRender,
226+
showScrollBar = 'optional',
226227
direction,
227228
omitDomProps,
228229

@@ -693,8 +694,19 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
693694
showSearch: mergedShowSearch,
694695
multiple,
695696
toggleOpen: onToggleOpen,
697+
showScrollBar,
696698
}),
697-
[props, notFoundContent, triggerOpen, mergedOpen, id, mergedShowSearch, multiple, onToggleOpen],
699+
[
700+
props,
701+
notFoundContent,
702+
triggerOpen,
703+
mergedOpen,
704+
id,
705+
mergedShowSearch,
706+
multiple,
707+
onToggleOpen,
708+
showScrollBar,
709+
],
698710
);
699711

700712
// ==================================================================

src/OptionList.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
4444
toggleOpen,
4545
notFoundContent,
4646
onPopupScroll,
47+
showScrollBar,
4748
} = useBaseProps();
4849
const {
4950
maxCount,
@@ -325,6 +326,7 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
325326
virtual={virtual}
326327
direction={direction}
327328
innerProps={virtual ? null : a11yProps}
329+
showScrollBar={showScrollBar}
328330
>
329331
{(item, itemIndex) => {
330332
const { group, groupOption, data, label, value } = item;

tests/ListScrollBar.test.tsx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import React from 'react';
2+
import { render, waitFor } from '@testing-library/react';
3+
import { spyElementPrototypes } from './utils/domHook';
4+
import Select from '../src';
5+
6+
jest.mock('../src/utils/platformUtil');
7+
// Mock VirtualList
8+
jest.mock('rc-virtual-list', () => {
9+
const OriReact = jest.requireActual('react');
10+
const OriList = jest.requireActual('rc-virtual-list').default;
11+
12+
return OriReact.forwardRef((props, ref) => {
13+
const oriRef = OriReact.useRef();
14+
15+
OriReact.useImperativeHandle(ref, () => ({
16+
...oriRef.current,
17+
scrollTo: (arg) => {
18+
global.scrollToArgs = arg;
19+
oriRef.current.scrollTo(arg);
20+
},
21+
}));
22+
23+
return <OriList {...props} ref={oriRef} />;
24+
});
25+
});
26+
27+
describe('List.Scroll', () => {
28+
let mockElement;
29+
let boundingRect = {
30+
top: 0,
31+
bottom: 0,
32+
width: 100,
33+
height: 50,
34+
};
35+
36+
beforeAll(() => {
37+
mockElement = spyElementPrototypes(HTMLElement, {
38+
offsetHeight: {
39+
get: () => 100,
40+
},
41+
clientHeight: {
42+
get: () => 50,
43+
},
44+
getBoundingClientRect: () => boundingRect,
45+
offsetParent: {
46+
get: () => document.body,
47+
},
48+
});
49+
});
50+
51+
afterAll(() => {
52+
mockElement.mockRestore();
53+
});
54+
55+
beforeEach(() => {
56+
boundingRect = {
57+
top: 0,
58+
bottom: 0,
59+
width: 100,
60+
height: 50,
61+
};
62+
jest.useFakeTimers();
63+
});
64+
65+
afterEach(() => {
66+
jest.useRealTimers();
67+
});
68+
69+
it('should show scrollbar when showScrollBar is true', async () => {
70+
const options = Array.from({ length: 10 }, (_, index) => ({
71+
label: `${index + 1}`,
72+
value: `${index + 1}`,
73+
}));
74+
75+
const { container } = render(<Select open showScrollBar options={options} />);
76+
77+
await waitFor(() => {
78+
const scrollbarElement = container.querySelector('.rc-virtual-list-scrollbar-visible');
79+
expect(scrollbarElement).not.toBeNull();
80+
});
81+
});
82+
it('should not have scrollbar when showScrollBar is false', async () => {
83+
const options = Array.from({ length: 10 }, (_, index) => ({
84+
label: `${index + 1}`,
85+
value: `${index + 1}`,
86+
}));
87+
88+
const { container } = render(<Select open showScrollBar={false} options={options} />);
89+
90+
await waitFor(() => {
91+
const scrollbarElement = container.querySelector('.rc-virtual-list-scrollbar-visible');
92+
expect(scrollbarElement).toBeNull();
93+
});
94+
});
95+
});

tests/utils/domHook.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* eslint-disable no-param-reassign */
2+
const NO_EXIST = { __NOT_EXIST: true };
3+
4+
export function spyElementPrototypes(Element, properties) {
5+
const propNames = Object.keys(properties);
6+
const originDescriptors = {};
7+
8+
propNames.forEach((propName) => {
9+
const originDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, propName);
10+
originDescriptors[propName] = originDescriptor || NO_EXIST;
11+
12+
const spyProp = properties[propName];
13+
14+
if (typeof spyProp === 'function') {
15+
// If is a function
16+
Element.prototype[propName] = function spyFunc(...args) {
17+
return spyProp.call(this, originDescriptor, ...args);
18+
};
19+
} else {
20+
// Otherwise tread as a property
21+
Object.defineProperty(Element.prototype, propName, {
22+
...spyProp,
23+
set(value) {
24+
if (spyProp.set) {
25+
return spyProp.set.call(this, originDescriptor, value);
26+
}
27+
return originDescriptor.set(value);
28+
},
29+
get() {
30+
if (spyProp.get) {
31+
return spyProp.get.call(this, originDescriptor);
32+
}
33+
return originDescriptor.get();
34+
},
35+
configurable: true,
36+
});
37+
}
38+
});
39+
40+
return {
41+
mockRestore() {
42+
propNames.forEach((propName) => {
43+
const originDescriptor = originDescriptors[propName];
44+
if (originDescriptor === NO_EXIST) {
45+
delete Element.prototype[propName];
46+
} else if (typeof originDescriptor === 'function') {
47+
Element.prototype[propName] = originDescriptor;
48+
} else {
49+
Object.defineProperty(Element.prototype, propName, originDescriptor);
50+
}
51+
});
52+
},
53+
};
54+
}
55+
56+
export function spyElementPrototype(Element, propName, property) {
57+
return spyElementPrototypes(Element, {
58+
[propName]: property,
59+
});
60+
}
61+
/* eslint-enable no-param-reassign */

0 commit comments

Comments
 (0)