@pyreon/coolgrid 0.12.13 → 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.13",
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/typescript": "^0.12.13",
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.13",
48
- "@pyreon/reactivity": "^0.12.13",
49
- "@pyreon/styler": "^0.12.13",
50
- "@pyreon/ui-core": "^0.12.13",
51
- "@pyreon/unistyle": "^0.12.13"
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
- it('returns a VNode', async () => {
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
- it('returns a VNode', async () => {
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
+ })