Skip to content

Commit

Permalink
dropdown as reusable component
Browse files Browse the repository at this point in the history
  • Loading branch information
prakriti-solankey authored Dec 18, 2024
1 parent 3161cfc commit 2dfb42a
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 93 deletions.
27 changes: 19 additions & 8 deletions frontend/src/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
chatModeLables,
largeFileSize,
llms,
prodllms,
RETRY_OPIONS,
tooltips,
} from '../utils/Constants';
Expand Down Expand Up @@ -880,15 +881,25 @@ const Content: React.FC<ContentProps> = ({
>
<div>
<DropdownComponent
onSelect={handleDropdownChange}
options={llms ?? ['']}
placeholder='Select LLM Model'
defaultValue={model}
view='ContentView'
onChange={(selectedOption) => handleDropdownChange(selectedOption as OptionType)}
options={(llms ?? ['']).map((value) => ({
label: String(value),
value: String(value),
isDisabled: process.env.VITE_ENV === 'PROD' && !prodllms.includes(value),
}))}
placeholder="Select LLM Model"
defaultValue={{ label: String(model), value: String(model) }
}
view="ContentView"
isDisabled={false}
label='LLM Models'
helpText='LLM Model used for Extraction & Chat'
size='medium'
label="LLM Models"
helpText="LLM Model used for Extraction & Chat"
size="medium"
customTooltip={(option: OptionType) =>
process.env.VITE_ENV === 'PROD' && !prodllms.includes(option.value)
? <div>{'This model is only available in the development environment.'}</div>
: null
}
/>
</div>
<Flex flexDirection='row' gap='4' className='self-end mb-2.5' flexWrap='wrap'>
Expand Down
134 changes: 69 additions & 65 deletions frontend/src/components/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,79 +1,83 @@
import { Tooltip, useMediaQuery, Select } from '@neo4j-ndl/react';
import { OptionType, ReusableDropdownProps } from '../types';
import { memo, useMemo } from 'react';
import { capitalize, capitalizeWithUnderscore } from '../utils/Utils';
import { prodllms } from '../utils/Constants';
const DropdownComponent: React.FC<ReusableDropdownProps> = ({
import { useMediaQuery, Select, Tooltip } from '@neo4j-ndl/react'; // Added Tooltip import
import { memo } from 'react';
import { capitalizeWithUnderscore } from '../utils/Utils';
import { DropdownComponentProps, OptionType } from '../types';

const DropdownComponent: React.FC<DropdownComponentProps> = ({
options,
placeholder,
defaultValue,
onSelect,
onChange,
children,
view,
isDisabled,
value,
label,
helpText,
size
size,
mapOptions,
customTooltip,
}) => {
const isProdEnv = process.env.VITE_ENV === 'PROD';
const isLargeDesktop = useMediaQuery(`(min-width:1440px )`);
const handleChange = (selectedOption: OptionType | null | void) => {
onSelect(selectedOption);
if (view === 'ContentView') {
const existingModel = localStorage.getItem('selectedModel');
if (existingModel != selectedOption?.value) {
localStorage.setItem('selectedModel', selectedOption?.value ?? '');
}
}
};
const allOptions = useMemo(() => options, [options]);
const isLargeDesktop = useMediaQuery('(min-width:1440px)');
const dropdownOptions = options.map((option) => {
const mappedOption = mapOptions ? mapOptions(option) : option;
const labelText =
typeof mappedOption.label === 'string'
? capitalizeWithUnderscore(mappedOption.label)
: mappedOption.label;
const tooltipContent = customTooltip?.(mappedOption);
return {
label: tooltipContent ? (
<Tooltip type="simple" placement={isLargeDesktop ? 'left' : 'right'}>
<Tooltip.Trigger>
<span>{labelText}</span>
</Tooltip.Trigger>
<Tooltip.Content>{tooltipContent}</Tooltip.Content>
</Tooltip>
) : (
<span>{labelText}</span>
),
value: mappedOption.value,
isDisabled: mappedOption.isDisabled || false,
};
});
return (
<>
<div className={view === 'ContentView' ? 'w-[150px]' : ''}>
<Select
type='select'
label={label}
helpText={<div className='!w-max'>{helpText}</div>}
selectProps={{
onChange: handleChange,
// @ts-ignore
options: allOptions?.map((option) => {
const label = typeof option === 'string' ? capitalizeWithUnderscore(option) : capitalize(option.label);
const value = typeof option === 'string' ? option : option.value;
const isModelSupported = !isProdEnv || prodllms?.includes(value);
return {
label: !isModelSupported && view === 'ContentView' ? (
<Tooltip type='simple' placement={isLargeDesktop ? 'left' : 'right'}>
<Tooltip.Trigger>
<span>{label}</span>
</Tooltip.Trigger>
<Tooltip.Content>Available In Development Version</Tooltip.Content>
</Tooltip>
) : (
<span>{label}</span>
),
value,
isDisabled: view === 'ContentView' ? !isModelSupported : isDisabled
};
}),
placeholder: placeholder || 'Select an option',
defaultValue: defaultValue
? { label: capitalizeWithUnderscore(defaultValue), value: defaultValue }
: undefined,
menuPlacement: 'auto',
isDisabled: isDisabled,
value: value,
}}
size={size}
isFluid
htmlAttributes={{
'aria-label': 'A selection dropdown',
}}
/>
{children}
</div>
</>
<div className={view === 'ContentView' ? 'w-[150px]' : ''}>
<Select
type='select'
label={label}
helpText={<div className='!w-max'>{helpText}</div>}
selectProps={{
onChange: (selectedOption) => {
if (selectedOption && typeof selectedOption === 'object' && 'value' in selectedOption) {
onChange(selectedOption as OptionType);
} else {
onChange(null);
}
},
options: dropdownOptions,
placeholder: placeholder || 'Select an option',
defaultValue: defaultValue
? {
label:
typeof defaultValue.label === 'string'
? capitalizeWithUnderscore(defaultValue.label)
: String(defaultValue.label),
value: defaultValue.value,
}
: undefined,
menuPlacement: 'auto',
isDisabled,
value,
}}
size={size}
isFluid
htmlAttributes={{
'aria-label': 'A selection dropdown',
}}
/>
{children}
</div>
);
};
export default memo(DropdownComponent);
38 changes: 22 additions & 16 deletions frontend/src/components/Graph/GraphViewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
const [disableRefresh, setDisableRefresh] = useState<boolean>(false);
const [selected, setSelected] = useState<{ type: EntityType; id: string } | undefined>(undefined);
const [mode, setMode] = useState<boolean>(false);
const [sliderValue, setSliderValue] = useState<string>(GRAPH_CHUNK_LIMIT);
const [dropdownValue, setDropdownValue] = useState<string>(GRAPH_CHUNK_LIMIT);

const graphQuery: string =
graphType.includes('DocumentChunk') && graphType.includes('Entities')
Expand Down Expand Up @@ -122,20 +122,19 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
userCredentials as UserCredentials,
graphQuery,
selectedRows?.map((f) => f.name),
sliderValue
dropdownValue
)
: await graphQueryAPI(userCredentials as UserCredentials, graphQuery, [inspectedName ?? ''], sliderValue);
: await graphQueryAPI(userCredentials as UserCredentials, graphQuery, [inspectedName ?? ''], dropdownValue);
return nodeRelationshipData;
} catch (error: any) {
console.log(error);
}
}, [viewPoint, selectedRows, graphQuery, inspectedName, userCredentials, sliderValue]);
}, [viewPoint, selectedRows, graphQuery, inspectedName, userCredentials, dropdownValue]);

// Api call to get the nodes and relations
const graphApi = async (mode?: string) => {
try {
const result = await fetchData();
// Check for valid result data
if (result?.data?.status === 'Success' && result.data.data.nodes.length > 0) {
const { nodes: neoNodes, relationships: neoRels } = result.data.data;
// Create a set of valid node IDs
Expand All @@ -154,7 +153,6 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
setNewScheme(schemeVal);
setLoading(false);
}
// Update state
setAllNodes(finalNodes);
setAllRelationships(finalRels);
setScheme(schemeVal);
Expand All @@ -166,7 +164,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
handleError(error);
}
};
// Helper function to handle empty result cases

const handleEmptyResult = (result: any) => {
setLoading(false);
setStatus('danger');
Expand All @@ -175,7 +173,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
: result?.data?.message || 'An error occurred';
setStatusMessage(message);
};
// Helper function to handle errors

const handleError = (error: any) => {
setLoading(false);
setStatus('danger');
Expand All @@ -187,7 +185,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
setLoading(true);
setGraphType([]);
if (viewPoint !== graphLabels.chatInfoView) {
graphApi();
graphApi('normalMode');
} else {
const { finalNodes, finalRels, schemeVal } = processGraphData(nodeValues ?? [], relationshipValues ?? []);
setAllNodes(finalNodes);
Expand All @@ -199,7 +197,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
setLoading(false);
}
}
}, [open, sliderValue]);
}, [open,dropdownValue]);

useEffect(() => {
if (debouncedQuery) {
Expand Down Expand Up @@ -338,7 +336,7 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
setAllRelationships([]);
setSearchQuery('');
setSelected(undefined);
setSliderValue(GRAPH_CHUNK_LIMIT);
setDropdownValue(GRAPH_CHUNK_LIMIT);
};

const mouseEventCallbacks = {
Expand All @@ -356,10 +354,13 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
onDrag: true,
};

// The set the chunk limit for graph viz
const handleDropdownChange = (selectedOption: OptionType | null | void) => {
setStatus('unknown');
if (selectedOption?.value) {
setSliderValue(selectedOption?.value);
setDropdownValue(selectedOption?.value);
// setLoading(true);
// graphApi('chunkMode');
}
};

Expand Down Expand Up @@ -391,10 +392,15 @@ const GraphViewModal: React.FunctionComponent<GraphViewModalProps> = ({
/>
)}
<DropdownComponent
onSelect={handleDropdownChange}
options={graph_chunk_limit ?? ['']}
placeholder='Select Chunk Limit'
defaultValue={GRAPH_CHUNK_LIMIT}
onChange={(selectedOption) => handleDropdownChange(selectedOption as OptionType)}
options={graph_chunk_limit.map((value) => ({
label: String(value),
value: String(value),
}))}
placeholder="Select Chunk Limit"
defaultValue={
{ label: String(GRAPH_CHUNK_LIMIT), value: String(GRAPH_CHUNK_LIMIT) }
}
view='GraphView'
isDisabled={loading}
label='Chunk Limit'
Expand Down
25 changes: 21 additions & 4 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ export interface CustomFile extends CustomFileBase {
id: string;
}

export interface OptionType {
readonly value: string;
readonly label: string;
}
export type OptionType = {
label: string | JSX.Element;
value: string;
isDisabled?: boolean;
};

export type UserCredentials = {
uri: string;
Expand Down Expand Up @@ -918,3 +919,19 @@ export interface GraphViewHandlerProps {
export interface ChatProps {
chatMessages: Messages[];
}

export type DropdownComponentProps = {
options: OptionType[];
placeholder: string;
defaultValue: OptionType;
value?: OptionType;
onChange: (selectedOption: OptionType | null) => void;
label: string;
helpText: string | JSX.Element;
size: 'small' | 'medium' | 'large';
isDisabled: boolean;
children?: React.ReactNode;
mapOptions?: (option: OptionType) => OptionType;
customTooltip?: (option: OptionType) => JSX.Element | null;
view: string;
};

0 comments on commit 2dfb42a

Please sign in to comment.