From 338517d4192867c65532a423dafe86bd57448b01 Mon Sep 17 00:00:00 2001 From: Harish Dhanraj Sugandhi Date: Thu, 11 Sep 2025 14:38:43 +0530 Subject: [PATCH 1/8] Add enhanced Select component with JSX options and search functionality - Introduced `SiteSelectExample` demonstrating complex JSX components in options. - Added `searchValue` prop to `Select.Option` for reliable search. - Updated `SelectOptions` to utilize `searchValue` for filtering. - Enhanced utility function to support complex JSX structures. --- .../select/examples/jsx-options-example.tsx | 94 ++++++++++ src/components/select/select-atom.stories.tsx | 167 ++++++++++++++++++ src/components/select/select-types.ts | 2 + src/components/select/select.tsx | 60 ++++--- src/components/select/utils.ts | 37 ++-- 5 files changed, 327 insertions(+), 33 deletions(-) create mode 100644 src/components/select/examples/jsx-options-example.tsx diff --git a/src/components/select/examples/jsx-options-example.tsx b/src/components/select/examples/jsx-options-example.tsx new file mode 100644 index 00000000..d9440777 --- /dev/null +++ b/src/components/select/examples/jsx-options-example.tsx @@ -0,0 +1,94 @@ +/** + * Force UI Select Component - JSX Options Example + * + * This example demonstrates the enhanced Select component that now supports + * complex JSX components in options while maintaining search functionality. + */ + +import React from 'react'; +import Select from '../select'; +import Tooltip from '../../tooltip'; +import { InfoIcon, CheckCircleIcon, XCircleIcon } from 'lucide-react'; + +interface SiteOption { + url: string; + isVerified: boolean; + status: 'verified' | 'unverified' | 'pending'; +} + +const sites: SiteOption[] = [ + { url: 'example.com', isVerified: true, status: 'verified' }, + { url: 'mysite.org', isVerified: false, status: 'unverified' }, + { url: 'newsite.net', isVerified: false, status: 'pending' }, +]; + +const SiteSelectExample: React.FC = () => { + const [selectedSite, setSelectedSite] = React.useState(''); + + return ( +
+ + + {/* Backward Compatibility Example */} +
+

Backward Compatibility

+ +
+
+ ); +}; + +export default SiteSelectExample; diff --git a/src/components/select/select-atom.stories.tsx b/src/components/select/select-atom.stories.tsx index 7d4ecdb5..9eb3df7f 100644 --- a/src/components/select/select-atom.stories.tsx +++ b/src/components/select/select-atom.stories.tsx @@ -3,6 +3,7 @@ import Select from './select'; import { expect, userEvent, within, screen } from '@storybook/test'; import { SelectOptionValue } from './select-types'; import { useState } from 'react'; +import { CheckCircle, XCircle, AlertCircle } from 'lucide-react'; const options = [ { id: '1', name: 'Red' }, @@ -433,3 +434,169 @@ GroupedSelect.play = async ( { canvasElement } ) => { expect( selectButton ).toHaveTextContent( 'Red' ); }; + +// Enhanced example with JSX components and searchValue +const siteOptions = [ + { + url: 'example.com', + status: 'verified', + label: 'Example.com', + description: 'Primary website' + }, + { + url: 'test-site.org', + status: 'pending', + label: 'Test Site', + description: 'Development environment' + }, + { + url: 'my-blog.net', + status: 'error', + label: 'My Blog', + description: 'Personal blog' + }, +]; + +export const SelectWithJSXAndSearchValue: Story = ( { + size, + combobox, + disabled, +} ) => { + const [ selectedSite, setSelectedSite ] = useState( null ); + + const getStatusIcon = ( status: string ) => { + switch ( status ) { + case 'verified': + return ; + case 'pending': + return ; + case 'error': + return ; + default: + return null; + } + }; + + const getStatusText = ( status: string ) => { + switch ( status ) { + case 'verified': + return 'Verified'; + case 'pending': + return 'Pending verification'; + case 'error': + return 'Verification failed'; + default: + return ''; + } + }; + + return ( +
+ +
+ ); +}; + +SelectWithJSXAndSearchValue.args = { + size: 'md', + combobox: true, + disabled: false, +}; + +SelectWithJSXAndSearchValue.parameters = { + docs: { + description: { + story: `This example demonstrates the new \`searchValue\` prop that enables reliable search functionality with complex JSX components. + +**Key Features:** +- **Complex JSX Structure**: Each option contains multiple elements with icons, labels, and descriptions +- **Reliable Search**: Uses \`searchValue\` prop to define what text should be searchable +- **Visual Indicators**: Status icons and text show different states +- **Multi-field Search**: Search works across label, URL, and description + +**The \`searchValue\` prop contains**: \`"Label URL Description"\` making all fields searchable while maintaining the rich visual design. + +**Try searching for:** +- \`"example"\` - matches the first site +- \`"development"\` - matches the test site description +- \`"blog"\` - matches both label and URL of the blog site +- \`"pending"\` - won't match (status text is not in searchValue)`, + }, + }, +}; + +SelectWithJSXAndSearchValue.play = async ( { canvasElement } ) => { + const canvas = within( canvasElement ); + + // Click on the select button + const selectButton = await canvas.findByRole( 'combobox' ); + await userEvent.click( selectButton ); + + // Check if all options are visible initially + const listBox = await screen.findByRole( 'listbox' ); + expect( listBox ).toHaveTextContent( 'Example.com' ); + expect( listBox ).toHaveTextContent( 'Test Site' ); + expect( listBox ).toHaveTextContent( 'My Blog' ); + + // Test search functionality + const searchInput = await screen.findByPlaceholderText( 'Search sites...' ); + + // Search for "development" - should match test site description + await userEvent.clear( searchInput ); + await userEvent.type( searchInput, 'development' ); + expect( listBox ).toHaveTextContent( 'Test Site' ); + expect( listBox ).not.toHaveTextContent( 'Example.com' ); + + // Search for "blog" - should match blog site + await userEvent.clear( searchInput ); + await userEvent.type( searchInput, 'blog' ); + expect( listBox ).toHaveTextContent( 'My Blog' ); + expect( listBox ).not.toHaveTextContent( 'Test Site' ); + + // Select the blog option + const blogOption = await screen.findByRole( 'option' ); + await userEvent.click( blogOption ); + + // Check if the button text is updated + expect( selectButton ).toHaveTextContent( 'My Blog' ); +}; diff --git a/src/components/select/select-types.ts b/src/components/select/select-types.ts index 4624eec9..6a62621c 100644 --- a/src/components/select/select-types.ts +++ b/src/components/select/select-types.ts @@ -134,6 +134,8 @@ export interface SelectOptionProps { children?: ReactNode; /** Additional class name for the Select Option. */ className?: string; + /** Search value for complex JSX components. If provided, this will be used for search instead of extracting text from children. */ + searchValue?: string; /** Additional Props */ [key: string]: unknown; } diff --git a/src/components/select/select.tsx b/src/components/select/select.tsx index 5e5fc283..20a225c0 100644 --- a/src/components/select/select.tsx +++ b/src/components/select/select.tsx @@ -522,21 +522,31 @@ export function SelectOptions( { return cloneElement( groupChild, childProps ); } - // Otherwise, apply normal filtering to individual options - if ( searchKeyword && ! searchFn ) { - const textContent = getTextContent( - ( groupChild.props as { children?: React.ReactNode } ).children - )?.toLowerCase(); - const searchTerm = searchKeyword.toLowerCase(); - - const textMatch = textContent?.includes( searchTerm ); - - if ( ! textMatch ) { - return null; - } - } + // Handle regular options when searchFn is not provided + if ( groupLabelMatches ) { + const childProps = { + ...( groupChild.props as SelectOptionProps ), + index: childIndex++, + }; + + return cloneElement( groupChild, childProps ); + } - const childProps = { + // Otherwise, apply normal filtering to individual options + if ( searchKeyword && ! searchFn ) { + const searchTerm = searchKeyword.toLowerCase(); + + // Use searchValue prop if available, otherwise extract text from children + const searchableText = ( groupChild.props as SelectOptionProps ).searchValue + ? ( groupChild.props as SelectOptionProps ).searchValue?.toLowerCase() + : getTextContent( ( groupChild.props as { children?: React.ReactNode } ).children )?.toLowerCase(); + + const textMatch = searchableText?.includes( searchTerm ); + + if ( ! textMatch ) { + return null; + } + } const childProps = { ...( groupChild.props as SelectOptionProps ), index: childIndex++, }; @@ -565,12 +575,14 @@ export function SelectOptions( { // Handle regular options when searchFn is not provided if ( searchKeyword && ! searchFn ) { - const textContent = getTextContent( - child.props?.children - )?.toLowerCase(); const searchTerm = searchKeyword.toLowerCase(); + + // Use searchValue prop if available, otherwise extract text from children + const searchableText = ( child.props as SelectOptionProps ).searchValue + ? ( child.props as SelectOptionProps ).searchValue?.toLowerCase() + : getTextContent( child.props?.children )?.toLowerCase(); - const textMatch = textContent?.includes( searchTerm ); + const textMatch = searchableText?.includes( searchTerm ); if ( ! textMatch ) { return null; @@ -610,20 +622,22 @@ export function SelectOptions( { return; } - const textContent = getTextContent( - child.props?.children - )?.toLowerCase(); + // Use searchValue prop if available, otherwise extract text from children + const searchableText = ( child.props as SelectOptionProps ).searchValue + ? ( child.props as SelectOptionProps ).searchValue?.toLowerCase() + : getTextContent( child.props?.children )?.toLowerCase(); + // Handle regular options when searchFn is not provided if ( searchKeyword && ! searchFn ) { const searchTerm = searchKeyword.toLowerCase(); - const textMatch = textContent?.includes( searchTerm ); + const textMatch = searchableText?.includes( searchTerm ); if ( ! textMatch ) { return; } } - listContentRef.current.push( textContent ); + listContentRef.current.push( searchableText ); } ); }, [ searchKeyword, searchFn ] ); diff --git a/src/components/select/utils.ts b/src/components/select/utils.ts index 23a0896e..89c71827 100644 --- a/src/components/select/utils.ts +++ b/src/components/select/utils.ts @@ -1,21 +1,38 @@ -import { type ReactNode } from 'react'; +import { + type ReactNode, + type ReactElement, +} from 'react'; /** - * Get text content of a node - * @param {ReactNode} node - React node - * @return {string} text content of the node + * Simple text extraction utility for Force UI Select component + * + * Extracts searchable text from React nodes. For complex JSX components, + * use the `searchValue` prop on Select.Option for reliable search. + * + * @param {ReactNode} node - React node to extract text from + * @return {string} Extracted text content for search functionality */ export const getTextContent = ( node: ReactNode ): string => { - if ( typeof node === 'string' ) { - return node; + // Handle primitives + if ( node === null || typeof node === 'boolean' ) { + return ''; } - if ( typeof node === 'object' && 'textContent' in node! ) { - return node.textContent?.toString().toLowerCase() || ''; + if ( typeof node === 'string' || typeof node === 'number' ) { + return node.toString(); } - if ( typeof node === 'object' && 'children' in node! ) { - return getTextContent( node.children ); + // Handle arrays + if ( Array.isArray( node ) ) { + return node.map( getTextContent ).join( ' ' ); + } + + // Handle React elements - extract from children + if ( typeof node === 'object' && node !== null && 'props' in node ) { + const element = node as ReactElement; + if ( element.props?.children ) { + return getTextContent( element.props.children ); + } } return ''; From 918084da047302f9973ed226adc1b953f03b09bb Mon Sep 17 00:00:00 2001 From: Harish Dhanraj Sugandhi Date: Thu, 11 Sep 2025 14:39:57 +0530 Subject: [PATCH 2/8] delete --- .../select/examples/jsx-options-example.tsx | 94 ------------------- 1 file changed, 94 deletions(-) delete mode 100644 src/components/select/examples/jsx-options-example.tsx diff --git a/src/components/select/examples/jsx-options-example.tsx b/src/components/select/examples/jsx-options-example.tsx deleted file mode 100644 index d9440777..00000000 --- a/src/components/select/examples/jsx-options-example.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Force UI Select Component - JSX Options Example - * - * This example demonstrates the enhanced Select component that now supports - * complex JSX components in options while maintaining search functionality. - */ - -import React from 'react'; -import Select from '../select'; -import Tooltip from '../../tooltip'; -import { InfoIcon, CheckCircleIcon, XCircleIcon } from 'lucide-react'; - -interface SiteOption { - url: string; - isVerified: boolean; - status: 'verified' | 'unverified' | 'pending'; -} - -const sites: SiteOption[] = [ - { url: 'example.com', isVerified: true, status: 'verified' }, - { url: 'mysite.org', isVerified: false, status: 'unverified' }, - { url: 'newsite.net', isVerified: false, status: 'pending' }, -]; - -const SiteSelectExample: React.FC = () => { - const [selectedSite, setSelectedSite] = React.useState(''); - - return ( -
- - - {/* Backward Compatibility Example */} -
-

Backward Compatibility

- -
-
- ); -}; - -export default SiteSelectExample; From cf053cc9b2b927957caab41976cfb42acc1acb10 Mon Sep 17 00:00:00 2001 From: Harish Dhanraj Sugandhi Date: Thu, 11 Sep 2025 14:45:23 +0530 Subject: [PATCH 3/8] Update changelog for version 1.7.6: Added `searchValue` prop to Select component for enhanced search functionality. --- changelog.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.txt b/changelog.txt index 40d8f925..66fa9587 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +Version 1.7.6 - 11th September, 2025 +- Improvements: Atom - Select - Added a `searchValue` prop to enhance search in Select component. + + Version 1.7.5 - 25th August, 2025 - New: Template - UAE Dashboard - New: Template - UAE Widgets From eb97afceefadcdc45827c4b99f0a04fa6cc088f3 Mon Sep 17 00:00:00 2001 From: Harish Dhanraj Sugandhi Date: Thu, 11 Sep 2025 23:02:35 +0530 Subject: [PATCH 4/8] Revert search value pro --- src/components/select/select-atom.stories.tsx | 167 ------------------ src/components/select/select-types.ts | 4 +- src/components/select/select.tsx | 60 +++---- 3 files changed, 24 insertions(+), 207 deletions(-) diff --git a/src/components/select/select-atom.stories.tsx b/src/components/select/select-atom.stories.tsx index 9eb3df7f..7d4ecdb5 100644 --- a/src/components/select/select-atom.stories.tsx +++ b/src/components/select/select-atom.stories.tsx @@ -3,7 +3,6 @@ import Select from './select'; import { expect, userEvent, within, screen } from '@storybook/test'; import { SelectOptionValue } from './select-types'; import { useState } from 'react'; -import { CheckCircle, XCircle, AlertCircle } from 'lucide-react'; const options = [ { id: '1', name: 'Red' }, @@ -434,169 +433,3 @@ GroupedSelect.play = async ( { canvasElement } ) => { expect( selectButton ).toHaveTextContent( 'Red' ); }; - -// Enhanced example with JSX components and searchValue -const siteOptions = [ - { - url: 'example.com', - status: 'verified', - label: 'Example.com', - description: 'Primary website' - }, - { - url: 'test-site.org', - status: 'pending', - label: 'Test Site', - description: 'Development environment' - }, - { - url: 'my-blog.net', - status: 'error', - label: 'My Blog', - description: 'Personal blog' - }, -]; - -export const SelectWithJSXAndSearchValue: Story = ( { - size, - combobox, - disabled, -} ) => { - const [ selectedSite, setSelectedSite ] = useState( null ); - - const getStatusIcon = ( status: string ) => { - switch ( status ) { - case 'verified': - return ; - case 'pending': - return ; - case 'error': - return ; - default: - return null; - } - }; - - const getStatusText = ( status: string ) => { - switch ( status ) { - case 'verified': - return 'Verified'; - case 'pending': - return 'Pending verification'; - case 'error': - return 'Verification failed'; - default: - return ''; - } - }; - - return ( -
- -
- ); -}; - -SelectWithJSXAndSearchValue.args = { - size: 'md', - combobox: true, - disabled: false, -}; - -SelectWithJSXAndSearchValue.parameters = { - docs: { - description: { - story: `This example demonstrates the new \`searchValue\` prop that enables reliable search functionality with complex JSX components. - -**Key Features:** -- **Complex JSX Structure**: Each option contains multiple elements with icons, labels, and descriptions -- **Reliable Search**: Uses \`searchValue\` prop to define what text should be searchable -- **Visual Indicators**: Status icons and text show different states -- **Multi-field Search**: Search works across label, URL, and description - -**The \`searchValue\` prop contains**: \`"Label URL Description"\` making all fields searchable while maintaining the rich visual design. - -**Try searching for:** -- \`"example"\` - matches the first site -- \`"development"\` - matches the test site description -- \`"blog"\` - matches both label and URL of the blog site -- \`"pending"\` - won't match (status text is not in searchValue)`, - }, - }, -}; - -SelectWithJSXAndSearchValue.play = async ( { canvasElement } ) => { - const canvas = within( canvasElement ); - - // Click on the select button - const selectButton = await canvas.findByRole( 'combobox' ); - await userEvent.click( selectButton ); - - // Check if all options are visible initially - const listBox = await screen.findByRole( 'listbox' ); - expect( listBox ).toHaveTextContent( 'Example.com' ); - expect( listBox ).toHaveTextContent( 'Test Site' ); - expect( listBox ).toHaveTextContent( 'My Blog' ); - - // Test search functionality - const searchInput = await screen.findByPlaceholderText( 'Search sites...' ); - - // Search for "development" - should match test site description - await userEvent.clear( searchInput ); - await userEvent.type( searchInput, 'development' ); - expect( listBox ).toHaveTextContent( 'Test Site' ); - expect( listBox ).not.toHaveTextContent( 'Example.com' ); - - // Search for "blog" - should match blog site - await userEvent.clear( searchInput ); - await userEvent.type( searchInput, 'blog' ); - expect( listBox ).toHaveTextContent( 'My Blog' ); - expect( listBox ).not.toHaveTextContent( 'Test Site' ); - - // Select the blog option - const blogOption = await screen.findByRole( 'option' ); - await userEvent.click( blogOption ); - - // Check if the button text is updated - expect( selectButton ).toHaveTextContent( 'My Blog' ); -}; diff --git a/src/components/select/select-types.ts b/src/components/select/select-types.ts index 6a62621c..623dae4c 100644 --- a/src/components/select/select-types.ts +++ b/src/components/select/select-types.ts @@ -134,8 +134,6 @@ export interface SelectOptionProps { children?: ReactNode; /** Additional class name for the Select Option. */ className?: string; - /** Search value for complex JSX components. If provided, this will be used for search instead of extracting text from children. */ - searchValue?: string; /** Additional Props */ [key: string]: unknown; } @@ -175,4 +173,4 @@ export type SelectContextValue = { searchPlaceholder?: string; searchFn?: ( keyword: string ) => Promise; debounceDelay?: number; -}; +}; \ No newline at end of file diff --git a/src/components/select/select.tsx b/src/components/select/select.tsx index 20a225c0..5e5fc283 100644 --- a/src/components/select/select.tsx +++ b/src/components/select/select.tsx @@ -522,31 +522,21 @@ export function SelectOptions( { return cloneElement( groupChild, childProps ); } - // Handle regular options when searchFn is not provided - if ( groupLabelMatches ) { - const childProps = { - ...( groupChild.props as SelectOptionProps ), - index: childIndex++, - }; - - return cloneElement( groupChild, childProps ); - } - - // Otherwise, apply normal filtering to individual options - if ( searchKeyword && ! searchFn ) { - const searchTerm = searchKeyword.toLowerCase(); - - // Use searchValue prop if available, otherwise extract text from children - const searchableText = ( groupChild.props as SelectOptionProps ).searchValue - ? ( groupChild.props as SelectOptionProps ).searchValue?.toLowerCase() - : getTextContent( ( groupChild.props as { children?: React.ReactNode } ).children )?.toLowerCase(); - - const textMatch = searchableText?.includes( searchTerm ); + // Otherwise, apply normal filtering to individual options + if ( searchKeyword && ! searchFn ) { + const textContent = getTextContent( + ( groupChild.props as { children?: React.ReactNode } ).children + )?.toLowerCase(); + const searchTerm = searchKeyword.toLowerCase(); + + const textMatch = textContent?.includes( searchTerm ); + + if ( ! textMatch ) { + return null; + } + } - if ( ! textMatch ) { - return null; - } - } const childProps = { + const childProps = { ...( groupChild.props as SelectOptionProps ), index: childIndex++, }; @@ -575,14 +565,12 @@ export function SelectOptions( { // Handle regular options when searchFn is not provided if ( searchKeyword && ! searchFn ) { + const textContent = getTextContent( + child.props?.children + )?.toLowerCase(); const searchTerm = searchKeyword.toLowerCase(); - - // Use searchValue prop if available, otherwise extract text from children - const searchableText = ( child.props as SelectOptionProps ).searchValue - ? ( child.props as SelectOptionProps ).searchValue?.toLowerCase() - : getTextContent( child.props?.children )?.toLowerCase(); - const textMatch = searchableText?.includes( searchTerm ); + const textMatch = textContent?.includes( searchTerm ); if ( ! textMatch ) { return null; @@ -622,22 +610,20 @@ export function SelectOptions( { return; } - // Use searchValue prop if available, otherwise extract text from children - const searchableText = ( child.props as SelectOptionProps ).searchValue - ? ( child.props as SelectOptionProps ).searchValue?.toLowerCase() - : getTextContent( child.props?.children )?.toLowerCase(); - + const textContent = getTextContent( + child.props?.children + )?.toLowerCase(); // Handle regular options when searchFn is not provided if ( searchKeyword && ! searchFn ) { const searchTerm = searchKeyword.toLowerCase(); - const textMatch = searchableText?.includes( searchTerm ); + const textMatch = textContent?.includes( searchTerm ); if ( ! textMatch ) { return; } } - listContentRef.current.push( searchableText ); + listContentRef.current.push( textContent ); } ); }, [ searchKeyword, searchFn ] ); From 2e4fa034930b861a0ae11a2e2653d4e62dcae14e Mon Sep 17 00:00:00 2001 From: Harish Dhanraj Sugandhi Date: Thu, 11 Sep 2025 23:03:11 +0530 Subject: [PATCH 5/8] improve : search --- src/components/select/select-types.ts | 2 +- src/components/select/utils.ts | 46 ++++++++++++++++----------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/components/select/select-types.ts b/src/components/select/select-types.ts index 623dae4c..4624eec9 100644 --- a/src/components/select/select-types.ts +++ b/src/components/select/select-types.ts @@ -173,4 +173,4 @@ export type SelectContextValue = { searchPlaceholder?: string; searchFn?: ( keyword: string ) => Promise; debounceDelay?: number; -}; \ No newline at end of file +}; diff --git a/src/components/select/utils.ts b/src/components/select/utils.ts index 89c71827..369700ad 100644 --- a/src/components/select/utils.ts +++ b/src/components/select/utils.ts @@ -1,38 +1,48 @@ -import { - type ReactNode, - type ReactElement, -} from 'react'; +import { type ReactNode, isValidElement } from 'react'; /** - * Simple text extraction utility for Force UI Select component - * - * Extracts searchable text from React nodes. For complex JSX components, - * use the `searchValue` prop on Select.Option for reliable search. - * - * @param {ReactNode} node - React node to extract text from - * @return {string} Extracted text content for search functionality + * Get text content of a node + * @param {ReactNode} node - React node + * @return {string} text content of the node */ export const getTextContent = ( node: ReactNode ): string => { - // Handle primitives + // Handle null, undefined, boolean if ( node === null || typeof node === 'boolean' ) { return ''; } + // Handle string and number if ( typeof node === 'string' || typeof node === 'number' ) { return node.toString(); } - // Handle arrays + // Handle arrays of React nodes if ( Array.isArray( node ) ) { return node.map( getTextContent ).join( ' ' ); } - // Handle React elements - extract from children - if ( typeof node === 'object' && node !== null && 'props' in node ) { - const element = node as ReactElement; - if ( element.props?.children ) { - return getTextContent( element.props.children ); + // Handle React elements + if ( isValidElement( node ) ) { + // If it has children, recursively get text from children + if ( node.props && node.props.children ) { + return getTextContent( node.props.children ); } + return ''; + } + + // Handle objects with textContent property (DOM nodes) + if ( typeof node === 'object' && 'textContent' in node! ) { + return node.textContent?.toString() || ''; + } + + // Handle objects with children property + if ( typeof node === 'object' && node && 'children' in node ) { + return getTextContent( ( node as { children: ReactNode } ).children ); + } + + // Fallback for other object types + if ( typeof node === 'object' ) { + return ''; } return ''; From 0ac11cf364dbd307efe6d8628923c0f53ec8f34a Mon Sep 17 00:00:00 2001 From: Harish Dhanraj Sugandhi Date: Thu, 11 Sep 2025 23:15:22 +0530 Subject: [PATCH 6/8] refactor: improve text content handling in getTextContent function --- src/components/select/utils.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/components/select/utils.ts b/src/components/select/utils.ts index 369700ad..2b18d816 100644 --- a/src/components/select/utils.ts +++ b/src/components/select/utils.ts @@ -18,12 +18,12 @@ export const getTextContent = ( node: ReactNode ): string => { // Handle arrays of React nodes if ( Array.isArray( node ) ) { - return node.map( getTextContent ).join( ' ' ); + return node.map( getTextContent ).join( ' ' ).trim(); } - // Handle React elements + // Handle React elements (JSX components) if ( isValidElement( node ) ) { - // If it has children, recursively get text from children + // Recursively get text from children if ( node.props && node.props.children ) { return getTextContent( node.props.children ); } @@ -32,18 +32,13 @@ export const getTextContent = ( node: ReactNode ): string => { // Handle objects with textContent property (DOM nodes) if ( typeof node === 'object' && 'textContent' in node! ) { - return node.textContent?.toString() || ''; + return node.textContent?.toString().toLowerCase() || ''; } // Handle objects with children property - if ( typeof node === 'object' && node && 'children' in node ) { + if ( typeof node === 'object' && 'children' in node! ) { return getTextContent( ( node as { children: ReactNode } ).children ); } - // Fallback for other object types - if ( typeof node === 'object' ) { - return ''; - } - return ''; }; From 3e3b223b09df5bc84efe12ad76338bddf50a1241 Mon Sep 17 00:00:00 2001 From: Harish Dhanraj Sugandhi Date: Thu, 11 Sep 2025 23:22:25 +0530 Subject: [PATCH 7/8] chore: update changelog for version 1.7.6 to reflect improved search logic in Select component --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 66fa9587..2aab836e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,5 @@ Version 1.7.6 - 11th September, 2025 -- Improvements: Atom - Select - Added a `searchValue` prop to enhance search in Select component. +- Improvements: Atom - Select - Improved search logic. Version 1.7.5 - 25th August, 2025 From 5610edbb3a7628947b92fb2caf980dafef7f6be9 Mon Sep 17 00:00:00 2001 From: Harish Dhanraj Sugandhi Date: Fri, 12 Sep 2025 16:34:20 +0530 Subject: [PATCH 8/8] chore: update version to 1.7.6 in package files --- README.md | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- version.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 46fd8286..a2baafe5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Using Force UI as a dependency in package.json - ```json "dependencies": { - "@bsf/force-ui": "git+https://github.com/brainstormforce/force-ui#1.7.5" + "@bsf/force-ui": "git+https://github.com/brainstormforce/force-ui#1.7.6" } ``` @@ -28,7 +28,7 @@ npm install Or you can directly run the following command to install the package - ```bash -npm i -S @bsf/force-ui@git+https://github.com/brainstormforce/force-ui.git#1.7.5 +npm i -S @bsf/force-ui@git+https://github.com/brainstormforce/force-ui.git#1.7.6 ```
diff --git a/package-lock.json b/package-lock.json index 315d0745..95ce9181 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bsf/force-ui", - "version": "1.7.5", + "version": "1.7.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bsf/force-ui", - "version": "1.7.5", + "version": "1.7.6", "license": "ISC", "dependencies": { "@emotion/is-prop-valid": "^1.3.0", diff --git a/package.json b/package.json index 84c20896..77814fa1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bsf/force-ui", - "version": "1.7.5", + "version": "1.7.6", "description": "Library of components for the BSF project", "main": "./dist/force-ui.cjs.js", "module": "./dist/force-ui.es.js", diff --git a/version.json b/version.json index 24659c2e..5e07b1fd 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "force-ui": "1.7.5" + "force-ui": "1.7.6" }