@elementor/editor-canvas 4.2.0-910 → 4.2.0-912
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/dist/index.js +228 -225
- package/dist/index.mjs +115 -112
- package/package.json +18 -18
- package/src/components/grid-outline/__tests__/grid-outline-overlay.test.tsx +2 -2
- package/src/components/grid-outline/__tests__/grid-outline.test.tsx +28 -27
- package/src/components/grid-outline/{grid-outline-line.tsx → grid-outline-cell.tsx} +11 -10
- package/src/components/grid-outline/grid-outline.tsx +10 -20
- package/src/hooks/__tests__/use-grid-tracks.test.ts +57 -35
- package/src/hooks/use-grid-tracks.ts +26 -25
- package/src/utils/__tests__/grid-outline-utils.test.ts +128 -38
- package/src/utils/grid-outline-utils.ts +52 -26
|
@@ -36,7 +36,7 @@ describe( '<GridOutline />', () => {
|
|
|
36
36
|
expect( svg ).toHaveAttribute( 'height', '200' );
|
|
37
37
|
} );
|
|
38
38
|
|
|
39
|
-
it( 'draws
|
|
39
|
+
it( 'draws one rect per cell for an N×M grid', () => {
|
|
40
40
|
const { container } = renderWithTheme(
|
|
41
41
|
<GridOutline
|
|
42
42
|
tracks={ makeTracks( { columns: [ 100, 100, 100 ], rows: [ 80, 80 ] } ) }
|
|
@@ -45,10 +45,10 @@ describe( '<GridOutline />', () => {
|
|
|
45
45
|
/>
|
|
46
46
|
);
|
|
47
47
|
|
|
48
|
-
expect( container.querySelectorAll( '
|
|
48
|
+
expect( container.querySelectorAll( 'rect' ) ).toHaveLength( 3 * 2 );
|
|
49
49
|
} );
|
|
50
50
|
|
|
51
|
-
it( '
|
|
51
|
+
it( 'keeps the cell count stable with a gap and offsets cells past the gap', () => {
|
|
52
52
|
const { container } = renderWithTheme(
|
|
53
53
|
<GridOutline
|
|
54
54
|
tracks={ makeTracks( {
|
|
@@ -62,14 +62,21 @@ describe( '<GridOutline />', () => {
|
|
|
62
62
|
/>
|
|
63
63
|
);
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
const rects = Array.from( container.querySelectorAll( 'rect' ) );
|
|
66
|
+
expect( rects ).toHaveLength( 3 * 2 );
|
|
67
|
+
|
|
68
|
+
const xs = rects.map( ( rect ) => rect.getAttribute( 'x' ) );
|
|
69
|
+
expect( xs ).toContain( '0.5' );
|
|
70
|
+
expect( xs ).toContain( '110.5' );
|
|
71
|
+
expect( xs ).toContain( '220.5' );
|
|
66
72
|
} );
|
|
67
73
|
|
|
68
|
-
it( 'snaps
|
|
74
|
+
it( 'snaps cell coordinates to half pixels for crisp 1px strokes', () => {
|
|
69
75
|
const { container } = renderWithTheme(
|
|
70
76
|
<GridOutline
|
|
71
77
|
tracks={ makeTracks( {
|
|
72
78
|
columns: [ 100, 100 ],
|
|
79
|
+
rows: [ 80 ],
|
|
73
80
|
padding: { top: 10, right: 10, bottom: 10, left: 10 },
|
|
74
81
|
} ) }
|
|
75
82
|
width={ 220 }
|
|
@@ -77,14 +84,11 @@ describe( '<GridOutline />', () => {
|
|
|
77
84
|
/>
|
|
78
85
|
);
|
|
79
86
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
expect( verticals.map( ( line ) => line.getAttribute( 'x1' ) ) ).toEqual( [ '10.5', '110.5', '210.5' ] );
|
|
87
|
+
const xs = Array.from( container.querySelectorAll( 'rect' ) ).map( ( rect ) => rect.getAttribute( 'x' ) );
|
|
88
|
+
expect( xs ).toEqual( [ '10.5', '110.5' ] );
|
|
85
89
|
} );
|
|
86
90
|
|
|
87
|
-
it( 'spans
|
|
91
|
+
it( 'spans a single full-width cell per row when only rows are defined', () => {
|
|
88
92
|
const { container } = renderWithTheme(
|
|
89
93
|
<GridOutline
|
|
90
94
|
tracks={ makeTracks( {
|
|
@@ -96,36 +100,33 @@ describe( '<GridOutline />', () => {
|
|
|
96
100
|
/>
|
|
97
101
|
);
|
|
98
102
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
for ( const line of horizontals ) {
|
|
105
|
-
expect( line ).toHaveAttribute( 'x1', '4' );
|
|
106
|
-
expect( line ).toHaveAttribute( 'x2', '288' );
|
|
103
|
+
const rects = Array.from( container.querySelectorAll( 'rect' ) );
|
|
104
|
+
expect( rects ).toHaveLength( 2 );
|
|
105
|
+
for ( const rect of rects ) {
|
|
106
|
+
expect( rect ).toHaveAttribute( 'x', '4.5' );
|
|
107
|
+
expect( rect ).toHaveAttribute( 'width', '284' );
|
|
107
108
|
}
|
|
108
109
|
} );
|
|
109
110
|
|
|
110
|
-
it( 'passes the resolved iframe border color through to each
|
|
111
|
+
it( 'passes the resolved iframe border color through to each cell', () => {
|
|
111
112
|
const { container } = renderWithTheme(
|
|
112
113
|
<GridOutline
|
|
113
|
-
tracks={ makeTracks( { columns: [ 100 ], borderColor: '#abcdef' } ) }
|
|
114
|
+
tracks={ makeTracks( { columns: [ 100 ], rows: [ 100 ], borderColor: '#abcdef' } ) }
|
|
114
115
|
width={ 100 }
|
|
115
116
|
height={ 100 }
|
|
116
117
|
/>
|
|
117
118
|
);
|
|
118
119
|
|
|
119
|
-
const
|
|
120
|
-
expect(
|
|
121
|
-
|
|
122
|
-
expect(
|
|
120
|
+
const rects = container.querySelectorAll( 'rect' );
|
|
121
|
+
expect( rects.length ).toBeGreaterThan( 0 );
|
|
122
|
+
rects.forEach( ( rect ) => {
|
|
123
|
+
expect( rect ).toHaveAttribute( 'stroke', '#abcdef' );
|
|
123
124
|
} );
|
|
124
125
|
} );
|
|
125
126
|
|
|
126
|
-
it( 'renders no
|
|
127
|
+
it( 'renders no cells when there are no tracks on either axis', () => {
|
|
127
128
|
const { container } = renderWithTheme( <GridOutline tracks={ makeTracks() } width={ 100 } height={ 100 } /> );
|
|
128
129
|
|
|
129
|
-
expect( container.querySelectorAll( '
|
|
130
|
+
expect( container.querySelectorAll( 'rect' ) ).toHaveLength( 0 );
|
|
130
131
|
} );
|
|
131
132
|
} );
|
|
@@ -4,20 +4,21 @@ const FALLBACK_COLOR = 'rgba(0, 0, 0, 0.12)';
|
|
|
4
4
|
export const DASH = '2 2';
|
|
5
5
|
|
|
6
6
|
type Props = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
11
|
color?: string;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export function
|
|
14
|
+
export function GridOutlineCell( { x, y, width, height, color }: Props ) {
|
|
15
15
|
return (
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
<rect
|
|
17
|
+
x={ x }
|
|
18
|
+
y={ y }
|
|
19
|
+
width={ width }
|
|
20
|
+
height={ height }
|
|
21
|
+
fill="none"
|
|
21
22
|
stroke={ color || FALLBACK_COLOR }
|
|
22
23
|
strokeWidth={ 1 }
|
|
23
24
|
strokeDasharray={ DASH }
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
import { type GridTracks } from '../../hooks/use-grid-tracks';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { computeCellRects, snapToHalfPixel } from '../../utils/grid-outline-utils';
|
|
5
|
+
import { GridOutlineCell } from './grid-outline-cell';
|
|
6
6
|
|
|
7
7
|
type Props = {
|
|
8
8
|
tracks: GridTracks;
|
|
@@ -11,7 +11,7 @@ type Props = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export function GridOutline( { tracks, width, height }: Props ) {
|
|
14
|
-
const
|
|
14
|
+
const cells = computeCellRects( tracks, width, height );
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
17
|
<svg
|
|
@@ -20,23 +20,13 @@ export function GridOutline( { tracks, width, height }: Props ) {
|
|
|
20
20
|
style={ { position: 'absolute', inset: 0, overflow: 'visible' } }
|
|
21
21
|
xmlns="http://www.w3.org/2000/svg"
|
|
22
22
|
>
|
|
23
|
-
{
|
|
24
|
-
<
|
|
25
|
-
key={
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
color={ tracks.borderColor }
|
|
31
|
-
/>
|
|
32
|
-
) ) }
|
|
33
|
-
{ horizontal.map( ( y, i ) => (
|
|
34
|
-
<GridOutlineLine
|
|
35
|
-
key={ `h-${ i }` }
|
|
36
|
-
x1={ left }
|
|
37
|
-
x2={ right }
|
|
38
|
-
y1={ snapToHalfPixel( y ) }
|
|
39
|
-
y2={ snapToHalfPixel( y ) }
|
|
23
|
+
{ cells.map( ( cell, i ) => (
|
|
24
|
+
<GridOutlineCell
|
|
25
|
+
key={ i }
|
|
26
|
+
x={ snapToHalfPixel( cell.x ) }
|
|
27
|
+
y={ snapToHalfPixel( cell.y ) }
|
|
28
|
+
width={ Math.round( cell.width ) }
|
|
29
|
+
height={ Math.round( cell.height ) }
|
|
40
30
|
color={ tracks.borderColor }
|
|
41
31
|
/>
|
|
42
32
|
) ) }
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dispatchWindowEvent } from 'test-utils';
|
|
2
|
+
import { ELEMENT_STYLE_CHANGE_EVENT } from '@elementor/editor-elements';
|
|
3
|
+
import { act, renderHook } from '@testing-library/react';
|
|
2
4
|
|
|
3
5
|
import { useGridTracks } from '../use-grid-tracks';
|
|
4
6
|
|
|
@@ -26,9 +28,12 @@ const DEFAULT_STYLE: Style = {
|
|
|
26
28
|
'--e-a-border-color-bold': '',
|
|
27
29
|
};
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
const DEVICE_MODE_CHANGE_EVENT = 'elementor/device-mode/change';
|
|
32
|
+
|
|
33
|
+
function mockElement( style: Partial< Style > = {} ) {
|
|
30
34
|
const resolved: Style = { ...DEFAULT_STYLE, ...style };
|
|
31
|
-
|
|
35
|
+
|
|
36
|
+
const getComputedStyle = jest.fn().mockImplementation( () => ( {
|
|
32
37
|
gridTemplateColumns: resolved.gridTemplateColumns,
|
|
33
38
|
gridTemplateRows: resolved.gridTemplateRows,
|
|
34
39
|
columnGap: resolved.columnGap,
|
|
@@ -38,13 +43,22 @@ function mockElement( style: Partial< Style > = {} ): HTMLElement {
|
|
|
38
43
|
paddingBottom: resolved.paddingBottom,
|
|
39
44
|
paddingLeft: resolved.paddingLeft,
|
|
40
45
|
getPropertyValue: ( name: string ) => ( name === '--e-a-border-color-bold' ? resolved[ name ] : '' ),
|
|
41
|
-
} );
|
|
46
|
+
} ) );
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
const element = {
|
|
44
49
|
ownerDocument: {
|
|
45
|
-
defaultView: {
|
|
50
|
+
defaultView: {
|
|
51
|
+
getComputedStyle,
|
|
52
|
+
requestAnimationFrame: ( cb: FrameRequestCallback ) => {
|
|
53
|
+
cb( 0 );
|
|
54
|
+
return 1;
|
|
55
|
+
},
|
|
56
|
+
cancelAnimationFrame: jest.fn(),
|
|
57
|
+
} as unknown as Window,
|
|
46
58
|
},
|
|
47
59
|
} as unknown as HTMLElement;
|
|
60
|
+
|
|
61
|
+
return { element, resolved, getComputedStyle };
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
const RECT = new DOMRect( 0, 0, 320, 200 );
|
|
@@ -73,7 +87,7 @@ describe( 'useGridTracks', () => {
|
|
|
73
87
|
} );
|
|
74
88
|
|
|
75
89
|
it( 'parses resolved track lists, gaps, and padding from computed style', () => {
|
|
76
|
-
const element = mockElement( {
|
|
90
|
+
const { element } = mockElement( {
|
|
77
91
|
gridTemplateColumns: '100px 100px 100px',
|
|
78
92
|
gridTemplateRows: '80px 80px',
|
|
79
93
|
columnGap: '10px',
|
|
@@ -96,33 +110,8 @@ describe( 'useGridTracks', () => {
|
|
|
96
110
|
} );
|
|
97
111
|
} );
|
|
98
112
|
|
|
99
|
-
it( 'reads the --e-a-border-color-bold CSS variable from the iframe', () => {
|
|
100
|
-
const element = mockElement( {
|
|
101
|
-
gridTemplateColumns: '100px',
|
|
102
|
-
'--e-a-border-color-bold': ' #d5d8dc ',
|
|
103
|
-
} );
|
|
104
|
-
|
|
105
|
-
const { result } = renderHook( () => useGridTracks( element, RECT ) );
|
|
106
|
-
|
|
107
|
-
expect( result.current.borderColor ).toBe( '#d5d8dc' );
|
|
108
|
-
} );
|
|
109
|
-
|
|
110
|
-
it( 'reports gap as 0 when computed style returns "normal"', () => {
|
|
111
|
-
const element = mockElement( {
|
|
112
|
-
gridTemplateColumns: '100px 100px',
|
|
113
|
-
columnGap: 'normal',
|
|
114
|
-
rowGap: 'normal',
|
|
115
|
-
} );
|
|
116
|
-
|
|
117
|
-
const { result } = renderHook( () => useGridTracks( element, RECT ) );
|
|
118
|
-
|
|
119
|
-
expect( result.current.columnGap ).toBe( 0 );
|
|
120
|
-
expect( result.current.rowGap ).toBe( 0 );
|
|
121
|
-
} );
|
|
122
|
-
|
|
123
113
|
it( 'recomputes when the rect dimensions change', () => {
|
|
124
|
-
const element = mockElement( { gridTemplateColumns: '100px' } );
|
|
125
|
-
const getComputedStyle = element.ownerDocument?.defaultView?.getComputedStyle as jest.Mock;
|
|
114
|
+
const { element, getComputedStyle } = mockElement( { gridTemplateColumns: '100px' } );
|
|
126
115
|
|
|
127
116
|
const { rerender } = renderHook( ( { rect } ) => useGridTracks( element, rect ), {
|
|
128
117
|
initialProps: { rect: new DOMRect( 0, 0, 320, 200 ) },
|
|
@@ -136,8 +125,7 @@ describe( 'useGridTracks', () => {
|
|
|
136
125
|
} );
|
|
137
126
|
|
|
138
127
|
it( 'does not recompute when the same rect dimensions are passed again', () => {
|
|
139
|
-
const element = mockElement( { gridTemplateColumns: '100px' } );
|
|
140
|
-
const getComputedStyle = element.ownerDocument?.defaultView?.getComputedStyle as jest.Mock;
|
|
128
|
+
const { element, getComputedStyle } = mockElement( { gridTemplateColumns: '100px' } );
|
|
141
129
|
|
|
142
130
|
const { rerender } = renderHook( ( { rect } ) => useGridTracks( element, rect ), {
|
|
143
131
|
initialProps: { rect: new DOMRect( 0, 0, 320, 200 ) },
|
|
@@ -149,4 +137,38 @@ describe( 'useGridTracks', () => {
|
|
|
149
137
|
|
|
150
138
|
expect( getComputedStyle.mock.calls.length ).toBe( callsBefore );
|
|
151
139
|
} );
|
|
140
|
+
|
|
141
|
+
it( 'recomputes when a grid style change event fires', () => {
|
|
142
|
+
const mock = mockElement( { gridTemplateColumns: '100px 100px' } );
|
|
143
|
+
|
|
144
|
+
const { result } = renderHook( () => useGridTracks( mock.element, RECT ) );
|
|
145
|
+
|
|
146
|
+
expect( result.current.columns ).toEqual( [ 100, 100 ] );
|
|
147
|
+
|
|
148
|
+
mock.resolved.gridTemplateColumns = '100px 100px 100px 100px';
|
|
149
|
+
|
|
150
|
+
act( () => {
|
|
151
|
+
dispatchWindowEvent( ELEMENT_STYLE_CHANGE_EVENT );
|
|
152
|
+
} );
|
|
153
|
+
|
|
154
|
+
expect( result.current.columns ).toEqual( [ 100, 100, 100, 100 ] );
|
|
155
|
+
} );
|
|
156
|
+
|
|
157
|
+
it( 'recomputes when the device mode changes', () => {
|
|
158
|
+
const mock = mockElement( { gridTemplateRows: '80px', rowGap: '8px' } );
|
|
159
|
+
|
|
160
|
+
const { result } = renderHook( () => useGridTracks( mock.element, RECT ) );
|
|
161
|
+
|
|
162
|
+
expect( result.current.rows ).toEqual( [ 80 ] );
|
|
163
|
+
|
|
164
|
+
mock.resolved.gridTemplateRows = '80px 80px';
|
|
165
|
+
mock.resolved.rowGap = '12px';
|
|
166
|
+
|
|
167
|
+
act( () => {
|
|
168
|
+
dispatchWindowEvent( DEVICE_MODE_CHANGE_EVENT );
|
|
169
|
+
} );
|
|
170
|
+
|
|
171
|
+
expect( result.current.rows ).toEqual( [ 80, 80 ] );
|
|
172
|
+
expect( result.current.rowGap ).toBe( 12 );
|
|
173
|
+
} );
|
|
152
174
|
} );
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { ELEMENT_STYLE_CHANGE_EVENT } from '@elementor/editor-elements';
|
|
3
|
+
import { __privateUseListenTo as useListenTo, windowEvent } from '@elementor/editor-v1-adapters';
|
|
2
4
|
|
|
3
|
-
import {
|
|
5
|
+
import { toGridTracks } from '../utils/grid-outline-utils';
|
|
4
6
|
|
|
5
7
|
export type GridTracks = {
|
|
6
8
|
columns: number[];
|
|
@@ -20,33 +22,32 @@ const EMPTY: GridTracks = {
|
|
|
20
22
|
borderColor: '',
|
|
21
23
|
};
|
|
22
24
|
|
|
25
|
+
const DEVICE_MODE_CHANGE_EVENT = 'elementor/device-mode/change';
|
|
26
|
+
|
|
23
27
|
export function useGridTracks( element: HTMLElement | null, rect: DOMRect ): GridTracks {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const [ tracks, setTracks ] = useState< GridTracks >( EMPTY );
|
|
29
|
+
|
|
30
|
+
const trigger = useListenTo(
|
|
31
|
+
[ windowEvent( ELEMENT_STYLE_CHANGE_EVENT ), windowEvent( DEVICE_MODE_CHANGE_EVENT ) ],
|
|
32
|
+
() => ( {} )
|
|
33
|
+
);
|
|
28
34
|
|
|
29
|
-
|
|
35
|
+
useEffect( () => {
|
|
36
|
+
const previewWindow = element?.ownerDocument?.defaultView;
|
|
30
37
|
|
|
31
|
-
if ( ! previewWindow ) {
|
|
32
|
-
|
|
38
|
+
if ( ! element || ! previewWindow ) {
|
|
39
|
+
setTracks( EMPTY );
|
|
40
|
+
return;
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
rowGap: toPx( computedStyle.rowGap ),
|
|
42
|
-
padding: {
|
|
43
|
-
top: toPx( computedStyle.paddingTop ),
|
|
44
|
-
right: toPx( computedStyle.paddingRight ),
|
|
45
|
-
bottom: toPx( computedStyle.paddingBottom ),
|
|
46
|
-
left: toPx( computedStyle.paddingLeft ),
|
|
47
|
-
},
|
|
48
|
-
borderColor: computedStyle.getPropertyValue( '--e-a-border-color-bold' ).trim(),
|
|
43
|
+
const frame = previewWindow.requestAnimationFrame( () => {
|
|
44
|
+
setTracks( toGridTracks( previewWindow.getComputedStyle( element ) ) );
|
|
45
|
+
} );
|
|
46
|
+
|
|
47
|
+
return () => {
|
|
48
|
+
previewWindow.cancelAnimationFrame( frame );
|
|
49
49
|
};
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
}, [ element, rect.width, rect.height, trigger ] );
|
|
51
|
+
|
|
52
|
+
return tracks;
|
|
52
53
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type GridTracks } from '../../hooks/use-grid-tracks';
|
|
2
|
-
import {
|
|
2
|
+
import { computeCellRects, parseTrackList, snapToHalfPixel, toGridTracks, toPx } from '../grid-outline-utils';
|
|
3
3
|
|
|
4
4
|
function makeTracks( partial: Partial< GridTracks > = {} ): GridTracks {
|
|
5
5
|
return {
|
|
@@ -13,74 +13,77 @@ function makeTracks( partial: Partial< GridTracks > = {} ): GridTracks {
|
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
describe( '
|
|
17
|
-
it( 'returns
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
expect( geometry.vertical ).toEqual( [] );
|
|
21
|
-
expect( geometry.horizontal ).toEqual( [] );
|
|
16
|
+
describe( 'computeCellRects', () => {
|
|
17
|
+
it( 'returns no cells when there are no tracks on either axis', () => {
|
|
18
|
+
expect( computeCellRects( makeTracks(), 100, 100 ) ).toEqual( [] );
|
|
22
19
|
} );
|
|
23
20
|
|
|
24
|
-
it( '
|
|
21
|
+
it( 'produces one rect per cell offset by the padding', () => {
|
|
25
22
|
const tracks = makeTracks( {
|
|
26
23
|
columns: [ 100, 100 ],
|
|
27
24
|
rows: [ 80, 80 ],
|
|
28
25
|
padding: { top: 5, right: 0, bottom: 0, left: 20 },
|
|
29
26
|
} );
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
expect( computeCellRects( tracks, 300, 300 ) ).toEqual( [
|
|
29
|
+
{ x: 20, y: 5, width: 100, height: 80 },
|
|
30
|
+
{ x: 120, y: 5, width: 100, height: 80 },
|
|
31
|
+
{ x: 20, y: 85, width: 100, height: 80 },
|
|
32
|
+
{ x: 120, y: 85, width: 100, height: 80 },
|
|
33
|
+
] );
|
|
35
34
|
} );
|
|
36
35
|
|
|
37
|
-
it( '
|
|
36
|
+
it( 'separates cells by the gap so the gap stays empty', () => {
|
|
38
37
|
const tracks = makeTracks( {
|
|
39
38
|
columns: [ 100, 100, 100 ],
|
|
39
|
+
rows: [ 80 ],
|
|
40
40
|
columnGap: 10,
|
|
41
41
|
} );
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
it( 'collapses gap boundaries when the gap is zero', () => {
|
|
50
|
-
const tracks = makeTracks( {
|
|
51
|
-
rows: [ 50, 50, 50 ],
|
|
52
|
-
rowGap: 0,
|
|
53
|
-
} );
|
|
54
|
-
|
|
55
|
-
const geometry = computeOutlineGeometry( tracks, 100, 200 );
|
|
56
|
-
|
|
57
|
-
expect( geometry.horizontal ).toEqual( [ 0, 50, 100, 150 ] );
|
|
43
|
+
expect( computeCellRects( tracks, 400, 100 ) ).toEqual( [
|
|
44
|
+
{ x: 0, y: 0, width: 100, height: 80 },
|
|
45
|
+
{ x: 110, y: 0, width: 100, height: 80 },
|
|
46
|
+
{ x: 220, y: 0, width: 100, height: 80 },
|
|
47
|
+
] );
|
|
58
48
|
} );
|
|
59
49
|
|
|
60
50
|
it( 'handles uneven track sizes', () => {
|
|
61
51
|
const tracks = makeTracks( {
|
|
62
52
|
columns: [ 100, 200, 100 ],
|
|
53
|
+
rows: [ 50 ],
|
|
63
54
|
columnGap: 5,
|
|
64
55
|
padding: { top: 0, right: 0, bottom: 0, left: 10 },
|
|
65
56
|
} );
|
|
66
57
|
|
|
67
|
-
|
|
58
|
+
expect( computeCellRects( tracks, 500, 100 ) ).toEqual( [
|
|
59
|
+
{ x: 10, y: 0, width: 100, height: 50 },
|
|
60
|
+
{ x: 115, y: 0, width: 200, height: 50 },
|
|
61
|
+
{ x: 320, y: 0, width: 100, height: 50 },
|
|
62
|
+
] );
|
|
63
|
+
} );
|
|
68
64
|
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
it( 'spans a single full-width cell per row when only rows are defined', () => {
|
|
66
|
+
const tracks = makeTracks( {
|
|
67
|
+
rows: [ 50, 50 ],
|
|
68
|
+
padding: { top: 8, right: 12, bottom: 6, left: 4 },
|
|
69
|
+
} );
|
|
70
|
+
|
|
71
|
+
expect( computeCellRects( tracks, 300, 120 ) ).toEqual( [
|
|
72
|
+
{ x: 4, y: 8, width: 284, height: 50 },
|
|
73
|
+
{ x: 4, y: 58, width: 284, height: 50 },
|
|
74
|
+
] );
|
|
71
75
|
} );
|
|
72
76
|
|
|
73
|
-
it( '
|
|
77
|
+
it( 'spans a single full-height cell per column when only columns are defined', () => {
|
|
74
78
|
const tracks = makeTracks( {
|
|
79
|
+
columns: [ 100, 100 ],
|
|
75
80
|
padding: { top: 5, right: 8, bottom: 12, left: 20 },
|
|
76
81
|
} );
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
expect( geometry.right ).toBe( 292 );
|
|
83
|
-
expect( geometry.bottom ).toBe( 188 );
|
|
83
|
+
expect( computeCellRects( tracks, 300, 200 ) ).toEqual( [
|
|
84
|
+
{ x: 20, y: 5, width: 100, height: 183 },
|
|
85
|
+
{ x: 120, y: 5, width: 100, height: 183 },
|
|
86
|
+
] );
|
|
84
87
|
} );
|
|
85
88
|
} );
|
|
86
89
|
|
|
@@ -140,3 +143,90 @@ describe( 'toPx', () => {
|
|
|
140
143
|
expect( toPx( input ) ).toBe( expected );
|
|
141
144
|
} );
|
|
142
145
|
} );
|
|
146
|
+
|
|
147
|
+
type ComputedStyleParts = {
|
|
148
|
+
gridTemplateColumns: string;
|
|
149
|
+
gridTemplateRows: string;
|
|
150
|
+
columnGap: string;
|
|
151
|
+
rowGap: string;
|
|
152
|
+
paddingTop: string;
|
|
153
|
+
paddingRight: string;
|
|
154
|
+
paddingBottom: string;
|
|
155
|
+
paddingLeft: string;
|
|
156
|
+
borderColor: string;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
function mockComputedStyle( parts: Partial< ComputedStyleParts > = {} ): CSSStyleDeclaration {
|
|
160
|
+
const resolved: ComputedStyleParts = {
|
|
161
|
+
gridTemplateColumns: 'none',
|
|
162
|
+
gridTemplateRows: 'none',
|
|
163
|
+
columnGap: 'normal',
|
|
164
|
+
rowGap: 'normal',
|
|
165
|
+
paddingTop: '0px',
|
|
166
|
+
paddingRight: '0px',
|
|
167
|
+
paddingBottom: '0px',
|
|
168
|
+
paddingLeft: '0px',
|
|
169
|
+
borderColor: '',
|
|
170
|
+
...parts,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
gridTemplateColumns: resolved.gridTemplateColumns,
|
|
175
|
+
gridTemplateRows: resolved.gridTemplateRows,
|
|
176
|
+
columnGap: resolved.columnGap,
|
|
177
|
+
rowGap: resolved.rowGap,
|
|
178
|
+
paddingTop: resolved.paddingTop,
|
|
179
|
+
paddingRight: resolved.paddingRight,
|
|
180
|
+
paddingBottom: resolved.paddingBottom,
|
|
181
|
+
paddingLeft: resolved.paddingLeft,
|
|
182
|
+
getPropertyValue: ( name: string ) => ( name === '--e-a-border-color-bold' ? resolved.borderColor : '' ),
|
|
183
|
+
} as unknown as CSSStyleDeclaration;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
describe( 'toGridTracks', () => {
|
|
187
|
+
it( 'parses resolved track lists, gaps, and padding from computed style', () => {
|
|
188
|
+
const tracks = toGridTracks(
|
|
189
|
+
mockComputedStyle( {
|
|
190
|
+
gridTemplateColumns: '100px 100px 100px',
|
|
191
|
+
gridTemplateRows: '80px 80px',
|
|
192
|
+
columnGap: '10px',
|
|
193
|
+
rowGap: '8px',
|
|
194
|
+
paddingTop: '5px',
|
|
195
|
+
paddingRight: '6px',
|
|
196
|
+
paddingBottom: '7px',
|
|
197
|
+
paddingLeft: '8px',
|
|
198
|
+
} )
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect( tracks ).toEqual( {
|
|
202
|
+
columns: [ 100, 100, 100 ],
|
|
203
|
+
rows: [ 80, 80 ],
|
|
204
|
+
columnGap: 10,
|
|
205
|
+
rowGap: 8,
|
|
206
|
+
padding: { top: 5, right: 6, bottom: 7, left: 8 },
|
|
207
|
+
borderColor: '',
|
|
208
|
+
} );
|
|
209
|
+
} );
|
|
210
|
+
|
|
211
|
+
it( 'returns empty track lists when the template is "none"', () => {
|
|
212
|
+
const tracks = toGridTracks( mockComputedStyle() );
|
|
213
|
+
|
|
214
|
+
expect( tracks.columns ).toEqual( [] );
|
|
215
|
+
expect( tracks.rows ).toEqual( [] );
|
|
216
|
+
} );
|
|
217
|
+
|
|
218
|
+
it( 'reports gap as 0 when computed style returns "normal"', () => {
|
|
219
|
+
const tracks = toGridTracks(
|
|
220
|
+
mockComputedStyle( { gridTemplateColumns: '100px 100px', columnGap: 'normal', rowGap: 'normal' } )
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect( tracks.columnGap ).toBe( 0 );
|
|
224
|
+
expect( tracks.rowGap ).toBe( 0 );
|
|
225
|
+
} );
|
|
226
|
+
|
|
227
|
+
it( 'trims the --e-a-border-color-bold CSS variable', () => {
|
|
228
|
+
const tracks = toGridTracks( mockComputedStyle( { borderColor: ' #d5d8dc ' } ) );
|
|
229
|
+
|
|
230
|
+
expect( tracks.borderColor ).toBe( '#d5d8dc' );
|
|
231
|
+
} );
|
|
232
|
+
} );
|