From 1c5b819ed9978492e1f68d9b4f33ddaa13069c72 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Wed, 9 Jul 2025 14:56:33 -0700 Subject: [PATCH] Add native support for `css.types.*` functions These functions pass the value straight through on native. Fix #331 --- apps/examples/src/components/App.js | 4 +- apps/examples/src/components/tokens.stylex.js | 6 +- .../react-strict-dom/src/native/css/index.js | 75 ++++++++++++++++ packages/react-strict-dom/src/native/index.js | 15 ++-- packages/react-strict-dom/src/types/styles.js | 4 +- .../css-test.native.js.snap-native | 72 +++++++++++++++ .../react-strict-dom/tests/css-test.native.js | 89 +++++++++++++++++++ 7 files changed, 252 insertions(+), 13 deletions(-) diff --git a/apps/examples/src/components/App.js b/apps/examples/src/components/App.js index 1801e4a7..7dd114fd 100644 --- a/apps/examples/src/components/App.js +++ b/apps/examples/src/components/App.js @@ -665,14 +665,14 @@ const animateSequence = css.keyframes({ }); const themedTokens = css.createTheme(tokens, { - squareColor: 'purple', + squareColor: css.types.color('purple'), textColor: 'purple', inputColor: 'purple', inputPlaceholderColor: 'mediumpurple' }); const themedTokensAlt = css.createTheme(tokens, { - squareColor: 'darkorange', + squareColor: css.types.color('darkorange'), textColor: 'darkorange', inputColor: 'orangered', inputPlaceholderColor: 'orange' diff --git a/apps/examples/src/components/tokens.stylex.js b/apps/examples/src/components/tokens.stylex.js index 3e5d7820..947bc199 100644 --- a/apps/examples/src/components/tokens.stylex.js +++ b/apps/examples/src/components/tokens.stylex.js @@ -7,19 +7,19 @@ * @flow strict-local */ -import type { StyleVars } from 'react-strict-dom'; +import type { StyleVars, StyleTypes } from 'react-strict-dom'; import { css } from 'react-strict-dom'; export const tokens: StyleVars< $ReadOnly<{ - squareColor: string, + squareColor: StyleTypes.Color, textColor: string, inputColor: string, inputPlaceholderColor: string }> > = css.defineVars({ - squareColor: 'red', + squareColor: css.types.color('red'), textColor: { default: 'darkred', '@media (prefers-color-scheme: dark)': 'lightred' diff --git a/packages/react-strict-dom/src/native/css/index.js b/packages/react-strict-dom/src/native/css/index.js index e6324b1c..3ade3cc2 100644 --- a/packages/react-strict-dom/src/native/css/index.js +++ b/packages/react-strict-dom/src/native/css/index.js @@ -512,3 +512,78 @@ export function props( return nativeProps; } + +type ValueWithDefault<+T> = + | T + | $ReadOnly<{ + default: ValueWithDefault, + [string]: ValueWithDefault + }>; + +export const types = { + angle: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + color: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + image: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + integer: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + length: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + lengthPercentage: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + number: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + percentage: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + resolution: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + time: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + transformFunction: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + transformList: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + }, + url: ( + value: ValueWithDefault + ): ValueWithDefault => { + return value; + } +}; diff --git a/packages/react-strict-dom/src/native/index.js b/packages/react-strict-dom/src/native/index.js index a586ce4c..8b7ddae7 100644 --- a/packages/react-strict-dom/src/native/index.js +++ b/packages/react-strict-dom/src/native/index.js @@ -12,6 +12,7 @@ import type { StyleXStylesWithout, StaticStyles, Theme, + Types, VarGroup } from '@stylexjs/stylex'; @@ -24,11 +25,6 @@ import * as _css from './css'; import { ProvideCustomProperties } from './modules/ContextCustomProperties'; import { ProvideViewportScale } from './modules/ContextViewportScale'; -type StyleTheme = Theme; -type StyleVars = VarGroup; -type Styles = StyleXStyles; -type StylesWithout = StyleXStylesWithout; - type ProviderValue = $ReadOnly<{ [string]: string | number }>; type ProviderProps = $ReadOnly<{ @@ -36,8 +32,6 @@ type ProviderProps = $ReadOnly<{ customProperties: ProviderValue }>; -export type { StaticStyles, StyleTheme, StyleVars, Styles, StylesWithout }; - function ThemeProvider(props: ProviderProps): React.Node { const { children, customProperties } = props; @@ -56,4 +50,11 @@ const contexts = { // Export using StyleX types as the shim has divergent types internally. const css: TStyleX = _css as $FlowFixMe; +export type { StaticStyles }; +export type { StyleXStyles as Styles }; +export type { StyleXStylesWithout as StylesWithout }; +export type { Theme as StyleTheme }; +export type { Types as StyleTypes }; +export type { VarGroup as StyleVars }; + export { compat, contexts, css, html }; diff --git a/packages/react-strict-dom/src/types/styles.js b/packages/react-strict-dom/src/types/styles.js index 797fa5ee..30a79cf6 100644 --- a/packages/react-strict-dom/src/types/styles.js +++ b/packages/react-strict-dom/src/types/styles.js @@ -12,7 +12,8 @@ import type { StyleXArray, StyleXStyles, Theme, - VarGroup + VarGroup, + Types } from '@stylexjs/stylex'; import typeof * as TStyleX from '@stylexjs/stylex'; @@ -37,3 +38,4 @@ export type IStyleX = $ReadOnly<{ export type MutableCustomProperties = { [string]: mixed }; export type CustomProperties = $ReadOnly; +export type { Types }; diff --git a/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native b/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native index 1e18e109..e936d759 100644 --- a/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native +++ b/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native @@ -1,5 +1,77 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`css.types.* types.angle: tokens 1`] = ` +{ + "angle": "var(--angle__id__6)", +} +`; + +exports[`css.types.* types.color: tokens 1`] = ` +{ + "color": "var(--color__id__7)", +} +`; + +exports[`css.types.* types.image: tokens 1`] = ` +{ + "image": "var(--image__id__8)", +} +`; + +exports[`css.types.* types.integer: tokens 1`] = ` +{ + "integer": "var(--integer__id__9)", +} +`; + +exports[`css.types.* types.length: tokens 1`] = ` +{ + "length": "var(--length__id__10)", +} +`; + +exports[`css.types.* types.lengthPercentage: tokens 1`] = ` +{ + "lengthPercentage": "var(--lengthPercentage__id__11)", +} +`; + +exports[`css.types.* types.number: tokens 1`] = ` +{ + "number": "var(--number__id__12)", +} +`; + +exports[`css.types.* types.percentage: tokens 1`] = ` +{ + "percentage": "var(--percentage__id__13)", +} +`; + +exports[`css.types.* types.resolution: tokens 1`] = ` +{ + "resolution": "var(--resolution__id__14)", +} +`; + +exports[`css.types.* types.time: tokens 1`] = ` +{ + "time": "var(--time__id__15)", +} +`; + +exports[`css.types.* types.transformFunction: tokens 1`] = ` +{ + "transformFunction": "var(--transformFunction__id__16)", +} +`; + +exports[`css.types.* types.url: tokens 1`] = ` +{ + "url": "var(--url__id__17)", +} +`; + exports[`properties: custom property css.createTheme: css.__customProperties 1`] = ` { "rootColor__id__1": "red", diff --git a/packages/react-strict-dom/tests/css-test.native.js b/packages/react-strict-dom/tests/css-test.native.js index e1b52a48..2acbe74c 100644 --- a/packages/react-strict-dom/tests/css-test.native.js +++ b/packages/react-strict-dom/tests/css-test.native.js @@ -1841,3 +1841,92 @@ describe('queries: @media', () => { expect(props.style.color).toBe('white'); }); }); + +describe('css.types.*', () => { + test('types.angle', () => { + const tokens = css.defineVars({ + angle: css.types.angle('40deg') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.color', () => { + const tokens = css.defineVars({ + color: css.types.color({ + default: 'blue', + '@media (prefers-color-scheme: dark)': 'lightblue' + }) + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.image', () => { + const tokens = css.defineVars({ + image: css.types.image('./jpg') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.integer', () => { + const tokens = css.defineVars({ + integer: css.types.integer(4) + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.length', () => { + const tokens = css.defineVars({ + length: css.types.length('4px') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.lengthPercentage', () => { + const tokens = css.defineVars({ + lengthPercentage: css.types.lengthPercentage('100%') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.number', () => { + const tokens = css.defineVars({ + number: css.types.number(4.4) + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.percentage', () => { + const tokens = css.defineVars({ + percentage: css.types.percentage('100%') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.resolution', () => { + const tokens = css.defineVars({ + resolution: css.types.resolution('96dpi') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.time', () => { + const tokens = css.defineVars({ + time: css.types.time('1s') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.transformFunction', () => { + const tokens = css.defineVars({ + transformFunction: css.types.transformFunction('translateX(10px)') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); + + test('types.url', () => { + const tokens = css.defineVars({ + url: css.types.url('https://foobar.com') + }); + expect(tokens).toMatchSnapshot('tokens'); + }); +});