mirror of
https://github.com/datahub-project/datahub.git
synced 2025-09-25 09:00:50 +00:00
fix(ui): Various component library updates (#14249)
Co-authored-by: John Joyce <john@Mac-4089.lan> Co-authored-by: John Joyce <john@Mac-4260.lan>
This commit is contained in:
parent
c5a8d04496
commit
6e8c8c6b85
@ -16,6 +16,7 @@ export function DatePicker({
|
||||
variant = datePickerDefault.variant,
|
||||
disabled = datePickerDefault.disabled,
|
||||
disabledDate,
|
||||
placeholder,
|
||||
}: DatePickerProps) {
|
||||
const [internalValue, setInternalValue] = useState<DatePickerValue | undefined>(value);
|
||||
|
||||
@ -38,8 +39,9 @@ export function DatePicker({
|
||||
open: isOpen,
|
||||
setValue: setInternalValue,
|
||||
},
|
||||
placeholder,
|
||||
});
|
||||
}, [disabled, isOpen, inputRender]);
|
||||
}, [disabled, placeholder, isOpen, inputRender]);
|
||||
|
||||
return (
|
||||
<StyledAntdDatePicker
|
||||
|
@ -9,6 +9,7 @@ export type DatePickerProps = {
|
||||
disabled?: boolean;
|
||||
disabledDate?: (value: DatePickerValue) => boolean;
|
||||
variant?: DatePickerVariant;
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export type DatePickerState = {
|
||||
|
@ -0,0 +1,23 @@
|
||||
import { Input } from '@components';
|
||||
import React from 'react';
|
||||
|
||||
import { ExtendedInputRenderProps } from '@components/components/DatePicker/types';
|
||||
|
||||
export function DefaultDatePickerInput({ datePickerProps, ...props }: ExtendedInputRenderProps) {
|
||||
const { disabled } = datePickerProps;
|
||||
return (
|
||||
<Input
|
||||
{...props}
|
||||
label=""
|
||||
value={props.value || ''}
|
||||
isDisabled={disabled}
|
||||
placeholder={props.placeholder || 'Select date'}
|
||||
icon={{ icon: 'CalendarMonth' }}
|
||||
isReadOnly
|
||||
style={{
|
||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||
...props.style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
@ -2,7 +2,14 @@ import React from 'react';
|
||||
|
||||
import { StyledCalendarWrapper } from '@components/components/DatePicker/components';
|
||||
import { VariantProps } from '@components/components/DatePicker/types';
|
||||
import { DefaultDatePickerInput } from '@components/components/DatePicker/variants/common/components';
|
||||
|
||||
export const CommonVariantProps: VariantProps = {
|
||||
panelRender: (panel) => <StyledCalendarWrapper>{panel}</StyledCalendarWrapper>,
|
||||
inputRender: (props) => <DefaultDatePickerInput {...props} />,
|
||||
bordered: false,
|
||||
allowClear: false,
|
||||
format: 'll',
|
||||
suffixIcon: null,
|
||||
$noDefaultPaddings: true,
|
||||
};
|
||||
|
@ -49,7 +49,7 @@ const CaretWrapper = styled.div<{ $disabled?: boolean }>`
|
||||
& svg {
|
||||
color: ${colors.gray[1800]};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: start;
|
||||
cursor: ${(props) => (props.$disabled ? 'not-allowed' : 'pointer')};
|
||||
|
||||
:hover {
|
||||
|
@ -6,10 +6,5 @@ import { DateSwitcherInput } from '@components/components/DatePicker/variants/da
|
||||
|
||||
export const DateSwitcherVariantProps: VariantProps = {
|
||||
...CommonVariantProps,
|
||||
bordered: false,
|
||||
allowClear: false,
|
||||
format: 'll',
|
||||
suffixIcon: null,
|
||||
inputRender: (props) => <DateSwitcherInput {...props} />,
|
||||
$noDefaultPaddings: true,
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ export const InputWrapper = styled.div({
|
||||
export const InputContainer = styled.div(
|
||||
({ isSuccess, warning, isDisabled, isInvalid }: InputProps) => ({
|
||||
border: `${borders['1px']} ${getStatusColors(isSuccess, warning, isInvalid)}`,
|
||||
backgroundColor: isDisabled ? colors.gray[100] : colors.white,
|
||||
backgroundColor: isDisabled ? colors.gray[1500] : colors.white,
|
||||
paddingRight: spacing.md,
|
||||
}),
|
||||
{
|
||||
@ -67,6 +67,10 @@ export const InputField = styled.input({
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
backgroundColor: colors.gray[1500],
|
||||
},
|
||||
});
|
||||
|
||||
export const Required = styled.span({
|
||||
|
@ -1,9 +1,15 @@
|
||||
import { Button, Icon } from '@components';
|
||||
import { Button } from '@components';
|
||||
import { Checkbox } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Icon } from '@components/components/Icon';
|
||||
import { SelectLabelVariants, SelectSizeOptions, SelectStyleProps } from '@components/components/Select/types';
|
||||
import { getOptionLabelStyle, getSelectFontStyles, getSelectStyle } from '@components/components/Select/utils';
|
||||
import {
|
||||
getDropdownStyle,
|
||||
getOptionLabelStyle,
|
||||
getSelectFontStyles,
|
||||
getSelectStyle,
|
||||
} from '@components/components/Select/utils';
|
||||
import {
|
||||
formLabelTextStyles,
|
||||
inputPlaceholderTextStyles,
|
||||
@ -40,7 +46,7 @@ export const SelectLabelContainer = styled.div({
|
||||
gap: spacing.xsm,
|
||||
lineHeight: typography.lineHeights.none,
|
||||
alignItems: 'center',
|
||||
maxWidth: 'calc(100% - 54px)',
|
||||
maxWidth: 'calc(100% - 10px)',
|
||||
});
|
||||
|
||||
/**
|
||||
@ -89,6 +95,7 @@ export const Container = styled.div<ContainerProps>(({ size, width, $selectLabel
|
||||
});
|
||||
|
||||
export const DropdownContainer = styled.div<{ ignoreMaxHeight?: boolean }>(({ ignoreMaxHeight }) => ({
|
||||
...getDropdownStyle(),
|
||||
borderRadius: radius.md,
|
||||
background: colors.white,
|
||||
zIndex: zIndices.dropdown,
|
||||
@ -220,8 +227,8 @@ export const ArrowIcon = styled.span<{ isOpen: boolean }>(({ isOpen }) => ({
|
||||
|
||||
export const StyledCheckbox = styled(Checkbox)({
|
||||
'.ant-checkbox-checked:not(.ant-checkbox-disabled) .ant-checkbox-inner': {
|
||||
backgroundColor: colors.violet[500],
|
||||
borderColor: `${colors.violet[500]} !important`,
|
||||
backgroundColor: `${(props) => props.theme.styles['primary-color']}`,
|
||||
borderColor: `${(props) => props.theme.styles['primary-color']} !important`,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -109,11 +109,11 @@ export const getSelectStyle = (props: SelectStyleProps) => {
|
||||
|
||||
const baseStyle = {
|
||||
borderRadius: radius.md,
|
||||
border: `1px solid ${isDisabled ? colors.gray[1800] : colors.gray[100]}`,
|
||||
border: `1px solid ${colors.gray[100]}`,
|
||||
fontFamily: typography.fonts.body,
|
||||
backgroundColor: isDisabled ? colors.gray[1500] : colors.white,
|
||||
color: isDisabled ? colors.gray[300] : colors.gray[600],
|
||||
cursor: isDisabled || isReadOnly ? 'not-allowed' : 'pointer',
|
||||
backgroundColor: isDisabled ? colors.gray[1500] : 'initial',
|
||||
boxShadow: '0px 1px 2px 0px rgba(33, 23, 95, 0.07)',
|
||||
textWrap: 'nowrap',
|
||||
|
||||
@ -125,7 +125,7 @@ export const getSelectStyle = (props: SelectStyleProps) => {
|
||||
...(isOpen
|
||||
? {
|
||||
borderColor: colors.gray[1800],
|
||||
outline: `1px solid ${colors.violet[300]}`,
|
||||
outline: `1px solid ${colors.violet[200]}`,
|
||||
}
|
||||
: {}),
|
||||
|
||||
@ -150,3 +150,11 @@ export const getSelectStyle = (props: SelectStyleProps) => {
|
||||
...minHeightStyles,
|
||||
};
|
||||
};
|
||||
|
||||
export const getDropdownStyle = () => {
|
||||
const baseStyle = {
|
||||
fontFamily: typography.fonts.body,
|
||||
};
|
||||
|
||||
return { ...baseStyle };
|
||||
};
|
||||
|
@ -38,7 +38,7 @@ export const StyledIcon = styled(Icon)({
|
||||
export const TextAreaContainer = styled.div(
|
||||
({ isSuccess, warning, isDisabled, isInvalid }: TextAreaProps) => ({
|
||||
border: `${borders['1px']} ${getStatusColors(isSuccess, warning, isInvalid)}`,
|
||||
backgroundColor: isDisabled ? colors.gray[100] : colors.white,
|
||||
backgroundColor: isDisabled ? colors.gray[1500] : colors.white,
|
||||
}),
|
||||
{
|
||||
...defaultFlexStyles,
|
||||
@ -78,6 +78,10 @@ export const TextAreaField = styled.textarea<{ icon?: IconNames }>(({ icon }) =>
|
||||
'&::placeholder': {
|
||||
...inputPlaceholderTextStyles,
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
backgroundColor: colors.gray[1500],
|
||||
},
|
||||
}));
|
||||
|
||||
export const Label = styled.div({
|
||||
|
@ -11,6 +11,7 @@ export * from './components/Button';
|
||||
export * from './components/CalendarChart';
|
||||
export * from './components/Card';
|
||||
export * from './components/Checkbox';
|
||||
export * from './components/ColorPicker';
|
||||
export * from './components/DatePicker';
|
||||
export * from './components/Drawer';
|
||||
export * from './components/Dropdown';
|
||||
@ -29,6 +30,7 @@ export * from './components/Pills';
|
||||
export * from './components/Popover';
|
||||
export * from './components/SearchBar';
|
||||
export * from './components/Select';
|
||||
export * from './components/StructuredPopover';
|
||||
export * from './components/Switch';
|
||||
export * from './components/Tabs';
|
||||
export * from './components/Table';
|
||||
@ -38,5 +40,3 @@ export * from './components/Timeline';
|
||||
export * from './components/Tooltip';
|
||||
export * from './components/Utils';
|
||||
export * from './components/WhiskerChart';
|
||||
export * from './components/ColorPicker';
|
||||
export * from './components/StructuredPopover';
|
||||
|
@ -5,7 +5,6 @@ import { Theme } from '@conf/theme/types';
|
||||
|
||||
import { ColorOptions, DEFAULT_VALUE, FontSizeOptions, MiscColorOptions, RotationOptions } from './config';
|
||||
import { foundations } from './foundations';
|
||||
import { semanticTokens } from './semantic-tokens';
|
||||
|
||||
const { colors, typography, transform } = foundations;
|
||||
/*
|
||||
@ -67,5 +66,5 @@ export const getStatusColors = (isSuccess?: boolean, warning?: string, isInvalid
|
||||
if (warning) {
|
||||
return colors.yellow[600];
|
||||
}
|
||||
return semanticTokens.colors['border-color'];
|
||||
return colors.gray[100];
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import { Tooltip } from '@components';
|
||||
import { Select, Tag } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { EntitySearchInputResult } from '@app/entityV2/shared/EntitySearchInput/EntitySearchInputResult';
|
||||
import EntitySearchInputResultV2 from '@app/entityV2/shared/EntitySearchInput/EntitySearchInputResultV2';
|
||||
import { useEntityRegistry } from '@app/useEntityRegistry';
|
||||
|
||||
import { useGetEntitiesLazyQuery } from '@graphql/entity.generated';
|
||||
@ -173,7 +173,7 @@ export const EntitySearchInput = ({
|
||||
style={optionStyle}
|
||||
data-testid={`${result.entity.urn}-entity-search-input-result`}
|
||||
>
|
||||
<EntitySearchInputResult entity={result.entity} />
|
||||
<EntitySearchInputResultV2 entity={result.entity} />
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Icon, Text } from '@components';
|
||||
import { Text } from '@components';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import EntityRegistry from '@app/entityV2/EntityRegistry';
|
||||
import { getDisplayedEntityType } from '@app/entityV2/shared/containers/profile/header/utils';
|
||||
import ContextPath from '@app/previewV2/ContextPath';
|
||||
import { useEntityRegistry } from '@app/useEntityRegistry';
|
||||
@ -17,6 +18,7 @@ const Wrapper = styled.div`
|
||||
const TextWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// TODO: Add this as a prop if needed
|
||||
max-width: 600px;
|
||||
`;
|
||||
|
||||
@ -30,7 +32,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function EntitySearchInputResultV2({ entity }: Props) {
|
||||
const entityRegistry = useEntityRegistry();
|
||||
const entityRegistry = useEntityRegistry() as EntityRegistry;
|
||||
const properties = entityRegistry.getGenericEntityProperties(entity.type, entity);
|
||||
const platformIcon = properties?.platform?.properties?.logoUrl;
|
||||
|
||||
@ -38,10 +40,9 @@ export default function EntitySearchInputResultV2({ entity }: Props) {
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
{!platformIcon && <Icon size="4xl" source="phosphor" icon="Placeholder" />}
|
||||
{platformIcon && <IconContainer src={platformIcon} />}
|
||||
<TextWrapper>
|
||||
<Text size="lg">{entityRegistry.getDisplayName(entity.type, entity)}</Text>
|
||||
<Text size="md">{entityRegistry.getDisplayName(entity.type, entity)}</Text>
|
||||
<ContextPath
|
||||
entityType={entity.type}
|
||||
displayedEntityType={displayedEntityType}
|
||||
|
@ -0,0 +1,342 @@
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Dropdown from '@components/components/Dropdown/Dropdown';
|
||||
import { Input } from '@components/components/Input/Input';
|
||||
import {
|
||||
Container,
|
||||
DropdownContainer,
|
||||
LabelContainer,
|
||||
OptionContainer,
|
||||
OptionLabel,
|
||||
OptionList,
|
||||
SelectBase,
|
||||
SelectLabel,
|
||||
SelectLabelContainer,
|
||||
StyledCheckbox,
|
||||
StyledIcon,
|
||||
} from '@components/components/Select/components';
|
||||
import SelectActionButtons from '@components/components/Select/private/SelectActionButtons';
|
||||
import SelectLabelRenderer from '@components/components/Select/private/SelectLabelRenderer/SelectLabelRenderer';
|
||||
import useSelectDropdown from '@components/components/Select/private/hooks/useSelectDropdown';
|
||||
import { SelectOption, SelectSizeOptions } from '@components/components/Select/types';
|
||||
|
||||
import EntitySearchInputResultV2 from '@app/entityV2/shared/EntitySearchInput/EntitySearchInputResultV2';
|
||||
import { useEntityRegistry } from '@app/useEntityRegistry';
|
||||
|
||||
import { useGetEntitiesLazyQuery } from '@graphql/entity.generated';
|
||||
import { useGetSearchResultsForMultipleLazyQuery } from '@graphql/search.generated';
|
||||
import { Entity, EntityType } from '@types';
|
||||
|
||||
const EmptyState = styled.div`
|
||||
padding: 16px 12px;
|
||||
text-align: center;
|
||||
color: #8c8c8c;
|
||||
font-style: italic;
|
||||
`;
|
||||
|
||||
const LoadingState = styled.div`
|
||||
padding: 16px 12px;
|
||||
text-align: center;
|
||||
color: #8c8c8c;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
`;
|
||||
|
||||
const SearchInputContainer = styled.div`
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
`;
|
||||
|
||||
const EntityOptionContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export interface EntitySearchSelectProps {
|
||||
selectedUrns?: string[];
|
||||
entityTypes: EntityType[];
|
||||
placeholder?: string;
|
||||
size?: SelectSizeOptions;
|
||||
isMultiSelect?: boolean;
|
||||
isDisabled?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
label?: string;
|
||||
width?: number | 'full' | 'fit-content';
|
||||
onUpdate?: (selectedUrns: string[]) => void;
|
||||
showClear?: boolean;
|
||||
isRequired?: boolean;
|
||||
icon?: any;
|
||||
}
|
||||
|
||||
interface EntityOption extends SelectOption {
|
||||
entity: Entity;
|
||||
}
|
||||
|
||||
const addToCache = (cache: Map<string, Entity>, entity: Entity) => {
|
||||
cache.set(entity.urn, entity);
|
||||
return cache;
|
||||
};
|
||||
|
||||
const buildCache = (entities: Entity[]) => {
|
||||
const cache = new Map();
|
||||
entities.forEach((entity) => cache.set(entity.urn, entity));
|
||||
return cache;
|
||||
};
|
||||
|
||||
const isResolutionRequired = (urns: string[], cache: Map<string, Entity>) => {
|
||||
const uncachedUrns = urns.filter((urn) => !cache.has(urn));
|
||||
return uncachedUrns.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A standardized entity search and selection component that allows users to search
|
||||
* for DataHub entities and select one or multiple entities. Built on top of the
|
||||
* Select component library infrastructure for consistency.
|
||||
*/
|
||||
export const EntitySearchSelect: React.FC<EntitySearchSelectProps> = ({
|
||||
selectedUrns = [],
|
||||
entityTypes,
|
||||
placeholder = 'Search for entities...',
|
||||
size = 'md',
|
||||
isMultiSelect = false,
|
||||
isDisabled = false,
|
||||
isReadOnly = false,
|
||||
label,
|
||||
width = 255,
|
||||
onUpdate,
|
||||
showClear = true,
|
||||
isRequired = false,
|
||||
icon,
|
||||
}) => {
|
||||
const entityRegistry = useEntityRegistry();
|
||||
const [entityCache, setEntityCache] = useState<Map<string, Entity>>(new Map());
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const selectRef = useRef<HTMLDivElement>(null);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {
|
||||
isOpen,
|
||||
isVisible,
|
||||
close: closeDropdown,
|
||||
toggle: toggleDropdown,
|
||||
} = useSelectDropdown(false, selectRef, dropdownRef);
|
||||
|
||||
/**
|
||||
* Bootstrap by resolving all URNs that are not in the cache yet.
|
||||
*/
|
||||
const [getEntities, { data: resolvedEntitiesData, loading: entitiesLoading }] = useGetEntitiesLazyQuery();
|
||||
useEffect(() => {
|
||||
if (isResolutionRequired(selectedUrns, entityCache)) {
|
||||
getEntities({ variables: { urns: selectedUrns } });
|
||||
}
|
||||
}, [selectedUrns, entityCache, getEntities]);
|
||||
|
||||
/**
|
||||
* Build cache from resolved entities
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (resolvedEntitiesData && resolvedEntitiesData.entities?.length) {
|
||||
const entities: Entity[] = (resolvedEntitiesData?.entities as Entity[]) || [];
|
||||
setEntityCache(buildCache(entities));
|
||||
}
|
||||
}, [resolvedEntitiesData]);
|
||||
|
||||
/**
|
||||
* Search functionality
|
||||
*/
|
||||
const [searchResources, { data: resourcesSearchData, loading: searchLoading }] =
|
||||
useGetSearchResultsForMultipleLazyQuery();
|
||||
|
||||
const entityOptions: EntityOption[] = useMemo(() => {
|
||||
const results = resourcesSearchData?.searchAcrossEntities?.searchResults || [];
|
||||
return results.map((result) => ({
|
||||
label: entityRegistry.getDisplayName(result.entity.type, result.entity),
|
||||
value: result.entity.urn,
|
||||
entity: result.entity as Entity,
|
||||
}));
|
||||
}, [resourcesSearchData, entityRegistry]);
|
||||
|
||||
const handleSelectClick = useCallback(() => {
|
||||
if (!isDisabled && !isReadOnly) {
|
||||
toggleDropdown();
|
||||
}
|
||||
}, [toggleDropdown, isDisabled, isReadOnly]);
|
||||
|
||||
const handleOptionClick = useCallback(
|
||||
(option: EntityOption) => {
|
||||
// Add entity to cache
|
||||
setEntityCache(addToCache(entityCache, option.entity));
|
||||
|
||||
let newUrns: string[];
|
||||
if (isMultiSelect) {
|
||||
newUrns = selectedUrns.includes(option.value)
|
||||
? selectedUrns.filter((urn) => urn !== option.value)
|
||||
: [...selectedUrns, option.value];
|
||||
} else {
|
||||
newUrns = [option.value];
|
||||
closeDropdown();
|
||||
}
|
||||
|
||||
onUpdate?.(newUrns);
|
||||
},
|
||||
[selectedUrns, isMultiSelect, onUpdate, closeDropdown, entityCache],
|
||||
);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
(value: string) => {
|
||||
setSearchQuery(value);
|
||||
searchResources({
|
||||
variables: {
|
||||
input: {
|
||||
types: entityTypes,
|
||||
query: value || '*',
|
||||
start: 0,
|
||||
count: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
[entityTypes, searchResources],
|
||||
);
|
||||
|
||||
const handleClearSelection = useCallback(() => {
|
||||
onUpdate?.([]);
|
||||
}, [onUpdate]);
|
||||
|
||||
const removeOption = useCallback(
|
||||
(option: SelectOption) => {
|
||||
const newUrns = selectedUrns.filter((urn) => urn !== option.value);
|
||||
onUpdate?.(newUrns);
|
||||
},
|
||||
[selectedUrns, onUpdate],
|
||||
);
|
||||
|
||||
/**
|
||||
* Issue a default search on component mount
|
||||
*/
|
||||
useEffect(() => {
|
||||
searchResources({
|
||||
variables: {
|
||||
input: {
|
||||
types: entityTypes,
|
||||
query: '*',
|
||||
start: 0,
|
||||
count: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
}, [entityTypes, searchResources]);
|
||||
|
||||
// Create options for selected values from cache
|
||||
const selectedOptions: SelectOption[] = useMemo(() => {
|
||||
return selectedUrns.map((urn) => {
|
||||
const entity = entityCache.get(urn);
|
||||
return {
|
||||
label: entity ? entityRegistry.getDisplayName(entity.type, entity) : urn,
|
||||
value: urn,
|
||||
};
|
||||
});
|
||||
}, [selectedUrns, entityCache, entityRegistry]);
|
||||
|
||||
const isLoading = entitiesLoading || searchLoading;
|
||||
|
||||
return (
|
||||
<Container ref={selectRef} size={size} width={width}>
|
||||
{label && <SelectLabel onClick={handleSelectClick}>{label}</SelectLabel>}
|
||||
{isVisible && (
|
||||
<Dropdown
|
||||
open={isOpen}
|
||||
disabled={isDisabled}
|
||||
placement="bottomRight"
|
||||
dropdownRender={() => (
|
||||
<DropdownContainer ref={dropdownRef}>
|
||||
<SearchInputContainer>
|
||||
<Input
|
||||
label=""
|
||||
value={searchQuery}
|
||||
setValue={(value) => {
|
||||
const newValue = typeof value === 'function' ? value(searchQuery) : value;
|
||||
handleSearchChange(newValue);
|
||||
}}
|
||||
placeholder="Search..."
|
||||
icon={{ icon: 'Search' }}
|
||||
data-testid="entity-search-select-input"
|
||||
/>
|
||||
</SearchInputContainer>
|
||||
<OptionList>
|
||||
{isLoading && (
|
||||
<LoadingState>
|
||||
<LoadingOutlined />
|
||||
</LoadingState>
|
||||
)}
|
||||
{!isLoading && entityOptions.length === 0 && <EmptyState>No entities found</EmptyState>}
|
||||
{!isLoading &&
|
||||
entityOptions.map((option) => (
|
||||
<OptionLabel
|
||||
key={option.value}
|
||||
onClick={() => !isMultiSelect && handleOptionClick(option)}
|
||||
isSelected={selectedUrns.includes(option.value)}
|
||||
isMultiSelect={isMultiSelect}
|
||||
data-testid={`entity-search-option-${option.entity.urn.split(':').pop()}`}
|
||||
>
|
||||
{isMultiSelect ? (
|
||||
<LabelContainer>
|
||||
<EntityOptionContainer>
|
||||
<EntitySearchInputResultV2 entity={option.entity} />
|
||||
</EntityOptionContainer>
|
||||
<StyledCheckbox
|
||||
onClick={() => handleOptionClick(option)}
|
||||
checked={selectedUrns.includes(option.value)}
|
||||
/>
|
||||
</LabelContainer>
|
||||
) : (
|
||||
<OptionContainer>
|
||||
<EntitySearchInputResultV2 entity={option.entity} />
|
||||
</OptionContainer>
|
||||
)}
|
||||
</OptionLabel>
|
||||
))}
|
||||
</OptionList>
|
||||
</DropdownContainer>
|
||||
)}
|
||||
>
|
||||
<SelectBase
|
||||
isDisabled={isDisabled}
|
||||
isReadOnly={isReadOnly}
|
||||
isRequired={isRequired}
|
||||
isOpen={isOpen}
|
||||
onClick={handleSelectClick}
|
||||
fontSize={size}
|
||||
width={width}
|
||||
data-testid="entity-search-select-select"
|
||||
>
|
||||
<SelectLabelContainer>
|
||||
{icon && <StyledIcon icon={icon} size="lg" />}
|
||||
<SelectLabelRenderer
|
||||
selectedValues={selectedUrns}
|
||||
options={selectedOptions}
|
||||
placeholder={placeholder}
|
||||
isMultiSelect={isMultiSelect}
|
||||
removeOption={removeOption}
|
||||
disabledValues={[]}
|
||||
showDescriptions={false}
|
||||
/>
|
||||
</SelectLabelContainer>
|
||||
<SelectActionButtons
|
||||
hasSelectedValues={selectedUrns.length > 0}
|
||||
isOpen={isOpen}
|
||||
isDisabled={!!isDisabled}
|
||||
isReadOnly={!!isReadOnly}
|
||||
handleClearSelection={handleClearSelection}
|
||||
fontSize={size}
|
||||
showClear={!!showClear}
|
||||
/>
|
||||
</SelectBase>
|
||||
</Dropdown>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user