Merge pull request #12550 from ToolJet/fix/dropdown-interactions

Fix: Changed Interaction and added option to remove the clear button and remove the search box
This commit is contained in:
Johnson Cherian 2025-04-23 09:34:36 +05:30 committed by GitHub
commit c068ba8208
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 341 additions and 154 deletions

View file

@ -75,6 +75,18 @@ export const dropdownV2Config = {
accordian: 'Options',
isFxNotRequired: true,
},
showClearBtn: {
type: 'toggle',
displayName: 'Show clear selection button',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
showSearchInput: {
type: 'toggle',
displayName: 'Show search in options',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
@ -314,6 +326,8 @@ export const dropdownV2Config = {
optionsLoadingState: { value: '{{false}}' },
sort: { value: 'asc' },
placeholder: { value: 'Select an option' },
showClearBtn: { value: '{{true}}' },
showSearchInput: { value: '{{true}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },

View file

@ -142,6 +142,18 @@ export const multiselectV2Config = {
accordian: 'Options',
isFxNotRequired: true,
},
showClearBtn: {
type: 'toggle',
displayName: 'Show clear selection button',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
showSearchInput: {
type: 'toggle',
displayName: 'Show search in options',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
@ -327,6 +339,8 @@ export const multiselectV2Config = {
optionsLoadingState: { value: '{{false}}' },
sort: { value: 'asc' },
placeholder: { value: 'Select the options' },
showClearBtn: { value: '{{true}}' },
showSearchInput: { value: '{{true}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },

View file

@ -1,87 +1,118 @@
import React from 'react';
import React, { useEffect, useRef } from 'react';
import { components } from 'react-select';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import Loader from '@/ToolJetUI/Loader/Loader';
import './dropdownV2.scss';
import { FormCheck } from 'react-bootstrap';
// eslint-disable-next-line import/no-unresolved
import { useVirtualizer } from '@tanstack/react-virtual';
import cx from 'classnames';
const { MenuList } = components;
// This Menulist also used in MultiselectV2
const CustomMenuList = ({ selectProps, ...props }) => {
const {
onInputChange,
onMenuInputFocus,
showAllOption,
isSelectAllSelected,
optionsLoadingState,
darkMode,
setSelected,
setIsSelectAllSelected,
fireEvent,
inputValue,
menuId,
} = selectProps;
const { onInputChange, onMenuInputFocus, optionsLoadingState, darkMode, inputValue, menuId, showSearchInput } =
selectProps;
const handleSelectAll = (e) => {
e.target.checked && fireEvent();
if (e.target.checked) {
setSelected(props.options);
} else {
setSelected([]);
const parentRef = useRef(null);
const virtualizer = useVirtualizer({
count: props?.children?.length || 0,
getScrollElement: () => parentRef.current,
estimateSize: () => 40,
overscan: 15,
});
useEffect(() => {
const searchInput = document.querySelector('.dropdown-multiselect-widget-search-box');
if (searchInput) {
searchInput.focus();
}
setIsSelectAllSelected(e.target.checked);
};
}, []);
return (
<div
id={`dropdown-multiselect-widget-custom-menu-list-${menuId}`}
className={cx('dropdown-multiselect-widget-custom-menu-list', { 'theme-dark dark-theme': darkMode })}
onClick={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
>
<div className="dropdown-multiselect-widget-search-box-wrapper">
<span>
<SolidIcon name="search01" width="14" />
</span>
<input
autoCorrect="off"
autoComplete="off"
spellCheck="false"
type="text"
value={inputValue}
onChange={(e) =>
onInputChange(e.currentTarget.value, {
action: 'input-change',
})
}
onMouseDown={(e) => {
e.stopPropagation();
e.target.focus();
}}
onTouchEnd={(e) => {
e.stopPropagation();
e.target.focus();
}}
onFocus={onMenuInputFocus}
placeholder="Search"
className="dropdown-multiselect-widget-search-box"
/>
</div>
{showAllOption && !optionsLoadingState && (
<label htmlFor="select-all-checkbox" className="multiselect-custom-menulist-select-all">
<FormCheck id="select-all-checkbox" checked={isSelectAllSelected} onChange={handleSelectAll} />
<span style={{ marginLeft: '4px' }}>Select all</span>
</label>
{showSearchInput && (
<div className="dropdown-multiselect-widget-search-box-wrapper">
<span>
<SolidIcon name="search01" width="14" />
</span>
<input
autoCorrect="off"
autoComplete="off"
spellCheck="false"
type="text"
value={inputValue}
onChange={(e) =>
onInputChange(e.currentTarget.value, {
action: 'input-change',
})
}
onMouseDown={(e) => {
e.stopPropagation();
e.target.focus();
}}
onTouchEnd={(e) => {
e.stopPropagation();
e.target.focus();
}}
onFocus={onMenuInputFocus}
placeholder="Search"
className="dropdown-multiselect-widget-search-box"
/>
</div>
)}
<MenuList {...props} selectProps={selectProps}>
{optionsLoadingState ? (
<div class="text-center py-4" style={{ minHeight: '188px' }}>
<Loader style={{ zIndex: 3, position: 'absolute' }} width="36" />
{!optionsLoadingState && (
<div
ref={parentRef}
className="dropdown-multiselect-widget-custom-menu-list-body"
style={{
maxHeight: selectProps.maxMenuHeight || 300,
}}
>
<div
style={{
height: `${virtualizer.getTotalSize() || 38}px`,
position: 'relative',
marginTop: '5px',
}}
>
{!virtualizer.getTotalSize() && props.children}
{virtualizer.getVirtualItems().map((virtualItem) => {
const option = props.options[virtualItem.index];
const child = props.children[virtualItem.index];
const isSelectAll = option?.value === 'multiselect-custom-menulist-select-all';
return (
<div
key={option.value}
style={{
position: isSelectAll ? 'sticky' : 'absolute',
width: '100%',
top: 0,
zIndex: isSelectAll && 10,
transform: `translateY(${virtualItem.start}px)`,
}}
data-index={virtualItem.index}
ref={virtualizer.measureElement}
>
<MenuList {...props} selectProps={selectProps}>
<div>{child}</div>
</MenuList>
</div>
);
})}
</div>
) : (
props.children
)}
</MenuList>
</div>
)}
{optionsLoadingState && (
<div className="text-center py-4" style={{ minHeight: '188px' }}>
<Loader style={{ zIndex: 3, position: 'absolute' }} width="36" />
</div>
)}
</div>
);
};

View file

@ -6,7 +6,17 @@ import { highlightText } from './utils';
const CustomOption = (props) => {
return (
<components.Option {...props}>
<components.Option
{...props}
innerProps={{
...props.innerProps,
onTouchEnd: (e) => {
e.preventDefault();
e.stopPropagation();
props.selectOption(props.data);
},
}}
>
<div className="cursor-pointer">
{props.isSelected && (
<span style={{ maxHeight: '20px', marginRight: '8px', marginLeft: '-28px' }}>

View file

@ -15,7 +15,6 @@ import Label from '@/_ui/Label';
import cx from 'classnames';
import { getInputBackgroundColor, getInputBorderColor, getInputFocusedColor, sortArray } from './utils';
import { isMobileDevice } from '@/_helpers/appUtils';
import useStore from '@/AppBuilder/_stores/store';
const { DropdownIndicator, ClearIndicator } = components;
const INDICATOR_CONTAINER_WIDTH = 60;
@ -69,6 +68,8 @@ export const DropdownV2 = ({
disabledState,
optionsLoadingState,
sort,
showClearBtn,
showSearchInput,
} = properties;
const {
selectedTextColor,
@ -104,8 +105,6 @@ export const DropdownV2 = ({
const [isDropdownDisabled, setIsDropdownDisabled] = useState(disabledState);
const [searchInputValue, setSearchInputValue] = useState('');
const [userInteracted, setUserInteracted] = useState(false);
const currentMode = useStore((state) => state.currentMode);
const isEditor = currentMode === 'edit';
const _height = padding === 'default' ? `${height}px` : `${height + 4}px`;
const labelRef = useRef();
@ -173,12 +172,43 @@ export const DropdownV2 = ({
setExposedVariable('isValid', validationStatus?.isValid);
};
const handleClickInEditor = (e) => {
if (e.target.className.includes('clear-indicator') || isMenuOpen) return;
e.stopPropagation();
selectRef.current?.onControlMouseDown(e);
const handleClickInsideSelect = () => {
if (isDropdownDisabled || isDropdownLoading) return;
if (isMenuOpen) {
setIsMenuOpen(false);
fireEvent('onBlur');
setSearchInputValue('');
} else {
setIsMenuOpen(true);
fireEvent('onFocus');
if (!showSearchInput) {
selectRef.current.focus();
}
}
};
const handleClickOutsideSelect = (event) => {
const menu = document.querySelector(`._tooljet-${componentName}`);
if (
isMenuOpen &&
menu &&
dropdownRef.current &&
!dropdownRef.current.contains(event.target) &&
!menu.contains(event.target)
) {
setIsMenuOpen(false);
fireEvent('onBlur');
setSearchInputValue('');
}
};
useEffect(() => {
document.addEventListener('mousedown', handleClickOutsideSelect);
return () => {
document.removeEventListener('mousedown', handleClickOutsideSelect);
};
}, [isMenuOpen, componentName]);
useEffect(() => {
setInputValue(findDefaultItem(advanced ? schema : options));
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -386,7 +416,7 @@ export const DropdownV2 = ({
}),
menuList: (provided) => ({
...provided,
padding: '8px',
padding: '0 8px',
borderRadius: '8px',
// this is needed otherwise :active state doesn't look nice, gap is required
display: 'flex',
@ -446,7 +476,8 @@ export const DropdownV2 = ({
<div
className="w-100 px-0 h-100 dropdownV2-widget"
ref={ref}
onMouseDownCapture={isEditor && handleClickInEditor}
onClick={handleClickInsideSelect}
onTouchEnd={handleClickInsideSelect}
>
<Select
ref={selectRef}
@ -456,17 +487,21 @@ export const DropdownV2 = ({
onChange={(selectedOption, actionProps) => {
if (actionProps.action === 'clear') {
setInputValue(null);
fireEvent('onSelect');
}
if (actionProps.action === 'select-option') {
setInputValue(selectedOption.value);
fireEvent('onSelect');
if (currentValue === selectedOption.value) {
setInputValue(null);
} else setInputValue(selectedOption.value);
}
fireEvent('onSelect');
setSearchInputValue('');
setIsMenuOpen(false);
setUserInteracted(true);
}}
options={selectOptions}
styles={customStyles}
isLoading={isDropdownLoading}
showSearchInput={showSearchInput}
onInputChange={onSearchTextChange}
inputValue={searchInputValue}
placeholder={placeholder}
@ -477,7 +512,7 @@ export const DropdownV2 = ({
Option: CustomOption,
LoadingIndicator: () => <Loader style={{ right: '11px', zIndex: 3, position: 'absolute' }} width="16" />,
DropdownIndicator: isDropdownLoading ? () => null : CustomDropdownIndicator,
ClearIndicator: CustomClearIndicator,
ClearIndicator: showClearBtn ? CustomClearIndicator : () => null,
}}
isClearable
tabSelectsValue={false}
@ -488,21 +523,15 @@ export const DropdownV2 = ({
darkMode={darkMode}
optionsLoadingState={optionsLoadingState && advanced}
menuPlacement="auto"
onMenuOpen={() => {
setIsMenuOpen(true);
fireEvent('onFocus');
}}
onMenuClose={() => {
setIsMenuOpen(false);
fireEvent('onBlur');
}}
onKeyDown={(e) => {
if (e.key === 'Enter' && !isMenuOpen) {
if (e.key === 'Enter' && !isMenuOpen && !isDropdownLoading) {
setIsMenuOpen(true);
fireEvent('onFocus');
e.preventDefault();
}
if (e.key === 'Escape' && isMenuOpen) {
setIsMenuOpen(false);
fireEvent('onBlur');
e.preventDefault();
}
}}

View file

@ -0,0 +1,7 @@
.dropdown-multiselect-widget-custom-menu-list-body {
overflow-y: auto;
position: relative;
padding: 0 0 5px;
background-color: var(--surfaces-surface-01) !important;
border-radius: 0 0 8px 8px;
}

View file

@ -7,11 +7,23 @@ import { highlightText } from '../DropdownV2/utils';
const CustomOption = (props) => {
return (
<Option {...props}>
<Option
{...props}
innerProps={{
...props.innerProps,
onTouchEnd: (e) => {
e.preventDefault();
e.stopPropagation();
props.selectOption(props.data);
},
}}
>
<div className="d-flex multiselct-widget-option">
<FormCheck checked={props.isSelected} disabled={props?.isDisabled} />
<span style={{ marginLeft: '5px' }}>
{highlightText(props.label?.toString(), props.selectProps.inputValue)}
{props.label?.includes('Select all')
? 'Select all'
: highlightText(props.label?.toString(), props.selectProps.inputValue)}
</span>
</div>
</Option>

View file

@ -42,7 +42,7 @@ const CustomValueContainer = ({ children, ...props }) => {
{/* Rendering children except Placeholder component to preserve the default behavior of react-select like focus
handling */}
{React.Children.map(children, (child) => {
if (child.type !== Placeholder) {
if (child?.type !== Placeholder) {
return child;
}
})}

View file

@ -12,7 +12,6 @@ import Label from '@/_ui/Label';
const tinycolor = require('tinycolor2');
import { CustomDropdownIndicator, CustomClearIndicator } from '../DropdownV2/DropdownV2';
import { getInputBackgroundColor, getInputBorderColor, getInputFocusedColor, sortArray } from '../DropdownV2/utils';
import useStore from '@/AppBuilder/_stores/store';
export const MultiselectV2 = ({
id,
@ -38,6 +37,8 @@ export const MultiselectV2 = ({
loadingState: multiSelectLoadingState,
optionsLoadingState,
sort,
showClearBtn,
showSearchInput,
} = properties;
const {
selectedTextColor,
@ -73,12 +74,9 @@ export const MultiselectV2 = ({
const [visibility, setVisibility] = useState(properties.visibility);
const [isMultiSelectLoading, setIsMultiSelectLoading] = useState(multiSelectLoadingState);
const [isMultiSelectDisabled, setIsMultiSelectDisabled] = useState(disabledState);
const [isSelectAllSelected, setIsSelectAllSelected] = useState(false);
const [searchInputValue, setSearchInputValue] = useState('');
const _height = padding === 'default' ? `${height}px` : `${height + 4}px`;
const [userInteracted, setUserInteracted] = useState(false);
const currentMode = useStore((state) => state.currentMode);
const isEditor = currentMode === 'edit';
const [isMultiselectOpen, setIsMultiselectOpen] = useState(false);
useEffect(() => {
@ -93,18 +91,29 @@ export const MultiselectV2 = ({
const _options = advanced ? schema : options;
let _selectOptions = Array.isArray(_options)
? _options
.filter((data) => data?.visible ?? true)
.map((data) => ({
...data,
label: data?.label,
value: data?.value,
isDisabled: data?.disable ?? false,
}))
.filter((data) => data?.visible ?? true)
.map((data) => ({
...data,
label: data?.label,
value: data?.value,
isDisabled: data?.disable ?? false,
}))
: [];
return sortArray(_selectOptions, sort);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [advanced, JSON.stringify(schema), JSON.stringify(options), sort]);
const modifiedSelectOptions = useMemo(() => {
// Adding select all option dynamically to the options
if (showAllOption && !optionsLoadingState) {
return [
// Appended search input value so that it is always visible
{ label: `Select all ${searchInputValue}`, value: 'multiselect-custom-menulist-select-all' },
...selectOptions,
];
} else return selectOptions;
}, [showAllOption, JSON.stringify(selectOptions), optionsLoadingState, searchInputValue]);
function findDefaultItem(value, isAdvanced, isDefault) {
if (isAdvanced) {
const foundItem = Array.isArray(schema) ? schema.filter((item) => item?.visible && item?.default) : [];
@ -129,8 +138,28 @@ export const MultiselectV2 = ({
}
return false;
}
const onChangeHandler = (items, action) => {
setInputValue(items);
const SELECT_ALL = 'multiselect-custom-menulist-select-all';
if (action.option?.value === SELECT_ALL) {
// Case 1 - If select all is selected
if (action.action === 'select-option') {
setInputValue(modifiedSelectOptions);
} else {
setInputValue([]);
}
} else if (items?.some((item) => item.value === SELECT_ALL)) {
// Case 2 - If select all is not selected but selected options include select all
setInputValue(items.filter((item) => item.value !== SELECT_ALL));
} else if (selectOptions?.length === items?.length) {
// Case 3 - If all options are selected except select all
setInputValue(modifiedSelectOptions);
} else {
// Case 4 - Normal selection
setInputValue(items);
}
fireEvent('onSelect');
setUserInteracted(true);
};
@ -270,26 +299,34 @@ export const MultiselectV2 = ({
fireEvent('onSearchTextChanged');
}
};
const handleClickOutside = (event) => {
const handleClickOutsideSelect = (event) => {
let menu = document.getElementById(`dropdown-multiselect-widget-custom-menu-list-${id}`);
if (
isMultiselectOpen &&
multiselectRef.current &&
!multiselectRef.current.contains(event.target) &&
menu &&
!menu.contains(event.target)
) {
if (isMultiselectOpen) {
fireEvent('onBlur');
setIsMultiselectOpen(false);
setSearchInputValue('');
}
setIsMultiselectOpen(false);
fireEvent('onBlur');
setSearchInputValue('');
}
};
const handleClickInEditor = (e) => {
if (e.target.className.includes('clear-indicator') || isMultiselectOpen) return;
e.stopPropagation();
selectRef.current?.onControlMouseDown(e);
const handleClickInsideSelect = () => {
if (isMultiSelectDisabled || isMultiSelectLoading) return;
if (isMultiselectOpen) {
setIsMultiselectOpen(false);
fireEvent('onBlur');
setSearchInputValue('');
} else {
setIsMultiselectOpen(true);
fireEvent('onFocus');
if (!showSearchInput) {
selectRef.current.focus();
}
}
};
const setInputValue = (values) => {
@ -304,20 +341,11 @@ export const MultiselectV2 = ({
};
useEffect(() => {
document.addEventListener('mousedown', handleClickOutside, { capture: true });
document.addEventListener('mousedown', handleClickOutsideSelect, { capture: true });
return () => {
document.removeEventListener('mousedown', handleClickOutside, { capture: true });
document.removeEventListener('mousedown', handleClickOutsideSelect, { capture: true });
};
}, [isMultiselectOpen]);
// Handle Select all logic
useEffect(() => {
if (selectOptions?.length === selected?.length) {
setIsSelectAllSelected(true);
} else {
setIsSelectAllSelected(false);
}
}, [selectOptions, selected]);
}, [isMultiselectOpen, componentName]);
const customStyles = {
container: (base) => ({
@ -365,8 +393,8 @@ export const MultiselectV2 = ({
selectedTextColor !== '#1B1F24'
? selectedTextColor
: isMultiSelectLoading || isMultiSelectDisabled
? 'var(--text-disabled)'
: 'var(--text-primary)',
? 'var(--text-disabled)'
: 'var(--text-primary)',
}),
input: (provided, _state) => ({
@ -401,10 +429,10 @@ export const MultiselectV2 = ({
color: _state.isDisabled
? 'var(_--text-disbled)'
: selectedTextColor !== '#1B1F24'
? selectedTextColor
: isMultiSelectDisabled || isMultiSelectLoading
? 'var(--text-disabled)'
: 'var(--text-primary)',
? selectedTextColor
: isMultiSelectDisabled || isMultiSelectLoading
? 'var(--text-disabled)'
: 'var(--text-primary)',
borderRadius: _state.isFocused && '8px',
padding: '8px 6px 8px 12px',
'&:hover': {
@ -415,7 +443,7 @@ export const MultiselectV2 = ({
}),
menuList: (provided) => ({
...provided,
padding: '4px',
padding: '0 4px',
// this is needed otherwise :active state doesn't look nice, gap is required
display: 'flex',
flexDirection: 'column',
@ -426,6 +454,7 @@ export const MultiselectV2 = ({
menu: (provided) => ({
...provided,
marginTop: '5px',
borderRadius: '8px',
}),
};
const _width = (labelWidth / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value
@ -436,7 +465,7 @@ export const MultiselectV2 = ({
data-cy={`label-${String(componentName).toLowerCase()} `}
className={cx('multiselect-widget', 'd-flex', {
[alignment === 'top' &&
((labelWidth != 0 && label?.length != 0) || (auto && labelWidth == 0 && label && label?.length != 0))
((labelWidth != 0 && label?.length != 0) || (auto && labelWidth == 0 && label && label?.length != 0))
? 'flex-column'
: 'align-items-center']: true,
'flex-row-reverse': direction === 'right' && alignment === 'side',
@ -468,17 +497,18 @@ export const MultiselectV2 = ({
_width={_width}
top={'1px'}
/>
<div className="w-100 px-0 h-100" onMouseDownCapture={isEditor && handleClickInEditor}>
<div className="w-100 px-0 h-100" onClick={handleClickInsideSelect} onTouchEnd={handleClickInsideSelect}>
<Select
ref={selectRef}
menuId={id}
isDisabled={isMultiSelectDisabled}
value={selected}
onChange={onChangeHandler}
options={selectOptions}
options={modifiedSelectOptions}
styles={customStyles}
// Only show loading when dynamic options are enabled
isLoading={isMultiSelectLoading}
showSearchInput={showSearchInput}
onInputChange={onSearchTextChange}
inputValue={searchInputValue}
menuIsOpen={isMultiselectOpen}
@ -488,7 +518,7 @@ export const MultiselectV2 = ({
ValueContainer: CustomValueContainer,
Option: CustomOption,
LoadingIndicator: () => <Loader style={{ right: '11px', zIndex: 3, position: 'absolute' }} width="16" />,
ClearIndicator: CustomClearIndicator,
ClearIndicator: showClearBtn ? CustomClearIndicator : () => null,
DropdownIndicator: isMultiSelectLoading ? () => null : CustomDropdownIndicator,
}}
isClearable
@ -498,21 +528,15 @@ export const MultiselectV2 = ({
tabSelectsValue={false}
controlShouldRenderValue={false}
isSearchable={false}
onMenuOpen={() => {
setIsMultiselectOpen(true);
fireEvent('onFocus');
}}
onMenuClose={() => {
setIsMultiselectOpen(false);
fireEvent('onBlur');
}}
onKeyDown={(e) => {
if (e.key === 'Enter' && !isMultiselectOpen) {
if (e.key === 'Enter' && !isMultiselectOpen && !isMultiSelectLoading) {
setIsMultiselectOpen(true);
fireEvent('onFocus');
e.preventDefault();
}
if (e.key === 'Escape' && isMultiselectOpen) {
setIsMultiselectOpen(false);
fireEvent('onBlur');
e.preventDefault();
}
}}
@ -520,19 +544,9 @@ export const MultiselectV2 = ({
icon={icon}
doShowIcon={iconVisibility}
containerRef={valueContainerRef}
showAllOption={showAllOption}
isSelectAllSelected={isSelectAllSelected}
setIsSelectAllSelected={(value) => {
setIsSelectAllSelected(value);
if (!value) {
fireEvent('onSelect');
}
}}
setSelected={setInputValue}
iconColor={iconColor}
optionsLoadingState={optionsLoadingState && advanced}
darkMode={darkMode}
fireEvent={() => fireEvent('onSelect')}
menuPlacement="auto"
menuPortalTarget={document.body}
/>

View file

@ -75,6 +75,18 @@ export const dropdownV2Config = {
accordian: 'Options',
isFxNotRequired: true,
},
showClearBtn: {
type: 'toggle',
displayName: 'Show clear selection button',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
showSearchInput: {
type: 'toggle',
displayName: 'Show search in options',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
@ -314,6 +326,8 @@ export const dropdownV2Config = {
optionsLoadingState: { value: '{{false}}' },
sort: { value: 'asc' },
placeholder: { value: 'Select an option' },
showClearBtn: { value: '{{true}}' },
showSearchInput: { value: '{{true}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },

View file

@ -142,6 +142,18 @@ export const multiselectV2Config = {
accordian: 'Options',
isFxNotRequired: true,
},
showClearBtn: {
type: 'toggle',
displayName: 'Show clear selection button',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
showSearchInput: {
type: 'toggle',
displayName: 'Show search in options',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
@ -327,6 +339,8 @@ export const multiselectV2Config = {
optionsLoadingState: { value: '{{false}}' },
sort: { value: 'asc' },
placeholder: { value: 'Select the options' },
showClearBtn: { value: '{{true}}' },
showSearchInput: { value: '{{true}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },

View file

@ -75,6 +75,18 @@ export const dropdownV2Config = {
accordian: 'Options',
isFxNotRequired: true,
},
showClearBtn: {
type: 'toggle',
displayName: 'Show clear selection button',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
showSearchInput: {
type: 'toggle',
displayName: 'Show search in options',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
@ -314,6 +326,8 @@ export const dropdownV2Config = {
optionsLoadingState: { value: '{{false}}' },
sort: { value: 'asc' },
placeholder: { value: 'Select an option' },
showClearBtn: { value: '{{true}}' },
showSearchInput: { value: '{{true}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },

View file

@ -142,6 +142,18 @@ export const multiselectV2Config = {
accordian: 'Options',
isFxNotRequired: true,
},
showClearBtn: {
type: 'toggle',
displayName: 'Show clear selection button',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
showSearchInput: {
type: 'toggle',
displayName: 'Show search in options',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
@ -327,6 +339,8 @@ export const multiselectV2Config = {
optionsLoadingState: { value: '{{false}}' },
sort: { value: 'asc' },
placeholder: { value: 'Select the options' },
showClearBtn: { value: '{{true}}' },
showSearchInput: { value: '{{true}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },