@pyreon/coolgrid 0.12.12 → 0.12.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/coolgrid",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.14",
|
|
4
4
|
"description": "Responsive grid system for Pyreon",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,20 +35,24 @@
|
|
|
35
35
|
"build:watch": "bun run vl_rolldown_build-watch",
|
|
36
36
|
"lint": "oxlint .",
|
|
37
37
|
"test": "vitest run",
|
|
38
|
+
"test:browser": "vitest run --config ./vitest.browser.config.ts",
|
|
38
39
|
"test:coverage": "vitest run --coverage",
|
|
39
40
|
"test:watch": "vitest",
|
|
40
41
|
"typecheck": "tsc --noEmit"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@pyreon/
|
|
44
|
+
"@pyreon/test-utils": "^0.12.10",
|
|
45
|
+
"@pyreon/typescript": "^0.12.14",
|
|
46
|
+
"@pyreon/ui-core": "^0.12.14",
|
|
47
|
+
"@vitest/browser-playwright": "^4.1.4",
|
|
44
48
|
"@vitus-labs/tools-rolldown": "^1.15.3"
|
|
45
49
|
},
|
|
46
50
|
"peerDependencies": {
|
|
47
|
-
"@pyreon/core": "^0.12.
|
|
48
|
-
"@pyreon/reactivity": "^0.12.
|
|
49
|
-
"@pyreon/styler": "^0.12.
|
|
50
|
-
"@pyreon/ui-core": "^0.12.
|
|
51
|
-
"@pyreon/unistyle": "^0.12.
|
|
51
|
+
"@pyreon/core": "^0.12.14",
|
|
52
|
+
"@pyreon/reactivity": "^0.12.14",
|
|
53
|
+
"@pyreon/styler": "^0.12.14",
|
|
54
|
+
"@pyreon/ui-core": "^0.12.14",
|
|
55
|
+
"@pyreon/unistyle": "^0.12.14"
|
|
52
56
|
},
|
|
53
57
|
"engines": {
|
|
54
58
|
"node": ">= 22"
|
|
@@ -37,7 +37,14 @@ describe('Col', () => {
|
|
|
37
37
|
mockUseContext.mockReturnValue({})
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
// First test in this file pays the cold-import cost for the entire coolgrid
|
|
41
|
+
// module graph (@pyreon/core + ui-core + unistyle + styler + rocketstyle).
|
|
42
|
+
// Warm it takes ~200ms, but on cold CI shared runners the Vite transform of
|
|
43
|
+
// that graph can spike past the 15s default and time out (PR #225 flakes).
|
|
44
|
+
// Per-test timeout bump — subsequent tests in the file reuse the cached
|
|
45
|
+
// module and run in 0ms, so the global 15s budget stays strict everywhere
|
|
46
|
+
// else.
|
|
47
|
+
it('returns a VNode', { timeout: 30000 }, async () => {
|
|
41
48
|
const Col = (await import('../Col')).default
|
|
42
49
|
const result = asVNode(Col({ children: 'test' }))
|
|
43
50
|
expect(result).toBeDefined()
|
|
@@ -29,7 +29,12 @@ describe('Container', () => {
|
|
|
29
29
|
mockUseContext.mockReturnValue({ theme: {} })
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
-
it('returns a VNode', async () => {
|
|
32
|
+
it('returns a VNode', { timeout: 30_000 }, async () => {
|
|
33
|
+
// Long timeout: this test dynamic-imports Container on first run,
|
|
34
|
+
// which cold-loads the full styler → unistyle chain. On slow CI
|
|
35
|
+
// runners it consistently hits ~13-14s, flaking against the
|
|
36
|
+
// default 15s timeout. 30s gives comfortable headroom without
|
|
37
|
+
// masking real hangs.
|
|
33
38
|
const Container = (await import('../Container')).default
|
|
34
39
|
const result = asVNode(Container({ children: 'test' }))
|
|
35
40
|
expect(result).toBeDefined()
|
|
@@ -29,7 +29,14 @@ describe('Row', () => {
|
|
|
29
29
|
mockUseContext.mockReturnValue({})
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
// First test in this file pays the cold-import cost for the entire coolgrid
|
|
33
|
+
// module graph (@pyreon/core + ui-core + unistyle + styler + rocketstyle).
|
|
34
|
+
// Warm it takes ~200ms, but on cold CI shared runners the Vite transform of
|
|
35
|
+
// that graph can spike past the 15s default and time out (PR #225 flakes).
|
|
36
|
+
// Per-test timeout bump — subsequent tests in the file reuse the cached
|
|
37
|
+
// module and run in 0ms, so the global 15s budget stays strict everywhere
|
|
38
|
+
// else.
|
|
39
|
+
it('returns a VNode', { timeout: 30000 }, async () => {
|
|
33
40
|
const Row = (await import('../Row')).default
|
|
34
41
|
const result = asVNode(Row({ children: 'test' }))
|
|
35
42
|
expect(result).toBeDefined()
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/** @jsxImportSource @pyreon/core */
|
|
2
|
+
import { h } from '@pyreon/core'
|
|
3
|
+
import { sheet } from '@pyreon/styler'
|
|
4
|
+
import { mountInBrowser } from '@pyreon/test-utils/browser'
|
|
5
|
+
import { PyreonUI } from '@pyreon/ui-core'
|
|
6
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
7
|
+
import { Col, Container, Row } from '../index'
|
|
8
|
+
import gridTheme from '../theme'
|
|
9
|
+
|
|
10
|
+
// Real-Chromium smoke for @pyreon/coolgrid.
|
|
11
|
+
//
|
|
12
|
+
// Vitest browser mode runs at a small viewport (≈414px), so we assert
|
|
13
|
+
// RATIOS against the actual rendered Row width rather than pegging
|
|
14
|
+
// absolute pixels to a Container max-width that gets capped by the
|
|
15
|
+
// viewport.
|
|
16
|
+
//
|
|
17
|
+
// Wraps in `PyreonUI` (the unified provider that replaces the
|
|
18
|
+
// deprecated `<Provider>` from @pyreon/unistyle), matching production
|
|
19
|
+
// usage.
|
|
20
|
+
|
|
21
|
+
describe('@pyreon/coolgrid in real browser', () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
sheet.clearCache()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('Container mounts and Chromium computes flex layout', () => {
|
|
27
|
+
const { container, unmount } = mountInBrowser(
|
|
28
|
+
h(PyreonUI, { theme: gridTheme }, h(Container, { id: 'c' }, 'hello')),
|
|
29
|
+
)
|
|
30
|
+
const el = container.querySelector<HTMLElement>('#c')!
|
|
31
|
+
const cs = getComputedStyle(el)
|
|
32
|
+
expect(cs.display).toBe('flex')
|
|
33
|
+
expect(cs.flexDirection).toBe('column')
|
|
34
|
+
expect(el.textContent).toContain('hello')
|
|
35
|
+
unmount()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('Col size=6 in a 12-col grid yields 50% width of its Row', () => {
|
|
39
|
+
const { container, unmount } = mountInBrowser(
|
|
40
|
+
h(
|
|
41
|
+
PyreonUI,
|
|
42
|
+
{ theme: gridTheme },
|
|
43
|
+
h(Container, null, h(Row, { id: 'row' }, h(Col, { id: 'half', size: 6 }, 'half'))),
|
|
44
|
+
),
|
|
45
|
+
)
|
|
46
|
+
const row = container.querySelector<HTMLElement>('#row')!
|
|
47
|
+
const half = container.querySelector<HTMLElement>('#half')!
|
|
48
|
+
const ratio = half.getBoundingClientRect().width / row.getBoundingClientRect().width
|
|
49
|
+
expect(ratio).toBeGreaterThan(0.495)
|
|
50
|
+
expect(ratio).toBeLessThan(0.505)
|
|
51
|
+
unmount()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('Col size=2 in a 6-col grid yields ~33% — grid columns aren\'t hardcoded to 12', () => {
|
|
55
|
+
// Explicitly overrides `grid.columns` to 6 to prove the math is
|
|
56
|
+
// `size / columns`, not `size / 12`.
|
|
57
|
+
const theme = { ...gridTheme, grid: { ...gridTheme.grid, columns: 6 } }
|
|
58
|
+
const { container, unmount } = mountInBrowser(
|
|
59
|
+
h(
|
|
60
|
+
PyreonUI,
|
|
61
|
+
{ theme },
|
|
62
|
+
h(Container, null, h(Row, { id: 'row' }, h(Col, { id: 'third', size: 2 }, 't'))),
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
const row = container.querySelector<HTMLElement>('#row')!
|
|
66
|
+
const third = container.querySelector<HTMLElement>('#third')!
|
|
67
|
+
const ratio = third.getBoundingClientRect().width / row.getBoundingClientRect().width
|
|
68
|
+
expect(ratio).toBeGreaterThan(0.33)
|
|
69
|
+
expect(ratio).toBeLessThan(0.34)
|
|
70
|
+
// flex-basis emitted from size/columns
|
|
71
|
+
expect(getComputedStyle(third).flexBasis).toBe('33.3333%')
|
|
72
|
+
unmount()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('two Cols of size 6 each lay out side-by-side (sum ≈ 100% of Row)', () => {
|
|
76
|
+
const { container, unmount } = mountInBrowser(
|
|
77
|
+
h(
|
|
78
|
+
PyreonUI,
|
|
79
|
+
{ theme: gridTheme },
|
|
80
|
+
h(
|
|
81
|
+
Container,
|
|
82
|
+
null,
|
|
83
|
+
h(
|
|
84
|
+
Row,
|
|
85
|
+
{ id: 'row' },
|
|
86
|
+
h(Col, { id: 'a', size: 6 }, 'A'),
|
|
87
|
+
h(Col, { id: 'b', size: 6 }, 'B'),
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
const row = container.querySelector<HTMLElement>('#row')!
|
|
93
|
+
const a = container.querySelector<HTMLElement>('#a')!
|
|
94
|
+
const b = container.querySelector<HTMLElement>('#b')!
|
|
95
|
+
const ar = a.getBoundingClientRect()
|
|
96
|
+
const br = b.getBoundingClientRect()
|
|
97
|
+
expect(br.left).toBeGreaterThanOrEqual(ar.right - 1)
|
|
98
|
+
const sum = ar.width + br.width
|
|
99
|
+
const rowW = row.getBoundingClientRect().width
|
|
100
|
+
expect(sum / rowW).toBeGreaterThan(0.99)
|
|
101
|
+
expect(sum / rowW).toBeLessThan(1.01)
|
|
102
|
+
unmount()
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('flex-basis on Col is the literal percentage authored from size/columns', () => {
|
|
106
|
+
const { container, unmount } = mountInBrowser(
|
|
107
|
+
h(
|
|
108
|
+
PyreonUI,
|
|
109
|
+
{ theme: gridTheme },
|
|
110
|
+
h(Container, null, h(Row, null, h(Col, { id: 'c', size: 3 }, 'q'))),
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
const el = container.querySelector<HTMLElement>('#c')!
|
|
114
|
+
expect(getComputedStyle(el).flexBasis).toBe('25%')
|
|
115
|
+
unmount()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('`gap` subtracts from Col flex-basis via calc() — different emit path', () => {
|
|
119
|
+
// With gap set on Row, Col's widthStyles takes the
|
|
120
|
+
// `calc(${width}% - ${g}px)` branch (see Col/styled.ts:37).
|
|
121
|
+
// Chromium resolves the calc() and produces a width strictly less
|
|
122
|
+
// than the gap-less 50% value, which is the load-bearing behavior.
|
|
123
|
+
const { container, unmount } = mountInBrowser(
|
|
124
|
+
h(
|
|
125
|
+
PyreonUI,
|
|
126
|
+
{ theme: gridTheme },
|
|
127
|
+
h(
|
|
128
|
+
Container,
|
|
129
|
+
null,
|
|
130
|
+
h(
|
|
131
|
+
Row,
|
|
132
|
+
{ id: 'row', gap: 24 },
|
|
133
|
+
h(Col, { id: 'g', size: 6 }, 'G'),
|
|
134
|
+
),
|
|
135
|
+
),
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
const row = container.querySelector<HTMLElement>('#row')!
|
|
139
|
+
const col = container.querySelector<HTMLElement>('#g')!
|
|
140
|
+
const cs = getComputedStyle(col)
|
|
141
|
+
// Chromium preserves the `calc(... - 24px)` literal in computed style.
|
|
142
|
+
expect(cs.flexBasis).toMatch(/calc\([^)]+- 24px\)/)
|
|
143
|
+
const colW = col.getBoundingClientRect().width
|
|
144
|
+
const rowW = row.getBoundingClientRect().width
|
|
145
|
+
// Gap subtraction: col width should be less than exactly 50%
|
|
146
|
+
expect(colW).toBeLessThan(rowW * 0.5)
|
|
147
|
+
// …but not dramatically so — confirms it's a subtraction, not a failure
|
|
148
|
+
expect(colW).toBeGreaterThan(rowW * 0.4)
|
|
149
|
+
unmount()
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('responsive `size` array applies a per-breakpoint width at the current viewport', () => {
|
|
153
|
+
// The responsive-per-breakpoint feature: `size={[12, 6, 4]}`
|
|
154
|
+
// means "size 12 on xs, 6 on sm, 4 on md+". At the vitest browser
|
|
155
|
+
// viewport (~414px), xs is the only applicable breakpoint, so
|
|
156
|
+
// size=12 should apply (100% width of Row).
|
|
157
|
+
const { container, unmount } = mountInBrowser(
|
|
158
|
+
h(
|
|
159
|
+
PyreonUI,
|
|
160
|
+
{ theme: gridTheme },
|
|
161
|
+
h(
|
|
162
|
+
Container,
|
|
163
|
+
null,
|
|
164
|
+
h(
|
|
165
|
+
Row,
|
|
166
|
+
{ id: 'row' },
|
|
167
|
+
h(Col, { id: 'r', size: [12, 6, 4] } as any, 'x'),
|
|
168
|
+
),
|
|
169
|
+
),
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
const row = container.querySelector<HTMLElement>('#row')!
|
|
173
|
+
const col = container.querySelector<HTMLElement>('#r')!
|
|
174
|
+
expect(window.innerWidth).toBeLessThan(576) // below the `sm` breakpoint
|
|
175
|
+
const ratio = col.getBoundingClientRect().width / row.getBoundingClientRect().width
|
|
176
|
+
// xs size=12 on 12-column grid → 100% of Row
|
|
177
|
+
expect(ratio).toBeGreaterThan(0.99)
|
|
178
|
+
expect(ratio).toBeLessThan(1.01)
|
|
179
|
+
unmount()
|
|
180
|
+
})
|
|
181
|
+
})
|