Skip to content

Commit 589d5d2

Browse files
committed
feat(utils): add case-insensitive option to filterVariables
Add caseInsensitive option to filterVariables() for flexible name matching, enabling case-agnostic search in variable filtering scenarios. Problem: - Users searching for 'color' wouldn't find 'Primary Color' - No way to perform case-insensitive variable name searches - Common UX pattern in search interfaces not supported Solution: - Added caseInsensitive boolean option to FilterVariablesCriteria - When true, both variable name and search term are lowercased - Defaults to false for backward compatibility Usage: import { filterVariables } from '@figma-vars/hooks'; // Case-insensitive search filterVariables(vars, { name: 'color', caseInsensitive: true }); // Matches: 'Primary Color', 'COLOR_BG', 'MyColor' Changes: - src/utils/filterVariables.ts: Added caseInsensitive option + interface - src/utils/index.ts: Export FilterVariablesCriteria type - tests/utils/filterVariables.test.ts: Added 6 tests for case-insensitive Test coverage: 100% Refs: Codex Audit Item #13
1 parent c8bf21c commit 589d5d2

3 files changed

Lines changed: 90 additions & 4 deletions

File tree

src/utils/filterVariables.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
11
import type { FigmaVariable, ResolvedType } from 'types'
22

3+
/**
4+
* Criteria for filtering Figma variables.
5+
*
6+
* @public
7+
*/
8+
export interface FilterVariablesCriteria {
9+
/**
10+
* Filter by resolved variable type (e.g., 'COLOR', 'FLOAT', 'STRING', 'BOOLEAN').
11+
*/
12+
resolvedType?: ResolvedType
13+
/**
14+
* Substring to match against variable names.
15+
*/
16+
name?: string
17+
/**
18+
* When true, name matching is case-insensitive.
19+
* @defaultValue false
20+
*/
21+
caseInsensitive?: boolean
22+
}
23+
324
/**
425
* Utility function to filter Figma variables by type and/or substring name match.
526
*
627
* @remarks
7-
* Returns a new array of variables matching the provided criteria. Use for building type pickers, variable search, dashboard views, or bulk edit tools. Filtering is case-sensitive for `name`. Pass no criteria to return all variables unfiltered.
28+
* Returns a new array of variables matching the provided criteria. Use for building type pickers, variable search, dashboard views, or bulk edit tools. By default, filtering is case-sensitive for `name`. Set `caseInsensitive: true` for case-insensitive matching. Pass no criteria to return all variables unfiltered.
829
*
930
* @param variables - The array of FigmaVariable objects to filter.
1031
* @param criteria - Object specifying filter fields. Provide a `resolvedType` (e.g., 'COLOR', 'FLOAT') and/or a `name` substring to match variable names.
@@ -22,19 +43,30 @@ import type { FigmaVariable, ResolvedType } from 'types'
2243
*
2344
* // Example 3: Filter variables that are COLOR and include 'brand' in name
2445
* const filtered = filterVariables(allVars, { resolvedType: 'COLOR', name: 'brand' });
46+
*
47+
* // Example 4: Case-insensitive name search
48+
* const matches = filterVariables(allVars, { name: 'BRAND', caseInsensitive: true });
2549
* ```
2650
*
2751
* @public
2852
*/
2953
export function filterVariables(
3054
variables: FigmaVariable[],
31-
criteria: { resolvedType?: ResolvedType; name?: string }
55+
criteria: FilterVariablesCriteria
3256
): FigmaVariable[] {
3357
return variables.filter(v => {
3458
let match = true
35-
if (criteria.resolvedType)
59+
if (criteria.resolvedType) {
3660
match = match && v.resolvedType === criteria.resolvedType
37-
if (criteria.name) match = match && v.name.includes(criteria.name)
61+
}
62+
if (criteria.name) {
63+
if (criteria.caseInsensitive) {
64+
match =
65+
match && v.name.toLowerCase().includes(criteria.name.toLowerCase())
66+
} else {
67+
match = match && v.name.includes(criteria.name)
68+
}
69+
}
3870
return match
3971
})
4072
}

src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* @public
2020
*/
2121
export { filterVariables } from 'utils/filterVariables'
22+
export type { FilterVariablesCriteria } from 'utils/filterVariables'
2223
export {
2324
isFigmaApiError,
2425
getErrorStatus,

tests/utils/filterVariables.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,57 @@ describe('filterVariables', () => {
7474
const result = filterVariables(mockVariables, { name: 'NonExistent' })
7575
expect(result).toHaveLength(0)
7676
})
77+
78+
describe('caseInsensitive option', () => {
79+
it('should match case-insensitively when caseInsensitive is true', () => {
80+
const result = filterVariables(mockVariables, {
81+
name: 'color',
82+
caseInsensitive: true,
83+
})
84+
expect(result).toHaveLength(2)
85+
expect(result.map(v => v.name)).toEqual([
86+
'Primary Color',
87+
'Secondary Color',
88+
])
89+
})
90+
91+
it('should match uppercase search against lowercase names', () => {
92+
const result = filterVariables(mockVariables, {
93+
name: 'COLOR',
94+
caseInsensitive: true,
95+
})
96+
expect(result).toHaveLength(2)
97+
})
98+
99+
it('should match mixed case search', () => {
100+
const result = filterVariables(mockVariables, {
101+
name: 'CoLoR',
102+
caseInsensitive: true,
103+
})
104+
expect(result).toHaveLength(2)
105+
})
106+
107+
it('should be case-sensitive by default', () => {
108+
const result = filterVariables(mockVariables, { name: 'color' })
109+
expect(result).toHaveLength(0) // No match because 'Color' !== 'color'
110+
})
111+
112+
it('should be case-sensitive when caseInsensitive is false', () => {
113+
const result = filterVariables(mockVariables, {
114+
name: 'color',
115+
caseInsensitive: false,
116+
})
117+
expect(result).toHaveLength(0)
118+
})
119+
120+
it('should combine caseInsensitive with resolvedType filter', () => {
121+
const result = filterVariables(mockVariables, {
122+
resolvedType: 'COLOR',
123+
name: 'primary',
124+
caseInsensitive: true,
125+
})
126+
expect(result).toHaveLength(1)
127+
expect(result[0]!.name).toBe('Primary Color')
128+
})
129+
})
77130
})

0 commit comments

Comments
 (0)