mirror of https://github.com/apache/superset.git
chore: Improves the Select component UI/UX - iteration 2 (#15235)
This commit is contained in:
parent
5b2eb8d933
commit
fc1a62b78c
|
@ -1,244 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import Select, { OptionsType, SelectProps } from './AntdSelect';
|
||||
|
||||
export default {
|
||||
title: 'Select',
|
||||
component: Select,
|
||||
};
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: 'Such an incredibly awesome long long label',
|
||||
value: 'Such an incredibly awesome long long label',
|
||||
},
|
||||
{
|
||||
label: 'Another incredibly awesome long long label',
|
||||
value: 'Another incredibly awesome long long label',
|
||||
},
|
||||
{ label: 'Just a label', value: 'Just a label' },
|
||||
{ label: 'A', value: 'A' },
|
||||
{ label: 'B', value: 'B' },
|
||||
{ label: 'C', value: 'C' },
|
||||
{ label: 'D', value: 'D' },
|
||||
{ label: 'E', value: 'E' },
|
||||
{ label: 'F', value: 'F' },
|
||||
{ label: 'G', value: 'G' },
|
||||
{ label: 'H', value: 'H' },
|
||||
{ label: 'I', value: 'I' },
|
||||
];
|
||||
|
||||
const selectPositions = [
|
||||
{
|
||||
id: 'topLeft',
|
||||
style: { top: '0', left: '0' },
|
||||
},
|
||||
{
|
||||
id: 'topRight',
|
||||
style: { top: '0', right: '0' },
|
||||
},
|
||||
{
|
||||
id: 'bottomLeft',
|
||||
style: { bottom: '0', left: '0' },
|
||||
},
|
||||
{
|
||||
id: 'bottomRight',
|
||||
style: { bottom: '0', right: '0' },
|
||||
},
|
||||
];
|
||||
|
||||
export const AtEveryCorner = () => (
|
||||
<>
|
||||
{selectPositions.map(position => (
|
||||
<div
|
||||
key={position.id}
|
||||
style={{
|
||||
...position.style,
|
||||
width: '120px',
|
||||
position: 'absolute',
|
||||
}}
|
||||
>
|
||||
<Select ariaLabel={`gallery-${position.id}`} options={options} />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
AtEveryCorner.story = {
|
||||
parameters: {
|
||||
actions: {
|
||||
disable: true,
|
||||
},
|
||||
controls: {
|
||||
disable: true,
|
||||
},
|
||||
knobs: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async function fetchUserList(search: string, page = 0): Promise<OptionsType> {
|
||||
const username = search.trim().toLowerCase();
|
||||
return new Promise(resolve => {
|
||||
const users = [
|
||||
'John',
|
||||
'Liam',
|
||||
'Olivia',
|
||||
'Emma',
|
||||
'Noah',
|
||||
'Ava',
|
||||
'Oliver',
|
||||
'Elijah',
|
||||
'Charlotte',
|
||||
'Diego',
|
||||
'Evan',
|
||||
'Michael',
|
||||
'Giovanni',
|
||||
'Luca',
|
||||
'Paolo',
|
||||
'Francesca',
|
||||
'Chiara',
|
||||
'Sara',
|
||||
'Valentina',
|
||||
'Jessica',
|
||||
'Angelica',
|
||||
'Mario',
|
||||
'Marco',
|
||||
'Andrea',
|
||||
'Luigi',
|
||||
'Quarto',
|
||||
'Quinto',
|
||||
'Sesto',
|
||||
'Franco',
|
||||
'Sandro',
|
||||
'Alehandro',
|
||||
'Johnny',
|
||||
'Nikole',
|
||||
'Igor',
|
||||
'Sipatha',
|
||||
'Thami',
|
||||
'Munei',
|
||||
'Guilherme',
|
||||
'Umair',
|
||||
'Ashfaq',
|
||||
'Amna',
|
||||
'Irfan',
|
||||
'George',
|
||||
'Naseer',
|
||||
'Mohammad',
|
||||
'Rick',
|
||||
'Saliya',
|
||||
'Claire',
|
||||
'Benedetta',
|
||||
'Ilenia',
|
||||
];
|
||||
|
||||
let results: { label: string; value: string }[] = [];
|
||||
|
||||
if (!username) {
|
||||
results = users.map(u => ({
|
||||
label: u,
|
||||
value: u,
|
||||
}));
|
||||
} else {
|
||||
const foundUsers = users.find(u => u.toLowerCase().includes(username));
|
||||
if (foundUsers && Array.isArray(foundUsers)) {
|
||||
results = foundUsers.map(u => ({ label: u, value: u }));
|
||||
}
|
||||
if (foundUsers && typeof foundUsers === 'string') {
|
||||
const u = foundUsers;
|
||||
results = [{ label: u, value: u }];
|
||||
}
|
||||
}
|
||||
const offset = !page ? 0 : page * 10;
|
||||
const resultsNum = !page ? 10 : (page + 1) * 10;
|
||||
results = results.length ? results.splice(offset, resultsNum) : [];
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Emulating network request for search string: ${
|
||||
username || '"empty"'
|
||||
} and page: ${page} with results: [${results
|
||||
.map(u => u.value)
|
||||
.join(', ')}]`,
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
resolve(results);
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchUserListError(): Promise<OptionsType> {
|
||||
return new Promise((_, reject) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
reject('This is an error');
|
||||
});
|
||||
}
|
||||
|
||||
export const AsyncSelect = (args: SelectProps & { withError: boolean }) => (
|
||||
<Select
|
||||
{...args}
|
||||
options={args.withError ? fetchUserListError : fetchUserList}
|
||||
/>
|
||||
);
|
||||
|
||||
AsyncSelect.args = {
|
||||
withError: false,
|
||||
allowNewOptions: false,
|
||||
paginatedFetch: false,
|
||||
};
|
||||
|
||||
AsyncSelect.argTypes = {
|
||||
mode: {
|
||||
control: { type: 'select', options: ['single', 'multiple', 'tags'] },
|
||||
},
|
||||
};
|
||||
|
||||
AsyncSelect.story = {
|
||||
parameters: {
|
||||
knobs: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const InteractiveSelect = (args: SelectProps) => <Select {...args} />;
|
||||
|
||||
InteractiveSelect.args = {
|
||||
allowNewOptions: false,
|
||||
options,
|
||||
showSearch: false,
|
||||
};
|
||||
|
||||
InteractiveSelect.argTypes = {
|
||||
mode: {
|
||||
control: { type: 'select', options: ['single', 'multiple', 'tags'] },
|
||||
},
|
||||
};
|
||||
|
||||
InteractiveSelect.story = {
|
||||
parameters: {
|
||||
knobs: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,313 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { ReactNode, useState, useCallback } from 'react';
|
||||
import Select, { SelectProps, OptionsPromiseResult } from './Select';
|
||||
|
||||
export default {
|
||||
title: 'Select',
|
||||
component: Select,
|
||||
};
|
||||
|
||||
const DEFAULT_WIDTH = 200;
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: 'Such an incredibly awesome long long label',
|
||||
value: 'Such an incredibly awesome long long label',
|
||||
},
|
||||
{
|
||||
label: 'Another incredibly awesome long long label',
|
||||
value: 'Another incredibly awesome long long label',
|
||||
},
|
||||
{ label: 'Just a label', value: 'Just a label' },
|
||||
{ label: 'A', value: 'A' },
|
||||
{ label: 'B', value: 'B' },
|
||||
{ label: 'C', value: 'C' },
|
||||
{ label: 'D', value: 'D' },
|
||||
{ label: 'E', value: 'E' },
|
||||
{ label: 'F', value: 'F' },
|
||||
{ label: 'G', value: 'G' },
|
||||
{ label: 'H', value: 'H' },
|
||||
{ label: 'I', value: 'I' },
|
||||
];
|
||||
|
||||
const selectPositions = [
|
||||
{
|
||||
id: 'topLeft',
|
||||
style: { top: '0', left: '0' },
|
||||
},
|
||||
{
|
||||
id: 'topRight',
|
||||
style: { top: '0', right: '0' },
|
||||
},
|
||||
{
|
||||
id: 'bottomLeft',
|
||||
style: { bottom: '0', left: '0' },
|
||||
},
|
||||
{
|
||||
id: 'bottomRight',
|
||||
style: { bottom: '0', right: '0' },
|
||||
},
|
||||
];
|
||||
|
||||
export const AtEveryCorner = () => (
|
||||
<>
|
||||
{selectPositions.map(position => (
|
||||
<div
|
||||
key={position.id}
|
||||
style={{
|
||||
...position.style,
|
||||
width: DEFAULT_WIDTH,
|
||||
position: 'absolute',
|
||||
}}
|
||||
>
|
||||
<Select ariaLabel={`gallery-${position.id}`} options={options} />
|
||||
</div>
|
||||
))}
|
||||
<p style={{ position: 'absolute', top: '40%', left: '33%', width: 500 }}>
|
||||
The objective of this panel is to show how the Select behaves when in
|
||||
touch with the viewport extremities. In particular, how the drop-down is
|
||||
displayed and if the tooltips of truncated items are correctly positioned.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
|
||||
AtEveryCorner.story = {
|
||||
parameters: {
|
||||
actions: {
|
||||
disable: true,
|
||||
},
|
||||
controls: {
|
||||
disable: true,
|
||||
},
|
||||
knobs: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const USERS = [
|
||||
'John',
|
||||
'Liam',
|
||||
'Olivia',
|
||||
'Emma',
|
||||
'Noah',
|
||||
'Ava',
|
||||
'Oliver',
|
||||
'Elijah',
|
||||
'Charlotte',
|
||||
'Diego',
|
||||
'Evan',
|
||||
'Michael',
|
||||
'Giovanni',
|
||||
'Luca',
|
||||
'Paolo',
|
||||
'Francesca',
|
||||
'Chiara',
|
||||
'Sara',
|
||||
'Valentina',
|
||||
'Jessica',
|
||||
'Angelica',
|
||||
'Mario',
|
||||
'Marco',
|
||||
'Andrea',
|
||||
'Luigi',
|
||||
'Quarto',
|
||||
'Quinto',
|
||||
'Sesto',
|
||||
'Franco',
|
||||
'Sandro',
|
||||
'Alehandro',
|
||||
'Johnny',
|
||||
'Nikole',
|
||||
'Igor',
|
||||
'Sipatha',
|
||||
'Thami',
|
||||
'Munei',
|
||||
'Guilherme',
|
||||
'Umair',
|
||||
'Ashfaq',
|
||||
'Amna',
|
||||
'Irfan',
|
||||
'George',
|
||||
'Naseer',
|
||||
'Mohammad',
|
||||
'Rick',
|
||||
'Saliya',
|
||||
'Claire',
|
||||
'Benedetta',
|
||||
'Ilenia',
|
||||
];
|
||||
|
||||
export const AsyncSelect = (
|
||||
args: SelectProps & { withError: boolean; responseTime: number },
|
||||
) => {
|
||||
const [requests, setRequests] = useState<ReactNode[]>([]);
|
||||
|
||||
const fetchUserList = useCallback(
|
||||
(search: string, page = 0): Promise<OptionsPromiseResult> => {
|
||||
const username = search.trim().toLowerCase();
|
||||
return new Promise(resolve => {
|
||||
let results: { label: string; value: string }[] = [];
|
||||
|
||||
if (!username) {
|
||||
results = USERS.map(u => ({
|
||||
label: u,
|
||||
value: u,
|
||||
}));
|
||||
} else {
|
||||
const foundUsers = USERS.find(u =>
|
||||
u.toLowerCase().includes(username),
|
||||
);
|
||||
if (foundUsers && Array.isArray(foundUsers)) {
|
||||
results = foundUsers.map(u => ({ label: u, value: u }));
|
||||
}
|
||||
if (foundUsers && typeof foundUsers === 'string') {
|
||||
const u = foundUsers;
|
||||
results = [{ label: u, value: u }];
|
||||
}
|
||||
}
|
||||
|
||||
const pageSize = 10;
|
||||
const offset = !page ? 0 : page * pageSize;
|
||||
const resultsNum = !page ? pageSize : (page + 1) * pageSize;
|
||||
results = results.length ? results.splice(offset, resultsNum) : [];
|
||||
|
||||
const request = (
|
||||
<>
|
||||
Emulating network request for page <b>{page}</b> and search{' '}
|
||||
<b>{username || 'empty'}</b> ... <b>{resultsNum}</b> results
|
||||
</>
|
||||
);
|
||||
|
||||
setRequests(requests => [request, ...requests]);
|
||||
|
||||
const totalPages =
|
||||
USERS.length / pageSize + (USERS.length % pageSize > 0 ? 1 : 0);
|
||||
|
||||
const result: OptionsPromiseResult = {
|
||||
data: results,
|
||||
hasMoreData: page + 1 < totalPages,
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
resolve(result);
|
||||
}, args.responseTime * 1000);
|
||||
});
|
||||
},
|
||||
[args.responseTime],
|
||||
);
|
||||
|
||||
async function fetchUserListError(): Promise<OptionsPromiseResult> {
|
||||
return new Promise((_, reject) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
reject('This is an error');
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
width: DEFAULT_WIDTH,
|
||||
}}
|
||||
>
|
||||
<Select
|
||||
{...args}
|
||||
options={args.withError ? fetchUserListError : fetchUserList}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 32,
|
||||
left: DEFAULT_WIDTH + 100,
|
||||
height: 400,
|
||||
width: 600,
|
||||
overflowY: 'auto',
|
||||
border: '1px solid #d9d9d9',
|
||||
padding: 20,
|
||||
}}
|
||||
>
|
||||
{requests.map(request => (
|
||||
<p>{request}</p>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
AsyncSelect.args = {
|
||||
withError: false,
|
||||
allowNewOptions: false,
|
||||
paginatedFetch: false,
|
||||
};
|
||||
|
||||
AsyncSelect.argTypes = {
|
||||
mode: {
|
||||
control: { type: 'select', options: ['single', 'multiple', 'tags'] },
|
||||
},
|
||||
responseTime: {
|
||||
defaultValue: 1,
|
||||
name: 'responseTime (seconds)',
|
||||
control: {
|
||||
type: 'range',
|
||||
min: 1,
|
||||
max: 5,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
AsyncSelect.story = {
|
||||
parameters: {
|
||||
knobs: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const InteractiveSelect = (args: SelectProps) => (
|
||||
<div
|
||||
style={{
|
||||
width: DEFAULT_WIDTH,
|
||||
}}
|
||||
>
|
||||
<Select {...args} />
|
||||
</div>
|
||||
);
|
||||
|
||||
InteractiveSelect.args = {
|
||||
allowNewOptions: false,
|
||||
options,
|
||||
showSearch: false,
|
||||
};
|
||||
|
||||
InteractiveSelect.argTypes = {
|
||||
mode: {
|
||||
control: { type: 'select', options: ['single', 'multiple', 'tags'] },
|
||||
},
|
||||
};
|
||||
|
||||
InteractiveSelect.story = {
|
||||
parameters: {
|
||||
knobs: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
};
|
|
@ -39,6 +39,7 @@ import { getClientErrorObject } from 'src/utils/getClientErrorObject';
|
|||
import { hasOption } from './utils';
|
||||
|
||||
type AntdSelectAllProps = AntdSelectProps<AntdSelectValue>;
|
||||
|
||||
type PickedSelectProps = Pick<
|
||||
AntdSelectAllProps,
|
||||
| 'allowClear'
|
||||
|
@ -55,16 +56,25 @@ type PickedSelectProps = Pick<
|
|||
| 'showSearch'
|
||||
| 'value'
|
||||
>;
|
||||
|
||||
export type OptionsType = Exclude<AntdSelectAllProps['options'], undefined>;
|
||||
|
||||
export type OptionsPromiseResult = {
|
||||
data: OptionsType;
|
||||
hasMoreData: boolean;
|
||||
};
|
||||
|
||||
export type OptionsPromise = (
|
||||
search: string,
|
||||
page?: number,
|
||||
) => Promise<OptionsType>;
|
||||
) => Promise<OptionsPromiseResult>;
|
||||
|
||||
export enum ESelectTypes {
|
||||
MULTIPLE = 'multiple',
|
||||
TAGS = 'tags',
|
||||
SINGLE = '',
|
||||
}
|
||||
|
||||
export interface SelectProps extends PickedSelectProps {
|
||||
allowNewOptions?: boolean;
|
||||
ariaLabel: string;
|
||||
|
@ -75,10 +85,15 @@ export interface SelectProps extends PickedSelectProps {
|
|||
paginatedFetch?: boolean;
|
||||
}
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
// unexposed default behaviors
|
||||
const MAX_TAG_COUNT = 4;
|
||||
const TOKEN_SEPARATORS = [',', '\n', '\t', ';'];
|
||||
const DEBOUNCE_TIMEOUT = 800;
|
||||
const DEBOUNCE_TIMEOUT = 500;
|
||||
|
||||
const Error = ({ error }: { error: string }) => {
|
||||
const StyledError = styled.div`
|
||||
|
@ -108,7 +123,7 @@ const DropdownContent = ({
|
|||
return content;
|
||||
};
|
||||
|
||||
const SelectComponent = ({
|
||||
const Select = ({
|
||||
allowNewOptions = false,
|
||||
ariaLabel,
|
||||
filterOption,
|
||||
|
@ -135,6 +150,7 @@ const SelectComponent = ({
|
|||
const [isLoading, setLoading] = useState(loading);
|
||||
const [error, setError] = useState('');
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState(false);
|
||||
const [hasMoreData, setHasMoreData] = useState(false);
|
||||
const fetchRef = useRef(0);
|
||||
|
||||
const handleSelectMode = () => {
|
||||
|
@ -191,9 +207,8 @@ const SelectComponent = ({
|
|||
}
|
||||
};
|
||||
|
||||
const handleFetch = useMemo(() => {
|
||||
const fetchOptions = options as OptionsPromise;
|
||||
const loadOptions = (value: string, paginate?: 'paginate') => {
|
||||
const handleFetch = useMemo(
|
||||
() => (value: string, paginate?: 'paginate') => {
|
||||
if (paginate) {
|
||||
fetchRef.current += 1;
|
||||
} else {
|
||||
|
@ -201,15 +216,17 @@ const SelectComponent = ({
|
|||
}
|
||||
const fetchId = fetchRef.current;
|
||||
const page = paginatedFetch ? fetchId : undefined;
|
||||
|
||||
const fetchOptions = options as OptionsPromise;
|
||||
fetchOptions(value, page)
|
||||
.then((newOptions: OptionsType) => {
|
||||
.then((result: OptionsPromiseResult) => {
|
||||
const { data, hasMoreData } = result;
|
||||
setHasMoreData(hasMoreData);
|
||||
if (fetchId !== fetchRef.current) return;
|
||||
if (newOptions && Array.isArray(newOptions) && newOptions.length) {
|
||||
if (data && Array.isArray(data) && data.length) {
|
||||
// merges with existing and creates unique options
|
||||
setOptions(prevOptions => [
|
||||
...prevOptions,
|
||||
...newOptions.filter(
|
||||
...data.filter(
|
||||
newOpt =>
|
||||
!prevOptions.find(prevOpt => prevOpt.value === newOpt.value),
|
||||
),
|
||||
|
@ -223,11 +240,11 @@ const SelectComponent = ({
|
|||
}),
|
||||
)
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
return debounce(loadOptions, DEBOUNCE_TIMEOUT);
|
||||
}, [options, paginatedFetch]);
|
||||
},
|
||||
[options, paginatedFetch],
|
||||
);
|
||||
|
||||
const handleOnSearch = (search: string) => {
|
||||
const handleOnSearch = debounce((search: string) => {
|
||||
const searchValue = search.trim();
|
||||
// enables option creation
|
||||
if (allowNewOptions && isSingleMode) {
|
||||
|
@ -252,11 +269,12 @@ const SelectComponent = ({
|
|||
}
|
||||
}
|
||||
setSearchedValue(searchValue);
|
||||
};
|
||||
}, DEBOUNCE_TIMEOUT);
|
||||
|
||||
const handlePagination = (e: UIEvent<HTMLElement>) => {
|
||||
const vScroll = e.currentTarget;
|
||||
if (
|
||||
hasMoreData &&
|
||||
isAsync &&
|
||||
paginatedFetch &&
|
||||
vScroll.scrollTop === vScroll.scrollHeight - vScroll.offsetHeight
|
||||
|
@ -310,19 +328,21 @@ const SelectComponent = ({
|
|||
}
|
||||
}, [allowNewOptions, isAsync, handleFetch, searchedValue]);
|
||||
|
||||
const dropdownRender = (
|
||||
originNode: ReactElement & { ref?: RefObject<HTMLElement> },
|
||||
) => {
|
||||
if (!isDropdownVisible) {
|
||||
originNode.ref?.current?.scrollTo({ top: 0 });
|
||||
}
|
||||
return <DropdownContent content={originNode} error={error} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledContainer>
|
||||
{header}
|
||||
<AntdSelect
|
||||
aria-label={ariaLabel || name}
|
||||
dropdownRender={(
|
||||
originNode: ReactElement & { ref?: RefObject<HTMLElement> },
|
||||
) => {
|
||||
if (!isDropdownVisible) {
|
||||
originNode.ref?.current?.scrollTo({ top: 0 });
|
||||
}
|
||||
return <DropdownContent content={originNode} error={error} />;
|
||||
}}
|
||||
dropdownRender={dropdownRender}
|
||||
filterOption={handleFilterOption as any}
|
||||
getPopupContainer={triggerNode => triggerNode.parentNode}
|
||||
loading={isLoading}
|
||||
|
@ -339,17 +359,11 @@ const SelectComponent = ({
|
|||
showSearch={shouldShowSearch}
|
||||
tokenSeparators={TOKEN_SEPARATORS}
|
||||
value={selectValue}
|
||||
style={{ width: '100%' }}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const Select = styled((
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
{ ...props }: SelectProps,
|
||||
) => <SelectComponent {...props} />)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export default Select;
|
|
@ -23,7 +23,7 @@ import {
|
|||
GroupedOptionsType,
|
||||
} from 'react-select';
|
||||
|
||||
import { OptionsType as AntdOptionsType } from './AntdSelect';
|
||||
import { OptionsType as AntdOptionsType } from './Select';
|
||||
|
||||
/**
|
||||
* Find Option value that matches a possibly string value.
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { default as Select } from './Select/AntdSelect';
|
||||
export { default as Select } from './Select/Select';
|
||||
|
|
Loading…
Reference in New Issue