Files
gui/pkgs/core/core-test/styledContextTokens.web.test.tsx
zeekay ba9f436467 chore: drop 'hanzo' prefix from file/identifier names, use 'gui'/'Gui'
Internal renames so source matches the published @hanzogui/[email protected] dist
(Gui.ts, createGui.ts, views/GuiProvider.mjs, views/GuiRoot.mjs).

Renames applied:
- Files: Hanzogui.ts -> Gui.ts, createHanzogui.ts -> createGui.ts,
  HanzoguiProvider.tsx -> GuiProvider.tsx, HanzoguiRoot[.native].tsx ->
  GuiRoot[.native].tsx, HanzoguiPlugin.ts -> GuiPlugin.ts,
  withHanzogui.ts -> withGui.ts, loadHanzogui.ts -> loadGui.ts,
  v4-hanzogui.ts -> v4-gui.ts, *.d.ts mirrors, hanzogui.config.ts ->
  gui.config.ts, hanzogui.build.ts -> gui.build.ts, hanzogui.css ->
  gui.css, hanzogui-build.js -> gui-build.js, etc.
- Directories: pkgs/core/create-hanzogui -> pkgs/core/create-gui,
  apps/sandbox/config/hanzogui -> apps/sandbox/config/gui.
- Identifiers: Hanzogui* -> Gui*, createHanzogui -> createGui, withHanzogui
  -> withGui, loadHanzogui -> loadGui, HANZOGUI_* env -> GUI_*,
  __hanzogui* globals -> __gui*, hanzoguiOptions/hanzoguiConfig/
  hanzoguiPlugin etc. -> guiOptions/guiConfig/guiPlugin, CSS vars
  --hanzogui-* -> --gui-*, data-hanzogui-* -> data-gui-*, [hanzogui]
  log prefix -> [gui].
- Removed duplicate pkgs/packages/hanzogui-dev-config; the canonical
  pkgs/packages/gui-dev-config (@hanzogui/dev-config) already existed
  with the correct shape. apps/demos now consumes @hanzogui/dev-config.

Preserved (public npm names):
- "hanzogui" umbrella package (pkgs/ui/hanzogui/package.json)
- "@hanzogui/*" namespace
- "hanzogui-loader" package name (pkgs/compiler/loader)
- "hanzogui-build-test-*" test fixture names
- "hanzogui-publish-smoke" workflow id
- pkgs/core/helpers-hanzogui dir (not on rename list)
- apps/gui.hanzo.ai/ marketing site directory
- AGENTS.md symlink restored after perl -i clobbered it

Smoke build (turbo run build): 169/169 workspaces successful.
Sample tests: pkgs/core/core-test getSplitStyles.android* + createGui.web
all green.
2026-05-26 18:25:52 -07:00

168 lines
5.4 KiB
TypeScript

import { beforeAll, describe, expect, test } from 'vitest'
import config from '../config-default'
import { View, Text, createStyledContext, createGui, styled } from '../core/src'
// Stack was removed in v2 - use View instead (same props)
import { simplifiedGetSplitStyles } from './utils'
beforeAll(() => {
createGui(config.getDefaultGuiConfig())
})
/**
* Test that styled context preserves original token values (e.g., "$4", "$red")
* instead of resolved CSS variables (e.g., "var(--space-4)", "var(--color-red)")
* when propagating values through context to child components.
*
* This is important because functional variants need access to the original
* token name to look up values in the tokens object.
*
* Issue: When a parent component sets a context value via a variant, the child's
* functional variant should receive the original token string, not the resolved
* CSS variable.
*
* Real-world example from user report:
* Parent sets gap="$4" via context
* Child has functional variant: `:number`: (nr, { props, tokens }) => {
* const gapVal = tokens.space[props.gap].val // FAILS if props.gap is "var(--space-4)"
* return { width: `calc(${100/nr}% - ${gapVal}px)` }
* }
*/
describe('styled context token preservation', () => {
test('overriddenContextProps should contain original token values not CSS variables', () => {
const GridContext = createStyledContext({
gap: '$4',
color: undefined as string | undefined,
})
const GridParent = styled(View, {
name: 'GridParent',
context: GridContext,
flexDirection: 'row',
flexWrap: 'wrap',
variants: {
spacing: {
small: {
gap: '$2',
},
medium: {
gap: '$4',
},
large: {
gap: '$8',
},
},
accent: {
true: {
color: '$red10',
},
},
} as const,
})
const parentStyles = simplifiedGetSplitStyles(GridParent, {
spacing: 'large', // sets gap: '$8'
accent: true, // sets color: '$red10'
})
// Check that overriddenContextProps preserves the token strings
const overridden = parentStyles.overriddenContextProps
expect(overridden).toBeDefined()
// gap should be "$8" not "var(--space-8)" or similar
expect(overridden!.gap).toBe('$8')
expect(overridden!.gap).not.toMatch(/^var\(/)
// color should be "$red10" not "var(--color-red10)" or similar
expect(overridden!.color).toBe('$red10')
expect(overridden!.color).not.toMatch(/^var\(/)
})
test('variant that sets context value should preserve token for children functional variants', () => {
// This mimics the real-world case from the bug report:
// Parent has variant that sets a context prop (like gap)
// Child's functional variant needs to use that value to look up token
const TestContext = createStyledContext({
gap: '$4' as string,
})
const Parent = styled(View, {
name: 'TestParent',
context: TestContext,
variants: {
spacing: {
compact: {
gap: '$2',
},
normal: {
gap: '$4',
},
large: {
gap: '$8',
},
},
} as const,
})
// Test compact
const compactStyles = simplifiedGetSplitStyles(Parent, {
spacing: 'compact',
})
expect(compactStyles.overriddenContextProps?.gap).toBe('$2')
// Test large
const largeStyles = simplifiedGetSplitStyles(Parent, {
spacing: 'large',
})
expect(largeStyles.overriddenContextProps?.gap).toBe('$8')
})
test('context props that are also valid style props should preserve tokens', () => {
// The key issue: when a context prop is ALSO a valid CSS style prop (like gap, color),
// it gets resolved to a CSS variable. But context props that are NOT style props
// (like custom props) should be preserved as tokens.
const ThemeContext = createStyledContext({
// 'gap' is a valid style prop - this is the problematic case
gap: '$4' as string,
// 'myCustomProp' is NOT a style prop - this should work fine
myCustomProp: '$4' as string,
})
const Parent = styled(View, {
name: 'Parent',
context: ThemeContext,
variants: {
spacing: {
lg: {
gap: '$8', // style prop - currently breaks
myCustomProp: '$8', // non-style prop - should work
},
},
} as const,
})
const parentStyles = simplifiedGetSplitStyles(Parent, { spacing: 'lg' })
// Both should preserve the token string for context propagation
// Currently gap fails because it's a style prop that gets resolved
expect(parentStyles.overriddenContextProps?.gap).toBe('$8')
expect(parentStyles.overriddenContextProps?.myCustomProp).toBe('$8')
})
// NOTE: Pseudo style override tests (hoverStyle, pressStyle) would require
// more complex test setup to properly simulate component state and style merging.
// The core token preservation functionality is verified by the above tests.
// Pseudo style context override tests can be added when needed with proper
// integration test setup (e.g., in kitchen-sink with actual rendered components).
//
// The fix ensures that when variants set context props, the original token
// string (like '$8') is preserved instead of the resolved CSS variable
// (like 'var(--t-space-8)'). This is the core issue reported in the bug.
})