Files
gui/pkgs/core/core-test/getStylesAtomic.web.test.tsx
zeekayandHanzo Dev 69c6e860d7 transplant onto tamagui v2.6.0, brand as a re-runnable transform
This repo could never sync. There was NO merge-base with tamagui — 372 commits
here against 15,791 there, unrelated histories — because it began as a
copy-paste rebrand with a flattened layout. Every "sync" since was a hand
re-application that drifted, which is why the tree sat on 7.3.1 while upstream
shipped fixes we then rediscovered by hand.

This commit's PARENT IS TAMAGUI. A future sync is `git merge tama/main` then
`node scripts/rebrand.mjs` — a real merge, because the history is now shared.

174 packages: 166 upstream, our 8 restored (chrome, shell, telemetry, recipes,
get, font-geist-{sans,mono}, @hanzo/booker), bento-get gained. 170/170 build.

Our patches to files UPSTREAM ALSO OWNS are carried, which the package-level
delta missed and which was where the value actually was:
  - useMedia's server snapshot (`initState ?? getMedia()`). Upstream still
    returns undefined there, so every Tamagui Dialog kills a prerender — the
    exact crash hanzo.app spent a day on.
  - data-slot on web host nodes, absent upstream entirely.
Both applied ONTO upstream's newer files (it rewrote useMedia +150/-63 and
createComponent +160/-55), marked `PATCH (ours, carried across the transplant)`.

Seven traps are encoded in scripts/rebrand.mjs, each found by a gate:
skip itself and lockfiles; special-case the double-branded names in BOTH scoped
and bare form; map path strings, not just paths; drop a level for the flattened
group; check disk rather than a stale index.

Rust build artifacts are NOT committed. An earlier draft of this commit swept
apps/desktop/src-tauri/target/debug in via `git add -A` — 3,859 files including
a 272MB .a, over GitHub's 100MB cap, so the push could never have landed. They
are gitignored now.

Co-Authored-By: Hanzo Dev <[email protected]>
2026-07-30 18:25:06 -07:00

189 lines
4.4 KiB
TypeScript

import { beforeAll, expect, test } from 'vitest'
import { StyleObjectIdentifier, StyleObjectRules } from '@hanzogui/helpers'
import config from '../config-default'
import { createGui, getCSSStylesAtomic } from '../web/src'
import { expandStyle } from '../web/src/helpers/expandStyle'
type StyleCompat = 'legacy' | 'react-native' | 'web'
beforeAll(() => {
createGui(config.getDefaultGuiConfig())
})
function setStyleCompat(styleCompat: StyleCompat) {
const next = config.getDefaultGuiConfig()
createGui({
...next,
settings: {
...next.settings,
styleCompat,
},
})
}
test(`should expand webkit user-select`, () => {
expect(
getCSSStylesAtomic({
// @ts-ignore
userSelect: 'none',
})
).toMatchInlineSnapshot(`
[
[
"userSelect",
"none",
"_ussel-none",
undefined,
[
":root ._ussel-none{user-select:none;-webkit-user-select:none;}",
],
],
]
`)
})
test(`should handle decimal placement differently`, () => {
const out = getCSSStylesAtomic({
left: 1.11,
right: 11.1,
})
expect(out[0][StyleObjectIdentifier]).toBe(`_l-1--11px`)
expect(out[1][StyleObjectIdentifier]).toBe(`_r-11--1px`)
})
test(`should turn columnGap into gap-column`, () => {
const out = getCSSStylesAtomic({
columnGap: 10,
})
expect(out[0][StyleObjectRules].includes(`column-gap:10px`))
})
test(`longhand border props get doubled selector for specificity`, () => {
const out = getCSSStylesAtomic({
borderTopWidth: 2,
})
const rule = out[0][StyleObjectRules][0]
// should have .cls.cls (doubled) for higher specificity over shorthand
expect(rule).toMatch(/\._[^\s]+\._[^\s]+\{/)
expect(rule).toContain('border-top-width:2px')
})
test(`shorthand border prop gets single selector`, () => {
const out = getCSSStylesAtomic({
// @ts-ignore
border: '1px solid red',
})
const rule = out[0][StyleObjectRules][0]
// should have single .cls selector
expect(rule).toMatch(/:root \._[^\s.]+\{/)
})
test(`outline longhands get doubled selector`, () => {
const out = getCSSStylesAtomic({
outlineWidth: 2,
})
const rule = out[0][StyleObjectRules][0]
expect(rule).toMatch(/\._[^\s]+\._[^\s]+\{/)
})
test(`styleCompat expands flex in legacy mode`, () => {
setStyleCompat('legacy')
expect(expandStyle('flex', 1)).toEqual([
['flexGrow', 1],
['flexShrink', 1],
['flexBasis', 'auto'],
])
})
test(`styleCompat expands flex in react-native mode`, () => {
setStyleCompat('react-native')
expect(expandStyle('flex', 1)).toEqual([
['flexGrow', 1],
['flexShrink', 0],
['flexBasis', 0],
])
expect(expandStyle('flex', 0)).toEqual([
['flexGrow', 0],
['flexShrink', 0],
['flexBasis', 'auto'],
])
expect(expandStyle('flex', -2)).toEqual([
['flexGrow', 0],
['flexShrink', 2],
['flexBasis', 'auto'],
])
})
test(`styleCompat expands flex in web mode`, () => {
setStyleCompat('web')
expect(expandStyle('flex', 1)).toEqual([
['flexGrow', 1],
['flexShrink', 1],
['flexBasis', 0],
])
expect(expandStyle('flex', -2)).toEqual([['flex', -2]])
})
test(`styleCompat preserves special and string flex values`, () => {
for (const styleCompat of ['legacy', 'react-native', 'web'] as const) {
setStyleCompat(styleCompat)
expect(expandStyle('flex', -1)).toEqual([
['flexGrow', 0],
['flexShrink', 1],
['flexBasis', 'auto'],
])
expect(expandStyle('flex', 'unset')).toEqual([['flex', 'unset']])
}
})
// test(`should be fast`, () => {
// // need to compare it in a cpu-insensitive way, so compare to common operations
// const start0 = performance.now()
// function baseline() {
// return new Array().fill(1000).map(() => {
// return [...new Set([`${Math.random() * Math.random() * Math.random()}`])]
// })
// }
// for (let i = 0; i < 100; i++) {
// baseline()
// }
// const end0 = performance.now() - start0
// console.log('end0', end0)
// const start = performance.now()
// function run() {
// getCSSStylesAtomic({
// backgroundColor: 'red',
// width: 100,
// height: 200,
// scale: 2,
// $gtLg: {
// backgroundColor: 'green',
// },
// hoverStyle: {
// margin: 20,
// padding: 20,
// },
// })
// }
// for (let i = 0; i < 100; i++) {
// run()
// }
// console.log('took', performance.now() - start)
// })