diff --git a/code/core/src/csf/includeConditionalArg.test.ts b/code/core/src/csf/includeConditionalArg.test.ts index 66cb4979d2e7..343e5cd04f01 100644 --- a/code/core/src/csf/includeConditionalArg.test.ts +++ b/code/core/src/csf/includeConditionalArg.test.ts @@ -160,4 +160,56 @@ describe('includeConditionalArg', () => { }); }); }); + + describe('function', () => { + it('should return true when function returns true', () => { + expect( + includeConditionalArg( + { if: (args) => args.a === 1 }, + { a: 1 }, + {} + ) + ).toBe(true); + }); + + it('should return false when function returns false', () => { + expect( + includeConditionalArg( + { if: (args) => args.a === 1 }, + { a: 2 }, + {} + ) + ).toBe(false); + }); + + it('should support multiple conditions via function', () => { + expect( + includeConditionalArg( + { if: (args) => args.type === 'pessoaFisica' && args.pais === 'Brasil' }, + { type: 'pessoaFisica', pais: 'Brasil' }, + {} + ) + ).toBe(true); + }); + + it('should support multiple conditions via function returning false', () => { + expect( + includeConditionalArg( + { if: (args) => args.type === 'pessoaFisica' && args.pais === 'Brasil' }, + { type: 'pessoaFisica', pais: 'Alemanha' }, + {} + ) + ).toBe(false); + }); + + it('should pass globals to the function', () => { + expect( + includeConditionalArg( + { if: (_args, globals) => globals.theme === 'dark' }, + {}, + { theme: 'dark' } + ) + ).toBe(true); + }); + }); }); diff --git a/code/core/src/csf/includeConditionalArg.ts b/code/core/src/csf/includeConditionalArg.ts index 1641e9f50c6e..0dfeb0390738 100644 --- a/code/core/src/csf/includeConditionalArg.ts +++ b/code/core/src/csf/includeConditionalArg.ts @@ -1,7 +1,7 @@ /* @ts-expect-error (has no typings) */ import { isEqual } from '@ngard/tiny-isequal'; -import type { Args, Conditional, Globals, InputType } from './story.ts'; +import type { Args, ConditionalFunction, Conditional, Globals, InputType } from './story.ts'; const count = (vals: any[]) => vals.map((v) => typeof v !== 'undefined').filter(Boolean).length; @@ -25,20 +25,23 @@ export const testValue = (cond: Omit, value: any) }; /** - * Helper function to include/exclude an arg based on the value of other other args aka "conditional - * args" + * Helper function to include/exclude an arg based on the value of other args aka "conditional + * args". Supports both object-based conditions and function-based conditions. */ export const includeConditionalArg = (argType: InputType, args: Args, globals: Globals) => { if (!argType.if) { return true; } + // Support function-based conditions + if (typeof argType.if === 'function') { + return (argType.if as ConditionalFunction)(args, globals); + } + const { arg, global } = argType.if as any; if (count([arg, global]) !== 1) { throw new Error(`Invalid conditional value ${JSON.stringify({ arg, global })}`); } - const value = arg ? args[arg] : globals[global]; - return testValue(argType.if!, value); -}; +}; \ No newline at end of file diff --git a/code/core/src/csf/story.ts b/code/core/src/csf/story.ts index 70ef08cc67bd..0d61f7a6cccb 100644 --- a/code/core/src/csf/story.ts +++ b/code/core/src/csf/story.ts @@ -58,7 +58,8 @@ type ControlType = type ConditionalTest = { truthy?: boolean } | { exists: boolean } | { eq: any } | { neq: any }; type ConditionalValue = { arg: string } | { global: string }; -export type Conditional = ConditionalValue & ConditionalTest; +export type ConditionalFunction = (args: Args, globals: Globals) => boolean; +export type Conditional = (ConditionalValue & ConditionalTest) | ConditionalFunction; interface ControlBase { [key: string]: any;