Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 43 additions & 23 deletions packages/babel-plugin-kstyled/src/__tests__/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ describe('babel-plugin-kstyled', () => {

const output = transform(input);

// Should contain paddingVertical and paddingHorizontal
expect(output).toContain('paddingVertical');
expect(output).toContain('paddingHorizontal');
// Should expand to longhand properties (not paddingVertical/paddingHorizontal)
expect(output).toContain('paddingTop');
expect(output).toContain('paddingRight');
expect(output).toContain('paddingBottom');
expect(output).toContain('paddingLeft');
// Should NOT contain the shorthand 'padding' property
expect(output).not.toContain('padding:');
});
Expand Down Expand Up @@ -62,8 +64,11 @@ describe('babel-plugin-kstyled', () => {

const output = transform(input);

expect(output).toContain('marginVertical');
expect(output).toContain('marginHorizontal');
// Should expand to longhand properties (not marginVertical/marginHorizontal)
expect(output).toContain('marginTop');
expect(output).toContain('marginRight');
expect(output).toContain('marginBottom');
expect(output).toContain('marginLeft');
});

test('should expand multiple shorthand properties', () => {
Expand All @@ -80,10 +85,15 @@ describe('babel-plugin-kstyled', () => {

const output = transform(input);

expect(output).toContain('paddingVertical');
expect(output).toContain('paddingHorizontal');
expect(output).toContain('marginVertical');
expect(output).toContain('marginHorizontal');
// Should expand all shorthands to longhand
expect(output).toContain('paddingTop');
expect(output).toContain('paddingRight');
expect(output).toContain('paddingBottom');
expect(output).toContain('paddingLeft');
expect(output).toContain('marginTop');
expect(output).toContain('marginRight');
expect(output).toContain('marginBottom');
expect(output).toContain('marginLeft');
expect(output).toContain('backgroundColor');
});
});
Expand Down Expand Up @@ -146,9 +156,11 @@ describe('babel-plugin-kstyled', () => {

// Should have getDynamicPatch function
expect(output).toContain('getDynamicPatch');
// Should have static padding styles
expect(output).toContain('paddingVertical');
expect(output).toContain('paddingHorizontal');
// Should have static padding styles expanded to longhand
expect(output).toContain('paddingTop');
expect(output).toContain('paddingRight');
expect(output).toContain('paddingBottom');
expect(output).toContain('paddingLeft');
// Dynamic function should be called with (p)
expect(output).toMatch(/backgroundColor.*\(p\)/);
});
Expand Down Expand Up @@ -214,9 +226,11 @@ describe('babel-plugin-kstyled', () => {

const output = transform(input);

// Should expand padding shorthand
expect(output).toContain('paddingVertical');
expect(output).toContain('paddingHorizontal');
// Should expand padding shorthand to longhand
expect(output).toContain('paddingTop');
expect(output).toContain('paddingRight');
expect(output).toContain('paddingBottom');
expect(output).toContain('paddingLeft');
// Should have attrs
expect(output).toContain('attrs');
expect(output).toContain('placeholder');
Expand Down Expand Up @@ -266,9 +280,11 @@ describe('babel-plugin-kstyled', () => {

const output = transform(input);

// Static styles
expect(output).toContain('paddingVertical');
expect(output).toContain('paddingHorizontal');
// Static styles - padding shorthand expanded to longhand
expect(output).toContain('paddingTop');
expect(output).toContain('paddingRight');
expect(output).toContain('paddingBottom');
expect(output).toContain('paddingLeft');
expect(output).toContain('borderRadius');
expect(output).toContain('alignItems');
expect(output).toContain('marginVertical');
Expand Down Expand Up @@ -686,11 +702,15 @@ describe('babel-plugin-kstyled', () => {
`;

const output = transform(input);
// Should expand shorthand with mixed units
expect(output).toContain('paddingVertical');
expect(output).toContain('paddingHorizontal');
expect(output).toContain('marginVertical');
expect(output).toContain('marginHorizontal');
// Should expand shorthand to longhand
expect(output).toContain('paddingTop');
expect(output).toContain('paddingRight');
expect(output).toContain('paddingBottom');
expect(output).toContain('paddingLeft');
expect(output).toContain('marginTop');
expect(output).toContain('marginRight');
expect(output).toContain('marginBottom');
expect(output).toContain('marginLeft');
expect(output).toContain('borderRadius');
expect(output).toContain('borderWidth');
});
Expand Down
20 changes: 14 additions & 6 deletions packages/babel-plugin-kstyled/src/css-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,20 @@ function expandShorthand(property: string, value: string): Record<string, any> |
if (property === 'padding') {
if (parts.length === 2) {
// padding: vertical horizontal
// IMPORTANT: Use longhand properties for proper style override in React Native
return {
paddingVertical: parts[0],
paddingHorizontal: parts[1],
paddingTop: parts[0],
paddingRight: parts[1],
paddingBottom: parts[0],
paddingLeft: parts[1],
};
} else if (parts.length === 3) {
// padding: top horizontal bottom
return {
paddingTop: parts[0],
paddingHorizontal: parts[1],
paddingRight: parts[1],
paddingBottom: parts[2],
paddingLeft: parts[1],
};
} else if (parts.length === 4) {
// padding: top right bottom left
Expand All @@ -235,16 +239,20 @@ function expandShorthand(property: string, value: string): Record<string, any> |
} else if (property === 'margin') {
if (parts.length === 2) {
// margin: vertical horizontal
// IMPORTANT: Use longhand properties for proper style override in React Native
return {
marginVertical: parts[0],
marginHorizontal: parts[1],
marginTop: parts[0],
marginRight: parts[1],
marginBottom: parts[0],
marginLeft: parts[1],
};
} else if (parts.length === 3) {
// margin: top horizontal bottom
return {
marginTop: parts[0],
marginHorizontal: parts[1],
marginRight: parts[1],
marginBottom: parts[2],
marginLeft: parts[1],
};
} else if (parts.length === 4) {
// margin: top right bottom left
Expand Down
5 changes: 3 additions & 2 deletions packages/babel-plugin-kstyled/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,12 +595,13 @@ export default function babelPluginKStyled(): PluginObj<PluginState> {
}
}

// Create style expression: props.style ? [props.style, __ks.base] : __ks.base
// Create style expression: props.style ? [__ks.base, props.style] : __ks.base
// IMPORTANT: Base styles first, external styles last (external overrides base)
const styleExpression = t.conditionalExpression(
t.memberExpression(t.identifier('props'), t.identifier('style')),
t.arrayExpression([
t.memberExpression(t.identifier('props'), t.identifier('style')),
t.memberExpression(t.identifier(styleId), t.identifier('base')),
t.memberExpression(t.identifier('props'), t.identifier('style')),
]),
t.memberExpression(t.identifier(styleId), t.identifier('base'))
);
Expand Down
1 change: 1 addition & 0 deletions packages/kstyled/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@babel/preset-react": "^7.26.0",
"@babel/preset-typescript": "^7.26.0",
"@types/babel__core": "^7.20.5",
"@types/jest": "^29.5.14",
"@types/react": "~19.1.10",
"@types/react-native": "^0.73.0",
"@typescript-eslint/eslint-plugin": "^8.19.1",
Expand Down
16 changes: 11 additions & 5 deletions packages/kstyled/src/css.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,24 @@ export const css: CssFactory = Object.assign(
const parts = value.split(/\s+/);
const prefix = camelProp; // 'padding' or 'margin'

// IMPORTANT: Always use longhand properties for proper override in React Native
if (parts.length === 1) {
const val = parseFloat(parts[0]);
styleObj[`${prefix}Vertical`] = val;
styleObj[`${prefix}Horizontal`] = val;
styleObj[`${prefix}Top`] = val;
styleObj[`${prefix}Right`] = val;
styleObj[`${prefix}Bottom`] = val;
styleObj[`${prefix}Left`] = val;
} else if (parts.length === 2) {
styleObj[`${prefix}Vertical`] = parseFloat(parts[0]);
styleObj[`${prefix}Horizontal`] = parseFloat(parts[1]);
styleObj[`${prefix}Top`] = parseFloat(parts[0]);
styleObj[`${prefix}Right`] = parseFloat(parts[1]);
styleObj[`${prefix}Bottom`] = parseFloat(parts[0]);
styleObj[`${prefix}Left`] = parseFloat(parts[1]);
} else if (parts.length === 3) {
// top, horizontal, bottom
styleObj[`${prefix}Top`] = parseFloat(parts[0]);
styleObj[`${prefix}Horizontal`] = parseFloat(parts[1]);
styleObj[`${prefix}Right`] = parseFloat(parts[1]);
styleObj[`${prefix}Bottom`] = parseFloat(parts[2]);
styleObj[`${prefix}Left`] = parseFloat(parts[1]);
} else if (parts.length === 4) {
styleObj[`${prefix}Top`] = parseFloat(parts[0]);
styleObj[`${prefix}Right`] = parseFloat(parts[1]);
Expand Down
9 changes: 6 additions & 3 deletions packages/kstyled/src/styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,13 @@ function styledFunction<C extends ComponentType<any>, P = {}, AttrsP = {}>(
mergedProps
);

// Build style array with correct priority
// Build style array with correct priority:
// 1. Static compiled styles (lowest)
// 2. Dynamic patch
// 3. External inline styles (highest - ensures override)
const styles = buildStyleArray(
compiledStyles,
styleKeys,
mergedCompiledStyles,
mergedStyleKeys,
dynamicPatch,
externalStyle as StyleValue
);
Expand Down
147 changes: 147 additions & 0 deletions packages/kstyled/src/utils/__tests__/style-merger.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { expandShorthandProperties } from '../style-merger';

describe('expandShorthandProperties', () => {
describe('padding shorthand expansion', () => {
it('should expand padding to all four longhand properties', () => {
const input = {
padding: 0,
};

const result = expandShorthandProperties(input);

expect(result).toEqual({
paddingTop: 0,
paddingRight: 0,
paddingBottom: 0,
paddingLeft: 0,
});
});

it('should expand paddingHorizontal to paddingLeft + paddingRight', () => {
const input = {
paddingHorizontal: 0,
};

const result = expandShorthandProperties(input);

expect(result).toEqual({
paddingLeft: 0,
paddingRight: 0,
});
});

it('should expand paddingVertical to paddingTop + paddingBottom', () => {
const input = {
paddingVertical: 8,
};

const result = expandShorthandProperties(input);

expect(result).toEqual({
paddingTop: 8,
paddingBottom: 8,
});
});
});

describe('margin shorthand expansion', () => {
it('should expand margin to all four longhand properties', () => {
const input = {
margin: 4,
};

const result = expandShorthandProperties(input);

expect(result).toEqual({
marginTop: 4,
marginRight: 4,
marginBottom: 4,
marginLeft: 4,
});
});

it('should expand marginHorizontal to marginLeft + marginRight', () => {
const input = {
marginHorizontal: 0,
};

const result = expandShorthandProperties(input);

expect(result).toEqual({
marginLeft: 0,
marginRight: 0,
});
});

it('should expand marginVertical to marginTop + marginBottom', () => {
const input = {
marginVertical: 12,
};

const result = expandShorthandProperties(input);

expect(result).toEqual({
marginTop: 12,
marginBottom: 12,
});
});
});

describe('preserving other properties', () => {
it('should preserve non-shorthand properties', () => {
const input = {
padding: 8,
backgroundColor: 'red',
flexDirection: 'row',
};

const result = expandShorthandProperties(input);

expect(result).toEqual({
paddingTop: 8,
paddingRight: 8,
paddingBottom: 8,
paddingLeft: 8,
backgroundColor: 'red',
flexDirection: 'row',
});
});

it('should handle mixed shorthand properties', () => {
const input = {
padding: 8,
margin: 4,
};

const result = expandShorthandProperties(input);

expect(result).toEqual({
paddingTop: 8,
paddingRight: 8,
paddingBottom: 8,
paddingLeft: 8,
marginTop: 4,
marginRight: 4,
marginBottom: 4,
marginLeft: 4,
});
});
});

describe('edge cases', () => {
it('should return null for null input', () => {
const result = expandShorthandProperties(null);
expect(result).toBeNull();
});

it('should return undefined for undefined input', () => {
const result = expandShorthandProperties(undefined);
expect(result).toBeUndefined();
});

it('should return empty object for empty input', () => {
const result = expandShorthandProperties({});
expect(result).toEqual({});
});
});
});
Loading