Skip to content

Commit 49eeafa

Browse files
committed
feat: added support for use props to determinate values into styles
1 parent a423179 commit 49eeafa

File tree

2 files changed

+137
-11
lines changed

2 files changed

+137
-11
lines changed

packages/jacaranda/src/index.test.ts

+113
Original file line numberDiff line numberDiff line change
@@ -483,4 +483,117 @@ describe('styled', () => {
483483

484484
expect(view.props.style).toMatchObject({});
485485
});
486+
487+
it('should support function-based styles with props', async () => {
488+
const { styled } = defineTokens({});
489+
490+
interface CustomProps {
491+
isActive: boolean;
492+
}
493+
494+
const StyledView = styled(View)<CustomProps>((props) => ({
495+
backgroundColor: props.isActive ? 'blue' : 'gray',
496+
}));
497+
498+
// Render with isActive=true
499+
const renderedActive = render(
500+
React.createElement(StyledView, {
501+
testID: 'styled-view-active',
502+
isActive: true,
503+
}),
504+
);
505+
const activeView = await renderedActive.getByTestId('styled-view-active');
506+
expect(activeView.props.style).toMatchObject({
507+
backgroundColor: 'blue',
508+
});
509+
510+
// Render with isActive=false
511+
const renderedInactive = render(
512+
React.createElement(StyledView, {
513+
testID: 'styled-view-inactive',
514+
isActive: false,
515+
}),
516+
);
517+
const inactiveView = await renderedInactive.getByTestId('styled-view-inactive');
518+
expect(inactiveView.props.style).toMatchObject({
519+
backgroundColor: 'gray',
520+
});
521+
});
522+
523+
it('should support function-based styles with tokens', async () => {
524+
const { styled } = defineTokens({
525+
colors: {
526+
active: 'green',
527+
inactive: 'red',
528+
},
529+
});
530+
531+
interface CustomProps {
532+
isActive: boolean;
533+
}
534+
535+
const StyledView = styled(View)<CustomProps>((props) => ({
536+
backgroundColor: props.isActive ? '$colors.active' : '$colors.inactive',
537+
borderWidth: 1,
538+
}));
539+
540+
// Render with isActive=true
541+
const renderedActive = render(
542+
React.createElement(StyledView, {
543+
testID: 'styled-view-token-active',
544+
isActive: true,
545+
}),
546+
);
547+
const activeView = await renderedActive.getByTestId('styled-view-token-active');
548+
expect(activeView.props.style).toMatchObject({
549+
backgroundColor: 'green',
550+
borderWidth: 1,
551+
});
552+
553+
// Render with isActive=false
554+
const renderedInactive = render(
555+
React.createElement(StyledView, {
556+
testID: 'styled-view-token-inactive',
557+
isActive: false,
558+
}),
559+
);
560+
const inactiveView = await renderedInactive.getByTestId('styled-view-token-inactive');
561+
expect(inactiveView.props.style).toMatchObject({
562+
backgroundColor: 'red',
563+
borderWidth: 1,
564+
});
565+
});
566+
567+
it('should correctly merge prop-based styles with styles from style prop', async () => {
568+
const { styled } = defineTokens({});
569+
570+
interface CustomProps {
571+
variant: 'primary' | 'secondary';
572+
}
573+
574+
const StyledView = styled(View)<CustomProps>((props) => ({
575+
backgroundColor: props.variant === 'primary' ? 'blue' : 'purple',
576+
padding: 10,
577+
}));
578+
579+
const rendered = render(
580+
React.createElement(StyledView, {
581+
testID: 'styled-view-merged',
582+
variant: 'primary',
583+
style: { padding: 20, margin: 5 },
584+
}),
585+
);
586+
587+
const view = await rendered.getByTestId('styled-view-merged');
588+
expect(view.props.style).toMatchObject([
589+
{
590+
backgroundColor: 'blue',
591+
padding: 10,
592+
},
593+
{
594+
padding: 20,
595+
margin: 5,
596+
},
597+
]);
598+
});
486599
});

packages/jacaranda/src/index.ts

+24-11
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
// }
4141
// });
4242

43+
// Example of using a styled component with a custom prop
44+
// const button = styled(Button)<{ otherProp: string }>((props) => {
45+
// position: props.otherProp ? 'absolute' : 'relative';
46+
// });
47+
4348
import React, { type ComponentType, type ReactNode } from 'react';
4449
import { ImageStyle, TextStyle, ViewStyle, StyleProp, StyleSheet } from 'react-native';
4550

@@ -199,7 +204,7 @@ type ComponentProps<T> = T extends ComponentType<infer P> ? P : never;
199204
type StyledFunction = <C extends ComponentType<any>>(
200205
Component: C,
201206
) => <P extends object = {}>(
202-
styleObject: StyleObject,
207+
styleObject: StyleObject | ((props: P & Omit<ComponentProps<C>, 'style'>) => StyleObject),
203208
) => ComponentType<
204209
P & Omit<ComponentProps<C>, 'style'> & { style?: StyleProp<ViewStyle | TextStyle | ImageStyle> }
205210
>;
@@ -290,15 +295,9 @@ export function defineTokens<T extends TokenConfig>(tokenConfig: T): CreateToken
290295
* Styled function for creating styled components with token-aware styles
291296
*/
292297
const styled: StyledFunction = <C extends ComponentType<any>>(Component: C) => {
293-
return <P extends object = {}>(styleObject: StyleObject) => {
294-
// Resolve tokens in the style object
295-
const resolvedStyle = resolveTokens(styleObject, tokens);
296-
297-
// Create StyleSheet using StyleSheet.create for better performance
298-
const styles = StyleSheet.create({
299-
style: resolvedStyle as ResolvedStyle,
300-
});
301-
298+
return <P extends object = {}>(
299+
styleObject: StyleObject | ((props: P & Omit<ComponentProps<C>, 'style'>) => StyleObject),
300+
) => {
302301
// Create and return a new component that applies the resolved styles
303302
const StyledComponent: ComponentType<
304303
P &
@@ -313,7 +312,21 @@ export function defineTokens<T extends TokenConfig>(tokenConfig: T): CreateToken
313312
} = props as {
314313
children?: ReactNode;
315314
style?: StyleProp<ViewStyle | TextStyle | ImageStyle>;
316-
} & Record<string, unknown>;
315+
};
316+
317+
// If styleObject is a function, call it with props to get the style object
318+
const styleToResolve =
319+
typeof styleObject === 'function'
320+
? styleObject(props as P & Omit<ComponentProps<C>, 'style'>)
321+
: styleObject;
322+
323+
// Resolve tokens in the style object
324+
const resolvedStyle = resolveTokens(styleToResolve, tokens);
325+
326+
// Create StyleSheet for the resolved style
327+
const styles = StyleSheet.create({
328+
style: resolvedStyle as ResolvedStyle,
329+
});
317330

318331
// Merge the StyleSheet style with any style passed in props
319332
const mergedStyle = propStyle

0 commit comments

Comments
 (0)